diff options
author | Fabian Schempp <fabianschempp@googlemail.com> | 2022-04-18 23:28:08 +0300 |
---|---|---|
committer | Fabian Schempp <fabianschempp@googlemail.com> | 2022-04-18 23:28:08 +0300 |
commit | 01616f9ed2da14b5cad40efac03d1a22dbb759f3 (patch) | |
tree | 4955ad12f260e557aa1c0f724a320e565f8bce5c | |
parent | f06d361da1249c93568153bae88bcdf43b4774a1 (diff) | |
parent | 314b27850ccb6d103cf5c73855187cfc11ec48d8 (diff) |
Merge branch 'master' into soc-2021-porting-modifiers-to-nodes-remesh-voxel
523 files changed, 18119 insertions, 8713 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index ca457ab6b37..cc39429742e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -446,7 +446,7 @@ if(NOT APPLE) endif() option(WITH_CYCLES_HIP_BINARIES "Build Cycles AMD HIP binaries" OFF) - set(CYCLES_HIP_BINARIES_ARCH gfx900 gfx906 gfx1010 gfx1011 gfx1012 gfx1030 gfx1031 gfx1032 gfx1034 CACHE STRING "AMD HIP architectures to build binaries for") + 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) endif() diff --git a/build_files/build_environment/install_deps.sh b/build_files/build_environment/install_deps.sh index 8018ac8ff1a..ecf5d215333 100755 --- a/build_files/build_environment/install_deps.sh +++ b/build_files/build_environment/install_deps.sh @@ -4004,6 +4004,7 @@ install_DEB() { WEBP_DEV="libwebp-dev" check_package_DEB $WEBP_DEV if [ $? -eq 0 ]; then + install_packages_DEB $WEBP_DEV WEBP_USE=true fi @@ -4435,9 +4436,6 @@ install_DEB() { if [ "$VPX_USE" = true ]; then _packages="$_packages $VPX_DEV" fi - if [ "$WEBP_USE" = true ]; then - _packages="$_packages $WEBP_DEV" - fi if [ "$OPUS_USE" = true ]; then _packages="$_packages $OPUS_DEV" fi @@ -4722,6 +4720,7 @@ install_RPM() { WEBP_DEV="libwebp-devel" check_package_RPM $WEBP_DEV if [ $? -eq 0 ]; then + install_packages_RPM $WEBP_DEV WEBP_USE=true fi @@ -5124,9 +5123,6 @@ install_RPM() { if [ "$VPX_USE" = true ]; then _packages="$_packages $VPX_DEV" fi - if [ "$WEBP_USE" = true ]; then - _packages="$_packages $WEBP_DEV" - fi if [ "$OPUS_USE" = true ]; then _packages="$_packages $OPUS_DEV" fi @@ -5300,6 +5296,7 @@ install_ARCH() { WEBP_DEV="libwebp" check_package_ARCH $WEBP_DEV if [ $? -eq 0 ]; then + install_packages_ARCH $WEBP_DEV WEBP_USE=true fi @@ -5704,16 +5701,12 @@ install_ARCH() { if [ "$VPX_USE" = true ]; then _packages="$_packages $VPX_DEV" fi - if [ "$WEBP_USE" = true ]; then - _packages="$_packages $WEBP_DEV" - fi if [ "$OPUS_USE" = true ]; then _packages="$_packages $OPUS_DEV" fi if [ "$MP3LAME_USE" = true ]; then _packages="$_packages $MP3LAME_DEV" fi - install_packages_ARCH $_packages compile_FFmpeg fi @@ -5961,6 +5954,12 @@ print_info() { fi fi + if [ "$WEBP_USE" = true ]; then + _1="-D WITH_IMAGE_WEBP=ON" + PRINT " $_1" + _buildargs="$_buildargs $_1" + fi + if [ -d $INST/openexr ]; then _1="-D OPENEXR_ROOT_DIR=$INST/openexr" PRINT " $_1" diff --git a/build_files/cmake/platform/platform_apple.cmake b/build_files/cmake/platform/platform_apple.cmake index 43ce23081af..cdc9aa91a53 100644 --- a/build_files/cmake/platform/platform_apple.cmake +++ b/build_files/cmake/platform/platform_apple.cmake @@ -214,7 +214,7 @@ if(WITH_SDL) find_package(SDL2) set(SDL_INCLUDE_DIR ${SDL2_INCLUDE_DIRS}) set(SDL_LIBRARY ${SDL2_LIBRARIES}) - string(APPEND PLATFORM_LINKFLAGS " -framework ForceFeedback") + string(APPEND PLATFORM_LINKFLAGS " -framework ForceFeedback -framework GameController -framework CoreHaptics") endif() set(PNG_ROOT ${LIBDIR}/png) diff --git a/extern/fast_float/LICENSE-MIT b/extern/fast_float/LICENSE-MIT new file mode 100644 index 00000000000..2fb2a37ad7f --- /dev/null +++ b/extern/fast_float/LICENSE-MIT @@ -0,0 +1,27 @@ +MIT License + +Copyright (c) 2021 The fast_float authors + +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. diff --git a/extern/fast_float/README.blender b/extern/fast_float/README.blender new file mode 100644 index 00000000000..a584a0511ee --- /dev/null +++ b/extern/fast_float/README.blender @@ -0,0 +1,7 @@ +Project: fast_float +URL: https://github.com/fastfloat/fast_float +License: MIT +Upstream version: 3.4.0 (b7f9d6c) +Local modifications: + +- Took only the fast_float.h header and the license/readme files diff --git a/extern/fast_float/README.md b/extern/fast_float/README.md new file mode 100644 index 00000000000..1e1c06d0a3e --- /dev/null +++ b/extern/fast_float/README.md @@ -0,0 +1,218 @@ +## fast_float number parsing library: 4x faster than strtod + +![Ubuntu 20.04 CI (GCC 9)](https://github.com/lemire/fast_float/workflows/Ubuntu%2020.04%20CI%20(GCC%209)/badge.svg) +![Ubuntu 18.04 CI (GCC 7)](https://github.com/lemire/fast_float/workflows/Ubuntu%2018.04%20CI%20(GCC%207)/badge.svg) +![Alpine Linux](https://github.com/lemire/fast_float/workflows/Alpine%20Linux/badge.svg) +![MSYS2-CI](https://github.com/lemire/fast_float/workflows/MSYS2-CI/badge.svg) +![VS16-CLANG-CI](https://github.com/lemire/fast_float/workflows/VS16-CLANG-CI/badge.svg) +[![VS16-CI](https://github.com/fastfloat/fast_float/actions/workflows/vs16-ci.yml/badge.svg)](https://github.com/fastfloat/fast_float/actions/workflows/vs16-ci.yml) + +The fast_float library provides fast header-only implementations for the C++ from_chars +functions for `float` and `double` types. These functions convert ASCII strings representing +decimal values (e.g., `1.3e10`) into binary types. We provide exact rounding (including +round to even). In our experience, these `fast_float` functions many times faster than comparable number-parsing functions from existing C++ standard libraries. + +Specifically, `fast_float` provides the following two functions with a C++17-like syntax (the library itself only requires C++11): + +```C++ +from_chars_result from_chars(const char* first, const char* last, float& value, ...); +from_chars_result from_chars(const char* first, const char* last, double& value, ...); +``` + +The return type (`from_chars_result`) is defined as the struct: +```C++ +struct from_chars_result { + const char* ptr; + std::errc ec; +}; +``` + +It parses the character sequence [first,last) for a number. It parses floating-point numbers expecting +a locale-independent format equivalent to the C++17 from_chars function. +The resulting floating-point value is the closest floating-point values (using either float or double), +using the "round to even" convention for values that would otherwise fall right in-between two values. +That is, we provide exact parsing according to the IEEE standard. + + +Given a successful parse, the pointer (`ptr`) in the returned value is set to point right after the +parsed number, and the `value` referenced is set to the parsed value. In case of error, the returned +`ec` contains a representative error, otherwise the default (`std::errc()`) value is stored. + +The implementation does not throw and does not allocate memory (e.g., with `new` or `malloc`). + +It will parse infinity and nan values. + +Example: + +``` C++ +#include "fast_float/fast_float.h" +#include <iostream> + +int main() { + const std::string input = "3.1416 xyz "; + double result; + auto answer = fast_float::from_chars(input.data(), input.data()+input.size(), result); + if(answer.ec != std::errc()) { std::cerr << "parsing failure\n"; return EXIT_FAILURE; } + std::cout << "parsed the number " << result << std::endl; + return EXIT_SUCCESS; +} +``` + + +Like the C++17 standard, the `fast_float::from_chars` functions take an optional last argument of +the type `fast_float::chars_format`. It is a bitset value: we check whether +`fmt & fast_float::chars_format::fixed` and `fmt & fast_float::chars_format::scientific` are set +to determine whether we allow the fixed point and scientific notation respectively. +The default is `fast_float::chars_format::general` which allows both `fixed` and `scientific`. + +The library seeks to follow the C++17 (see [20.19.3](http://eel.is/c++draft/charconv.from.chars).(7.1)) specification. +* The `from_chars` function does not skip leading white-space characters. +* [A leading `+` sign](https://en.cppreference.com/w/cpp/utility/from_chars) is forbidden. +* It is generally impossible to represent a decimal value exactly as binary floating-point number (`float` and `double` types). We seek the nearest value. We round to an even mantissa when we are in-between two binary floating-point numbers. + +Furthermore, we have the following restrictions: +* We only support `float` and `double` types at this time. +* We only support the decimal format: we do not support hexadecimal strings. +* For values that are either very large or very small (e.g., `1e9999`), we represent it using the infinity or negative infinity value. + +We support Visual Studio, macOS, Linux, freeBSD. We support big and little endian. We support 32-bit and 64-bit systems. + + + +## Using commas as decimal separator + + +The C++ standard stipulate that `from_chars` has to be locale-independent. In +particular, the decimal separator has to be the period (`.`). However, +some users still want to use the `fast_float` library with in a locale-dependent +manner. Using a separate function called `from_chars_advanced`, we allow the users +to pass a `parse_options` instance which contains a custom decimal separator (e.g., +the comma). You may use it as follows. + +```C++ +#include "fast_float/fast_float.h" +#include <iostream> + +int main() { + const std::string input = "3,1416 xyz "; + double result; + fast_float::parse_options options{fast_float::chars_format::general, ','}; + auto answer = fast_float::from_chars_advanced(input.data(), input.data()+input.size(), result, options); + if((answer.ec != std::errc()) || ((result != 3.1416))) { std::cerr << "parsing failure\n"; return EXIT_FAILURE; } + std::cout << "parsed the number " << result << std::endl; + return EXIT_SUCCESS; +} +``` + + +## Reference + +- Daniel Lemire, [Number Parsing at a Gigabyte per Second](https://arxiv.org/abs/2101.11408), Software: Pratice and Experience 51 (8), 2021. + +## Other programming languages + +- [There is an R binding](https://github.com/eddelbuettel/rcppfastfloat) called `rcppfastfloat`. +- [There is a Rust port of the fast_float library](https://github.com/aldanor/fast-float-rust/) called `fast-float-rust`. +- [There is a Java port of the fast_float library](https://github.com/wrandelshofer/FastDoubleParser) called `FastDoubleParser`. +- [There is a C# port of the fast_float library](https://github.com/CarlVerret/csFastFloat) called `csFastFloat`. + + +## Relation With Other Work + +The fastfloat algorithm is part of the [LLVM standard libraries](https://github.com/llvm/llvm-project/commit/87c016078ad72c46505461e4ff8bfa04819fe7ba). + +The fast_float library provides a performance similar to that of the [fast_double_parser](https://github.com/lemire/fast_double_parser) library but using an updated algorithm reworked from the ground up, and while offering an API more in line with the expectations of C++ programmers. The fast_double_parser library is part of the [Microsoft LightGBM machine-learning framework](https://github.com/microsoft/LightGBM). + +## Users + +The fast_float library is used by [Apache Arrow](https://github.com/apache/arrow/pull/8494) where it multiplied the number parsing speed by two or three times. It is also used by [Yandex ClickHouse](https://github.com/ClickHouse/ClickHouse) and by [Google Jsonnet](https://github.com/google/jsonnet). + + +## How fast is it? + +It can parse random floating-point numbers at a speed of 1 GB/s on some systems. We find that it is often twice as fast as the best available competitor, and many times faster than many standard-library implementations. + +<img src="http://lemire.me/blog/wp-content/uploads/2020/11/fastfloat_speed.png" width="400"> + +``` +$ ./build/benchmarks/benchmark +# parsing random integers in the range [0,1) +volume = 2.09808 MB +netlib : 271.18 MB/s (+/- 1.2 %) 12.93 Mfloat/s +doubleconversion : 225.35 MB/s (+/- 1.2 %) 10.74 Mfloat/s +strtod : 190.94 MB/s (+/- 1.6 %) 9.10 Mfloat/s +abseil : 430.45 MB/s (+/- 2.2 %) 20.52 Mfloat/s +fastfloat : 1042.38 MB/s (+/- 9.9 %) 49.68 Mfloat/s +``` + +See https://github.com/lemire/simple_fastfloat_benchmark for our benchmarking code. + + +## Video + +[![Go Systems 2020](http://img.youtube.com/vi/AVXgvlMeIm4/0.jpg)](http://www.youtube.com/watch?v=AVXgvlMeIm4)<br /> + +## Using as a CMake dependency + +This library is header-only by design. The CMake file provides the `fast_float` target +which is merely a pointer to the `include` directory. + +If you drop the `fast_float` repository in your CMake project, you should be able to use +it in this manner: + +```cmake +add_subdirectory(fast_float) +target_link_libraries(myprogram PUBLIC fast_float) +``` + +Or you may want to retrieve the dependency automatically if you have a sufficiently recent version of CMake (3.11 or better at least): + +```cmake +FetchContent_Declare( + fast_float + GIT_REPOSITORY https://github.com/lemire/fast_float.git + GIT_TAG tags/v1.1.2 + GIT_SHALLOW TRUE) + +FetchContent_MakeAvailable(fast_float) +target_link_libraries(myprogram PUBLIC fast_float) + +``` + +You should change the `GIT_TAG` line so that you recover the version you wish to use. + +## Using as single header + +The script `script/amalgamate.py` may be used to generate a single header +version of the library if so desired. +Just run the script from the root directory of this repository. +You can customize the license type and output file if desired as described in +the command line help. + +You may directly download automatically generated single-header files: + +https://github.com/fastfloat/fast_float/releases/download/v1.1.2/fast_float.h + +## Credit + +Though this work is inspired by many different people, this work benefited especially from exchanges with +Michael Eisel, who motivated the original research with his key insights, and with Nigel Tao who provided +invaluable feedback. Rémy Oudompheng first implemented a fast path we use in the case of long digits. + +The library includes code adapted from Google Wuffs (written by Nigel Tao) which was originally published +under the Apache 2.0 license. + +## License + +<sup> +Licensed under either of <a href="LICENSE-APACHE">Apache License, Version +2.0</a> or <a href="LICENSE-MIT">MIT license</a> at your option. +</sup> + +<br> + +<sub> +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in this repository by you, as defined in the Apache-2.0 license, +shall be dual licensed as above, without any additional terms or conditions. +</sub> diff --git a/extern/fast_float/fast_float.h b/extern/fast_float/fast_float.h new file mode 100644 index 00000000000..2482dfdbd6c --- /dev/null +++ b/extern/fast_float/fast_float.h @@ -0,0 +1,2979 @@ +// fast_float by Daniel Lemire +// fast_float by João Paulo Magalhaes +// +// with contributions from Eugene Golushkov +// with contributions from Maksim Kita +// with contributions from Marcin Wojdyr +// with contributions from Neal Richardson +// with contributions from Tim Paine +// with contributions from Fabio Pellacini +// +// Licensed under the Apache License, Version 2.0, or the +// MIT License at your option. This file may not be copied, +// modified, or distributed except according to those terms. +// +// MIT License Notice +// +// MIT License +// +// Copyright (c) 2021 The fast_float authors +// +// 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. +// +// Apache License (Version 2.0) Notice +// +// Copyright 2021 The fast_float authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// + +#ifndef FASTFLOAT_FAST_FLOAT_H +#define FASTFLOAT_FAST_FLOAT_H + +#include <system_error> + +namespace fast_float { +enum chars_format { + scientific = 1<<0, + fixed = 1<<2, + hex = 1<<3, + general = fixed | scientific +}; + + +struct from_chars_result { + const char *ptr; + std::errc ec; +}; + +struct parse_options { + constexpr explicit parse_options(chars_format fmt = chars_format::general, + char dot = '.') + : format(fmt), decimal_point(dot) {} + + /** Which number formats are accepted */ + chars_format format; + /** The character used as decimal point */ + char decimal_point; +}; + +/** + * This function parses the character sequence [first,last) for a number. It parses floating-point numbers expecting + * a locale-indepent format equivalent to what is used by std::strtod in the default ("C") locale. + * The resulting floating-point value is the closest floating-point values (using either float or double), + * using the "round to even" convention for values that would otherwise fall right in-between two values. + * That is, we provide exact parsing according to the IEEE standard. + * + * Given a successful parse, the pointer (`ptr`) in the returned value is set to point right after the + * parsed number, and the `value` referenced is set to the parsed value. In case of error, the returned + * `ec` contains a representative error, otherwise the default (`std::errc()`) value is stored. + * + * The implementation does not throw and does not allocate memory (e.g., with `new` or `malloc`). + * + * Like the C++17 standard, the `fast_float::from_chars` functions take an optional last argument of + * the type `fast_float::chars_format`. It is a bitset value: we check whether + * `fmt & fast_float::chars_format::fixed` and `fmt & fast_float::chars_format::scientific` are set + * to determine whether we allowe the fixed point and scientific notation respectively. + * The default is `fast_float::chars_format::general` which allows both `fixed` and `scientific`. + */ +template<typename T> +from_chars_result from_chars(const char *first, const char *last, + T &value, chars_format fmt = chars_format::general) noexcept; + +/** + * Like from_chars, but accepts an `options` argument to govern number parsing. + */ +template<typename T> +from_chars_result from_chars_advanced(const char *first, const char *last, + T &value, parse_options options) noexcept; + +} +#endif // FASTFLOAT_FAST_FLOAT_H + +#ifndef FASTFLOAT_FLOAT_COMMON_H +#define FASTFLOAT_FLOAT_COMMON_H + +#include <cfloat> +#include <cstdint> +#include <cassert> +#include <cstring> +#include <type_traits> + +#if (defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) \ + || defined(__amd64) || defined(__aarch64__) || defined(_M_ARM64) \ + || defined(__MINGW64__) \ + || defined(__s390x__) \ + || (defined(__ppc64__) || defined(__PPC64__) || defined(__ppc64le__) || defined(__PPC64LE__)) \ + || defined(__EMSCRIPTEN__)) +#define FASTFLOAT_64BIT +#elif (defined(__i386) || defined(__i386__) || defined(_M_IX86) \ + || defined(__arm__) || defined(_M_ARM) \ + || defined(__MINGW32__)) +#define FASTFLOAT_32BIT +#else + // Need to check incrementally, since SIZE_MAX is a size_t, avoid overflow. + // We can never tell the register width, but the SIZE_MAX is a good approximation. + // UINTPTR_MAX and INTPTR_MAX are optional, so avoid them for max portability. + #if SIZE_MAX == 0xffff + #error Unknown platform (16-bit, unsupported) + #elif SIZE_MAX == 0xffffffff + #define FASTFLOAT_32BIT + #elif SIZE_MAX == 0xffffffffffffffff + #define FASTFLOAT_64BIT + #else + #error Unknown platform (not 32-bit, not 64-bit?) + #endif +#endif + +#if ((defined(_WIN32) || defined(_WIN64)) && !defined(__clang__)) +#include <intrin.h> +#endif + +#if defined(_MSC_VER) && !defined(__clang__) +#define FASTFLOAT_VISUAL_STUDIO 1 +#endif + +#ifdef _WIN32 +#define FASTFLOAT_IS_BIG_ENDIAN 0 +#else +#if defined(__APPLE__) || defined(__FreeBSD__) +#include <machine/endian.h> +#elif defined(sun) || defined(__sun) +#include <sys/byteorder.h> +#else +#include <endian.h> +#endif +# +#ifndef __BYTE_ORDER__ +// safe choice +#define FASTFLOAT_IS_BIG_ENDIAN 0 +#endif +# +#ifndef __ORDER_LITTLE_ENDIAN__ +// safe choice +#define FASTFLOAT_IS_BIG_ENDIAN 0 +#endif +# +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +#define FASTFLOAT_IS_BIG_ENDIAN 0 +#else +#define FASTFLOAT_IS_BIG_ENDIAN 1 +#endif +#endif + +#ifdef FASTFLOAT_VISUAL_STUDIO +#define fastfloat_really_inline __forceinline +#else +#define fastfloat_really_inline inline __attribute__((always_inline)) +#endif + +#ifndef FASTFLOAT_ASSERT +#define FASTFLOAT_ASSERT(x) { if (!(x)) abort(); } +#endif + +#ifndef FASTFLOAT_DEBUG_ASSERT +#include <cassert> +#define FASTFLOAT_DEBUG_ASSERT(x) assert(x) +#endif + +// rust style `try!()` macro, or `?` operator +#define FASTFLOAT_TRY(x) { if (!(x)) return false; } + +namespace fast_float { + +// Compares two ASCII strings in a case insensitive manner. +inline bool fastfloat_strncasecmp(const char *input1, const char *input2, + size_t length) { + char running_diff{0}; + for (size_t i = 0; i < length; i++) { + running_diff |= (input1[i] ^ input2[i]); + } + return (running_diff == 0) || (running_diff == 32); +} + +#ifndef FLT_EVAL_METHOD +#error "FLT_EVAL_METHOD should be defined, please include cfloat." +#endif + +// a pointer and a length to a contiguous block of memory +template <typename T> +struct span { + const T* ptr; + size_t length; + span(const T* _ptr, size_t _length) : ptr(_ptr), length(_length) {} + span() : ptr(nullptr), length(0) {} + + constexpr size_t len() const noexcept { + return length; + } + + const T& operator[](size_t index) const noexcept { + FASTFLOAT_DEBUG_ASSERT(index < length); + return ptr[index]; + } +}; + +struct value128 { + uint64_t low; + uint64_t high; + value128(uint64_t _low, uint64_t _high) : low(_low), high(_high) {} + value128() : low(0), high(0) {} +}; + +/* result might be undefined when input_num is zero */ +fastfloat_really_inline int leading_zeroes(uint64_t input_num) { + assert(input_num > 0); +#ifdef FASTFLOAT_VISUAL_STUDIO + #if defined(_M_X64) || defined(_M_ARM64) + unsigned long leading_zero = 0; + // Search the mask data from most significant bit (MSB) + // to least significant bit (LSB) for a set bit (1). + _BitScanReverse64(&leading_zero, input_num); + return (int)(63 - leading_zero); + #else + int last_bit = 0; + if(input_num & uint64_t(0xffffffff00000000)) input_num >>= 32, last_bit |= 32; + if(input_num & uint64_t( 0xffff0000)) input_num >>= 16, last_bit |= 16; + if(input_num & uint64_t( 0xff00)) input_num >>= 8, last_bit |= 8; + if(input_num & uint64_t( 0xf0)) input_num >>= 4, last_bit |= 4; + if(input_num & uint64_t( 0xc)) input_num >>= 2, last_bit |= 2; + if(input_num & uint64_t( 0x2)) input_num >>= 1, last_bit |= 1; + return 63 - last_bit; + #endif +#else + return __builtin_clzll(input_num); +#endif +} + +#ifdef FASTFLOAT_32BIT + +// slow emulation routine for 32-bit +fastfloat_really_inline uint64_t emulu(uint32_t x, uint32_t y) { + return x * (uint64_t)y; +} + +// slow emulation routine for 32-bit +#if !defined(__MINGW64__) +fastfloat_really_inline uint64_t _umul128(uint64_t ab, uint64_t cd, + uint64_t *hi) { + uint64_t ad = emulu((uint32_t)(ab >> 32), (uint32_t)cd); + uint64_t bd = emulu((uint32_t)ab, (uint32_t)cd); + uint64_t adbc = ad + emulu((uint32_t)ab, (uint32_t)(cd >> 32)); + uint64_t adbc_carry = !!(adbc < ad); + uint64_t lo = bd + (adbc << 32); + *hi = emulu((uint32_t)(ab >> 32), (uint32_t)(cd >> 32)) + (adbc >> 32) + + (adbc_carry << 32) + !!(lo < bd); + return lo; +} +#endif // !__MINGW64__ + +#endif // FASTFLOAT_32BIT + + +// compute 64-bit a*b +fastfloat_really_inline value128 full_multiplication(uint64_t a, + uint64_t b) { + value128 answer; +#ifdef _M_ARM64 + // ARM64 has native support for 64-bit multiplications, no need to emulate + answer.high = __umulh(a, b); + answer.low = a * b; +#elif defined(FASTFLOAT_32BIT) || (defined(_WIN64) && !defined(__clang__)) + answer.low = _umul128(a, b, &answer.high); // _umul128 not available on ARM64 +#elif defined(FASTFLOAT_64BIT) + __uint128_t r = ((__uint128_t)a) * b; + answer.low = uint64_t(r); + answer.high = uint64_t(r >> 64); +#else + #error Not implemented +#endif + return answer; +} + +struct adjusted_mantissa { + uint64_t mantissa{0}; + int32_t power2{0}; // a negative value indicates an invalid result + adjusted_mantissa() = default; + bool operator==(const adjusted_mantissa &o) const { + return mantissa == o.mantissa && power2 == o.power2; + } + bool operator!=(const adjusted_mantissa &o) const { + return mantissa != o.mantissa || power2 != o.power2; + } +}; + +// Bias so we can get the real exponent with an invalid adjusted_mantissa. +constexpr static int32_t invalid_am_bias = -0x8000; + +constexpr static double powers_of_ten_double[] = { + 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11, + 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22}; +constexpr static float powers_of_ten_float[] = {1e0, 1e1, 1e2, 1e3, 1e4, 1e5, + 1e6, 1e7, 1e8, 1e9, 1e10}; + +template <typename T> struct binary_format { + using equiv_uint = typename std::conditional<sizeof(T) == 4, uint32_t, uint64_t>::type; + + static inline constexpr int mantissa_explicit_bits(); + static inline constexpr int minimum_exponent(); + static inline constexpr int infinite_power(); + static inline constexpr int sign_index(); + static inline constexpr int min_exponent_fast_path(); + static inline constexpr int max_exponent_fast_path(); + static inline constexpr int max_exponent_round_to_even(); + static inline constexpr int min_exponent_round_to_even(); + static inline constexpr uint64_t max_mantissa_fast_path(); + static inline constexpr int largest_power_of_ten(); + static inline constexpr int smallest_power_of_ten(); + static inline constexpr T exact_power_of_ten(int64_t power); + static inline constexpr size_t max_digits(); + static inline constexpr equiv_uint exponent_mask(); + static inline constexpr equiv_uint mantissa_mask(); + static inline constexpr equiv_uint hidden_bit_mask(); +}; + +template <> inline constexpr int binary_format<double>::mantissa_explicit_bits() { + return 52; +} +template <> inline constexpr int binary_format<float>::mantissa_explicit_bits() { + return 23; +} + +template <> inline constexpr int binary_format<double>::max_exponent_round_to_even() { + return 23; +} + +template <> inline constexpr int binary_format<float>::max_exponent_round_to_even() { + return 10; +} + +template <> inline constexpr int binary_format<double>::min_exponent_round_to_even() { + return -4; +} + +template <> inline constexpr int binary_format<float>::min_exponent_round_to_even() { + return -17; +} + +template <> inline constexpr int binary_format<double>::minimum_exponent() { + return -1023; +} +template <> inline constexpr int binary_format<float>::minimum_exponent() { + return -127; +} + +template <> inline constexpr int binary_format<double>::infinite_power() { + return 0x7FF; +} +template <> inline constexpr int binary_format<float>::infinite_power() { + return 0xFF; +} + +template <> inline constexpr int binary_format<double>::sign_index() { return 63; } +template <> inline constexpr int binary_format<float>::sign_index() { return 31; } + +template <> inline constexpr int binary_format<double>::min_exponent_fast_path() { +#if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0) + return 0; +#else + return -22; +#endif +} +template <> inline constexpr int binary_format<float>::min_exponent_fast_path() { +#if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0) + return 0; +#else + return -10; +#endif +} + +template <> inline constexpr int binary_format<double>::max_exponent_fast_path() { + return 22; +} +template <> inline constexpr int binary_format<float>::max_exponent_fast_path() { + return 10; +} + +template <> inline constexpr uint64_t binary_format<double>::max_mantissa_fast_path() { + return uint64_t(2) << mantissa_explicit_bits(); +} +template <> inline constexpr uint64_t binary_format<float>::max_mantissa_fast_path() { + return uint64_t(2) << mantissa_explicit_bits(); +} + +template <> +inline constexpr double binary_format<double>::exact_power_of_ten(int64_t power) { + return powers_of_ten_double[power]; +} +template <> +inline constexpr float binary_format<float>::exact_power_of_ten(int64_t power) { + + return powers_of_ten_float[power]; +} + + +template <> +inline constexpr int binary_format<double>::largest_power_of_ten() { + return 308; +} +template <> +inline constexpr int binary_format<float>::largest_power_of_ten() { + return 38; +} + +template <> +inline constexpr int binary_format<double>::smallest_power_of_ten() { + return -342; +} +template <> +inline constexpr int binary_format<float>::smallest_power_of_ten() { + return -65; +} + +template <> inline constexpr size_t binary_format<double>::max_digits() { + return 769; +} +template <> inline constexpr size_t binary_format<float>::max_digits() { + return 114; +} + +template <> inline constexpr binary_format<float>::equiv_uint + binary_format<float>::exponent_mask() { + return 0x7F800000; +} +template <> inline constexpr binary_format<double>::equiv_uint + binary_format<double>::exponent_mask() { + return 0x7FF0000000000000; +} + +template <> inline constexpr binary_format<float>::equiv_uint + binary_format<float>::mantissa_mask() { + return 0x007FFFFF; +} +template <> inline constexpr binary_format<double>::equiv_uint + binary_format<double>::mantissa_mask() { + return 0x000FFFFFFFFFFFFF; +} + +template <> inline constexpr binary_format<float>::equiv_uint + binary_format<float>::hidden_bit_mask() { + return 0x00800000; +} +template <> inline constexpr binary_format<double>::equiv_uint + binary_format<double>::hidden_bit_mask() { + return 0x0010000000000000; +} + +template<typename T> +fastfloat_really_inline void to_float(bool negative, adjusted_mantissa am, T &value) { + uint64_t word = am.mantissa; + word |= uint64_t(am.power2) << binary_format<T>::mantissa_explicit_bits(); + word = negative + ? word | (uint64_t(1) << binary_format<T>::sign_index()) : word; +#if FASTFLOAT_IS_BIG_ENDIAN == 1 + if (std::is_same<T, float>::value) { + ::memcpy(&value, (char *)&word + 4, sizeof(T)); // extract value at offset 4-7 if float on big-endian + } else { + ::memcpy(&value, &word, sizeof(T)); + } +#else + // For little-endian systems: + ::memcpy(&value, &word, sizeof(T)); +#endif +} + +} // namespace fast_float + +#endif + +#ifndef FASTFLOAT_ASCII_NUMBER_H +#define FASTFLOAT_ASCII_NUMBER_H + +#include <cctype> +#include <cstdint> +#include <cstring> +#include <iterator> + + +namespace fast_float { + +// Next function can be micro-optimized, but compilers are entirely +// able to optimize it well. +fastfloat_really_inline bool is_integer(char c) noexcept { return c >= '0' && c <= '9'; } + +fastfloat_really_inline uint64_t byteswap(uint64_t val) { + return (val & 0xFF00000000000000) >> 56 + | (val & 0x00FF000000000000) >> 40 + | (val & 0x0000FF0000000000) >> 24 + | (val & 0x000000FF00000000) >> 8 + | (val & 0x00000000FF000000) << 8 + | (val & 0x0000000000FF0000) << 24 + | (val & 0x000000000000FF00) << 40 + | (val & 0x00000000000000FF) << 56; +} + +fastfloat_really_inline uint64_t read_u64(const char *chars) { + uint64_t val; + ::memcpy(&val, chars, sizeof(uint64_t)); +#if FASTFLOAT_IS_BIG_ENDIAN == 1 + // Need to read as-if the number was in little-endian order. + val = byteswap(val); +#endif + return val; +} + +fastfloat_really_inline void write_u64(uint8_t *chars, uint64_t val) { +#if FASTFLOAT_IS_BIG_ENDIAN == 1 + // Need to read as-if the number was in little-endian order. + val = byteswap(val); +#endif + ::memcpy(chars, &val, sizeof(uint64_t)); +} + +// credit @aqrit +fastfloat_really_inline uint32_t parse_eight_digits_unrolled(uint64_t val) { + const uint64_t mask = 0x000000FF000000FF; + const uint64_t mul1 = 0x000F424000000064; // 100 + (1000000ULL << 32) + const uint64_t mul2 = 0x0000271000000001; // 1 + (10000ULL << 32) + val -= 0x3030303030303030; + val = (val * 10) + (val >> 8); // val = (val * 2561) >> 8; + val = (((val & mask) * mul1) + (((val >> 16) & mask) * mul2)) >> 32; + return uint32_t(val); +} + +fastfloat_really_inline uint32_t parse_eight_digits_unrolled(const char *chars) noexcept { + return parse_eight_digits_unrolled(read_u64(chars)); +} + +// credit @aqrit +fastfloat_really_inline bool is_made_of_eight_digits_fast(uint64_t val) noexcept { + return !((((val + 0x4646464646464646) | (val - 0x3030303030303030)) & + 0x8080808080808080)); +} + +fastfloat_really_inline bool is_made_of_eight_digits_fast(const char *chars) noexcept { + return is_made_of_eight_digits_fast(read_u64(chars)); +} + +typedef span<const char> byte_span; + +struct parsed_number_string { + int64_t exponent{0}; + uint64_t mantissa{0}; + const char *lastmatch{nullptr}; + bool negative{false}; + bool valid{false}; + bool too_many_digits{false}; + // contains the range of the significant digits + byte_span integer{}; // non-nullable + byte_span fraction{}; // nullable +}; + +// Assuming that you use no more than 19 digits, this will +// parse an ASCII string. +fastfloat_really_inline +parsed_number_string parse_number_string(const char *p, const char *pend, parse_options options) noexcept { + const chars_format fmt = options.format; + const char decimal_point = options.decimal_point; + + parsed_number_string answer; + answer.valid = false; + answer.too_many_digits = false; + answer.negative = (*p == '-'); + if (*p == '-') { // C++17 20.19.3.(7.1) explicitly forbids '+' sign here + ++p; + if (p == pend) { + return answer; + } + if (!is_integer(*p) && (*p != decimal_point)) { // a sign must be followed by an integer or the dot + return answer; + } + } + const char *const start_digits = p; + + uint64_t i = 0; // an unsigned int avoids signed overflows (which are bad) + + while ((std::distance(p, pend) >= 8) && is_made_of_eight_digits_fast(p)) { + i = i * 100000000 + parse_eight_digits_unrolled(p); // in rare cases, this will overflow, but that's ok + p += 8; + } + while ((p != pend) && is_integer(*p)) { + // a multiplication by 10 is cheaper than an arbitrary integer + // multiplication + i = 10 * i + + uint64_t(*p - '0'); // might overflow, we will handle the overflow later + ++p; + } + const char *const end_of_integer_part = p; + int64_t digit_count = int64_t(end_of_integer_part - start_digits); + answer.integer = byte_span(start_digits, size_t(digit_count)); + int64_t exponent = 0; + if ((p != pend) && (*p == decimal_point)) { + ++p; + const char* before = p; + // can occur at most twice without overflowing, but let it occur more, since + // for integers with many digits, digit parsing is the primary bottleneck. + while ((std::distance(p, pend) >= 8) && is_made_of_eight_digits_fast(p)) { + i = i * 100000000 + parse_eight_digits_unrolled(p); // in rare cases, this will overflow, but that's ok + p += 8; + } + while ((p != pend) && is_integer(*p)) { + uint8_t digit = uint8_t(*p - '0'); + ++p; + i = i * 10 + digit; // in rare cases, this will overflow, but that's ok + } + exponent = before - p; + answer.fraction = byte_span(before, size_t(p - before)); + digit_count -= exponent; + } + // we must have encountered at least one integer! + if (digit_count == 0) { + return answer; + } + int64_t exp_number = 0; // explicit exponential part + if ((fmt & chars_format::scientific) && (p != pend) && (('e' == *p) || ('E' == *p))) { + const char * location_of_e = p; + ++p; + bool neg_exp = false; + if ((p != pend) && ('-' == *p)) { + neg_exp = true; + ++p; + } else if ((p != pend) && ('+' == *p)) { // '+' on exponent is allowed by C++17 20.19.3.(7.1) + ++p; + } + if ((p == pend) || !is_integer(*p)) { + if(!(fmt & chars_format::fixed)) { + // We are in error. + return answer; + } + // Otherwise, we will be ignoring the 'e'. + p = location_of_e; + } else { + while ((p != pend) && is_integer(*p)) { + uint8_t digit = uint8_t(*p - '0'); + if (exp_number < 0x10000000) { + exp_number = 10 * exp_number + digit; + } + ++p; + } + if(neg_exp) { exp_number = - exp_number; } + exponent += exp_number; + } + } else { + // If it scientific and not fixed, we have to bail out. + if((fmt & chars_format::scientific) && !(fmt & chars_format::fixed)) { return answer; } + } + answer.lastmatch = p; + answer.valid = true; + + // If we frequently had to deal with long strings of digits, + // we could extend our code by using a 128-bit integer instead + // of a 64-bit integer. However, this is uncommon. + // + // We can deal with up to 19 digits. + if (digit_count > 19) { // this is uncommon + // It is possible that the integer had an overflow. + // We have to handle the case where we have 0.0000somenumber. + // We need to be mindful of the case where we only have zeroes... + // E.g., 0.000000000...000. + const char *start = start_digits; + while ((start != pend) && (*start == '0' || *start == decimal_point)) { + if(*start == '0') { digit_count --; } + start++; + } + if (digit_count > 19) { + answer.too_many_digits = true; + // Let us start again, this time, avoiding overflows. + // We don't need to check if is_integer, since we use the + // pre-tokenized spans from above. + i = 0; + p = answer.integer.ptr; + const char* int_end = p + answer.integer.len(); + const uint64_t minimal_nineteen_digit_integer{1000000000000000000}; + while((i < minimal_nineteen_digit_integer) && (p != int_end)) { + i = i * 10 + uint64_t(*p - '0'); + ++p; + } + if (i >= minimal_nineteen_digit_integer) { // We have a big integers + exponent = end_of_integer_part - p + exp_number; + } else { // We have a value with a fractional component. + p = answer.fraction.ptr; + const char* frac_end = p + answer.fraction.len(); + while((i < minimal_nineteen_digit_integer) && (p != frac_end)) { + i = i * 10 + uint64_t(*p - '0'); + ++p; + } + exponent = answer.fraction.ptr - p + exp_number; + } + // We have now corrected both exponent and i, to a truncated value + } + } + answer.exponent = exponent; + answer.mantissa = i; + return answer; +} + +} // namespace fast_float + +#endif + +#ifndef FASTFLOAT_FAST_TABLE_H +#define FASTFLOAT_FAST_TABLE_H + +#include <cstdint> + +namespace fast_float { + +/** + * When mapping numbers from decimal to binary, + * we go from w * 10^q to m * 2^p but we have + * 10^q = 5^q * 2^q, so effectively + * we are trying to match + * w * 2^q * 5^q to m * 2^p. Thus the powers of two + * are not a concern since they can be represented + * exactly using the binary notation, only the powers of five + * affect the binary significand. + */ + +/** + * The smallest non-zero float (binary64) is 2^−1074. + * We take as input numbers of the form w x 10^q where w < 2^64. + * We have that w * 10^-343 < 2^(64-344) 5^-343 < 2^-1076. + * However, we have that + * (2^64-1) * 10^-342 = (2^64-1) * 2^-342 * 5^-342 > 2^−1074. + * Thus it is possible for a number of the form w * 10^-342 where + * w is a 64-bit value to be a non-zero floating-point number. + ********* + * Any number of form w * 10^309 where w>= 1 is going to be + * infinite in binary64 so we never need to worry about powers + * of 5 greater than 308. + */ +template <class unused = void> +struct powers_template { + +constexpr static int smallest_power_of_five = binary_format<double>::smallest_power_of_ten(); +constexpr static int largest_power_of_five = binary_format<double>::largest_power_of_ten(); +constexpr static int number_of_entries = 2 * (largest_power_of_five - smallest_power_of_five + 1); +// Powers of five from 5^-342 all the way to 5^308 rounded toward one. +static const uint64_t power_of_five_128[number_of_entries]; +}; + +template <class unused> +const uint64_t powers_template<unused>::power_of_five_128[number_of_entries] = { + 0xeef453d6923bd65a,0x113faa2906a13b3f, + 0x9558b4661b6565f8,0x4ac7ca59a424c507, + 0xbaaee17fa23ebf76,0x5d79bcf00d2df649, + 0xe95a99df8ace6f53,0xf4d82c2c107973dc, + 0x91d8a02bb6c10594,0x79071b9b8a4be869, + 0xb64ec836a47146f9,0x9748e2826cdee284, + 0xe3e27a444d8d98b7,0xfd1b1b2308169b25, + 0x8e6d8c6ab0787f72,0xfe30f0f5e50e20f7, + 0xb208ef855c969f4f,0xbdbd2d335e51a935, + 0xde8b2b66b3bc4723,0xad2c788035e61382, + 0x8b16fb203055ac76,0x4c3bcb5021afcc31, + 0xaddcb9e83c6b1793,0xdf4abe242a1bbf3d, + 0xd953e8624b85dd78,0xd71d6dad34a2af0d, + 0x87d4713d6f33aa6b,0x8672648c40e5ad68, + 0xa9c98d8ccb009506,0x680efdaf511f18c2, + 0xd43bf0effdc0ba48,0x212bd1b2566def2, + 0x84a57695fe98746d,0x14bb630f7604b57, + 0xa5ced43b7e3e9188,0x419ea3bd35385e2d, + 0xcf42894a5dce35ea,0x52064cac828675b9, + 0x818995ce7aa0e1b2,0x7343efebd1940993, + 0xa1ebfb4219491a1f,0x1014ebe6c5f90bf8, + 0xca66fa129f9b60a6,0xd41a26e077774ef6, + 0xfd00b897478238d0,0x8920b098955522b4, + 0x9e20735e8cb16382,0x55b46e5f5d5535b0, + 0xc5a890362fddbc62,0xeb2189f734aa831d, + 0xf712b443bbd52b7b,0xa5e9ec7501d523e4, + 0x9a6bb0aa55653b2d,0x47b233c92125366e, + 0xc1069cd4eabe89f8,0x999ec0bb696e840a, + 0xf148440a256e2c76,0xc00670ea43ca250d, + 0x96cd2a865764dbca,0x380406926a5e5728, + 0xbc807527ed3e12bc,0xc605083704f5ecf2, + 0xeba09271e88d976b,0xf7864a44c633682e, + 0x93445b8731587ea3,0x7ab3ee6afbe0211d, + 0xb8157268fdae9e4c,0x5960ea05bad82964, + 0xe61acf033d1a45df,0x6fb92487298e33bd, + 0x8fd0c16206306bab,0xa5d3b6d479f8e056, + 0xb3c4f1ba87bc8696,0x8f48a4899877186c, + 0xe0b62e2929aba83c,0x331acdabfe94de87, + 0x8c71dcd9ba0b4925,0x9ff0c08b7f1d0b14, + 0xaf8e5410288e1b6f,0x7ecf0ae5ee44dd9, + 0xdb71e91432b1a24a,0xc9e82cd9f69d6150, + 0x892731ac9faf056e,0xbe311c083a225cd2, + 0xab70fe17c79ac6ca,0x6dbd630a48aaf406, + 0xd64d3d9db981787d,0x92cbbccdad5b108, + 0x85f0468293f0eb4e,0x25bbf56008c58ea5, + 0xa76c582338ed2621,0xaf2af2b80af6f24e, + 0xd1476e2c07286faa,0x1af5af660db4aee1, + 0x82cca4db847945ca,0x50d98d9fc890ed4d, + 0xa37fce126597973c,0xe50ff107bab528a0, + 0xcc5fc196fefd7d0c,0x1e53ed49a96272c8, + 0xff77b1fcbebcdc4f,0x25e8e89c13bb0f7a, + 0x9faacf3df73609b1,0x77b191618c54e9ac, + 0xc795830d75038c1d,0xd59df5b9ef6a2417, + 0xf97ae3d0d2446f25,0x4b0573286b44ad1d, + 0x9becce62836ac577,0x4ee367f9430aec32, + 0xc2e801fb244576d5,0x229c41f793cda73f, + 0xf3a20279ed56d48a,0x6b43527578c1110f, + 0x9845418c345644d6,0x830a13896b78aaa9, + 0xbe5691ef416bd60c,0x23cc986bc656d553, + 0xedec366b11c6cb8f,0x2cbfbe86b7ec8aa8, + 0x94b3a202eb1c3f39,0x7bf7d71432f3d6a9, + 0xb9e08a83a5e34f07,0xdaf5ccd93fb0cc53, + 0xe858ad248f5c22c9,0xd1b3400f8f9cff68, + 0x91376c36d99995be,0x23100809b9c21fa1, + 0xb58547448ffffb2d,0xabd40a0c2832a78a, + 0xe2e69915b3fff9f9,0x16c90c8f323f516c, + 0x8dd01fad907ffc3b,0xae3da7d97f6792e3, + 0xb1442798f49ffb4a,0x99cd11cfdf41779c, + 0xdd95317f31c7fa1d,0x40405643d711d583, + 0x8a7d3eef7f1cfc52,0x482835ea666b2572, + 0xad1c8eab5ee43b66,0xda3243650005eecf, + 0xd863b256369d4a40,0x90bed43e40076a82, + 0x873e4f75e2224e68,0x5a7744a6e804a291, + 0xa90de3535aaae202,0x711515d0a205cb36, + 0xd3515c2831559a83,0xd5a5b44ca873e03, + 0x8412d9991ed58091,0xe858790afe9486c2, + 0xa5178fff668ae0b6,0x626e974dbe39a872, + 0xce5d73ff402d98e3,0xfb0a3d212dc8128f, + 0x80fa687f881c7f8e,0x7ce66634bc9d0b99, + 0xa139029f6a239f72,0x1c1fffc1ebc44e80, + 0xc987434744ac874e,0xa327ffb266b56220, + 0xfbe9141915d7a922,0x4bf1ff9f0062baa8, + 0x9d71ac8fada6c9b5,0x6f773fc3603db4a9, + 0xc4ce17b399107c22,0xcb550fb4384d21d3, + 0xf6019da07f549b2b,0x7e2a53a146606a48, + 0x99c102844f94e0fb,0x2eda7444cbfc426d, + 0xc0314325637a1939,0xfa911155fefb5308, + 0xf03d93eebc589f88,0x793555ab7eba27ca, + 0x96267c7535b763b5,0x4bc1558b2f3458de, + 0xbbb01b9283253ca2,0x9eb1aaedfb016f16, + 0xea9c227723ee8bcb,0x465e15a979c1cadc, + 0x92a1958a7675175f,0xbfacd89ec191ec9, + 0xb749faed14125d36,0xcef980ec671f667b, + 0xe51c79a85916f484,0x82b7e12780e7401a, + 0x8f31cc0937ae58d2,0xd1b2ecb8b0908810, + 0xb2fe3f0b8599ef07,0x861fa7e6dcb4aa15, + 0xdfbdcece67006ac9,0x67a791e093e1d49a, + 0x8bd6a141006042bd,0xe0c8bb2c5c6d24e0, + 0xaecc49914078536d,0x58fae9f773886e18, + 0xda7f5bf590966848,0xaf39a475506a899e, + 0x888f99797a5e012d,0x6d8406c952429603, + 0xaab37fd7d8f58178,0xc8e5087ba6d33b83, + 0xd5605fcdcf32e1d6,0xfb1e4a9a90880a64, + 0x855c3be0a17fcd26,0x5cf2eea09a55067f, + 0xa6b34ad8c9dfc06f,0xf42faa48c0ea481e, + 0xd0601d8efc57b08b,0xf13b94daf124da26, + 0x823c12795db6ce57,0x76c53d08d6b70858, + 0xa2cb1717b52481ed,0x54768c4b0c64ca6e, + 0xcb7ddcdda26da268,0xa9942f5dcf7dfd09, + 0xfe5d54150b090b02,0xd3f93b35435d7c4c, + 0x9efa548d26e5a6e1,0xc47bc5014a1a6daf, + 0xc6b8e9b0709f109a,0x359ab6419ca1091b, + 0xf867241c8cc6d4c0,0xc30163d203c94b62, + 0x9b407691d7fc44f8,0x79e0de63425dcf1d, + 0xc21094364dfb5636,0x985915fc12f542e4, + 0xf294b943e17a2bc4,0x3e6f5b7b17b2939d, + 0x979cf3ca6cec5b5a,0xa705992ceecf9c42, + 0xbd8430bd08277231,0x50c6ff782a838353, + 0xece53cec4a314ebd,0xa4f8bf5635246428, + 0x940f4613ae5ed136,0x871b7795e136be99, + 0xb913179899f68584,0x28e2557b59846e3f, + 0xe757dd7ec07426e5,0x331aeada2fe589cf, + 0x9096ea6f3848984f,0x3ff0d2c85def7621, + 0xb4bca50b065abe63,0xfed077a756b53a9, + 0xe1ebce4dc7f16dfb,0xd3e8495912c62894, + 0x8d3360f09cf6e4bd,0x64712dd7abbbd95c, + 0xb080392cc4349dec,0xbd8d794d96aacfb3, + 0xdca04777f541c567,0xecf0d7a0fc5583a0, + 0x89e42caaf9491b60,0xf41686c49db57244, + 0xac5d37d5b79b6239,0x311c2875c522ced5, + 0xd77485cb25823ac7,0x7d633293366b828b, + 0x86a8d39ef77164bc,0xae5dff9c02033197, + 0xa8530886b54dbdeb,0xd9f57f830283fdfc, + 0xd267caa862a12d66,0xd072df63c324fd7b, + 0x8380dea93da4bc60,0x4247cb9e59f71e6d, + 0xa46116538d0deb78,0x52d9be85f074e608, + 0xcd795be870516656,0x67902e276c921f8b, + 0x806bd9714632dff6,0xba1cd8a3db53b6, + 0xa086cfcd97bf97f3,0x80e8a40eccd228a4, + 0xc8a883c0fdaf7df0,0x6122cd128006b2cd, + 0xfad2a4b13d1b5d6c,0x796b805720085f81, + 0x9cc3a6eec6311a63,0xcbe3303674053bb0, + 0xc3f490aa77bd60fc,0xbedbfc4411068a9c, + 0xf4f1b4d515acb93b,0xee92fb5515482d44, + 0x991711052d8bf3c5,0x751bdd152d4d1c4a, + 0xbf5cd54678eef0b6,0xd262d45a78a0635d, + 0xef340a98172aace4,0x86fb897116c87c34, + 0x9580869f0e7aac0e,0xd45d35e6ae3d4da0, + 0xbae0a846d2195712,0x8974836059cca109, + 0xe998d258869facd7,0x2bd1a438703fc94b, + 0x91ff83775423cc06,0x7b6306a34627ddcf, + 0xb67f6455292cbf08,0x1a3bc84c17b1d542, + 0xe41f3d6a7377eeca,0x20caba5f1d9e4a93, + 0x8e938662882af53e,0x547eb47b7282ee9c, + 0xb23867fb2a35b28d,0xe99e619a4f23aa43, + 0xdec681f9f4c31f31,0x6405fa00e2ec94d4, + 0x8b3c113c38f9f37e,0xde83bc408dd3dd04, + 0xae0b158b4738705e,0x9624ab50b148d445, + 0xd98ddaee19068c76,0x3badd624dd9b0957, + 0x87f8a8d4cfa417c9,0xe54ca5d70a80e5d6, + 0xa9f6d30a038d1dbc,0x5e9fcf4ccd211f4c, + 0xd47487cc8470652b,0x7647c3200069671f, + 0x84c8d4dfd2c63f3b,0x29ecd9f40041e073, + 0xa5fb0a17c777cf09,0xf468107100525890, + 0xcf79cc9db955c2cc,0x7182148d4066eeb4, + 0x81ac1fe293d599bf,0xc6f14cd848405530, + 0xa21727db38cb002f,0xb8ada00e5a506a7c, + 0xca9cf1d206fdc03b,0xa6d90811f0e4851c, + 0xfd442e4688bd304a,0x908f4a166d1da663, + 0x9e4a9cec15763e2e,0x9a598e4e043287fe, + 0xc5dd44271ad3cdba,0x40eff1e1853f29fd, + 0xf7549530e188c128,0xd12bee59e68ef47c, + 0x9a94dd3e8cf578b9,0x82bb74f8301958ce, + 0xc13a148e3032d6e7,0xe36a52363c1faf01, + 0xf18899b1bc3f8ca1,0xdc44e6c3cb279ac1, + 0x96f5600f15a7b7e5,0x29ab103a5ef8c0b9, + 0xbcb2b812db11a5de,0x7415d448f6b6f0e7, + 0xebdf661791d60f56,0x111b495b3464ad21, + 0x936b9fcebb25c995,0xcab10dd900beec34, + 0xb84687c269ef3bfb,0x3d5d514f40eea742, + 0xe65829b3046b0afa,0xcb4a5a3112a5112, + 0x8ff71a0fe2c2e6dc,0x47f0e785eaba72ab, + 0xb3f4e093db73a093,0x59ed216765690f56, + 0xe0f218b8d25088b8,0x306869c13ec3532c, + 0x8c974f7383725573,0x1e414218c73a13fb, + 0xafbd2350644eeacf,0xe5d1929ef90898fa, + 0xdbac6c247d62a583,0xdf45f746b74abf39, + 0x894bc396ce5da772,0x6b8bba8c328eb783, + 0xab9eb47c81f5114f,0x66ea92f3f326564, + 0xd686619ba27255a2,0xc80a537b0efefebd, + 0x8613fd0145877585,0xbd06742ce95f5f36, + 0xa798fc4196e952e7,0x2c48113823b73704, + 0xd17f3b51fca3a7a0,0xf75a15862ca504c5, + 0x82ef85133de648c4,0x9a984d73dbe722fb, + 0xa3ab66580d5fdaf5,0xc13e60d0d2e0ebba, + 0xcc963fee10b7d1b3,0x318df905079926a8, + 0xffbbcfe994e5c61f,0xfdf17746497f7052, + 0x9fd561f1fd0f9bd3,0xfeb6ea8bedefa633, + 0xc7caba6e7c5382c8,0xfe64a52ee96b8fc0, + 0xf9bd690a1b68637b,0x3dfdce7aa3c673b0, + 0x9c1661a651213e2d,0x6bea10ca65c084e, + 0xc31bfa0fe5698db8,0x486e494fcff30a62, + 0xf3e2f893dec3f126,0x5a89dba3c3efccfa, + 0x986ddb5c6b3a76b7,0xf89629465a75e01c, + 0xbe89523386091465,0xf6bbb397f1135823, + 0xee2ba6c0678b597f,0x746aa07ded582e2c, + 0x94db483840b717ef,0xa8c2a44eb4571cdc, + 0xba121a4650e4ddeb,0x92f34d62616ce413, + 0xe896a0d7e51e1566,0x77b020baf9c81d17, + 0x915e2486ef32cd60,0xace1474dc1d122e, + 0xb5b5ada8aaff80b8,0xd819992132456ba, + 0xe3231912d5bf60e6,0x10e1fff697ed6c69, + 0x8df5efabc5979c8f,0xca8d3ffa1ef463c1, + 0xb1736b96b6fd83b3,0xbd308ff8a6b17cb2, + 0xddd0467c64bce4a0,0xac7cb3f6d05ddbde, + 0x8aa22c0dbef60ee4,0x6bcdf07a423aa96b, + 0xad4ab7112eb3929d,0x86c16c98d2c953c6, + 0xd89d64d57a607744,0xe871c7bf077ba8b7, + 0x87625f056c7c4a8b,0x11471cd764ad4972, + 0xa93af6c6c79b5d2d,0xd598e40d3dd89bcf, + 0xd389b47879823479,0x4aff1d108d4ec2c3, + 0x843610cb4bf160cb,0xcedf722a585139ba, + 0xa54394fe1eedb8fe,0xc2974eb4ee658828, + 0xce947a3da6a9273e,0x733d226229feea32, + 0x811ccc668829b887,0x806357d5a3f525f, + 0xa163ff802a3426a8,0xca07c2dcb0cf26f7, + 0xc9bcff6034c13052,0xfc89b393dd02f0b5, + 0xfc2c3f3841f17c67,0xbbac2078d443ace2, + 0x9d9ba7832936edc0,0xd54b944b84aa4c0d, + 0xc5029163f384a931,0xa9e795e65d4df11, + 0xf64335bcf065d37d,0x4d4617b5ff4a16d5, + 0x99ea0196163fa42e,0x504bced1bf8e4e45, + 0xc06481fb9bcf8d39,0xe45ec2862f71e1d6, + 0xf07da27a82c37088,0x5d767327bb4e5a4c, + 0x964e858c91ba2655,0x3a6a07f8d510f86f, + 0xbbe226efb628afea,0x890489f70a55368b, + 0xeadab0aba3b2dbe5,0x2b45ac74ccea842e, + 0x92c8ae6b464fc96f,0x3b0b8bc90012929d, + 0xb77ada0617e3bbcb,0x9ce6ebb40173744, + 0xe55990879ddcaabd,0xcc420a6a101d0515, + 0x8f57fa54c2a9eab6,0x9fa946824a12232d, + 0xb32df8e9f3546564,0x47939822dc96abf9, + 0xdff9772470297ebd,0x59787e2b93bc56f7, + 0x8bfbea76c619ef36,0x57eb4edb3c55b65a, + 0xaefae51477a06b03,0xede622920b6b23f1, + 0xdab99e59958885c4,0xe95fab368e45eced, + 0x88b402f7fd75539b,0x11dbcb0218ebb414, + 0xaae103b5fcd2a881,0xd652bdc29f26a119, + 0xd59944a37c0752a2,0x4be76d3346f0495f, + 0x857fcae62d8493a5,0x6f70a4400c562ddb, + 0xa6dfbd9fb8e5b88e,0xcb4ccd500f6bb952, + 0xd097ad07a71f26b2,0x7e2000a41346a7a7, + 0x825ecc24c873782f,0x8ed400668c0c28c8, + 0xa2f67f2dfa90563b,0x728900802f0f32fa, + 0xcbb41ef979346bca,0x4f2b40a03ad2ffb9, + 0xfea126b7d78186bc,0xe2f610c84987bfa8, + 0x9f24b832e6b0f436,0xdd9ca7d2df4d7c9, + 0xc6ede63fa05d3143,0x91503d1c79720dbb, + 0xf8a95fcf88747d94,0x75a44c6397ce912a, + 0x9b69dbe1b548ce7c,0xc986afbe3ee11aba, + 0xc24452da229b021b,0xfbe85badce996168, + 0xf2d56790ab41c2a2,0xfae27299423fb9c3, + 0x97c560ba6b0919a5,0xdccd879fc967d41a, + 0xbdb6b8e905cb600f,0x5400e987bbc1c920, + 0xed246723473e3813,0x290123e9aab23b68, + 0x9436c0760c86e30b,0xf9a0b6720aaf6521, + 0xb94470938fa89bce,0xf808e40e8d5b3e69, + 0xe7958cb87392c2c2,0xb60b1d1230b20e04, + 0x90bd77f3483bb9b9,0xb1c6f22b5e6f48c2, + 0xb4ecd5f01a4aa828,0x1e38aeb6360b1af3, + 0xe2280b6c20dd5232,0x25c6da63c38de1b0, + 0x8d590723948a535f,0x579c487e5a38ad0e, + 0xb0af48ec79ace837,0x2d835a9df0c6d851, + 0xdcdb1b2798182244,0xf8e431456cf88e65, + 0x8a08f0f8bf0f156b,0x1b8e9ecb641b58ff, + 0xac8b2d36eed2dac5,0xe272467e3d222f3f, + 0xd7adf884aa879177,0x5b0ed81dcc6abb0f, + 0x86ccbb52ea94baea,0x98e947129fc2b4e9, + 0xa87fea27a539e9a5,0x3f2398d747b36224, + 0xd29fe4b18e88640e,0x8eec7f0d19a03aad, + 0x83a3eeeef9153e89,0x1953cf68300424ac, + 0xa48ceaaab75a8e2b,0x5fa8c3423c052dd7, + 0xcdb02555653131b6,0x3792f412cb06794d, + 0x808e17555f3ebf11,0xe2bbd88bbee40bd0, + 0xa0b19d2ab70e6ed6,0x5b6aceaeae9d0ec4, + 0xc8de047564d20a8b,0xf245825a5a445275, + 0xfb158592be068d2e,0xeed6e2f0f0d56712, + 0x9ced737bb6c4183d,0x55464dd69685606b, + 0xc428d05aa4751e4c,0xaa97e14c3c26b886, + 0xf53304714d9265df,0xd53dd99f4b3066a8, + 0x993fe2c6d07b7fab,0xe546a8038efe4029, + 0xbf8fdb78849a5f96,0xde98520472bdd033, + 0xef73d256a5c0f77c,0x963e66858f6d4440, + 0x95a8637627989aad,0xdde7001379a44aa8, + 0xbb127c53b17ec159,0x5560c018580d5d52, + 0xe9d71b689dde71af,0xaab8f01e6e10b4a6, + 0x9226712162ab070d,0xcab3961304ca70e8, + 0xb6b00d69bb55c8d1,0x3d607b97c5fd0d22, + 0xe45c10c42a2b3b05,0x8cb89a7db77c506a, + 0x8eb98a7a9a5b04e3,0x77f3608e92adb242, + 0xb267ed1940f1c61c,0x55f038b237591ed3, + 0xdf01e85f912e37a3,0x6b6c46dec52f6688, + 0x8b61313bbabce2c6,0x2323ac4b3b3da015, + 0xae397d8aa96c1b77,0xabec975e0a0d081a, + 0xd9c7dced53c72255,0x96e7bd358c904a21, + 0x881cea14545c7575,0x7e50d64177da2e54, + 0xaa242499697392d2,0xdde50bd1d5d0b9e9, + 0xd4ad2dbfc3d07787,0x955e4ec64b44e864, + 0x84ec3c97da624ab4,0xbd5af13bef0b113e, + 0xa6274bbdd0fadd61,0xecb1ad8aeacdd58e, + 0xcfb11ead453994ba,0x67de18eda5814af2, + 0x81ceb32c4b43fcf4,0x80eacf948770ced7, + 0xa2425ff75e14fc31,0xa1258379a94d028d, + 0xcad2f7f5359a3b3e,0x96ee45813a04330, + 0xfd87b5f28300ca0d,0x8bca9d6e188853fc, + 0x9e74d1b791e07e48,0x775ea264cf55347e, + 0xc612062576589dda,0x95364afe032a819e, + 0xf79687aed3eec551,0x3a83ddbd83f52205, + 0x9abe14cd44753b52,0xc4926a9672793543, + 0xc16d9a0095928a27,0x75b7053c0f178294, + 0xf1c90080baf72cb1,0x5324c68b12dd6339, + 0x971da05074da7bee,0xd3f6fc16ebca5e04, + 0xbce5086492111aea,0x88f4bb1ca6bcf585, + 0xec1e4a7db69561a5,0x2b31e9e3d06c32e6, + 0x9392ee8e921d5d07,0x3aff322e62439fd0, + 0xb877aa3236a4b449,0x9befeb9fad487c3, + 0xe69594bec44de15b,0x4c2ebe687989a9b4, + 0x901d7cf73ab0acd9,0xf9d37014bf60a11, + 0xb424dc35095cd80f,0x538484c19ef38c95, + 0xe12e13424bb40e13,0x2865a5f206b06fba, + 0x8cbccc096f5088cb,0xf93f87b7442e45d4, + 0xafebff0bcb24aafe,0xf78f69a51539d749, + 0xdbe6fecebdedd5be,0xb573440e5a884d1c, + 0x89705f4136b4a597,0x31680a88f8953031, + 0xabcc77118461cefc,0xfdc20d2b36ba7c3e, + 0xd6bf94d5e57a42bc,0x3d32907604691b4d, + 0x8637bd05af6c69b5,0xa63f9a49c2c1b110, + 0xa7c5ac471b478423,0xfcf80dc33721d54, + 0xd1b71758e219652b,0xd3c36113404ea4a9, + 0x83126e978d4fdf3b,0x645a1cac083126ea, + 0xa3d70a3d70a3d70a,0x3d70a3d70a3d70a4, + 0xcccccccccccccccc,0xcccccccccccccccd, + 0x8000000000000000,0x0, + 0xa000000000000000,0x0, + 0xc800000000000000,0x0, + 0xfa00000000000000,0x0, + 0x9c40000000000000,0x0, + 0xc350000000000000,0x0, + 0xf424000000000000,0x0, + 0x9896800000000000,0x0, + 0xbebc200000000000,0x0, + 0xee6b280000000000,0x0, + 0x9502f90000000000,0x0, + 0xba43b74000000000,0x0, + 0xe8d4a51000000000,0x0, + 0x9184e72a00000000,0x0, + 0xb5e620f480000000,0x0, + 0xe35fa931a0000000,0x0, + 0x8e1bc9bf04000000,0x0, + 0xb1a2bc2ec5000000,0x0, + 0xde0b6b3a76400000,0x0, + 0x8ac7230489e80000,0x0, + 0xad78ebc5ac620000,0x0, + 0xd8d726b7177a8000,0x0, + 0x878678326eac9000,0x0, + 0xa968163f0a57b400,0x0, + 0xd3c21bcecceda100,0x0, + 0x84595161401484a0,0x0, + 0xa56fa5b99019a5c8,0x0, + 0xcecb8f27f4200f3a,0x0, + 0x813f3978f8940984,0x4000000000000000, + 0xa18f07d736b90be5,0x5000000000000000, + 0xc9f2c9cd04674ede,0xa400000000000000, + 0xfc6f7c4045812296,0x4d00000000000000, + 0x9dc5ada82b70b59d,0xf020000000000000, + 0xc5371912364ce305,0x6c28000000000000, + 0xf684df56c3e01bc6,0xc732000000000000, + 0x9a130b963a6c115c,0x3c7f400000000000, + 0xc097ce7bc90715b3,0x4b9f100000000000, + 0xf0bdc21abb48db20,0x1e86d40000000000, + 0x96769950b50d88f4,0x1314448000000000, + 0xbc143fa4e250eb31,0x17d955a000000000, + 0xeb194f8e1ae525fd,0x5dcfab0800000000, + 0x92efd1b8d0cf37be,0x5aa1cae500000000, + 0xb7abc627050305ad,0xf14a3d9e40000000, + 0xe596b7b0c643c719,0x6d9ccd05d0000000, + 0x8f7e32ce7bea5c6f,0xe4820023a2000000, + 0xb35dbf821ae4f38b,0xdda2802c8a800000, + 0xe0352f62a19e306e,0xd50b2037ad200000, + 0x8c213d9da502de45,0x4526f422cc340000, + 0xaf298d050e4395d6,0x9670b12b7f410000, + 0xdaf3f04651d47b4c,0x3c0cdd765f114000, + 0x88d8762bf324cd0f,0xa5880a69fb6ac800, + 0xab0e93b6efee0053,0x8eea0d047a457a00, + 0xd5d238a4abe98068,0x72a4904598d6d880, + 0x85a36366eb71f041,0x47a6da2b7f864750, + 0xa70c3c40a64e6c51,0x999090b65f67d924, + 0xd0cf4b50cfe20765,0xfff4b4e3f741cf6d, + 0x82818f1281ed449f,0xbff8f10e7a8921a4, + 0xa321f2d7226895c7,0xaff72d52192b6a0d, + 0xcbea6f8ceb02bb39,0x9bf4f8a69f764490, + 0xfee50b7025c36a08,0x2f236d04753d5b4, + 0x9f4f2726179a2245,0x1d762422c946590, + 0xc722f0ef9d80aad6,0x424d3ad2b7b97ef5, + 0xf8ebad2b84e0d58b,0xd2e0898765a7deb2, + 0x9b934c3b330c8577,0x63cc55f49f88eb2f, + 0xc2781f49ffcfa6d5,0x3cbf6b71c76b25fb, + 0xf316271c7fc3908a,0x8bef464e3945ef7a, + 0x97edd871cfda3a56,0x97758bf0e3cbb5ac, + 0xbde94e8e43d0c8ec,0x3d52eeed1cbea317, + 0xed63a231d4c4fb27,0x4ca7aaa863ee4bdd, + 0x945e455f24fb1cf8,0x8fe8caa93e74ef6a, + 0xb975d6b6ee39e436,0xb3e2fd538e122b44, + 0xe7d34c64a9c85d44,0x60dbbca87196b616, + 0x90e40fbeea1d3a4a,0xbc8955e946fe31cd, + 0xb51d13aea4a488dd,0x6babab6398bdbe41, + 0xe264589a4dcdab14,0xc696963c7eed2dd1, + 0x8d7eb76070a08aec,0xfc1e1de5cf543ca2, + 0xb0de65388cc8ada8,0x3b25a55f43294bcb, + 0xdd15fe86affad912,0x49ef0eb713f39ebe, + 0x8a2dbf142dfcc7ab,0x6e3569326c784337, + 0xacb92ed9397bf996,0x49c2c37f07965404, + 0xd7e77a8f87daf7fb,0xdc33745ec97be906, + 0x86f0ac99b4e8dafd,0x69a028bb3ded71a3, + 0xa8acd7c0222311bc,0xc40832ea0d68ce0c, + 0xd2d80db02aabd62b,0xf50a3fa490c30190, + 0x83c7088e1aab65db,0x792667c6da79e0fa, + 0xa4b8cab1a1563f52,0x577001b891185938, + 0xcde6fd5e09abcf26,0xed4c0226b55e6f86, + 0x80b05e5ac60b6178,0x544f8158315b05b4, + 0xa0dc75f1778e39d6,0x696361ae3db1c721, + 0xc913936dd571c84c,0x3bc3a19cd1e38e9, + 0xfb5878494ace3a5f,0x4ab48a04065c723, + 0x9d174b2dcec0e47b,0x62eb0d64283f9c76, + 0xc45d1df942711d9a,0x3ba5d0bd324f8394, + 0xf5746577930d6500,0xca8f44ec7ee36479, + 0x9968bf6abbe85f20,0x7e998b13cf4e1ecb, + 0xbfc2ef456ae276e8,0x9e3fedd8c321a67e, + 0xefb3ab16c59b14a2,0xc5cfe94ef3ea101e, + 0x95d04aee3b80ece5,0xbba1f1d158724a12, + 0xbb445da9ca61281f,0x2a8a6e45ae8edc97, + 0xea1575143cf97226,0xf52d09d71a3293bd, + 0x924d692ca61be758,0x593c2626705f9c56, + 0xb6e0c377cfa2e12e,0x6f8b2fb00c77836c, + 0xe498f455c38b997a,0xb6dfb9c0f956447, + 0x8edf98b59a373fec,0x4724bd4189bd5eac, + 0xb2977ee300c50fe7,0x58edec91ec2cb657, + 0xdf3d5e9bc0f653e1,0x2f2967b66737e3ed, + 0x8b865b215899f46c,0xbd79e0d20082ee74, + 0xae67f1e9aec07187,0xecd8590680a3aa11, + 0xda01ee641a708de9,0xe80e6f4820cc9495, + 0x884134fe908658b2,0x3109058d147fdcdd, + 0xaa51823e34a7eede,0xbd4b46f0599fd415, + 0xd4e5e2cdc1d1ea96,0x6c9e18ac7007c91a, + 0x850fadc09923329e,0x3e2cf6bc604ddb0, + 0xa6539930bf6bff45,0x84db8346b786151c, + 0xcfe87f7cef46ff16,0xe612641865679a63, + 0x81f14fae158c5f6e,0x4fcb7e8f3f60c07e, + 0xa26da3999aef7749,0xe3be5e330f38f09d, + 0xcb090c8001ab551c,0x5cadf5bfd3072cc5, + 0xfdcb4fa002162a63,0x73d9732fc7c8f7f6, + 0x9e9f11c4014dda7e,0x2867e7fddcdd9afa, + 0xc646d63501a1511d,0xb281e1fd541501b8, + 0xf7d88bc24209a565,0x1f225a7ca91a4226, + 0x9ae757596946075f,0x3375788de9b06958, + 0xc1a12d2fc3978937,0x52d6b1641c83ae, + 0xf209787bb47d6b84,0xc0678c5dbd23a49a, + 0x9745eb4d50ce6332,0xf840b7ba963646e0, + 0xbd176620a501fbff,0xb650e5a93bc3d898, + 0xec5d3fa8ce427aff,0xa3e51f138ab4cebe, + 0x93ba47c980e98cdf,0xc66f336c36b10137, + 0xb8a8d9bbe123f017,0xb80b0047445d4184, + 0xe6d3102ad96cec1d,0xa60dc059157491e5, + 0x9043ea1ac7e41392,0x87c89837ad68db2f, + 0xb454e4a179dd1877,0x29babe4598c311fb, + 0xe16a1dc9d8545e94,0xf4296dd6fef3d67a, + 0x8ce2529e2734bb1d,0x1899e4a65f58660c, + 0xb01ae745b101e9e4,0x5ec05dcff72e7f8f, + 0xdc21a1171d42645d,0x76707543f4fa1f73, + 0x899504ae72497eba,0x6a06494a791c53a8, + 0xabfa45da0edbde69,0x487db9d17636892, + 0xd6f8d7509292d603,0x45a9d2845d3c42b6, + 0x865b86925b9bc5c2,0xb8a2392ba45a9b2, + 0xa7f26836f282b732,0x8e6cac7768d7141e, + 0xd1ef0244af2364ff,0x3207d795430cd926, + 0x8335616aed761f1f,0x7f44e6bd49e807b8, + 0xa402b9c5a8d3a6e7,0x5f16206c9c6209a6, + 0xcd036837130890a1,0x36dba887c37a8c0f, + 0x802221226be55a64,0xc2494954da2c9789, + 0xa02aa96b06deb0fd,0xf2db9baa10b7bd6c, + 0xc83553c5c8965d3d,0x6f92829494e5acc7, + 0xfa42a8b73abbf48c,0xcb772339ba1f17f9, + 0x9c69a97284b578d7,0xff2a760414536efb, + 0xc38413cf25e2d70d,0xfef5138519684aba, + 0xf46518c2ef5b8cd1,0x7eb258665fc25d69, + 0x98bf2f79d5993802,0xef2f773ffbd97a61, + 0xbeeefb584aff8603,0xaafb550ffacfd8fa, + 0xeeaaba2e5dbf6784,0x95ba2a53f983cf38, + 0x952ab45cfa97a0b2,0xdd945a747bf26183, + 0xba756174393d88df,0x94f971119aeef9e4, + 0xe912b9d1478ceb17,0x7a37cd5601aab85d, + 0x91abb422ccb812ee,0xac62e055c10ab33a, + 0xb616a12b7fe617aa,0x577b986b314d6009, + 0xe39c49765fdf9d94,0xed5a7e85fda0b80b, + 0x8e41ade9fbebc27d,0x14588f13be847307, + 0xb1d219647ae6b31c,0x596eb2d8ae258fc8, + 0xde469fbd99a05fe3,0x6fca5f8ed9aef3bb, + 0x8aec23d680043bee,0x25de7bb9480d5854, + 0xada72ccc20054ae9,0xaf561aa79a10ae6a, + 0xd910f7ff28069da4,0x1b2ba1518094da04, + 0x87aa9aff79042286,0x90fb44d2f05d0842, + 0xa99541bf57452b28,0x353a1607ac744a53, + 0xd3fa922f2d1675f2,0x42889b8997915ce8, + 0x847c9b5d7c2e09b7,0x69956135febada11, + 0xa59bc234db398c25,0x43fab9837e699095, + 0xcf02b2c21207ef2e,0x94f967e45e03f4bb, + 0x8161afb94b44f57d,0x1d1be0eebac278f5, + 0xa1ba1ba79e1632dc,0x6462d92a69731732, + 0xca28a291859bbf93,0x7d7b8f7503cfdcfe, + 0xfcb2cb35e702af78,0x5cda735244c3d43e, + 0x9defbf01b061adab,0x3a0888136afa64a7, + 0xc56baec21c7a1916,0x88aaa1845b8fdd0, + 0xf6c69a72a3989f5b,0x8aad549e57273d45, + 0x9a3c2087a63f6399,0x36ac54e2f678864b, + 0xc0cb28a98fcf3c7f,0x84576a1bb416a7dd, + 0xf0fdf2d3f3c30b9f,0x656d44a2a11c51d5, + 0x969eb7c47859e743,0x9f644ae5a4b1b325, + 0xbc4665b596706114,0x873d5d9f0dde1fee, + 0xeb57ff22fc0c7959,0xa90cb506d155a7ea, + 0x9316ff75dd87cbd8,0x9a7f12442d588f2, + 0xb7dcbf5354e9bece,0xc11ed6d538aeb2f, + 0xe5d3ef282a242e81,0x8f1668c8a86da5fa, + 0x8fa475791a569d10,0xf96e017d694487bc, + 0xb38d92d760ec4455,0x37c981dcc395a9ac, + 0xe070f78d3927556a,0x85bbe253f47b1417, + 0x8c469ab843b89562,0x93956d7478ccec8e, + 0xaf58416654a6babb,0x387ac8d1970027b2, + 0xdb2e51bfe9d0696a,0x6997b05fcc0319e, + 0x88fcf317f22241e2,0x441fece3bdf81f03, + 0xab3c2fddeeaad25a,0xd527e81cad7626c3, + 0xd60b3bd56a5586f1,0x8a71e223d8d3b074, + 0x85c7056562757456,0xf6872d5667844e49, + 0xa738c6bebb12d16c,0xb428f8ac016561db, + 0xd106f86e69d785c7,0xe13336d701beba52, + 0x82a45b450226b39c,0xecc0024661173473, + 0xa34d721642b06084,0x27f002d7f95d0190, + 0xcc20ce9bd35c78a5,0x31ec038df7b441f4, + 0xff290242c83396ce,0x7e67047175a15271, + 0x9f79a169bd203e41,0xf0062c6e984d386, + 0xc75809c42c684dd1,0x52c07b78a3e60868, + 0xf92e0c3537826145,0xa7709a56ccdf8a82, + 0x9bbcc7a142b17ccb,0x88a66076400bb691, + 0xc2abf989935ddbfe,0x6acff893d00ea435, + 0xf356f7ebf83552fe,0x583f6b8c4124d43, + 0x98165af37b2153de,0xc3727a337a8b704a, + 0xbe1bf1b059e9a8d6,0x744f18c0592e4c5c, + 0xeda2ee1c7064130c,0x1162def06f79df73, + 0x9485d4d1c63e8be7,0x8addcb5645ac2ba8, + 0xb9a74a0637ce2ee1,0x6d953e2bd7173692, + 0xe8111c87c5c1ba99,0xc8fa8db6ccdd0437, + 0x910ab1d4db9914a0,0x1d9c9892400a22a2, + 0xb54d5e4a127f59c8,0x2503beb6d00cab4b, + 0xe2a0b5dc971f303a,0x2e44ae64840fd61d, + 0x8da471a9de737e24,0x5ceaecfed289e5d2, + 0xb10d8e1456105dad,0x7425a83e872c5f47, + 0xdd50f1996b947518,0xd12f124e28f77719, + 0x8a5296ffe33cc92f,0x82bd6b70d99aaa6f, + 0xace73cbfdc0bfb7b,0x636cc64d1001550b, + 0xd8210befd30efa5a,0x3c47f7e05401aa4e, + 0x8714a775e3e95c78,0x65acfaec34810a71, + 0xa8d9d1535ce3b396,0x7f1839a741a14d0d, + 0xd31045a8341ca07c,0x1ede48111209a050, + 0x83ea2b892091e44d,0x934aed0aab460432, + 0xa4e4b66b68b65d60,0xf81da84d5617853f, + 0xce1de40642e3f4b9,0x36251260ab9d668e, + 0x80d2ae83e9ce78f3,0xc1d72b7c6b426019, + 0xa1075a24e4421730,0xb24cf65b8612f81f, + 0xc94930ae1d529cfc,0xdee033f26797b627, + 0xfb9b7cd9a4a7443c,0x169840ef017da3b1, + 0x9d412e0806e88aa5,0x8e1f289560ee864e, + 0xc491798a08a2ad4e,0xf1a6f2bab92a27e2, + 0xf5b5d7ec8acb58a2,0xae10af696774b1db, + 0x9991a6f3d6bf1765,0xacca6da1e0a8ef29, + 0xbff610b0cc6edd3f,0x17fd090a58d32af3, + 0xeff394dcff8a948e,0xddfc4b4cef07f5b0, + 0x95f83d0a1fb69cd9,0x4abdaf101564f98e, + 0xbb764c4ca7a4440f,0x9d6d1ad41abe37f1, + 0xea53df5fd18d5513,0x84c86189216dc5ed, + 0x92746b9be2f8552c,0x32fd3cf5b4e49bb4, + 0xb7118682dbb66a77,0x3fbc8c33221dc2a1, + 0xe4d5e82392a40515,0xfabaf3feaa5334a, + 0x8f05b1163ba6832d,0x29cb4d87f2a7400e, + 0xb2c71d5bca9023f8,0x743e20e9ef511012, + 0xdf78e4b2bd342cf6,0x914da9246b255416, + 0x8bab8eefb6409c1a,0x1ad089b6c2f7548e, + 0xae9672aba3d0c320,0xa184ac2473b529b1, + 0xda3c0f568cc4f3e8,0xc9e5d72d90a2741e, + 0x8865899617fb1871,0x7e2fa67c7a658892, + 0xaa7eebfb9df9de8d,0xddbb901b98feeab7, + 0xd51ea6fa85785631,0x552a74227f3ea565, + 0x8533285c936b35de,0xd53a88958f87275f, + 0xa67ff273b8460356,0x8a892abaf368f137, + 0xd01fef10a657842c,0x2d2b7569b0432d85, + 0x8213f56a67f6b29b,0x9c3b29620e29fc73, + 0xa298f2c501f45f42,0x8349f3ba91b47b8f, + 0xcb3f2f7642717713,0x241c70a936219a73, + 0xfe0efb53d30dd4d7,0xed238cd383aa0110, + 0x9ec95d1463e8a506,0xf4363804324a40aa, + 0xc67bb4597ce2ce48,0xb143c6053edcd0d5, + 0xf81aa16fdc1b81da,0xdd94b7868e94050a, + 0x9b10a4e5e9913128,0xca7cf2b4191c8326, + 0xc1d4ce1f63f57d72,0xfd1c2f611f63a3f0, + 0xf24a01a73cf2dccf,0xbc633b39673c8cec, + 0x976e41088617ca01,0xd5be0503e085d813, + 0xbd49d14aa79dbc82,0x4b2d8644d8a74e18, + 0xec9c459d51852ba2,0xddf8e7d60ed1219e, + 0x93e1ab8252f33b45,0xcabb90e5c942b503, + 0xb8da1662e7b00a17,0x3d6a751f3b936243, + 0xe7109bfba19c0c9d,0xcc512670a783ad4, + 0x906a617d450187e2,0x27fb2b80668b24c5, + 0xb484f9dc9641e9da,0xb1f9f660802dedf6, + 0xe1a63853bbd26451,0x5e7873f8a0396973, + 0x8d07e33455637eb2,0xdb0b487b6423e1e8, + 0xb049dc016abc5e5f,0x91ce1a9a3d2cda62, + 0xdc5c5301c56b75f7,0x7641a140cc7810fb, + 0x89b9b3e11b6329ba,0xa9e904c87fcb0a9d, + 0xac2820d9623bf429,0x546345fa9fbdcd44, + 0xd732290fbacaf133,0xa97c177947ad4095, + 0x867f59a9d4bed6c0,0x49ed8eabcccc485d, + 0xa81f301449ee8c70,0x5c68f256bfff5a74, + 0xd226fc195c6a2f8c,0x73832eec6fff3111, + 0x83585d8fd9c25db7,0xc831fd53c5ff7eab, + 0xa42e74f3d032f525,0xba3e7ca8b77f5e55, + 0xcd3a1230c43fb26f,0x28ce1bd2e55f35eb, + 0x80444b5e7aa7cf85,0x7980d163cf5b81b3, + 0xa0555e361951c366,0xd7e105bcc332621f, + 0xc86ab5c39fa63440,0x8dd9472bf3fefaa7, + 0xfa856334878fc150,0xb14f98f6f0feb951, + 0x9c935e00d4b9d8d2,0x6ed1bf9a569f33d3, + 0xc3b8358109e84f07,0xa862f80ec4700c8, + 0xf4a642e14c6262c8,0xcd27bb612758c0fa, + 0x98e7e9cccfbd7dbd,0x8038d51cb897789c, + 0xbf21e44003acdd2c,0xe0470a63e6bd56c3, + 0xeeea5d5004981478,0x1858ccfce06cac74, + 0x95527a5202df0ccb,0xf37801e0c43ebc8, + 0xbaa718e68396cffd,0xd30560258f54e6ba, + 0xe950df20247c83fd,0x47c6b82ef32a2069, + 0x91d28b7416cdd27e,0x4cdc331d57fa5441, + 0xb6472e511c81471d,0xe0133fe4adf8e952, + 0xe3d8f9e563a198e5,0x58180fddd97723a6, + 0x8e679c2f5e44ff8f,0x570f09eaa7ea7648,}; +using powers = powers_template<>; + +} + +#endif + +#ifndef FASTFLOAT_DECIMAL_TO_BINARY_H +#define FASTFLOAT_DECIMAL_TO_BINARY_H + +#include <cfloat> +#include <cinttypes> +#include <cmath> +#include <cstdint> +#include <cstdlib> +#include <cstring> + +namespace fast_float { + +// This will compute or rather approximate w * 5**q and return a pair of 64-bit words approximating +// the result, with the "high" part corresponding to the most significant bits and the +// low part corresponding to the least significant bits. +// +template <int bit_precision> +fastfloat_really_inline +value128 compute_product_approximation(int64_t q, uint64_t w) { + const int index = 2 * int(q - powers::smallest_power_of_five); + // For small values of q, e.g., q in [0,27], the answer is always exact because + // The line value128 firstproduct = full_multiplication(w, power_of_five_128[index]); + // gives the exact answer. + value128 firstproduct = full_multiplication(w, powers::power_of_five_128[index]); + static_assert((bit_precision >= 0) && (bit_precision <= 64), " precision should be in (0,64]"); + constexpr uint64_t precision_mask = (bit_precision < 64) ? + (uint64_t(0xFFFFFFFFFFFFFFFF) >> bit_precision) + : uint64_t(0xFFFFFFFFFFFFFFFF); + if((firstproduct.high & precision_mask) == precision_mask) { // could further guard with (lower + w < lower) + // regarding the second product, we only need secondproduct.high, but our expectation is that the compiler will optimize this extra work away if needed. + value128 secondproduct = full_multiplication(w, powers::power_of_five_128[index + 1]); + firstproduct.low += secondproduct.high; + if(secondproduct.high > firstproduct.low) { + firstproduct.high++; + } + } + return firstproduct; +} + +namespace detail { +/** + * For q in (0,350), we have that + * f = (((152170 + 65536) * q ) >> 16); + * is equal to + * floor(p) + q + * where + * p = log(5**q)/log(2) = q * log(5)/log(2) + * + * For negative values of q in (-400,0), we have that + * f = (((152170 + 65536) * q ) >> 16); + * is equal to + * -ceil(p) + q + * where + * p = log(5**-q)/log(2) = -q * log(5)/log(2) + */ + constexpr fastfloat_really_inline int32_t power(int32_t q) noexcept { + return (((152170 + 65536) * q) >> 16) + 63; + } +} // namespace detail + +// create an adjusted mantissa, biased by the invalid power2 +// for significant digits already multiplied by 10 ** q. +template <typename binary> +fastfloat_really_inline +adjusted_mantissa compute_error_scaled(int64_t q, uint64_t w, int lz) noexcept { + int hilz = int(w >> 63) ^ 1; + adjusted_mantissa answer; + answer.mantissa = w << hilz; + int bias = binary::mantissa_explicit_bits() - binary::minimum_exponent(); + answer.power2 = int32_t(detail::power(int32_t(q)) + bias - hilz - lz - 62 + invalid_am_bias); + return answer; +} + +// w * 10 ** q, without rounding the representation up. +// the power2 in the exponent will be adjusted by invalid_am_bias. +template <typename binary> +fastfloat_really_inline +adjusted_mantissa compute_error(int64_t q, uint64_t w) noexcept { + int lz = leading_zeroes(w); + w <<= lz; + value128 product = compute_product_approximation<binary::mantissa_explicit_bits() + 3>(q, w); + return compute_error_scaled<binary>(q, product.high, lz); +} + +// w * 10 ** q +// The returned value should be a valid ieee64 number that simply need to be packed. +// However, in some very rare cases, the computation will fail. In such cases, we +// return an adjusted_mantissa with a negative power of 2: the caller should recompute +// in such cases. +template <typename binary> +fastfloat_really_inline +adjusted_mantissa compute_float(int64_t q, uint64_t w) noexcept { + adjusted_mantissa answer; + if ((w == 0) || (q < binary::smallest_power_of_ten())) { + answer.power2 = 0; + answer.mantissa = 0; + // result should be zero + return answer; + } + if (q > binary::largest_power_of_ten()) { + // we want to get infinity: + answer.power2 = binary::infinite_power(); + answer.mantissa = 0; + return answer; + } + // At this point in time q is in [powers::smallest_power_of_five, powers::largest_power_of_five]. + + // We want the most significant bit of i to be 1. Shift if needed. + int lz = leading_zeroes(w); + w <<= lz; + + // The required precision is binary::mantissa_explicit_bits() + 3 because + // 1. We need the implicit bit + // 2. We need an extra bit for rounding purposes + // 3. We might lose a bit due to the "upperbit" routine (result too small, requiring a shift) + + value128 product = compute_product_approximation<binary::mantissa_explicit_bits() + 3>(q, w); + if(product.low == 0xFFFFFFFFFFFFFFFF) { // could guard it further + // In some very rare cases, this could happen, in which case we might need a more accurate + // computation that what we can provide cheaply. This is very, very unlikely. + // + const bool inside_safe_exponent = (q >= -27) && (q <= 55); // always good because 5**q <2**128 when q>=0, + // and otherwise, for q<0, we have 5**-q<2**64 and the 128-bit reciprocal allows for exact computation. + if(!inside_safe_exponent) { + return compute_error_scaled<binary>(q, product.high, lz); + } + } + // The "compute_product_approximation" function can be slightly slower than a branchless approach: + // value128 product = compute_product(q, w); + // but in practice, we can win big with the compute_product_approximation if its additional branch + // is easily predicted. Which is best is data specific. + int upperbit = int(product.high >> 63); + + answer.mantissa = product.high >> (upperbit + 64 - binary::mantissa_explicit_bits() - 3); + + answer.power2 = int32_t(detail::power(int32_t(q)) + upperbit - lz - binary::minimum_exponent()); + if (answer.power2 <= 0) { // we have a subnormal? + // Here have that answer.power2 <= 0 so -answer.power2 >= 0 + if(-answer.power2 + 1 >= 64) { // if we have more than 64 bits below the minimum exponent, you have a zero for sure. + answer.power2 = 0; + answer.mantissa = 0; + // result should be zero + return answer; + } + // next line is safe because -answer.power2 + 1 < 64 + answer.mantissa >>= -answer.power2 + 1; + // Thankfully, we can't have both "round-to-even" and subnormals because + // "round-to-even" only occurs for powers close to 0. + answer.mantissa += (answer.mantissa & 1); // round up + answer.mantissa >>= 1; + // There is a weird scenario where we don't have a subnormal but just. + // Suppose we start with 2.2250738585072013e-308, we end up + // with 0x3fffffffffffff x 2^-1023-53 which is technically subnormal + // whereas 0x40000000000000 x 2^-1023-53 is normal. Now, we need to round + // up 0x3fffffffffffff x 2^-1023-53 and once we do, we are no longer + // subnormal, but we can only know this after rounding. + // So we only declare a subnormal if we are smaller than the threshold. + answer.power2 = (answer.mantissa < (uint64_t(1) << binary::mantissa_explicit_bits())) ? 0 : 1; + return answer; + } + + // usually, we round *up*, but if we fall right in between and and we have an + // even basis, we need to round down + // We are only concerned with the cases where 5**q fits in single 64-bit word. + if ((product.low <= 1) && (q >= binary::min_exponent_round_to_even()) && (q <= binary::max_exponent_round_to_even()) && + ((answer.mantissa & 3) == 1) ) { // we may fall between two floats! + // To be in-between two floats we need that in doing + // answer.mantissa = product.high >> (upperbit + 64 - binary::mantissa_explicit_bits() - 3); + // ... we dropped out only zeroes. But if this happened, then we can go back!!! + if((answer.mantissa << (upperbit + 64 - binary::mantissa_explicit_bits() - 3)) == product.high) { + answer.mantissa &= ~uint64_t(1); // flip it so that we do not round up + } + } + + answer.mantissa += (answer.mantissa & 1); // round up + answer.mantissa >>= 1; + if (answer.mantissa >= (uint64_t(2) << binary::mantissa_explicit_bits())) { + answer.mantissa = (uint64_t(1) << binary::mantissa_explicit_bits()); + answer.power2++; // undo previous addition + } + + answer.mantissa &= ~(uint64_t(1) << binary::mantissa_explicit_bits()); + if (answer.power2 >= binary::infinite_power()) { // infinity + answer.power2 = binary::infinite_power(); + answer.mantissa = 0; + } + return answer; +} + +} // namespace fast_float + +#endif + +#ifndef FASTFLOAT_BIGINT_H +#define FASTFLOAT_BIGINT_H + +#include <algorithm> +#include <cstdint> +#include <climits> +#include <cstring> + + +namespace fast_float { + +// the limb width: we want efficient multiplication of double the bits in +// limb, or for 64-bit limbs, at least 64-bit multiplication where we can +// extract the high and low parts efficiently. this is every 64-bit +// architecture except for sparc, which emulates 128-bit multiplication. +// we might have platforms where `CHAR_BIT` is not 8, so let's avoid +// doing `8 * sizeof(limb)`. +#if defined(FASTFLOAT_64BIT) && !defined(__sparc) +#define FASTFLOAT_64BIT_LIMB +typedef uint64_t limb; +constexpr size_t limb_bits = 64; +#else +#define FASTFLOAT_32BIT_LIMB +typedef uint32_t limb; +constexpr size_t limb_bits = 32; +#endif + +typedef span<limb> limb_span; + +// number of bits in a bigint. this needs to be at least the number +// of bits required to store the largest bigint, which is +// `log2(10**(digits + max_exp))`, or `log2(10**(767 + 342))`, or +// ~3600 bits, so we round to 4000. +constexpr size_t bigint_bits = 4000; +constexpr size_t bigint_limbs = bigint_bits / limb_bits; + +// vector-like type that is allocated on the stack. the entire +// buffer is pre-allocated, and only the length changes. +template <uint16_t size> +struct stackvec { + limb data[size]; + // we never need more than 150 limbs + uint16_t length{0}; + + stackvec() = default; + stackvec(const stackvec &) = delete; + stackvec &operator=(const stackvec &) = delete; + stackvec(stackvec &&) = delete; + stackvec &operator=(stackvec &&other) = delete; + + // create stack vector from existing limb span. + stackvec(limb_span s) { + FASTFLOAT_ASSERT(try_extend(s)); + } + + limb& operator[](size_t index) noexcept { + FASTFLOAT_DEBUG_ASSERT(index < length); + return data[index]; + } + const limb& operator[](size_t index) const noexcept { + FASTFLOAT_DEBUG_ASSERT(index < length); + return data[index]; + } + // index from the end of the container + const limb& rindex(size_t index) const noexcept { + FASTFLOAT_DEBUG_ASSERT(index < length); + size_t rindex = length - index - 1; + return data[rindex]; + } + + // set the length, without bounds checking. + void set_len(size_t len) noexcept { + length = uint16_t(len); + } + constexpr size_t len() const noexcept { + return length; + } + constexpr bool is_empty() const noexcept { + return length == 0; + } + constexpr size_t capacity() const noexcept { + return size; + } + // append item to vector, without bounds checking + void push_unchecked(limb value) noexcept { + data[length] = value; + length++; + } + // append item to vector, returning if item was added + bool try_push(limb value) noexcept { + if (len() < capacity()) { + push_unchecked(value); + return true; + } else { + return false; + } + } + // add items to the vector, from a span, without bounds checking + void extend_unchecked(limb_span s) noexcept { + limb* ptr = data + length; + ::memcpy((void*)ptr, (const void*)s.ptr, sizeof(limb) * s.len()); + set_len(len() + s.len()); + } + // try to add items to the vector, returning if items were added + bool try_extend(limb_span s) noexcept { + if (len() + s.len() <= capacity()) { + extend_unchecked(s); + return true; + } else { + return false; + } + } + // resize the vector, without bounds checking + // if the new size is longer than the vector, assign value to each + // appended item. + void resize_unchecked(size_t new_len, limb value) noexcept { + if (new_len > len()) { + size_t count = new_len - len(); + limb* first = data + len(); + limb* last = first + count; + ::std::fill(first, last, value); + set_len(new_len); + } else { + set_len(new_len); + } + } + // try to resize the vector, returning if the vector was resized. + bool try_resize(size_t new_len, limb value) noexcept { + if (new_len > capacity()) { + return false; + } else { + resize_unchecked(new_len, value); + return true; + } + } + // check if any limbs are non-zero after the given index. + // this needs to be done in reverse order, since the index + // is relative to the most significant limbs. + bool nonzero(size_t index) const noexcept { + while (index < len()) { + if (rindex(index) != 0) { + return true; + } + index++; + } + return false; + } + // normalize the big integer, so most-significant zero limbs are removed. + void normalize() noexcept { + while (len() > 0 && rindex(0) == 0) { + length--; + } + } +}; + +fastfloat_really_inline +uint64_t empty_hi64(bool& truncated) noexcept { + truncated = false; + return 0; +} + +fastfloat_really_inline +uint64_t uint64_hi64(uint64_t r0, bool& truncated) noexcept { + truncated = false; + int shl = leading_zeroes(r0); + return r0 << shl; +} + +fastfloat_really_inline +uint64_t uint64_hi64(uint64_t r0, uint64_t r1, bool& truncated) noexcept { + int shl = leading_zeroes(r0); + if (shl == 0) { + truncated = r1 != 0; + return r0; + } else { + int shr = 64 - shl; + truncated = (r1 << shl) != 0; + return (r0 << shl) | (r1 >> shr); + } +} + +fastfloat_really_inline +uint64_t uint32_hi64(uint32_t r0, bool& truncated) noexcept { + return uint64_hi64(r0, truncated); +} + +fastfloat_really_inline +uint64_t uint32_hi64(uint32_t r0, uint32_t r1, bool& truncated) noexcept { + uint64_t x0 = r0; + uint64_t x1 = r1; + return uint64_hi64((x0 << 32) | x1, truncated); +} + +fastfloat_really_inline +uint64_t uint32_hi64(uint32_t r0, uint32_t r1, uint32_t r2, bool& truncated) noexcept { + uint64_t x0 = r0; + uint64_t x1 = r1; + uint64_t x2 = r2; + return uint64_hi64(x0, (x1 << 32) | x2, truncated); +} + +// add two small integers, checking for overflow. +// we want an efficient operation. for msvc, where +// we don't have built-in intrinsics, this is still +// pretty fast. +fastfloat_really_inline +limb scalar_add(limb x, limb y, bool& overflow) noexcept { + limb z; + +// gcc and clang +#if defined(__has_builtin) + #if __has_builtin(__builtin_add_overflow) + overflow = __builtin_add_overflow(x, y, &z); + return z; + #endif +#endif + + // generic, this still optimizes correctly on MSVC. + z = x + y; + overflow = z < x; + return z; +} + +// multiply two small integers, getting both the high and low bits. +fastfloat_really_inline +limb scalar_mul(limb x, limb y, limb& carry) noexcept { +#ifdef FASTFLOAT_64BIT_LIMB + #if defined(__SIZEOF_INT128__) + // GCC and clang both define it as an extension. + __uint128_t z = __uint128_t(x) * __uint128_t(y) + __uint128_t(carry); + carry = limb(z >> limb_bits); + return limb(z); + #else + // fallback, no native 128-bit integer multiplication with carry. + // on msvc, this optimizes identically, somehow. + value128 z = full_multiplication(x, y); + bool overflow; + z.low = scalar_add(z.low, carry, overflow); + z.high += uint64_t(overflow); // cannot overflow + carry = z.high; + return z.low; + #endif +#else + uint64_t z = uint64_t(x) * uint64_t(y) + uint64_t(carry); + carry = limb(z >> limb_bits); + return limb(z); +#endif +} + +// add scalar value to bigint starting from offset. +// used in grade school multiplication +template <uint16_t size> +inline bool small_add_from(stackvec<size>& vec, limb y, size_t start) noexcept { + size_t index = start; + limb carry = y; + bool overflow; + while (carry != 0 && index < vec.len()) { + vec[index] = scalar_add(vec[index], carry, overflow); + carry = limb(overflow); + index += 1; + } + if (carry != 0) { + FASTFLOAT_TRY(vec.try_push(carry)); + } + return true; +} + +// add scalar value to bigint. +template <uint16_t size> +fastfloat_really_inline bool small_add(stackvec<size>& vec, limb y) noexcept { + return small_add_from(vec, y, 0); +} + +// multiply bigint by scalar value. +template <uint16_t size> +inline bool small_mul(stackvec<size>& vec, limb y) noexcept { + limb carry = 0; + for (size_t index = 0; index < vec.len(); index++) { + vec[index] = scalar_mul(vec[index], y, carry); + } + if (carry != 0) { + FASTFLOAT_TRY(vec.try_push(carry)); + } + return true; +} + +// add bigint to bigint starting from index. +// used in grade school multiplication +template <uint16_t size> +bool large_add_from(stackvec<size>& x, limb_span y, size_t start) noexcept { + // the effective x buffer is from `xstart..x.len()`, so exit early + // if we can't get that current range. + if (x.len() < start || y.len() > x.len() - start) { + FASTFLOAT_TRY(x.try_resize(y.len() + start, 0)); + } + + bool carry = false; + for (size_t index = 0; index < y.len(); index++) { + limb xi = x[index + start]; + limb yi = y[index]; + bool c1 = false; + bool c2 = false; + xi = scalar_add(xi, yi, c1); + if (carry) { + xi = scalar_add(xi, 1, c2); + } + x[index + start] = xi; + carry = c1 | c2; + } + + // handle overflow + if (carry) { + FASTFLOAT_TRY(small_add_from(x, 1, y.len() + start)); + } + return true; +} + +// add bigint to bigint. +template <uint16_t size> +fastfloat_really_inline bool large_add_from(stackvec<size>& x, limb_span y) noexcept { + return large_add_from(x, y, 0); +} + +// grade-school multiplication algorithm +template <uint16_t size> +bool long_mul(stackvec<size>& x, limb_span y) noexcept { + limb_span xs = limb_span(x.data, x.len()); + stackvec<size> z(xs); + limb_span zs = limb_span(z.data, z.len()); + + if (y.len() != 0) { + limb y0 = y[0]; + FASTFLOAT_TRY(small_mul(x, y0)); + for (size_t index = 1; index < y.len(); index++) { + limb yi = y[index]; + stackvec<size> zi; + if (yi != 0) { + // re-use the same buffer throughout + zi.set_len(0); + FASTFLOAT_TRY(zi.try_extend(zs)); + FASTFLOAT_TRY(small_mul(zi, yi)); + limb_span zis = limb_span(zi.data, zi.len()); + FASTFLOAT_TRY(large_add_from(x, zis, index)); + } + } + } + + x.normalize(); + return true; +} + +// grade-school multiplication algorithm +template <uint16_t size> +bool large_mul(stackvec<size>& x, limb_span y) noexcept { + if (y.len() == 1) { + FASTFLOAT_TRY(small_mul(x, y[0])); + } else { + FASTFLOAT_TRY(long_mul(x, y)); + } + return true; +} + +// big integer type. implements a small subset of big integer +// arithmetic, using simple algorithms since asymptotically +// faster algorithms are slower for a small number of limbs. +// all operations assume the big-integer is normalized. +struct bigint { + // storage of the limbs, in little-endian order. + stackvec<bigint_limbs> vec; + + bigint(): vec() {} + bigint(const bigint &) = delete; + bigint &operator=(const bigint &) = delete; + bigint(bigint &&) = delete; + bigint &operator=(bigint &&other) = delete; + + bigint(uint64_t value): vec() { +#ifdef FASTFLOAT_64BIT_LIMB + vec.push_unchecked(value); +#else + vec.push_unchecked(uint32_t(value)); + vec.push_unchecked(uint32_t(value >> 32)); +#endif + vec.normalize(); + } + + // get the high 64 bits from the vector, and if bits were truncated. + // this is to get the significant digits for the float. + uint64_t hi64(bool& truncated) const noexcept { +#ifdef FASTFLOAT_64BIT_LIMB + if (vec.len() == 0) { + return empty_hi64(truncated); + } else if (vec.len() == 1) { + return uint64_hi64(vec.rindex(0), truncated); + } else { + uint64_t result = uint64_hi64(vec.rindex(0), vec.rindex(1), truncated); + truncated |= vec.nonzero(2); + return result; + } +#else + if (vec.len() == 0) { + return empty_hi64(truncated); + } else if (vec.len() == 1) { + return uint32_hi64(vec.rindex(0), truncated); + } else if (vec.len() == 2) { + return uint32_hi64(vec.rindex(0), vec.rindex(1), truncated); + } else { + uint64_t result = uint32_hi64(vec.rindex(0), vec.rindex(1), vec.rindex(2), truncated); + truncated |= vec.nonzero(3); + return result; + } +#endif + } + + // compare two big integers, returning the large value. + // assumes both are normalized. if the return value is + // negative, other is larger, if the return value is + // positive, this is larger, otherwise they are equal. + // the limbs are stored in little-endian order, so we + // must compare the limbs in ever order. + int compare(const bigint& other) const noexcept { + if (vec.len() > other.vec.len()) { + return 1; + } else if (vec.len() < other.vec.len()) { + return -1; + } else { + for (size_t index = vec.len(); index > 0; index--) { + limb xi = vec[index - 1]; + limb yi = other.vec[index - 1]; + if (xi > yi) { + return 1; + } else if (xi < yi) { + return -1; + } + } + return 0; + } + } + + // shift left each limb n bits, carrying over to the new limb + // returns true if we were able to shift all the digits. + bool shl_bits(size_t n) noexcept { + // Internally, for each item, we shift left by n, and add the previous + // right shifted limb-bits. + // For example, we transform (for u8) shifted left 2, to: + // b10100100 b01000010 + // b10 b10010001 b00001000 + FASTFLOAT_DEBUG_ASSERT(n != 0); + FASTFLOAT_DEBUG_ASSERT(n < sizeof(limb) * 8); + + size_t shl = n; + size_t shr = limb_bits - shl; + limb prev = 0; + for (size_t index = 0; index < vec.len(); index++) { + limb xi = vec[index]; + vec[index] = (xi << shl) | (prev >> shr); + prev = xi; + } + + limb carry = prev >> shr; + if (carry != 0) { + return vec.try_push(carry); + } + return true; + } + + // move the limbs left by `n` limbs. + bool shl_limbs(size_t n) noexcept { + FASTFLOAT_DEBUG_ASSERT(n != 0); + if (n + vec.len() > vec.capacity()) { + return false; + } else if (!vec.is_empty()) { + // move limbs + limb* dst = vec.data + n; + const limb* src = vec.data; + ::memmove(dst, src, sizeof(limb) * vec.len()); + // fill in empty limbs + limb* first = vec.data; + limb* last = first + n; + ::std::fill(first, last, 0); + vec.set_len(n + vec.len()); + return true; + } else { + return true; + } + } + + // move the limbs left by `n` bits. + bool shl(size_t n) noexcept { + size_t rem = n % limb_bits; + size_t div = n / limb_bits; + if (rem != 0) { + FASTFLOAT_TRY(shl_bits(rem)); + } + if (div != 0) { + FASTFLOAT_TRY(shl_limbs(div)); + } + return true; + } + + // get the number of leading zeros in the bigint. + int ctlz() const noexcept { + if (vec.is_empty()) { + return 0; + } else { +#ifdef FASTFLOAT_64BIT_LIMB + return leading_zeroes(vec.rindex(0)); +#else + // no use defining a specialized leading_zeroes for a 32-bit type. + uint64_t r0 = vec.rindex(0); + return leading_zeroes(r0 << 32); +#endif + } + } + + // get the number of bits in the bigint. + int bit_length() const noexcept { + int lz = ctlz(); + return int(limb_bits * vec.len()) - lz; + } + + bool mul(limb y) noexcept { + return small_mul(vec, y); + } + + bool add(limb y) noexcept { + return small_add(vec, y); + } + + // multiply as if by 2 raised to a power. + bool pow2(uint32_t exp) noexcept { + return shl(exp); + } + + // multiply as if by 5 raised to a power. + bool pow5(uint32_t exp) noexcept { + // multiply by a power of 5 + static constexpr uint32_t large_step = 135; + static constexpr uint64_t small_power_of_5[] = { + 1UL, 5UL, 25UL, 125UL, 625UL, 3125UL, 15625UL, 78125UL, 390625UL, + 1953125UL, 9765625UL, 48828125UL, 244140625UL, 1220703125UL, + 6103515625UL, 30517578125UL, 152587890625UL, 762939453125UL, + 3814697265625UL, 19073486328125UL, 95367431640625UL, 476837158203125UL, + 2384185791015625UL, 11920928955078125UL, 59604644775390625UL, + 298023223876953125UL, 1490116119384765625UL, 7450580596923828125UL, + }; +#ifdef FASTFLOAT_64BIT_LIMB + constexpr static limb large_power_of_5[] = { + 1414648277510068013UL, 9180637584431281687UL, 4539964771860779200UL, + 10482974169319127550UL, 198276706040285095UL}; +#else + constexpr static limb large_power_of_5[] = { + 4279965485U, 329373468U, 4020270615U, 2137533757U, 4287402176U, + 1057042919U, 1071430142U, 2440757623U, 381945767U, 46164893U}; +#endif + size_t large_length = sizeof(large_power_of_5) / sizeof(limb); + limb_span large = limb_span(large_power_of_5, large_length); + while (exp >= large_step) { + FASTFLOAT_TRY(large_mul(vec, large)); + exp -= large_step; + } +#ifdef FASTFLOAT_64BIT_LIMB + uint32_t small_step = 27; + limb max_native = 7450580596923828125UL; +#else + uint32_t small_step = 13; + limb max_native = 1220703125U; +#endif + while (exp >= small_step) { + FASTFLOAT_TRY(small_mul(vec, max_native)); + exp -= small_step; + } + if (exp != 0) { + FASTFLOAT_TRY(small_mul(vec, limb(small_power_of_5[exp]))); + } + + return true; + } + + // multiply as if by 10 raised to a power. + bool pow10(uint32_t exp) noexcept { + FASTFLOAT_TRY(pow5(exp)); + return pow2(exp); + } +}; + +} // namespace fast_float + +#endif + +#ifndef FASTFLOAT_ASCII_NUMBER_H +#define FASTFLOAT_ASCII_NUMBER_H + +#include <cctype> +#include <cstdint> +#include <cstring> +#include <iterator> + + +namespace fast_float { + +// Next function can be micro-optimized, but compilers are entirely +// able to optimize it well. +fastfloat_really_inline bool is_integer(char c) noexcept { return c >= '0' && c <= '9'; } + +fastfloat_really_inline uint64_t byteswap(uint64_t val) { + return (val & 0xFF00000000000000) >> 56 + | (val & 0x00FF000000000000) >> 40 + | (val & 0x0000FF0000000000) >> 24 + | (val & 0x000000FF00000000) >> 8 + | (val & 0x00000000FF000000) << 8 + | (val & 0x0000000000FF0000) << 24 + | (val & 0x000000000000FF00) << 40 + | (val & 0x00000000000000FF) << 56; +} + +fastfloat_really_inline uint64_t read_u64(const char *chars) { + uint64_t val; + ::memcpy(&val, chars, sizeof(uint64_t)); +#if FASTFLOAT_IS_BIG_ENDIAN == 1 + // Need to read as-if the number was in little-endian order. + val = byteswap(val); +#endif + return val; +} + +fastfloat_really_inline void write_u64(uint8_t *chars, uint64_t val) { +#if FASTFLOAT_IS_BIG_ENDIAN == 1 + // Need to read as-if the number was in little-endian order. + val = byteswap(val); +#endif + ::memcpy(chars, &val, sizeof(uint64_t)); +} + +// credit @aqrit +fastfloat_really_inline uint32_t parse_eight_digits_unrolled(uint64_t val) { + const uint64_t mask = 0x000000FF000000FF; + const uint64_t mul1 = 0x000F424000000064; // 100 + (1000000ULL << 32) + const uint64_t mul2 = 0x0000271000000001; // 1 + (10000ULL << 32) + val -= 0x3030303030303030; + val = (val * 10) + (val >> 8); // val = (val * 2561) >> 8; + val = (((val & mask) * mul1) + (((val >> 16) & mask) * mul2)) >> 32; + return uint32_t(val); +} + +fastfloat_really_inline uint32_t parse_eight_digits_unrolled(const char *chars) noexcept { + return parse_eight_digits_unrolled(read_u64(chars)); +} + +// credit @aqrit +fastfloat_really_inline bool is_made_of_eight_digits_fast(uint64_t val) noexcept { + return !((((val + 0x4646464646464646) | (val - 0x3030303030303030)) & + 0x8080808080808080)); +} + +fastfloat_really_inline bool is_made_of_eight_digits_fast(const char *chars) noexcept { + return is_made_of_eight_digits_fast(read_u64(chars)); +} + +typedef span<const char> byte_span; + +struct parsed_number_string { + int64_t exponent{0}; + uint64_t mantissa{0}; + const char *lastmatch{nullptr}; + bool negative{false}; + bool valid{false}; + bool too_many_digits{false}; + // contains the range of the significant digits + byte_span integer{}; // non-nullable + byte_span fraction{}; // nullable +}; + +// Assuming that you use no more than 19 digits, this will +// parse an ASCII string. +fastfloat_really_inline +parsed_number_string parse_number_string(const char *p, const char *pend, parse_options options) noexcept { + const chars_format fmt = options.format; + const char decimal_point = options.decimal_point; + + parsed_number_string answer; + answer.valid = false; + answer.too_many_digits = false; + answer.negative = (*p == '-'); + if (*p == '-') { // C++17 20.19.3.(7.1) explicitly forbids '+' sign here + ++p; + if (p == pend) { + return answer; + } + if (!is_integer(*p) && (*p != decimal_point)) { // a sign must be followed by an integer or the dot + return answer; + } + } + const char *const start_digits = p; + + uint64_t i = 0; // an unsigned int avoids signed overflows (which are bad) + + while ((std::distance(p, pend) >= 8) && is_made_of_eight_digits_fast(p)) { + i = i * 100000000 + parse_eight_digits_unrolled(p); // in rare cases, this will overflow, but that's ok + p += 8; + } + while ((p != pend) && is_integer(*p)) { + // a multiplication by 10 is cheaper than an arbitrary integer + // multiplication + i = 10 * i + + uint64_t(*p - '0'); // might overflow, we will handle the overflow later + ++p; + } + const char *const end_of_integer_part = p; + int64_t digit_count = int64_t(end_of_integer_part - start_digits); + answer.integer = byte_span(start_digits, size_t(digit_count)); + int64_t exponent = 0; + if ((p != pend) && (*p == decimal_point)) { + ++p; + const char* before = p; + // can occur at most twice without overflowing, but let it occur more, since + // for integers with many digits, digit parsing is the primary bottleneck. + while ((std::distance(p, pend) >= 8) && is_made_of_eight_digits_fast(p)) { + i = i * 100000000 + parse_eight_digits_unrolled(p); // in rare cases, this will overflow, but that's ok + p += 8; + } + while ((p != pend) && is_integer(*p)) { + uint8_t digit = uint8_t(*p - '0'); + ++p; + i = i * 10 + digit; // in rare cases, this will overflow, but that's ok + } + exponent = before - p; + answer.fraction = byte_span(before, size_t(p - before)); + digit_count -= exponent; + } + // we must have encountered at least one integer! + if (digit_count == 0) { + return answer; + } + int64_t exp_number = 0; // explicit exponential part + if ((fmt & chars_format::scientific) && (p != pend) && (('e' == *p) || ('E' == *p))) { + const char * location_of_e = p; + ++p; + bool neg_exp = false; + if ((p != pend) && ('-' == *p)) { + neg_exp = true; + ++p; + } else if ((p != pend) && ('+' == *p)) { // '+' on exponent is allowed by C++17 20.19.3.(7.1) + ++p; + } + if ((p == pend) || !is_integer(*p)) { + if(!(fmt & chars_format::fixed)) { + // We are in error. + return answer; + } + // Otherwise, we will be ignoring the 'e'. + p = location_of_e; + } else { + while ((p != pend) && is_integer(*p)) { + uint8_t digit = uint8_t(*p - '0'); + if (exp_number < 0x10000000) { + exp_number = 10 * exp_number + digit; + } + ++p; + } + if(neg_exp) { exp_number = - exp_number; } + exponent += exp_number; + } + } else { + // If it scientific and not fixed, we have to bail out. + if((fmt & chars_format::scientific) && !(fmt & chars_format::fixed)) { return answer; } + } + answer.lastmatch = p; + answer.valid = true; + + // If we frequently had to deal with long strings of digits, + // we could extend our code by using a 128-bit integer instead + // of a 64-bit integer. However, this is uncommon. + // + // We can deal with up to 19 digits. + if (digit_count > 19) { // this is uncommon + // It is possible that the integer had an overflow. + // We have to handle the case where we have 0.0000somenumber. + // We need to be mindful of the case where we only have zeroes... + // E.g., 0.000000000...000. + const char *start = start_digits; + while ((start != pend) && (*start == '0' || *start == decimal_point)) { + if(*start == '0') { digit_count --; } + start++; + } + if (digit_count > 19) { + answer.too_many_digits = true; + // Let us start again, this time, avoiding overflows. + // We don't need to check if is_integer, since we use the + // pre-tokenized spans from above. + i = 0; + p = answer.integer.ptr; + const char* int_end = p + answer.integer.len(); + const uint64_t minimal_nineteen_digit_integer{1000000000000000000}; + while((i < minimal_nineteen_digit_integer) && (p != int_end)) { + i = i * 10 + uint64_t(*p - '0'); + ++p; + } + if (i >= minimal_nineteen_digit_integer) { // We have a big integers + exponent = end_of_integer_part - p + exp_number; + } else { // We have a value with a fractional component. + p = answer.fraction.ptr; + const char* frac_end = p + answer.fraction.len(); + while((i < minimal_nineteen_digit_integer) && (p != frac_end)) { + i = i * 10 + uint64_t(*p - '0'); + ++p; + } + exponent = answer.fraction.ptr - p + exp_number; + } + // We have now corrected both exponent and i, to a truncated value + } + } + answer.exponent = exponent; + answer.mantissa = i; + return answer; +} + +} // namespace fast_float + +#endif + +#ifndef FASTFLOAT_DIGIT_COMPARISON_H +#define FASTFLOAT_DIGIT_COMPARISON_H + +#include <algorithm> +#include <cstdint> +#include <cstring> +#include <iterator> + + +namespace fast_float { + +// 1e0 to 1e19 +constexpr static uint64_t powers_of_ten_uint64[] = { + 1UL, 10UL, 100UL, 1000UL, 10000UL, 100000UL, 1000000UL, 10000000UL, 100000000UL, + 1000000000UL, 10000000000UL, 100000000000UL, 1000000000000UL, 10000000000000UL, + 100000000000000UL, 1000000000000000UL, 10000000000000000UL, 100000000000000000UL, + 1000000000000000000UL, 10000000000000000000UL}; + +// calculate the exponent, in scientific notation, of the number. +// this algorithm is not even close to optimized, but it has no practical +// effect on performance: in order to have a faster algorithm, we'd need +// to slow down performance for faster algorithms, and this is still fast. +fastfloat_really_inline int32_t scientific_exponent(parsed_number_string& num) noexcept { + uint64_t mantissa = num.mantissa; + int32_t exponent = int32_t(num.exponent); + while (mantissa >= 10000) { + mantissa /= 10000; + exponent += 4; + } + while (mantissa >= 100) { + mantissa /= 100; + exponent += 2; + } + while (mantissa >= 10) { + mantissa /= 10; + exponent += 1; + } + return exponent; +} + +// this converts a native floating-point number to an extended-precision float. +template <typename T> +fastfloat_really_inline adjusted_mantissa to_extended(T value) noexcept { + using equiv_uint = typename binary_format<T>::equiv_uint; + constexpr equiv_uint exponent_mask = binary_format<T>::exponent_mask(); + constexpr equiv_uint mantissa_mask = binary_format<T>::mantissa_mask(); + constexpr equiv_uint hidden_bit_mask = binary_format<T>::hidden_bit_mask(); + + adjusted_mantissa am; + int32_t bias = binary_format<T>::mantissa_explicit_bits() - binary_format<T>::minimum_exponent(); + equiv_uint bits; + ::memcpy(&bits, &value, sizeof(T)); + if ((bits & exponent_mask) == 0) { + // denormal + am.power2 = 1 - bias; + am.mantissa = bits & mantissa_mask; + } else { + // normal + am.power2 = int32_t((bits & exponent_mask) >> binary_format<T>::mantissa_explicit_bits()); + am.power2 -= bias; + am.mantissa = (bits & mantissa_mask) | hidden_bit_mask; + } + + return am; +} + +// get the extended precision value of the halfway point between b and b+u. +// we are given a native float that represents b, so we need to adjust it +// halfway between b and b+u. +template <typename T> +fastfloat_really_inline adjusted_mantissa to_extended_halfway(T value) noexcept { + adjusted_mantissa am = to_extended(value); + am.mantissa <<= 1; + am.mantissa += 1; + am.power2 -= 1; + return am; +} + +// round an extended-precision float to the nearest machine float. +template <typename T, typename callback> +fastfloat_really_inline void round(adjusted_mantissa& am, callback cb) noexcept { + int32_t mantissa_shift = 64 - binary_format<T>::mantissa_explicit_bits() - 1; + if (-am.power2 >= mantissa_shift) { + // have a denormal float + int32_t shift = -am.power2 + 1; + cb(am, std::min(shift, 64)); + // check for round-up: if rounding-nearest carried us to the hidden bit. + am.power2 = (am.mantissa < (uint64_t(1) << binary_format<T>::mantissa_explicit_bits())) ? 0 : 1; + return; + } + + // have a normal float, use the default shift. + cb(am, mantissa_shift); + + // check for carry + if (am.mantissa >= (uint64_t(2) << binary_format<T>::mantissa_explicit_bits())) { + am.mantissa = (uint64_t(1) << binary_format<T>::mantissa_explicit_bits()); + am.power2++; + } + + // check for infinite: we could have carried to an infinite power + am.mantissa &= ~(uint64_t(1) << binary_format<T>::mantissa_explicit_bits()); + if (am.power2 >= binary_format<T>::infinite_power()) { + am.power2 = binary_format<T>::infinite_power(); + am.mantissa = 0; + } +} + +template <typename callback> +fastfloat_really_inline +void round_nearest_tie_even(adjusted_mantissa& am, int32_t shift, callback cb) noexcept { + uint64_t mask; + uint64_t halfway; + if (shift == 64) { + mask = UINT64_MAX; + } else { + mask = (uint64_t(1) << shift) - 1; + } + if (shift == 0) { + halfway = 0; + } else { + halfway = uint64_t(1) << (shift - 1); + } + uint64_t truncated_bits = am.mantissa & mask; + uint64_t is_above = truncated_bits > halfway; + uint64_t is_halfway = truncated_bits == halfway; + + // shift digits into position + if (shift == 64) { + am.mantissa = 0; + } else { + am.mantissa >>= shift; + } + am.power2 += shift; + + bool is_odd = (am.mantissa & 1) == 1; + am.mantissa += uint64_t(cb(is_odd, is_halfway, is_above)); +} + +fastfloat_really_inline void round_down(adjusted_mantissa& am, int32_t shift) noexcept { + if (shift == 64) { + am.mantissa = 0; + } else { + am.mantissa >>= shift; + } + am.power2 += shift; +} + +fastfloat_really_inline void skip_zeros(const char*& first, const char* last) noexcept { + uint64_t val; + while (std::distance(first, last) >= 8) { + ::memcpy(&val, first, sizeof(uint64_t)); + if (val != 0x3030303030303030) { + break; + } + first += 8; + } + while (first != last) { + if (*first != '0') { + break; + } + first++; + } +} + +// determine if any non-zero digits were truncated. +// all characters must be valid digits. +fastfloat_really_inline bool is_truncated(const char* first, const char* last) noexcept { + // do 8-bit optimizations, can just compare to 8 literal 0s. + uint64_t val; + while (std::distance(first, last) >= 8) { + ::memcpy(&val, first, sizeof(uint64_t)); + if (val != 0x3030303030303030) { + return true; + } + first += 8; + } + while (first != last) { + if (*first != '0') { + return true; + } + first++; + } + return false; +} + +fastfloat_really_inline bool is_truncated(byte_span s) noexcept { + return is_truncated(s.ptr, s.ptr + s.len()); +} + +fastfloat_really_inline +void parse_eight_digits(const char*& p, limb& value, size_t& counter, size_t& count) noexcept { + value = value * 100000000 + parse_eight_digits_unrolled(p); + p += 8; + counter += 8; + count += 8; +} + +fastfloat_really_inline +void parse_one_digit(const char*& p, limb& value, size_t& counter, size_t& count) noexcept { + value = value * 10 + limb(*p - '0'); + p++; + counter++; + count++; +} + +fastfloat_really_inline +void add_native(bigint& big, limb power, limb value) noexcept { + big.mul(power); + big.add(value); +} + +fastfloat_really_inline void round_up_bigint(bigint& big, size_t& count) noexcept { + // need to round-up the digits, but need to avoid rounding + // ....9999 to ...10000, which could cause a false halfway point. + add_native(big, 10, 1); + count++; +} + +// parse the significant digits into a big integer +inline void parse_mantissa(bigint& result, parsed_number_string& num, size_t max_digits, size_t& digits) noexcept { + // try to minimize the number of big integer and scalar multiplication. + // therefore, try to parse 8 digits at a time, and multiply by the largest + // scalar value (9 or 19 digits) for each step. + size_t counter = 0; + digits = 0; + limb value = 0; +#ifdef FASTFLOAT_64BIT_LIMB + size_t step = 19; +#else + size_t step = 9; +#endif + + // process all integer digits. + const char* p = num.integer.ptr; + const char* pend = p + num.integer.len(); + skip_zeros(p, pend); + // process all digits, in increments of step per loop + while (p != pend) { + while ((std::distance(p, pend) >= 8) && (step - counter >= 8) && (max_digits - digits >= 8)) { + parse_eight_digits(p, value, counter, digits); + } + while (counter < step && p != pend && digits < max_digits) { + parse_one_digit(p, value, counter, digits); + } + if (digits == max_digits) { + // add the temporary value, then check if we've truncated any digits + add_native(result, limb(powers_of_ten_uint64[counter]), value); + bool truncated = is_truncated(p, pend); + if (num.fraction.ptr != nullptr) { + truncated |= is_truncated(num.fraction); + } + if (truncated) { + round_up_bigint(result, digits); + } + return; + } else { + add_native(result, limb(powers_of_ten_uint64[counter]), value); + counter = 0; + value = 0; + } + } + + // add our fraction digits, if they're available. + if (num.fraction.ptr != nullptr) { + p = num.fraction.ptr; + pend = p + num.fraction.len(); + if (digits == 0) { + skip_zeros(p, pend); + } + // process all digits, in increments of step per loop + while (p != pend) { + while ((std::distance(p, pend) >= 8) && (step - counter >= 8) && (max_digits - digits >= 8)) { + parse_eight_digits(p, value, counter, digits); + } + while (counter < step && p != pend && digits < max_digits) { + parse_one_digit(p, value, counter, digits); + } + if (digits == max_digits) { + // add the temporary value, then check if we've truncated any digits + add_native(result, limb(powers_of_ten_uint64[counter]), value); + bool truncated = is_truncated(p, pend); + if (truncated) { + round_up_bigint(result, digits); + } + return; + } else { + add_native(result, limb(powers_of_ten_uint64[counter]), value); + counter = 0; + value = 0; + } + } + } + + if (counter != 0) { + add_native(result, limb(powers_of_ten_uint64[counter]), value); + } +} + +template <typename T> +inline adjusted_mantissa positive_digit_comp(bigint& bigmant, int32_t exponent) noexcept { + FASTFLOAT_ASSERT(bigmant.pow10(uint32_t(exponent))); + adjusted_mantissa answer; + bool truncated; + answer.mantissa = bigmant.hi64(truncated); + int bias = binary_format<T>::mantissa_explicit_bits() - binary_format<T>::minimum_exponent(); + answer.power2 = bigmant.bit_length() - 64 + bias; + + round<T>(answer, [truncated](adjusted_mantissa& a, int32_t shift) { + round_nearest_tie_even(a, shift, [truncated](bool is_odd, bool is_halfway, bool is_above) -> bool { + return is_above || (is_halfway && truncated) || (is_odd && is_halfway); + }); + }); + + return answer; +} + +// the scaling here is quite simple: we have, for the real digits `m * 10^e`, +// and for the theoretical digits `n * 2^f`. Since `e` is always negative, +// to scale them identically, we do `n * 2^f * 5^-f`, so we now have `m * 2^e`. +// we then need to scale by `2^(f- e)`, and then the two significant digits +// are of the same magnitude. +template <typename T> +inline adjusted_mantissa negative_digit_comp(bigint& bigmant, adjusted_mantissa am, int32_t exponent) noexcept { + bigint& real_digits = bigmant; + int32_t real_exp = exponent; + + // get the value of `b`, rounded down, and get a bigint representation of b+h + adjusted_mantissa am_b = am; + // gcc7 buf: use a lambda to remove the noexcept qualifier bug with -Wnoexcept-type. + round<T>(am_b, [](adjusted_mantissa&a, int32_t shift) { round_down(a, shift); }); + T b; + to_float(false, am_b, b); + adjusted_mantissa theor = to_extended_halfway(b); + bigint theor_digits(theor.mantissa); + int32_t theor_exp = theor.power2; + + // scale real digits and theor digits to be same power. + int32_t pow2_exp = theor_exp - real_exp; + uint32_t pow5_exp = uint32_t(-real_exp); + if (pow5_exp != 0) { + FASTFLOAT_ASSERT(theor_digits.pow5(pow5_exp)); + } + if (pow2_exp > 0) { + FASTFLOAT_ASSERT(theor_digits.pow2(uint32_t(pow2_exp))); + } else if (pow2_exp < 0) { + FASTFLOAT_ASSERT(real_digits.pow2(uint32_t(-pow2_exp))); + } + + // compare digits, and use it to director rounding + int ord = real_digits.compare(theor_digits); + adjusted_mantissa answer = am; + round<T>(answer, [ord](adjusted_mantissa& a, int32_t shift) { + round_nearest_tie_even(a, shift, [ord](bool is_odd, bool _, bool __) -> bool { + (void)_; // not needed, since we've done our comparison + (void)__; // not needed, since we've done our comparison + if (ord > 0) { + return true; + } else if (ord < 0) { + return false; + } else { + return is_odd; + } + }); + }); + + return answer; +} + +// parse the significant digits as a big integer to unambiguously round the +// the significant digits. here, we are trying to determine how to round +// an extended float representation close to `b+h`, halfway between `b` +// (the float rounded-down) and `b+u`, the next positive float. this +// algorithm is always correct, and uses one of two approaches. when +// the exponent is positive relative to the significant digits (such as +// 1234), we create a big-integer representation, get the high 64-bits, +// determine if any lower bits are truncated, and use that to direct +// rounding. in case of a negative exponent relative to the significant +// digits (such as 1.2345), we create a theoretical representation of +// `b` as a big-integer type, scaled to the same binary exponent as +// the actual digits. we then compare the big integer representations +// of both, and use that to direct rounding. +template <typename T> +inline adjusted_mantissa digit_comp(parsed_number_string& num, adjusted_mantissa am) noexcept { + // remove the invalid exponent bias + am.power2 -= invalid_am_bias; + + int32_t sci_exp = scientific_exponent(num); + size_t max_digits = binary_format<T>::max_digits(); + size_t digits = 0; + bigint bigmant; + parse_mantissa(bigmant, num, max_digits, digits); + // can't underflow, since digits is at most max_digits. + int32_t exponent = sci_exp + 1 - int32_t(digits); + if (exponent >= 0) { + return positive_digit_comp<T>(bigmant, exponent); + } else { + return negative_digit_comp<T>(bigmant, am, exponent); + } +} + +} // namespace fast_float + +#endif + +#ifndef FASTFLOAT_PARSE_NUMBER_H +#define FASTFLOAT_PARSE_NUMBER_H + + +#include <cmath> +#include <cstring> +#include <limits> +#include <system_error> + +namespace fast_float { + + +namespace detail { +/** + * Special case +inf, -inf, nan, infinity, -infinity. + * The case comparisons could be made much faster given that we know that the + * strings a null-free and fixed. + **/ +template <typename T> +from_chars_result parse_infnan(const char *first, const char *last, T &value) noexcept { + from_chars_result answer; + answer.ptr = first; + answer.ec = std::errc(); // be optimistic + bool minusSign = false; + if (*first == '-') { // assume first < last, so dereference without checks; C++17 20.19.3.(7.1) explicitly forbids '+' here + minusSign = true; + ++first; + } + if (last - first >= 3) { + if (fastfloat_strncasecmp(first, "nan", 3)) { + answer.ptr = (first += 3); + value = minusSign ? -std::numeric_limits<T>::quiet_NaN() : std::numeric_limits<T>::quiet_NaN(); + // Check for possible nan(n-char-seq-opt), C++17 20.19.3.7, C11 7.20.1.3.3. At least MSVC produces nan(ind) and nan(snan). + if(first != last && *first == '(') { + for(const char* ptr = first + 1; ptr != last; ++ptr) { + if (*ptr == ')') { + answer.ptr = ptr + 1; // valid nan(n-char-seq-opt) + break; + } + else if(!(('a' <= *ptr && *ptr <= 'z') || ('A' <= *ptr && *ptr <= 'Z') || ('0' <= *ptr && *ptr <= '9') || *ptr == '_')) + break; // forbidden char, not nan(n-char-seq-opt) + } + } + return answer; + } + if (fastfloat_strncasecmp(first, "inf", 3)) { + if ((last - first >= 8) && fastfloat_strncasecmp(first + 3, "inity", 5)) { + answer.ptr = first + 8; + } else { + answer.ptr = first + 3; + } + value = minusSign ? -std::numeric_limits<T>::infinity() : std::numeric_limits<T>::infinity(); + return answer; + } + } + answer.ec = std::errc::invalid_argument; + return answer; +} + +} // namespace detail + +template<typename T> +from_chars_result from_chars(const char *first, const char *last, + T &value, chars_format fmt /*= chars_format::general*/) noexcept { + return from_chars_advanced(first, last, value, parse_options{fmt}); +} + +template<typename T> +from_chars_result from_chars_advanced(const char *first, const char *last, + T &value, parse_options options) noexcept { + + static_assert (std::is_same<T, double>::value || std::is_same<T, float>::value, "only float and double are supported"); + + + from_chars_result answer; + if (first == last) { + answer.ec = std::errc::invalid_argument; + answer.ptr = first; + return answer; + } + parsed_number_string pns = parse_number_string(first, last, options); + if (!pns.valid) { + return detail::parse_infnan(first, last, value); + } + answer.ec = std::errc(); // be optimistic + answer.ptr = pns.lastmatch; + // Next is Clinger's fast path. + if (binary_format<T>::min_exponent_fast_path() <= pns.exponent && pns.exponent <= binary_format<T>::max_exponent_fast_path() && pns.mantissa <=binary_format<T>::max_mantissa_fast_path() && !pns.too_many_digits) { + value = T(pns.mantissa); + if (pns.exponent < 0) { value = value / binary_format<T>::exact_power_of_ten(-pns.exponent); } + else { value = value * binary_format<T>::exact_power_of_ten(pns.exponent); } + if (pns.negative) { value = -value; } + return answer; + } + adjusted_mantissa am = compute_float<binary_format<T>>(pns.exponent, pns.mantissa); + if(pns.too_many_digits && am.power2 >= 0) { + if(am != compute_float<binary_format<T>>(pns.exponent, pns.mantissa + 1)) { + am = compute_error<binary_format<T>>(pns.exponent, pns.mantissa); + } + } + // If we called compute_float<binary_format<T>>(pns.exponent, pns.mantissa) and we have an invalid power (am.power2 < 0), + // then we need to go the long way around again. This is very uncommon. + if(am.power2 < 0) { am = digit_comp<T>(pns, am); } + to_float(pns.negative, am, value); + return answer; +} + +} // namespace fast_float + +#endif + diff --git a/intern/cycles/blender/addon/properties.py b/intern/cycles/blender/addon/properties.py index ee051766c50..ed054b041d8 100644 --- a/intern/cycles/blender/addon/properties.py +++ b/intern/cycles/blender/addon/properties.py @@ -1507,7 +1507,7 @@ class CyclesPreferences(bpy.types.AddonPreferences): 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 Vega architecture", icon='BLANK1') + col.label(text="Requires discrete AMD GPU with RDNA architecture", icon='BLANK1') if sys.platform[:3] == "win": col.label(text="and AMD Radeon Pro 21.Q4 driver or newer", icon='BLANK1') elif device_type == 'METAL': diff --git a/intern/cycles/device/hip/util.h b/intern/cycles/device/hip/util.h index 4e4906171d1..adb68a2d44c 100644 --- a/intern/cycles/device/hip/util.h +++ b/intern/cycles/device/hip/util.h @@ -51,7 +51,7 @@ static inline bool hipSupportsDevice(const int hipDevId) hipDeviceGetAttribute(&major, hipDeviceAttributeComputeCapabilityMajor, hipDevId); hipDeviceGetAttribute(&minor, hipDeviceAttributeComputeCapabilityMinor, hipDevId); - return (major >= 9); + return (major > 10) || (major == 10 && minor >= 1); } CCL_NAMESPACE_END diff --git a/intern/cycles/device/metal/kernel.mm b/intern/cycles/device/metal/kernel.mm index 1434b297ddd..9555ca03c8e 100644 --- a/intern/cycles/device/metal/kernel.mm +++ b/intern/cycles/device/metal/kernel.mm @@ -459,7 +459,7 @@ bool MetalDeviceKernels::load(MetalDevice *device, int kernel_type) tbb::task_arena local_arena(max_mtlcompiler_threads); local_arena.execute([&]() { - tbb::parallel_for(int(0), int(DEVICE_KERNEL_NUM), [&](int i) { + parallel_for(int(0), int(DEVICE_KERNEL_NUM), [&](int i) { /* skip megakernel */ if (i == DEVICE_KERNEL_INTEGRATOR_MEGAKERNEL) { return; diff --git a/intern/cycles/hydra/camera.cpp b/intern/cycles/hydra/camera.cpp index c746a107899..62042cbbcd2 100644 --- a/intern/cycles/hydra/camera.cpp +++ b/intern/cycles/hydra/camera.cpp @@ -3,6 +3,7 @@ * Copyright 2022 Blender Foundation */ #include "hydra/camera.h" +#include "hydra/session.h" #include "scene/camera.h" #include <pxr/base/gf/frustum.h> @@ -12,6 +13,19 @@ HDCYCLES_NAMESPACE_OPEN_SCOPE extern Transform convert_transform(const GfMatrix4d &matrix); +Transform convert_camera_transform(const GfMatrix4d &matrix, float metersPerUnit) +{ + Transform t = convert_transform(matrix); + // Flip Z axis + t.x.z *= -1.0f; + t.y.z *= -1.0f; + t.z.z *= -1.0f; + // Scale translation + t.x.w *= metersPerUnit; + t.y.w *= metersPerUnit; + t.z.w *= metersPerUnit; + return t; +} #if PXR_VERSION < 2102 // clang-format off @@ -61,13 +75,20 @@ void HdCyclesCamera::Sync(HdSceneDelegate *sceneDelegate, if (*dirtyBits & DirtyBits::DirtyTransform) { sceneDelegate->SampleTransform(id, &_transformSamples); + bool transform_found = false; for (size_t i = 0; i < _transformSamples.count; ++i) { if (_transformSamples.times[i] == 0.0f) { _transform = _transformSamples.values[i]; _data.SetTransform(_transform); + transform_found = true; break; } } + + if (!transform_found && _transformSamples.count) { + _transform = _transformSamples.values[0]; + _data.SetTransform(_transform); + } } #else if (*dirtyBits & DirtyBits::DirtyViewMatrix) { @@ -236,18 +257,21 @@ void HdCyclesCamera::Finalize(HdRenderParam *renderParam) HdCamera::Finalize(renderParam); } -void HdCyclesCamera::ApplyCameraSettings(Camera *cam) const +void HdCyclesCamera::ApplyCameraSettings(HdRenderParam *renderParam, Camera *cam) const { - ApplyCameraSettings(_data, _windowPolicy, cam); + ApplyCameraSettings(renderParam, _data, _windowPolicy, cam); + + const float metersPerUnit = static_cast<HdCyclesSession *>(renderParam)->GetStageMetersPerUnit(); array<Transform> motion(_transformSamples.count); - for (size_t i = 0; i < _transformSamples.count; ++i) - motion[i] = convert_transform(_transformSamples.values[i]) * - transform_scale(1.0f, 1.0f, -1.0f); + for (size_t i = 0; i < _transformSamples.count; ++i) { + motion[i] = convert_camera_transform(_transformSamples.values[i], metersPerUnit); + } cam->set_motion(motion); } -void HdCyclesCamera::ApplyCameraSettings(const GfCamera &dataUnconformedWindow, +void HdCyclesCamera::ApplyCameraSettings(HdRenderParam *renderParam, + const GfCamera &dataUnconformedWindow, CameraUtilConformWindowPolicy windowPolicy, Camera *cam) { @@ -261,20 +285,22 @@ void HdCyclesCamera::ApplyCameraSettings(const GfCamera &dataUnconformedWindow, GfCamera::Orthographic == CAMERA_ORTHOGRAPHIC); cam->set_camera_type(static_cast<CameraType>(data.GetProjection())); + const float metersPerUnit = static_cast<HdCyclesSession *>(renderParam)->GetStageMetersPerUnit(); + auto viewplane = data.GetFrustum().GetWindow(); auto focalLength = 1.0f; if (data.GetProjection() == GfCamera::Perspective) { viewplane *= 2.0 / viewplane.GetSize()[1]; // Normalize viewplane - focalLength = data.GetFocalLength() * 1e-3f; + focalLength = data.GetFocalLength() * GfCamera::FOCAL_LENGTH_UNIT * metersPerUnit; cam->set_fov(GfDegreesToRadians(data.GetFieldOfView(GfCamera::FOVVertical))); } - cam->set_sensorwidth(data.GetHorizontalAperture() * GfCamera::APERTURE_UNIT); - cam->set_sensorheight(data.GetVerticalAperture() * GfCamera::APERTURE_UNIT); + cam->set_sensorwidth(data.GetHorizontalAperture() * GfCamera::APERTURE_UNIT * metersPerUnit); + cam->set_sensorheight(data.GetVerticalAperture() * GfCamera::APERTURE_UNIT * metersPerUnit); - cam->set_nearclip(data.GetClippingRange().GetMin()); - cam->set_farclip(data.GetClippingRange().GetMax()); + cam->set_nearclip(data.GetClippingRange().GetMin() * metersPerUnit); + cam->set_farclip(data.GetClippingRange().GetMax() * metersPerUnit); cam->set_viewplane_left(viewplane.GetMin()[0]); cam->set_viewplane_right(viewplane.GetMax()[0]); @@ -282,14 +308,15 @@ void HdCyclesCamera::ApplyCameraSettings(const GfCamera &dataUnconformedWindow, cam->set_viewplane_top(viewplane.GetMax()[1]); if (data.GetFStop() != 0.0f) { - cam->set_focaldistance(data.GetFocusDistance()); + cam->set_focaldistance(data.GetFocusDistance() * metersPerUnit); cam->set_aperturesize(focalLength / (2.0f * data.GetFStop())); } - cam->set_matrix(convert_transform(data.GetTransform()) * transform_scale(1.0f, 1.0f, -1.0f)); + cam->set_matrix(convert_camera_transform(data.GetTransform(), metersPerUnit)); } -void HdCyclesCamera::ApplyCameraSettings(const GfMatrix4d &worldToViewMatrix, +void HdCyclesCamera::ApplyCameraSettings(HdRenderParam *renderParam, + const GfMatrix4d &worldToViewMatrix, const GfMatrix4d &projectionMatrix, const std::vector<GfVec4d> &clipPlanes, Camera *cam) @@ -298,7 +325,7 @@ void HdCyclesCamera::ApplyCameraSettings(const GfMatrix4d &worldToViewMatrix, GfCamera data; data.SetFromViewAndProjectionMatrix(worldToViewMatrix, projectionMatrix); - ApplyCameraSettings(data, CameraUtilFit, cam); + ApplyCameraSettings(renderParam, data, CameraUtilFit, cam); #else TF_CODING_ERROR("Not implemented"); #endif diff --git a/intern/cycles/hydra/camera.h b/intern/cycles/hydra/camera.h index 8b7fed5a6bb..d839164317f 100644 --- a/intern/cycles/hydra/camera.h +++ b/intern/cycles/hydra/camera.h @@ -17,12 +17,14 @@ class HdCyclesCamera final : public PXR_NS::HdCamera { HdCyclesCamera(const PXR_NS::SdfPath &sprimId); ~HdCyclesCamera() override; - void ApplyCameraSettings(CCL_NS::Camera *targetCamera) const; + void ApplyCameraSettings(PXR_NS::HdRenderParam *renderParam, CCL_NS::Camera *targetCamera) const; - static void ApplyCameraSettings(const PXR_NS::GfCamera &cameraData, + static void ApplyCameraSettings(PXR_NS::HdRenderParam *renderParam, + const PXR_NS::GfCamera &cameraData, PXR_NS::CameraUtilConformWindowPolicy windowPolicy, CCL_NS::Camera *targetCamera); - static void ApplyCameraSettings(const PXR_NS::GfMatrix4d &worldToViewMatrix, + static void ApplyCameraSettings(PXR_NS::HdRenderParam *renderParam, + const PXR_NS::GfMatrix4d &worldToViewMatrix, const PXR_NS::GfMatrix4d &projectionMatrix, const std::vector<PXR_NS::GfVec4d> &clipPlanes, CCL_NS::Camera *targetCamera); diff --git a/intern/cycles/hydra/geometry.inl b/intern/cycles/hydra/geometry.inl index 007fc6f2667..3e02a59ea83 100644 --- a/intern/cycles/hydra/geometry.inl +++ b/intern/cycles/hydra/geometry.inl @@ -153,7 +153,11 @@ void HdCyclesGeometry<Base, CyclesBase>::Sync(HdSceneDelegate *sceneDelegate, // Update transforms of all instances for (size_t i = 0; i < transforms.size(); ++i) { - const Transform tfm = convert_transform(_geomTransform * transforms[i]); + const float metersPerUnit = + static_cast<HdCyclesSession *>(renderParam)->GetStageMetersPerUnit(); + + const Transform tfm = transform_scale(make_float3(metersPerUnit)) * + convert_transform(_geomTransform * transforms[i]); _instances[i]->set_tfm(tfm); } } diff --git a/intern/cycles/hydra/light.cpp b/intern/cycles/hydra/light.cpp index b691da0d6a6..c0b4b3a3f38 100644 --- a/intern/cycles/hydra/light.cpp +++ b/intern/cycles/hydra/light.cpp @@ -54,11 +54,16 @@ void HdCyclesLight::Sync(HdSceneDelegate *sceneDelegate, const SdfPath &id = GetId(); if (*dirtyBits & DirtyBits::DirtyTransform) { + const float metersPerUnit = + static_cast<HdCyclesSession *>(renderParam)->GetStageMetersPerUnit(); + + const Transform tfm = transform_scale(make_float3(metersPerUnit)) * #if PXR_VERSION >= 2011 - const Transform tfm = convert_transform(sceneDelegate->GetTransform(id)); + convert_transform(sceneDelegate->GetTransform(id)); #else - const Transform tfm = convert_transform( - sceneDelegate->GetLightParamValue(id, HdTokens->transform).Get<GfMatrix4d>()); + convert_transform( + sceneDelegate->GetLightParamValue(id, HdTokens->transform) + .Get<GfMatrix4d>()); #endif _light->set_tfm(tfm); diff --git a/intern/cycles/hydra/render_delegate.cpp b/intern/cycles/hydra/render_delegate.cpp index a954c3e4d72..faefe9382e9 100644 --- a/intern/cycles/hydra/render_delegate.cpp +++ b/intern/cycles/hydra/render_delegate.cpp @@ -33,11 +33,12 @@ TF_DEFINE_PRIVATE_TOKENS(_tokens, ); TF_DEFINE_PRIVATE_TOKENS(HdCyclesRenderSettingsTokens, + (stageMetersPerUnit) ((device, "cycles:device")) ((threads, "cycles:threads")) - ((time_limit, "cycles:time_limit")) + ((timeLimit, "cycles:time_limit")) ((samples, "cycles:samples")) - ((sample_offset, "cycles:sample_offset")) + ((sampleOffset, "cycles:sample_offset")) ); // clang-format on @@ -424,7 +425,7 @@ HdRenderSettingDescriptorList HdCyclesDelegate::GetRenderSettingDescriptors() co descriptors.push_back({ "Time Limit", - HdCyclesRenderSettingsTokens->time_limit, + HdCyclesRenderSettingsTokens->timeLimit, VtValue(0.0), }); descriptors.push_back({ @@ -434,7 +435,7 @@ HdRenderSettingDescriptorList HdCyclesDelegate::GetRenderSettingDescriptors() co }); descriptors.push_back({ "Sample Offset", - HdCyclesRenderSettingsTokens->sample_offset, + HdCyclesRenderSettingsTokens->sampleOffset, VtValue(0), }); @@ -452,7 +453,11 @@ void HdCyclesDelegate::SetRenderSetting(const PXR_NS::TfToken &key, const PXR_NS Scene *const scene = _renderParam->session->scene; Session *const session = _renderParam->session; - if (key == HdCyclesRenderSettingsTokens->time_limit) { + if (key == HdCyclesRenderSettingsTokens->stageMetersPerUnit) { + _renderParam->SetStageMetersPerUnit( + VtValue::Cast<double>(value).GetWithDefault(_renderParam->GetStageMetersPerUnit())); + } + else if (key == HdCyclesRenderSettingsTokens->timeLimit) { session->set_time_limit( VtValue::Cast<double>(value).GetWithDefault(session->params.time_limit)); } @@ -462,7 +467,7 @@ void HdCyclesDelegate::SetRenderSetting(const PXR_NS::TfToken &key, const PXR_NS samples = std::min(std::max(1, samples), max_samples); session->set_samples(samples); } - else if (key == HdCyclesRenderSettingsTokens->sample_offset) { + else if (key == HdCyclesRenderSettingsTokens->sampleOffset) { session->params.sample_offset = VtValue::Cast<int>(value).GetWithDefault( session->params.sample_offset); ++_settingsVersion; @@ -484,19 +489,22 @@ VtValue HdCyclesDelegate::GetRenderSetting(const TfToken &key) const Scene *const scene = _renderParam->session->scene; Session *const session = _renderParam->session; - if (key == HdCyclesRenderSettingsTokens->device) { + if (key == HdCyclesRenderSettingsTokens->stageMetersPerUnit) { + return VtValue(_renderParam->GetStageMetersPerUnit()); + } + else if (key == HdCyclesRenderSettingsTokens->device) { return VtValue(TfToken(Device::string_from_type(session->params.device.type))); } else if (key == HdCyclesRenderSettingsTokens->threads) { return VtValue(session->params.threads); } - else if (key == HdCyclesRenderSettingsTokens->time_limit) { + else if (key == HdCyclesRenderSettingsTokens->timeLimit) { return VtValue(session->params.time_limit); } else if (key == HdCyclesRenderSettingsTokens->samples) { return VtValue(session->params.samples); } - else if (key == HdCyclesRenderSettingsTokens->sample_offset) { + else if (key == HdCyclesRenderSettingsTokens->sampleOffset) { return VtValue(session->params.sample_offset); } else { diff --git a/intern/cycles/hydra/render_pass.cpp b/intern/cycles/hydra/render_pass.cpp index 9d47dfc5c8d..8f6f934b898 100644 --- a/intern/cycles/hydra/render_pass.cpp +++ b/intern/cycles/hydra/render_pass.cpp @@ -117,10 +117,11 @@ void HdCyclesRenderPass::_Execute(const HdRenderPassStateSharedPtr &renderPassSt #endif if (const auto camera = static_cast<const HdCyclesCamera *>(renderPassState->GetCamera())) { - camera->ApplyCameraSettings(scene->camera); + camera->ApplyCameraSettings(_renderParam, scene->camera); } else { - HdCyclesCamera::ApplyCameraSettings(renderPassState->GetWorldToViewMatrix(), + HdCyclesCamera::ApplyCameraSettings(_renderParam, + renderPassState->GetWorldToViewMatrix(), renderPassState->GetProjectionMatrix(), renderPassState->GetClipPlanes(), scene->camera); diff --git a/intern/cycles/hydra/session.h b/intern/cycles/hydra/session.h index 7e649c1847a..8d5553bf6d7 100644 --- a/intern/cycles/hydra/session.h +++ b/intern/cycles/hydra/session.h @@ -29,6 +29,16 @@ class HdCyclesSession final : public PXR_NS::HdRenderParam { void UpdateScene(); + double GetStageMetersPerUnit() const + { + return _stageMetersPerUnit; + } + + void SetStageMetersPerUnit(double stageMetersPerUnit) + { + _stageMetersPerUnit = stageMetersPerUnit; + } + PXR_NS::HdRenderPassAovBinding GetDisplayAovBinding() const { return _displayAovBinding; @@ -52,6 +62,7 @@ class HdCyclesSession final : public PXR_NS::HdRenderParam { private: const bool _ownCyclesSession; + double _stageMetersPerUnit = 0.01; PXR_NS::HdRenderPassAovBindingVector _aovBindings; PXR_NS::HdRenderPassAovBinding _displayAovBinding; }; diff --git a/intern/cycles/integrator/pass_accessor_cpu.cpp b/intern/cycles/integrator/pass_accessor_cpu.cpp index 509190c8a7e..02260a54bf4 100644 --- a/intern/cycles/integrator/pass_accessor_cpu.cpp +++ b/intern/cycles/integrator/pass_accessor_cpu.cpp @@ -44,7 +44,7 @@ inline void PassAccessorCPU::run_get_pass_kernel_processor_float( const int pixel_stride = destination.pixel_stride ? destination.pixel_stride : destination.num_components; - tbb::parallel_for(0, buffer_params.window_height, [&](int64_t y) { + 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; @@ -69,7 +69,7 @@ inline void PassAccessorCPU::run_get_pass_kernel_processor_half_rgba( const int destination_stride = destination.stride != 0 ? destination.stride : buffer_params.width; - tbb::parallel_for(0, buffer_params.window_height, [&](int64_t y) { + 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; func(kfilm_convert, buffer, pixel, buffer_params.window_width, pass_stride); diff --git a/intern/cycles/integrator/path_trace.cpp b/intern/cycles/integrator/path_trace.cpp index ab134179602..4ecd3b829e8 100644 --- a/intern/cycles/integrator/path_trace.cpp +++ b/intern/cycles/integrator/path_trace.cpp @@ -334,7 +334,7 @@ void PathTrace::init_render_buffers(const RenderWork &render_work) /* Handle initialization scheduled by the render scheduler. */ if (render_work.init_render_buffers) { - tbb::parallel_for_each(path_trace_works_, [&](unique_ptr<PathTraceWork> &path_trace_work) { + parallel_for_each(path_trace_works_, [&](unique_ptr<PathTraceWork> &path_trace_work) { path_trace_work->zero_render_buffers(); }); @@ -355,7 +355,9 @@ void PathTrace::path_trace(RenderWork &render_work) const int num_works = path_trace_works_.size(); - tbb::parallel_for(0, num_works, [&](int i) { + thread_capture_fp_settings(); + + parallel_for(0, num_works, [&](int i) { const double work_start_time = time_dt(); const int num_samples = render_work.path_trace.num_samples; @@ -405,7 +407,7 @@ void PathTrace::adaptive_sample(RenderWork &render_work) const double start_time = time_dt(); uint num_active_pixels = 0; - tbb::parallel_for_each(path_trace_works_, [&](unique_ptr<PathTraceWork> &path_trace_work) { + parallel_for_each(path_trace_works_, [&](unique_ptr<PathTraceWork> &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); @@ -483,7 +485,7 @@ void PathTrace::cryptomatte_postprocess(const RenderWork &render_work) } VLOG(3) << "Perform cryptomatte work."; - tbb::parallel_for_each(path_trace_works_, [&](unique_ptr<PathTraceWork> &path_trace_work) { + parallel_for_each(path_trace_works_, [&](unique_ptr<PathTraceWork> &path_trace_work) { path_trace_work->cryptomatte_postproces(); }); } @@ -536,7 +538,7 @@ void PathTrace::denoise(const RenderWork &render_work) if (multi_device_buffers) { multi_device_buffers->copy_from_device(); - tbb::parallel_for_each( + parallel_for_each( path_trace_works_, [&multi_device_buffers](unique_ptr<PathTraceWork> &path_trace_work) { path_trace_work->copy_from_denoised_render_buffers(multi_device_buffers.get()); }); @@ -806,7 +808,7 @@ void PathTrace::tile_buffer_read() } /* Read buffers back from device. */ - tbb::parallel_for_each(path_trace_works_, [&](unique_ptr<PathTraceWork> &path_trace_work) { + parallel_for_each(path_trace_works_, [&](unique_ptr<PathTraceWork> &path_trace_work) { path_trace_work->copy_render_buffers_from_device(); }); @@ -814,7 +816,7 @@ void PathTrace::tile_buffer_read() PathTraceTile tile(*this); if (output_driver_->read_render_tile(tile)) { /* Copy buffers to device again. */ - tbb::parallel_for_each(path_trace_works_, [](unique_ptr<PathTraceWork> &path_trace_work) { + parallel_for_each(path_trace_works_, [](unique_ptr<PathTraceWork> &path_trace_work) { path_trace_work->copy_render_buffers_to_device(); }); } @@ -878,20 +880,20 @@ void PathTrace::progress_set_status(const string &status, const string &substatu void PathTrace::copy_to_render_buffers(RenderBuffers *render_buffers) { - tbb::parallel_for_each(path_trace_works_, - [&render_buffers](unique_ptr<PathTraceWork> &path_trace_work) { - path_trace_work->copy_to_render_buffers(render_buffers); - }); + parallel_for_each(path_trace_works_, + [&render_buffers](unique_ptr<PathTraceWork> &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<PathTraceWork> &path_trace_work) { - path_trace_work->copy_from_render_buffers(render_buffers); - }); + parallel_for_each(path_trace_works_, + [&render_buffers](unique_ptr<PathTraceWork> &path_trace_work) { + path_trace_work->copy_from_render_buffers(render_buffers); + }); } bool PathTrace::copy_render_tile_from_device() @@ -903,7 +905,7 @@ bool PathTrace::copy_render_tile_from_device() bool success = true; - tbb::parallel_for_each(path_trace_works_, [&](unique_ptr<PathTraceWork> &path_trace_work) { + parallel_for_each(path_trace_works_, [&](unique_ptr<PathTraceWork> &path_trace_work) { if (!success) { return; } @@ -1004,7 +1006,7 @@ bool PathTrace::get_render_tile_pixels(const PassAccessor &pass_accessor, bool success = true; - tbb::parallel_for_each(path_trace_works_, [&](unique_ptr<PathTraceWork> &path_trace_work) { + parallel_for_each(path_trace_works_, [&](unique_ptr<PathTraceWork> &path_trace_work) { if (!success) { return; } @@ -1021,7 +1023,7 @@ bool PathTrace::set_render_tile_pixels(PassAccessor &pass_accessor, { bool success = true; - tbb::parallel_for_each(path_trace_works_, [&](unique_ptr<PathTraceWork> &path_trace_work) { + parallel_for_each(path_trace_works_, [&](unique_ptr<PathTraceWork> &path_trace_work) { if (!success) { return; } diff --git a/intern/cycles/integrator/path_trace_work_cpu.cpp b/intern/cycles/integrator/path_trace_work_cpu.cpp index 147e273284b..518ef3185f9 100644 --- a/intern/cycles/integrator/path_trace_work_cpu.cpp +++ b/intern/cycles/integrator/path_trace_work_cpu.cpp @@ -73,7 +73,7 @@ void PathTraceWorkCPU::render_samples(RenderStatistics &statistics, 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) { + parallel_for(int64_t(0), total_pixels_num, [&](int64_t work_index) { if (is_cancel_requested()) { return; } @@ -219,7 +219,7 @@ int PathTraceWorkCPU::adaptive_sampling_converge_filter_count_active(float thres /* 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) { + parallel_for(full_y, full_y + height, [&](int y) { CPUKernelThreadGlobals *kernel_globals = &kernel_thread_globals_[0]; bool row_converged = true; @@ -243,7 +243,7 @@ int PathTraceWorkCPU::adaptive_sampling_converge_filter_count_active(float thres if (num_active_pixels) { local_arena.execute([&]() { - tbb::parallel_for(full_x, full_x + width, [&](int x) { + 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); @@ -265,7 +265,7 @@ void PathTraceWorkCPU::cryptomatte_postproces() /* 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) { + parallel_for(0, height, [&](int y) { CPUKernelThreadGlobals *kernel_globals = &kernel_thread_globals_[0]; int pixel_index = y * width; diff --git a/intern/cycles/integrator/shader_eval.cpp b/intern/cycles/integrator/shader_eval.cpp index f5036b4020d..92b9d1c662d 100644 --- a/intern/cycles/integrator/shader_eval.cpp +++ b/intern/cycles/integrator/shader_eval.cpp @@ -92,7 +92,7 @@ bool ShaderEval::eval_cpu(Device *device, tbb::task_arena local_arena(device->info.cpu_threads); local_arena.execute([&]() { - tbb::parallel_for(int64_t(0), work_size, [&](int64_t work_index) { + parallel_for(int64_t(0), work_size, [&](int64_t work_index) { /* TODO: is this fast enough? */ if (progress_.get_cancel()) { success = false; diff --git a/intern/cycles/kernel/svm/blackbody.h b/intern/cycles/kernel/svm/blackbody.h index 1618341b655..af59c2fe747 100644 --- a/intern/cycles/kernel/svm/blackbody.h +++ b/intern/cycles/kernel/svm/blackbody.h @@ -23,7 +23,7 @@ ccl_device_noinline void svm_node_blackbody(KernelGlobals kg, /* Input */ float temperature = stack_load_float(stack, temperature_offset); - float3 color_rgb = svm_math_blackbody_color(temperature); + float3 color_rgb = rec709_to_rgb(kg, svm_math_blackbody_color_rec709(temperature)); stack_store_float3(stack, col_offset, color_rgb); } diff --git a/intern/cycles/kernel/svm/closure.h b/intern/cycles/kernel/svm/closure.h index 88b44cdbacf..305bd404d27 100644 --- a/intern/cycles/kernel/svm/closure.h +++ b/intern/cycles/kernel/svm/closure.h @@ -1111,7 +1111,8 @@ ccl_device_noinline int svm_node_principled_volume(KernelGlobals kg, if (intensity > CLOSURE_WEIGHT_CUTOFF) { float3 blackbody_tint = stack_load_float3(stack, node.w); - float3 bb = blackbody_tint * intensity * svm_math_blackbody_color(T); + float3 bb = blackbody_tint * intensity * + rec709_to_rgb(kg, svm_math_blackbody_color_rec709(T)); emission_setup(sd, bb); } } diff --git a/intern/cycles/kernel/svm/math_util.h b/intern/cycles/kernel/svm/math_util.h index 2a496aee1e1..9f2d9561e26 100644 --- a/intern/cycles/kernel/svm/math_util.h +++ b/intern/cycles/kernel/svm/math_util.h @@ -189,10 +189,8 @@ ccl_device float svm_math(NodeMathType type, float a, float b, float c) } } -ccl_device float3 svm_math_blackbody_color(float t) +ccl_device float3 svm_math_blackbody_color_rec709(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), diff --git a/intern/cycles/kernel/types.h b/intern/cycles/kernel/types.h index db499a1e1bc..422285cd346 100644 --- a/intern/cycles/kernel/types.h +++ b/intern/cycles/kernel/types.h @@ -1117,13 +1117,18 @@ typedef struct KernelFilm { float4 xyz_to_g; float4 xyz_to_b; float4 rgb_to_y; + /* Rec709 to rendering color space. */ + float4 rec709_to_r; + float4 rec709_to_g; + float4 rec709_to_b; + int is_rec709; int pass_bake_primitive; int pass_bake_differential; int use_approximate_shadow_catcher; - int pad1, pad2; + int pad1; } KernelFilm; static_assert_align(KernelFilm, 16); diff --git a/intern/cycles/kernel/util/color.h b/intern/cycles/kernel/util/color.h index 95b6b33795d..28978d873d6 100644 --- a/intern/cycles/kernel/util/color.h +++ b/intern/cycles/kernel/util/color.h @@ -14,6 +14,15 @@ ccl_device float3 xyz_to_rgb(KernelGlobals kg, float3 xyz) dot(float4_to_float3(kernel_data.film.xyz_to_b), xyz)); } +ccl_device float3 rec709_to_rgb(KernelGlobals kg, float3 rec709) +{ + return (kernel_data.film.is_rec709) ? + rec709 : + make_float3(dot(float4_to_float3(kernel_data.film.rec709_to_r), rec709), + dot(float4_to_float3(kernel_data.film.rec709_to_g), rec709), + dot(float4_to_float3(kernel_data.film.rec709_to_b), rec709)); +} + ccl_device float linear_rgb_to_gray(KernelGlobals kg, float3 c) { return dot(c, float4_to_float3(kernel_data.film.rgb_to_y)); diff --git a/intern/cycles/scene/mesh.cpp b/intern/cycles/scene/mesh.cpp index 05024a7790e..b5c0d9d92fb 100644 --- a/intern/cycles/scene/mesh.cpp +++ b/intern/cycles/scene/mesh.cpp @@ -690,12 +690,16 @@ void Mesh::pack_shaders(Scene *scene, uint *tri_shader) bool last_smooth = false; size_t triangles_size = num_triangles(); - int *shader_ptr = shader.data(); + const int *shader_ptr = shader.data(); + const bool *smooth_ptr = smooth.data(); for (size_t i = 0; i < triangles_size; i++) { - if (shader_ptr[i] != last_shader || last_smooth != smooth[i]) { - last_shader = shader_ptr[i]; - last_smooth = smooth[i]; + const int new_shader = shader_ptr ? shader_ptr[i] : INT_MAX; + const bool new_smooth = smooth_ptr ? smooth_ptr[i] : false; + + if (new_shader != last_shader || last_smooth != new_smooth) { + last_shader = new_shader; + last_smooth = new_smooth; Shader *shader = (last_shader < used_shaders.size()) ? static_cast<Shader *>(used_shaders[last_shader]) : scene->default_surface; diff --git a/intern/cycles/scene/shader.cpp b/intern/cycles/scene/shader.cpp index 8a08f2a5be9..e1af92ea8cf 100644 --- a/intern/cycles/scene/shader.cpp +++ b/intern/cycles/scene/shader.cpp @@ -579,6 +579,10 @@ void ShaderManager::device_update_common(Device * /*device*/, kfilm->xyz_to_g = float3_to_float4(xyz_to_g); kfilm->xyz_to_b = float3_to_float4(xyz_to_b); kfilm->rgb_to_y = float3_to_float4(rgb_to_y); + kfilm->rec709_to_r = float3_to_float4(rec709_to_r); + kfilm->rec709_to_g = float3_to_float4(rec709_to_g); + kfilm->rec709_to_b = float3_to_float4(rec709_to_b); + kfilm->is_rec709 = is_rec709; } void ShaderManager::device_free_common(Device *, DeviceScene *dscene, Scene *scene) @@ -740,6 +744,11 @@ float ShaderManager::linear_rgb_to_gray(float3 c) return dot(c, rgb_to_y); } +float3 ShaderManager::rec709_to_scene_linear(float3 c) +{ + return make_float3(dot(rec709_to_r, c), dot(rec709_to_g, c), dot(rec709_to_b, c)); +} + string ShaderManager::get_cryptomatte_materials(Scene *scene) { string manifest = "{"; @@ -802,11 +811,29 @@ void ShaderManager::init_xyz_transforms() { /* Default to ITU-BT.709 in case no appropriate transform found. * Note XYZ here is defined as having a D65 white point. */ - xyz_to_r = make_float3(3.2404542f, -1.5371385f, -0.4985314f); - xyz_to_g = make_float3(-0.9692660f, 1.8760108f, 0.0415560f); - xyz_to_b = make_float3(0.0556434f, -0.2040259f, 1.0572252f); + const Transform xyz_to_rec709 = make_transform(3.2404542f, + -1.5371385f, + -0.4985314f, + 0.0f, + -0.9692660f, + 1.8760108f, + 0.0415560f, + 0.0f, + 0.0556434f, + -0.2040259f, + 1.0572252f, + 0.0f); + + xyz_to_r = float4_to_float3(xyz_to_rec709.x); + xyz_to_g = float4_to_float3(xyz_to_rec709.y); + xyz_to_b = float4_to_float3(xyz_to_rec709.z); rgb_to_y = make_float3(0.2126729f, 0.7151522f, 0.0721750f); + rec709_to_r = make_float3(1.0f, 0.0f, 0.0f); + rec709_to_g = make_float3(0.0f, 1.0f, 0.0f); + rec709_to_b = make_float3(0.0f, 0.0f, 1.0f); + is_rec709 = true; + #ifdef WITH_OCIO /* Get from OpenColorO config if it has the required roles. */ OCIO::ConstConfigRcPtr config = OCIO::GetCurrentConfig(); @@ -857,6 +884,12 @@ void ShaderManager::init_xyz_transforms() const Transform rgb_to_xyz = transform_inverse(xyz_to_rgb); rgb_to_y = float4_to_float3(rgb_to_xyz.y); + + const Transform rec709_to_rgb = xyz_to_rgb * transform_inverse(xyz_to_rec709); + rec709_to_r = float4_to_float3(rec709_to_rgb.x); + rec709_to_g = float4_to_float3(rec709_to_rgb.y); + rec709_to_b = float4_to_float3(rec709_to_rgb.z); + is_rec709 = transform_equal_threshold(xyz_to_rgb, xyz_to_rec709, 0.0001f); #endif } diff --git a/intern/cycles/scene/shader.h b/intern/cycles/scene/shader.h index cbe331d8ec2..274bb9b4fa1 100644 --- a/intern/cycles/scene/shader.h +++ b/intern/cycles/scene/shader.h @@ -208,6 +208,7 @@ class ShaderManager { static void free_memory(); float linear_rgb_to_gray(float3 c); + float3 rec709_to_scene_linear(float3 c); string get_cryptomatte_materials(Scene *scene); @@ -239,6 +240,10 @@ class ShaderManager { float3 xyz_to_g; float3 xyz_to_b; float3 rgb_to_y; + float3 rec709_to_r; + float3 rec709_to_g; + float3 rec709_to_b; + bool is_rec709; }; CCL_NAMESPACE_END diff --git a/intern/cycles/scene/shader_nodes.cpp b/intern/cycles/scene/shader_nodes.cpp index a951a558731..95fccf725f3 100644 --- a/intern/cycles/scene/shader_nodes.cpp +++ b/intern/cycles/scene/shader_nodes.cpp @@ -5763,7 +5763,9 @@ BlackbodyNode::BlackbodyNode() : ShaderNode(get_node_type()) void BlackbodyNode::constant_fold(const ConstantFolder &folder) { if (folder.all_inputs_constant()) { - folder.make_constant(svm_math_blackbody_color(temperature)); + const float3 rgb_rec709 = svm_math_blackbody_color_rec709(temperature); + const float3 rgb = folder.scene->shader_manager->rec709_to_scene_linear(rgb_rec709); + folder.make_constant(rgb); } } diff --git a/intern/cycles/util/simd.h b/intern/cycles/util/simd.h index 15dda4e76a8..03783abd20f 100644 --- a/intern/cycles/util/simd.h +++ b/intern/cycles/util/simd.h @@ -32,6 +32,12 @@ # define SIMD_SET_FLUSH_TO_ZERO \ _MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON); \ _MM_SET_DENORMALS_ZERO_MODE(_MM_DENORMALS_ZERO_ON); +#elif defined(__aarch64__) || defined(_M_ARM64) +# define _MM_FLUSH_ZERO_ON 24 +# define __get_fpcr(__fpcr) __asm__ __volatile__("mrs %0,fpcr" : "=r"(__fpcr)) +# define __set_fpcr(__fpcr) __asm__ __volatile__("msr fpcr,%0" : : "ri"(__fpcr)) +# define SIMD_SET_FLUSH_TO_ZERO set_fz(_MM_FLUSH_ZERO_ON); +# define SIMD_GET_FLUSH_TO_ZERO get_fz(_MM_FLUSH_ZERO_ON) #else # define SIMD_SET_FLUSH_TO_ZERO #endif @@ -105,6 +111,23 @@ static struct StepTy { } step ccl_attr_maybe_unused; #endif +#if defined(__aarch64__) || defined(_M_ARM64) +__forceinline int set_fz(uint32_t flag) +{ + uint64_t old_fpcr, new_fpcr; + __get_fpcr(old_fpcr); + new_fpcr = old_fpcr | (1ULL << flag); + __set_fpcr(new_fpcr); + __get_fpcr(old_fpcr); + return old_fpcr == new_fpcr; +} +__forceinline int get_fz(uint32_t flag) +{ + uint64_t cur_fpcr; + __get_fpcr(cur_fpcr); + return (cur_fpcr & (1ULL << flag)) > 0 ? 1 : 0; +} +#endif /* Utilities used by Neon */ #if defined(__KERNEL_NEON__) diff --git a/intern/cycles/util/tbb.h b/intern/cycles/util/tbb.h index 7105ddda0f8..948bf2b3e0e 100644 --- a/intern/cycles/util/tbb.h +++ b/intern/cycles/util/tbb.h @@ -25,6 +25,17 @@ CCL_NAMESPACE_BEGIN using tbb::blocked_range; using tbb::enumerable_thread_specific; using tbb::parallel_for; +using tbb::parallel_for_each; + +static inline void thread_capture_fp_settings() +{ +#if TBB_INTERFACE_VERSION_MAJOR >= 12 + tbb::task_group_context *ctx = tbb::task::current_context(); +#else + tbb::task_group_context *ctx = tbb::task::self().group(); +#endif + ctx->capture_fp_settings(); +} static inline void parallel_for_cancel() { diff --git a/intern/cycles/util/transform.h b/intern/cycles/util/transform.h index 371dbb0f4aa..477272f0ba6 100644 --- a/intern/cycles/util/transform.h +++ b/intern/cycles/util/transform.h @@ -285,6 +285,21 @@ ccl_device_inline bool operator!=(const Transform &A, const Transform &B) return !(A == B); } +ccl_device_inline bool transform_equal_threshold(const Transform &A, + const Transform &B, + const float threshold) +{ + for (int x = 0; x < 3; x++) { + for (int y = 0; y < 4; y++) { + if (fabsf(A[x][y] - B[x][y]) > threshold) { + return false; + } + } + } + + return true; +} + ccl_device_inline float3 transform_get_column(const Transform *t, int column) { return make_float3(t->x[column], t->y[column], t->z[column]); diff --git a/intern/ghost/GHOST_C-api.h b/intern/ghost/GHOST_C-api.h index a82f634183d..ec641938f1f 100644 --- a/intern/ghost/GHOST_C-api.h +++ b/intern/ghost/GHOST_C-api.h @@ -30,8 +30,10 @@ extern GHOST_SystemHandle GHOST_CreateSystem(void); /** * Specifies whether debug messages are to be enabled for the specific system handle. + * \param systemhandle: The handle to the system. + * \param debug: Flag for systems to debug. */ -extern void GHOST_SystemInitDebug(GHOST_SystemHandle systemhandle, int is_debug_enabled); +extern void GHOST_SystemInitDebug(GHOST_SystemHandle systemhandle, GHOST_Debug debug); /** * Disposes the one and only system. diff --git a/intern/ghost/GHOST_ISystem.h b/intern/ghost/GHOST_ISystem.h index ed193ee7e5d..bb91abbadec 100644 --- a/intern/ghost/GHOST_ISystem.h +++ b/intern/ghost/GHOST_ISystem.h @@ -452,8 +452,9 @@ class GHOST_ISystem { /** * Specify whether debug messages are to be shown. + * \param debug: Flag for systems to debug. */ - virtual void initDebug(bool is_debug_enabled) = 0; + virtual void initDebug(GHOST_Debug debug) = 0; /** * Check whether debug messages are to be shown. diff --git a/intern/ghost/GHOST_Types.h b/intern/ghost/GHOST_Types.h index c654367072f..85913fbd10c 100644 --- a/intern/ghost/GHOST_Types.h +++ b/intern/ghost/GHOST_Types.h @@ -573,6 +573,16 @@ typedef struct { uint32_t frequency; } GHOST_DisplaySetting; +typedef enum { + /** Axis that cursor grab will wrap. */ + GHOST_kDebugDefault = (1 << 1), + GHOST_kDebugWintab = (1 << 2), +} GHOST_TDebugFlags; + +typedef struct { + int flags; +} GHOST_Debug; + #ifdef _WIN32 typedef void *GHOST_TEmbedderWindowID; #endif // _WIN32 diff --git a/intern/ghost/intern/GHOST_C-api.cpp b/intern/ghost/intern/GHOST_C-api.cpp index e3d01c24283..93e94893162 100644 --- a/intern/ghost/intern/GHOST_C-api.cpp +++ b/intern/ghost/intern/GHOST_C-api.cpp @@ -30,11 +30,11 @@ GHOST_SystemHandle GHOST_CreateSystem(void) return (GHOST_SystemHandle)system; } -void GHOST_SystemInitDebug(GHOST_SystemHandle systemhandle, int is_debug_enabled) +void GHOST_SystemInitDebug(GHOST_SystemHandle systemhandle, GHOST_Debug debug) { GHOST_ISystem *system = (GHOST_ISystem *)systemhandle; - system->initDebug(is_debug_enabled); + system->initDebug(debug); } GHOST_TSuccess GHOST_DisposeSystem(GHOST_SystemHandle systemhandle) diff --git a/intern/ghost/intern/GHOST_System.cpp b/intern/ghost/intern/GHOST_System.cpp index 3df85e18bc7..0d0d41972fd 100644 --- a/intern/ghost/intern/GHOST_System.cpp +++ b/intern/ghost/intern/GHOST_System.cpp @@ -390,9 +390,9 @@ void GHOST_System::useWindowFocus(const bool use_focus) m_windowFocus = use_focus; } -void GHOST_System::initDebug(bool is_debug_enabled) +void GHOST_System::initDebug(GHOST_Debug debug) { - m_is_debug_enabled = is_debug_enabled; + m_is_debug_enabled = debug.flags & GHOST_kDebugDefault; } bool GHOST_System::isDebugEnabled() diff --git a/intern/ghost/intern/GHOST_System.h b/intern/ghost/intern/GHOST_System.h index 0e1e3f734ae..4a3cded1fbd 100644 --- a/intern/ghost/intern/GHOST_System.h +++ b/intern/ghost/intern/GHOST_System.h @@ -334,8 +334,9 @@ class GHOST_System : public GHOST_ISystem { /** * Specify whether debug messages are to be shown. + * \param debug: Flag for systems to debug. */ - virtual void initDebug(bool is_debug_enabled); + virtual void initDebug(GHOST_Debug debug); /** * Check whether debug messages are to be shown. diff --git a/intern/ghost/intern/GHOST_SystemWin32.cpp b/intern/ghost/intern/GHOST_SystemWin32.cpp index e588c7485b4..83869188b65 100644 --- a/intern/ghost/intern/GHOST_SystemWin32.cpp +++ b/intern/ghost/intern/GHOST_SystemWin32.cpp @@ -872,6 +872,13 @@ GHOST_EventButton *GHOST_SystemWin32::processButtonEvent(GHOST_TEventType type, int msgPosY = GET_Y_LPARAM(msgPos); system->pushEvent(new GHOST_EventCursor( ::GetMessageTime(), GHOST_kEventCursorMove, window, msgPosX, msgPosY, td)); + + if (type == GHOST_kEventButtonDown) { + WINTAB_PRINTF("HWND %p OS button down\n", window->getHWND()); + } + else if (type == GHOST_kEventButtonUp) { + WINTAB_PRINTF("HWND %p OS button up\n", window->getHWND()); + } } window->updateMouseCapture(type == GHOST_kEventButtonDown ? MousePressed : MouseReleased); @@ -914,6 +921,8 @@ void GHOST_SystemWin32::processWintabEvent(GHOST_WindowWin32 *window) break; } case GHOST_kEventButtonDown: { + WINTAB_PRINTF("HWND %p Wintab button down", window->getHWND()); + UINT message; switch (info.button) { case GHOST_kButtonMaskLeft: @@ -939,9 +948,12 @@ void GHOST_SystemWin32::processWintabEvent(GHOST_WindowWin32 *window) /* Test for Win32/Wintab button down match. */ useWintabPos = wt->testCoordinates(msg.pt.x, msg.pt.y, info.x, info.y); if (!useWintabPos) { + WINTAB_PRINTF(" ... but associated system button mismatched position\n"); continue; } + WINTAB_PRINTF(" ... associated to system button\n"); + /* Steal the Win32 event which was previously peeked. */ PeekMessage(&msg, window->getHWND(), message, message, PM_REMOVE | PM_NOYIELD); @@ -958,9 +970,14 @@ void GHOST_SystemWin32::processWintabEvent(GHOST_WindowWin32 *window) mouseMoveHandled = true; break; } + else { + WINTAB_PRINTF(" ... but no system button\n"); + } } case GHOST_kEventButtonUp: { + WINTAB_PRINTF("HWND %p Wintab button up", window->getHWND()); if (!useWintabPos) { + WINTAB_PRINTF(" ... but Wintab position isn't trusted\n"); continue; } @@ -986,10 +1003,14 @@ void GHOST_SystemWin32::processWintabEvent(GHOST_WindowWin32 *window) if (PeekMessage(&msg, window->getHWND(), message, message, PM_REMOVE | PM_NOYIELD) && msg.message != WM_QUIT) { + WINTAB_PRINTF(" ... associated to system button\n"); window->updateMouseCapture(MouseReleased); system->pushEvent( new GHOST_EventButton(info.time, info.type, window, info.button, info.tabletData)); } + else { + WINTAB_PRINTF(" ... but no system button\n"); + } break; } default: @@ -1318,6 +1339,12 @@ void GHOST_SystemWin32::setTabletAPI(GHOST_TTabletAPI api) } } +void GHOST_SystemWin32::initDebug(GHOST_Debug debug) +{ + GHOST_System::initDebug(debug); + GHOST_Wintab::setDebug(debug.flags & GHOST_kDebugWintab); +} + void GHOST_SystemWin32::processMinMaxInfo(MINMAXINFO *minmax) { minmax->ptMinTrackSize.x = 320; @@ -1593,6 +1620,7 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, // Wintab events, processed //////////////////////////////////////////////////////////////////////// case WT_CSRCHANGE: { + WINTAB_PRINTF("HWND %p HCTX %p WT_CSRCHANGE\n", window->getHWND(), (void *)lParam); GHOST_Wintab *wt = window->getWintab(); if (wt) { wt->updateCursorInfo(); @@ -1601,6 +1629,20 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, break; } case WT_PROXIMITY: { + WINTAB_PRINTF("HWND %p HCTX %p WT_PROXIMITY\n", window->getHWND(), (void *)wParam); + if (LOWORD(lParam)) { + WINTAB_PRINTF(" Cursor entering context.\n"); + } + else { + WINTAB_PRINTF(" Cursor leaving context.\n"); + } + if (HIWORD(lParam)) { + WINTAB_PRINTF(" Cursor entering or leaving hardware proximity.\n"); + } + else { + WINTAB_PRINTF(" Cursor neither entering nor leaving hardware proximity.\n"); + } + GHOST_Wintab *wt = window->getWintab(); if (wt) { bool inRange = LOWORD(lParam); @@ -1616,6 +1658,7 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, break; } case WT_INFOCHANGE: { + WINTAB_PRINTF("HWND %p HCTX %p WT_INFOCHANGE\n", window->getHWND(), (void *)wParam); GHOST_Wintab *wt = window->getWintab(); if (wt) { wt->processInfoChange(lParam); @@ -1632,6 +1675,32 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, eventHandled = true; break; //////////////////////////////////////////////////////////////////////// + // Wintab events, debug + //////////////////////////////////////////////////////////////////////// + case WT_CTXOPEN: + WINTAB_PRINTF("HWND %p HCTX %p WT_CTXOPEN\n", window->getHWND(), (void *)wParam); + break; + case WT_CTXCLOSE: + WINTAB_PRINTF("HWND %p HCTX %p WT_CTXCLOSE\n", window->getHWND(), (void *)wParam); + break; + case WT_CTXUPDATE: + WINTAB_PRINTF("HWND %p HCTX %p WT_CTXUPDATE\n", window->getHWND(), (void *)wParam); + break; + case WT_CTXOVERLAP: + WINTAB_PRINTF("HWND %p HCTX %p WT_CTXOVERLAP", window->getHWND(), (void *)wParam); + switch (lParam) { + case CXS_DISABLED: + WINTAB_PRINTF(" CXS_DISABLED\n"); + break; + case CXS_OBSCURED: + WINTAB_PRINTF(" CXS_OBSCURED\n"); + break; + case CXS_ONTOP: + WINTAB_PRINTF(" CXS_ONTOP\n"); + break; + } + break; + //////////////////////////////////////////////////////////////////////// // Pointer events, processed //////////////////////////////////////////////////////////////////////// case WM_POINTERUPDATE: @@ -1692,6 +1761,7 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, break; case WM_MOUSEMOVE: if (!window->m_mousePresent) { + WINTAB_PRINTF("HWND %p mouse enter\n", window->getHWND()); TRACKMOUSEEVENT tme = {sizeof(tme)}; tme.dwFlags = TME_LEAVE; tme.hwndTrack = hwnd; @@ -1740,6 +1810,7 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, } break; case WM_MOUSELEAVE: { + WINTAB_PRINTF("HWND %p mouse leave\n", window->getHWND()); window->m_mousePresent = false; if (window->getTabletData().Active == GHOST_kTabletModeNone) { event = processCursorEvent(window); diff --git a/intern/ghost/intern/GHOST_SystemWin32.h b/intern/ghost/intern/GHOST_SystemWin32.h index 16ad5f041ca..9f8d52f9ca3 100644 --- a/intern/ghost/intern/GHOST_SystemWin32.h +++ b/intern/ghost/intern/GHOST_SystemWin32.h @@ -259,6 +259,16 @@ class GHOST_SystemWin32 : public GHOST_System { */ void setTabletAPI(GHOST_TTabletAPI api) override; + /*************************************************************************************** + ** Debug Info + ***************************************************************************************/ + + /** + * Specify which debug messages are to be shown. + * \param debug: Flag for systems to debug. + */ + void initDebug(GHOST_Debug debug) override; + protected: /** * Initializes the system. diff --git a/intern/ghost/intern/GHOST_WindowWin32.cpp b/intern/ghost/intern/GHOST_WindowWin32.cpp index 11a3c097958..2ce224b666b 100644 --- a/intern/ghost/intern/GHOST_WindowWin32.cpp +++ b/intern/ghost/intern/GHOST_WindowWin32.cpp @@ -960,6 +960,7 @@ GHOST_Wintab *GHOST_WindowWin32::getWintab() const void GHOST_WindowWin32::loadWintab(bool enable) { if (!m_wintab) { + WINTAB_PRINTF("Loading Wintab for window %p\n", m_hWnd); if (m_wintab = GHOST_Wintab::loadWintab(m_hWnd)) { if (enable) { m_wintab->enable(); @@ -982,6 +983,7 @@ void GHOST_WindowWin32::loadWintab(bool enable) void GHOST_WindowWin32::closeWintab() { + WINTAB_PRINTF("Closing Wintab for window %p\n", m_hWnd); delete m_wintab; m_wintab = NULL; } diff --git a/intern/ghost/intern/GHOST_Wintab.cpp b/intern/ghost/intern/GHOST_Wintab.cpp index 2547a38c0d1..be1a0a4b314 100644 --- a/intern/ghost/intern/GHOST_Wintab.cpp +++ b/intern/ghost/intern/GHOST_Wintab.cpp @@ -11,7 +11,6 @@ GHOST_Wintab *GHOST_Wintab::loadWintab(HWND hwnd) { /* Load Wintab library if available. */ - auto handle = unique_hmodule(::LoadLibrary("Wintab32.dll"), &::FreeLibrary); if (!handle) { return nullptr; @@ -116,6 +115,11 @@ GHOST_Wintab *GHOST_Wintab::loadWintab(HWND hwnd) } } + int sanityQueueSize = queueSizeGet(hctx.get()); + WINTAB_PRINTF("HCTX %p %s queueSize: %d, queueSizeGet: %d\n", hctx.get(), __func__, queueSize, sanityQueueSize); + + WINTAB_PRINTF("Loaded Wintab context %p\n", hctx.get()); + return new GHOST_Wintab(std::move(handle), info, get, @@ -183,7 +187,17 @@ GHOST_Wintab::GHOST_Wintab(unique_hmodule handle, m_pkts{queueSize} { m_fpInfo(WTI_INTERFACE, IFC_NDEVICES, &m_numDevices); + WINTAB_PRINTF("Wintab Devices: %d\n", m_numDevices); + updateCursorInfo(); + + /* Debug info. */ + printContextDebugInfo(); +} + +GHOST_Wintab::~GHOST_Wintab() +{ + WINTAB_PRINTF("Closing Wintab context %p\n", m_context.get()); } void GHOST_Wintab::enable() @@ -249,6 +263,7 @@ void GHOST_Wintab::updateCursorInfo() BOOL pressureSupport = m_fpInfo(WTI_DEVICES, DVC_NPRESSURE, &Pressure); m_maxPressure = pressureSupport ? Pressure.axMax : 0; + WINTAB_PRINTF("HCTX %p %s maxPressure: %d\n", m_context.get(), __func__, m_maxPressure); BOOL tiltSupport = m_fpInfo(WTI_DEVICES, DVC_ORIENTATION, &Orientation); /* Check if tablet supports azimuth [0] and altitude [1], encoded in axResolution. */ @@ -259,6 +274,7 @@ void GHOST_Wintab::updateCursorInfo() else { m_maxAzimuth = m_maxAltitude = 0; } + WINTAB_PRINTF("HCTX %p %s maxAzimuth: %d, maxAltitude: %d\n", m_context.get(), __func__, m_maxAzimuth, m_maxAltitude); } void GHOST_Wintab::processInfoChange(LPARAM lParam) @@ -266,6 +282,7 @@ void GHOST_Wintab::processInfoChange(LPARAM lParam) /* Update number of connected Wintab digitizers. */ if (LOWORD(lParam) == WTI_INTERFACE && HIWORD(lParam) == IFC_NDEVICES) { m_fpInfo(WTI_INTERFACE, IFC_NDEVICES, &m_numDevices); + WINTAB_PRINTF("HCTX %p %s numDevices: %d\n", m_context.get(), __func__, m_numDevices); } } @@ -456,3 +473,144 @@ bool GHOST_Wintab::testCoordinates(int sysX, int sysY, int wtX, int wtY) return false; } } + +bool GHOST_Wintab::m_debug = false; + +void GHOST_Wintab::setDebug(bool debug) +{ + m_debug = debug; +} + +bool GHOST_Wintab::getDebug() +{ + return m_debug; +} + +void GHOST_Wintab::printContextDebugInfo() +{ + if (!m_debug) { + return; + } + + /* Print button maps. */ + BYTE logicalButtons[32] = {0}; + BYTE systemButtons[32] = {0}; + for (int i = 0; i < 3; i++) { + printf("initializeWintab cursor %d buttons\n", i); + UINT lbut = m_fpInfo(WTI_CURSORS + i, CSR_BUTTONMAP, &logicalButtons); + if (lbut) { + printf("%d", logicalButtons[0]); + for (int j = 1; j < lbut; j++) { + printf(", %d", logicalButtons[j]); + } + printf("\n"); + } + else { + printf("logical button error\n"); + } + UINT sbut = m_fpInfo(WTI_CURSORS + i, CSR_SYSBTNMAP, &systemButtons); + if (sbut) { + printf("%d", systemButtons[0]); + for (int j = 1; j < sbut; j++) { + printf(", %d", systemButtons[j]); + } + printf("\n"); + } + else { + printf("system button error\n"); + } + } + + /* Print context information. */ + + /* Print open context constraints. */ + UINT maxcontexts, opencontexts; + m_fpInfo(WTI_INTERFACE, IFC_NCONTEXTS, &maxcontexts); + m_fpInfo(WTI_STATUS, STA_CONTEXTS, &opencontexts); + printf("%u max contexts, %u open contexts\n", maxcontexts, opencontexts); + + /* Print system information. */ + printf("left: %d, top: %d, width: %d, height: %d\n", + ::GetSystemMetrics(SM_XVIRTUALSCREEN), + ::GetSystemMetrics(SM_YVIRTUALSCREEN), + ::GetSystemMetrics(SM_CXVIRTUALSCREEN), + ::GetSystemMetrics(SM_CYVIRTUALSCREEN)); + + auto printContextRanges = [](LOGCONTEXT &lc) { + printf("lcInOrgX: %d, lcInOrgY: %d, lcInExtX: %d, lcInExtY: %d\n", + lc.lcInOrgX, + lc.lcInOrgY, + lc.lcInExtX, + lc.lcInExtY); + printf("lcOutOrgX: %d, lcOutOrgY: %d, lcOutExtX: %d, lcOutExtY: %d\n", + lc.lcOutOrgX, + lc.lcOutOrgY, + lc.lcOutExtX, + lc.lcOutExtY); + printf("lcSysOrgX: %d, lcSysOrgY: %d, lcSysExtX: %d, lcSysExtY: %d\n", + lc.lcSysOrgX, + lc.lcSysOrgY, + lc.lcSysExtX, + lc.lcSysExtY); + }; + + LOGCONTEXT lc; + + /* Print system context. */ + m_fpInfo(WTI_DEFSYSCTX, 0, &lc); + printf("WTI_DEFSYSCTX\n"); + printContextRanges(lc); + + /* Print system context, manually populated. */ + m_fpInfo(WTI_DEFSYSCTX, CTX_INORGX, &lc.lcInOrgX); + m_fpInfo(WTI_DEFSYSCTX, CTX_INORGY, &lc.lcInOrgY); + m_fpInfo(WTI_DEFSYSCTX, CTX_INEXTX, &lc.lcInExtX); + m_fpInfo(WTI_DEFSYSCTX, CTX_INEXTY, &lc.lcInExtY); + m_fpInfo(WTI_DEFSYSCTX, CTX_OUTORGX, &lc.lcOutOrgX); + m_fpInfo(WTI_DEFSYSCTX, CTX_OUTORGY, &lc.lcOutOrgY); + m_fpInfo(WTI_DEFSYSCTX, CTX_OUTEXTX, &lc.lcOutExtX); + m_fpInfo(WTI_DEFSYSCTX, CTX_OUTEXTY, &lc.lcOutExtY); + m_fpInfo(WTI_DEFSYSCTX, CTX_SYSORGX, &lc.lcSysOrgX); + m_fpInfo(WTI_DEFSYSCTX, CTX_SYSORGY, &lc.lcSysOrgY); + m_fpInfo(WTI_DEFSYSCTX, CTX_SYSEXTX, &lc.lcSysExtX); + m_fpInfo(WTI_DEFSYSCTX, CTX_SYSEXTY, &lc.lcSysExtY); + printf("WTI_DEFSYSCTX CTX_*\n"); + printContextRanges(lc); + + for (unsigned int i = 0; i < m_numDevices; i++) { + /* Print individual device system context. */ + m_fpInfo(WTI_DSCTXS + i, 0, &lc); + printf("WTI_DSCTXS %u\n", i); + printContextRanges(lc); + + /* Print individual device system context, manually populated. */ + m_fpInfo(WTI_DSCTXS + i, CTX_INORGX, &lc.lcInOrgX); + m_fpInfo(WTI_DSCTXS + i, CTX_INORGY, &lc.lcInOrgY); + m_fpInfo(WTI_DSCTXS + i, CTX_INEXTX, &lc.lcInExtX); + m_fpInfo(WTI_DSCTXS + i, CTX_INEXTY, &lc.lcInExtY); + m_fpInfo(WTI_DSCTXS + i, CTX_OUTORGX, &lc.lcOutOrgX); + m_fpInfo(WTI_DSCTXS + i, CTX_OUTORGY, &lc.lcOutOrgY); + m_fpInfo(WTI_DSCTXS + i, CTX_OUTEXTX, &lc.lcOutExtX); + m_fpInfo(WTI_DSCTXS + i, CTX_OUTEXTY, &lc.lcOutExtY); + m_fpInfo(WTI_DSCTXS + i, CTX_SYSORGX, &lc.lcSysOrgX); + m_fpInfo(WTI_DSCTXS + i, CTX_SYSORGY, &lc.lcSysOrgY); + m_fpInfo(WTI_DSCTXS + i, CTX_SYSEXTX, &lc.lcSysExtX); + m_fpInfo(WTI_DSCTXS + i, CTX_SYSEXTY, &lc.lcSysExtY); + printf("WTI_DSCTX %u CTX_*\n", i); + printContextRanges(lc); + + /* Print device axis. */ + AXIS axis_x, axis_y; + m_fpInfo(WTI_DEVICES + i, DVC_X, &axis_x); + m_fpInfo(WTI_DEVICES + i, DVC_Y, &axis_y); + printf("WTI_DEVICES %u axis_x org: %d, axis_y org: %d axis_x ext: %d, axis_y ext: %d\n", + i, + axis_x.axMin, + axis_y.axMin, + axis_x.axMax - axis_x.axMin + 1, + axis_y.axMax - axis_y.axMin + 1); + } + + /* Other stuff while we have a logcontext. */ + printf("sysmode %d\n", lc.lcSysMode); +}
\ No newline at end of file diff --git a/intern/ghost/intern/GHOST_Wintab.h b/intern/ghost/intern/GHOST_Wintab.h index a793d2d8f63..86a0143ecc0 100644 --- a/intern/ghost/intern/GHOST_Wintab.h +++ b/intern/ghost/intern/GHOST_Wintab.h @@ -13,6 +13,7 @@ #pragma once #include <memory> +#include <stdio.h> #include <vector> #include <wtypes.h> @@ -25,6 +26,14 @@ #define PACKETMODE 0 #include <pktdef.h> +#define WINTAB_PRINTF(x, ...) \ + { \ + if (GHOST_Wintab::getDebug()) { \ + printf(x, __VA_ARGS__); \ + } \ + } \ + (void)0 + /* Typedefs for Wintab functions to allow dynamic loading. */ typedef UINT(API *GHOST_WIN32_WTInfo)(UINT, UINT, LPVOID); typedef BOOL(API *GHOST_WIN32_WTGet)(HCTX, LPLOGCONTEXTA); @@ -55,9 +64,12 @@ class GHOST_Wintab { /** * Loads Wintab if available. * \param hwnd: Window to attach Wintab context to. + * \return Pointer to the initialized GHOST_Wintab object, or null if initialization failed. */ static GHOST_Wintab *loadWintab(HWND hwnd); + ~GHOST_Wintab(); + /** * Enables Wintab context. */ @@ -146,6 +158,16 @@ class GHOST_Wintab { */ GHOST_TabletData getLastTabletData(); + /* Sets Wintab debugging. + * \param debug: True to enable Wintab debugging. + */ + static void setDebug(bool debug); + + /* Returns whether Wintab logging should occur. + * \return True if Wintab logging should occur. + */ + static bool getDebug(); + private: /** Wintab DLL handle. */ unique_hmodule m_handle; @@ -200,6 +222,9 @@ class GHOST_Wintab { /** Most recently received tablet data, or none if pen is not in range. */ GHOST_TabletData m_lastTabletData = GHOST_TABLET_DATA_NONE; + /** Whether Wintab logging is enabled. */ + static bool m_debug; + GHOST_Wintab(unique_hmodule handle, GHOST_WIN32_WTInfo info, GHOST_WIN32_WTGet get, @@ -233,4 +258,7 @@ class GHOST_Wintab { * \param system: System coordinates. */ static void extractCoordinates(LOGCONTEXT &lc, Coord &tablet, Coord &system); + + /* Prints Wintab Context information. */ + void printContextDebugInfo(); }; diff --git a/intern/opencolorio/fallback_impl.cc b/intern/opencolorio/fallback_impl.cc index aaab8b4e6b9..d78b34d3c92 100644 --- a/intern/opencolorio/fallback_impl.cc +++ b/intern/opencolorio/fallback_impl.cc @@ -461,12 +461,13 @@ OCIO_ConstProcessorRcPtr *FallbackImpl::createDisplayProcessor(OCIO_ConstConfigR const char * /*display*/, const char * /*look*/, const float scale, - const float exponent) + const float exponent, + const bool inverse) { FallbackTransform transform; - transform.type = TRANSFORM_LINEAR_TO_SRGB; - transform.scale = scale; - transform.exponent = exponent; + transform.type = (inverse) ? TRANSFORM_SRGB_TO_LINEAR : TRANSFORM_LINEAR_TO_SRGB; + transform.scale = (inverse && scale != 0.0f) ? 1.0f / scale : scale; + transform.exponent = (inverse && exponent != 0.0f) ? 1.0f / exponent : exponent; return (OCIO_ConstProcessorRcPtr *)new FallbackProcessor(transform); } diff --git a/intern/opencolorio/ocio_capi.cc b/intern/opencolorio/ocio_capi.cc index 91784a288c8..5e4c2a87a0b 100644 --- a/intern/opencolorio/ocio_capi.cc +++ b/intern/opencolorio/ocio_capi.cc @@ -250,9 +250,11 @@ OCIO_ConstProcessorRcPtr *OCIO_createDisplayProcessor(OCIO_ConstConfigRcPtr *con const char *display, const char *look, const float scale, - const float exponent) + const float exponent, + const bool inverse) { - return impl->createDisplayProcessor(config, input, view, display, look, scale, exponent); + return impl->createDisplayProcessor( + config, input, view, display, look, scale, exponent, inverse); } OCIO_PackedImageDesc *OCIO_createOCIO_PackedImageDesc(float *data, diff --git a/intern/opencolorio/ocio_capi.h b/intern/opencolorio/ocio_capi.h index 5c036ec263a..9bd4ec374e2 100644 --- a/intern/opencolorio/ocio_capi.h +++ b/intern/opencolorio/ocio_capi.h @@ -166,7 +166,8 @@ OCIO_ConstProcessorRcPtr *OCIO_createDisplayProcessor(OCIO_ConstConfigRcPtr *con const char *display, const char *look, const float scale, - const float exponent); + const float exponent, + const bool inverse); OCIO_PackedImageDesc *OCIO_createOCIO_PackedImageDesc(float *data, long width, diff --git a/intern/opencolorio/ocio_impl.cc b/intern/opencolorio/ocio_impl.cc index ca1b7cc42e1..8d9c5dd2d49 100644 --- a/intern/opencolorio/ocio_impl.cc +++ b/intern/opencolorio/ocio_impl.cc @@ -254,7 +254,12 @@ const char *OCIOImpl::configGetDisplayColorSpaceName(OCIO_ConstConfigRcPtr *conf const char *view) { try { - return (*(ConstConfigRcPtr *)config)->getDisplayViewColorSpaceName(display, view); + const char *name = (*(ConstConfigRcPtr *)config)->getDisplayViewColorSpaceName(display, view); + /* OpenColorIO does not resolve this token for us, so do it ourselves. */ + if (strcasecmp(name, "<USE_DISPLAY_NAME>") == 0) { + return display; + } + return name; } catch (Exception &exception) { OCIO_reportException(exception); @@ -655,7 +660,8 @@ OCIO_ConstProcessorRcPtr *OCIOImpl::createDisplayProcessor(OCIO_ConstConfigRcPtr const char *display, const char *look, const float scale, - const float exponent) + const float exponent, + const bool inverse) { ConstConfigRcPtr config = *(ConstConfigRcPtr *)config_; @@ -718,6 +724,10 @@ OCIO_ConstProcessorRcPtr *OCIOImpl::createDisplayProcessor(OCIO_ConstConfigRcPtr group->appendTransform(et); } + if (inverse) { + group->setDirection(TRANSFORM_DIR_INVERSE); + } + /* Create processor from transform. This is the moment were OCIO validates * the entire transform, no need to check for the validity of inputs above. */ ConstProcessorRcPtr *p = MEM_new<ConstProcessorRcPtr>(__func__); diff --git a/intern/opencolorio/ocio_impl.h b/intern/opencolorio/ocio_impl.h index d42fa58121f..f8397c62e52 100644 --- a/intern/opencolorio/ocio_impl.h +++ b/intern/opencolorio/ocio_impl.h @@ -85,7 +85,8 @@ class IOCIOImpl { const char *display, const char *look, const float scale, - const float exponent) = 0; + const float exponent, + const bool inverse) = 0; virtual OCIO_PackedImageDesc *createOCIO_PackedImageDesc(float *data, long width, @@ -201,7 +202,8 @@ class FallbackImpl : public IOCIOImpl { const char *display, const char *look, const float scale, - const float exponent); + const float exponent, + const bool inverse); OCIO_PackedImageDesc *createOCIO_PackedImageDesc(float *data, long width, @@ -290,7 +292,8 @@ class OCIOImpl : public IOCIOImpl { const char *display, const char *look, const float scale, - const float exponent); + const float exponent, + const bool inverse); OCIO_PackedImageDesc *createOCIO_PackedImageDesc(float *data, long width, diff --git a/intern/opencolorio/ocio_impl_glsl.cc b/intern/opencolorio/ocio_impl_glsl.cc index 87f9d8837c7..150ed1a58bb 100644 --- a/intern/opencolorio/ocio_impl_glsl.cc +++ b/intern/opencolorio/ocio_impl_glsl.cc @@ -603,7 +603,7 @@ static OCIO_GPUDisplayShader &getGPUDisplayShader( OCIO_ConstProcessorRcPtr *processor_to_scene_linear = OCIO_configGetProcessorWithNames( config, input, ROLE_SCENE_LINEAR); OCIO_ConstProcessorRcPtr *processor_to_display = OCIO_createDisplayProcessor( - config, ROLE_SCENE_LINEAR, view, display, look, 1.0f, 1.0f); + config, ROLE_SCENE_LINEAR, view, display, look, 1.0f, 1.0f, false); /* Create shader descriptions. */ if (processor_to_scene_linear && processor_to_display) { diff --git a/intern/utfconv/utfconv.c b/intern/utfconv/utfconv.c index a9625309a55..4cc72ae110f 100644 --- a/intern/utfconv/utfconv.c +++ b/intern/utfconv/utfconv.c @@ -40,7 +40,7 @@ size_t count_utf_8_from_16(const wchar_t *string16) } else { if (u < 0xE000) { - /*illegal*/; + /* Illegal. */ } else { count += 3; diff --git a/release/datafiles/colormanagement/config.ocio b/release/datafiles/colormanagement/config.ocio index bdb04cbf9ce..55e52de7002 100644 --- a/release/datafiles/colormanagement/config.ocio +++ b/release/datafiles/colormanagement/config.ocio @@ -173,7 +173,7 @@ colorspaces: name: Non-Color family: raw description: | - Color space used for images which contains non-color data (i,e, normal maps) + Color space used for images which contains non-color data (i.e. normal maps) equalitygroup: bitdepth: 32f isdata: true diff --git a/release/datafiles/icons/brush.sculpt.displacement_smear.dat b/release/datafiles/icons/brush.sculpt.displacement_smear.dat Binary files differindex 5d422130ea3..9e4df45b2d2 100644 --- a/release/datafiles/icons/brush.sculpt.displacement_smear.dat +++ b/release/datafiles/icons/brush.sculpt.displacement_smear.dat diff --git a/release/datafiles/icons/brush.sculpt.draw_sharp.dat b/release/datafiles/icons/brush.sculpt.draw_sharp.dat Binary files differindex 1877c0ae4d4..9bea1b02894 100644 --- a/release/datafiles/icons/brush.sculpt.draw_sharp.dat +++ b/release/datafiles/icons/brush.sculpt.draw_sharp.dat diff --git a/release/datafiles/icons/brush.sculpt.multiplane_scrape.dat b/release/datafiles/icons/brush.sculpt.multiplane_scrape.dat Binary files differindex b785bb51431..6e17f520282 100644 --- a/release/datafiles/icons/brush.sculpt.multiplane_scrape.dat +++ b/release/datafiles/icons/brush.sculpt.multiplane_scrape.dat diff --git a/release/datafiles/icons/ops.curves.sculpt_grow_shrink.dat b/release/datafiles/icons/ops.curves.sculpt_grow_shrink.dat Binary files differindex 9b3453085e4..13f19185030 100644 --- a/release/datafiles/icons/ops.curves.sculpt_grow_shrink.dat +++ b/release/datafiles/icons/ops.curves.sculpt_grow_shrink.dat diff --git a/release/datafiles/icons/ops.curves.sculpt_snake_hook.dat b/release/datafiles/icons/ops.curves.sculpt_snake_hook.dat Binary files differnew file mode 100644 index 00000000000..15128701d0a --- /dev/null +++ b/release/datafiles/icons/ops.curves.sculpt_snake_hook.dat diff --git a/release/datafiles/icons/ops.sculpt.color_filter.dat b/release/datafiles/icons/ops.sculpt.color_filter.dat Binary files differindex d589b15a124..8a65043eb5f 100644 --- a/release/datafiles/icons/ops.sculpt.color_filter.dat +++ b/release/datafiles/icons/ops.sculpt.color_filter.dat diff --git a/release/datafiles/icons/ops.sculpt.mask_by_color.dat b/release/datafiles/icons/ops.sculpt.mask_by_color.dat Binary files differindex 637c47d2d84..6194ce172d5 100644 --- a/release/datafiles/icons/ops.sculpt.mask_by_color.dat +++ b/release/datafiles/icons/ops.sculpt.mask_by_color.dat diff --git a/release/datafiles/locale b/release/datafiles/locale -Subproject 716dc02ec30c0810513f7b4adc4ae865ae50c4e +Subproject 63699f968344db7dc853d2c5972325beea44900 diff --git a/release/scripts/addons b/release/scripts/addons -Subproject 787ea78f7fa6f0373d80ba1247768402df93f8a +Subproject baa581415c7ed23d7c45ef87363174813567268 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 58461309720..87d54213d1b 100644 --- a/release/scripts/modules/bl_i18n_utils/bl_extract_messages.py +++ b/release/scripts/modules/bl_i18n_utils/bl_extract_messages.py @@ -424,6 +424,13 @@ def dump_rna_messages(msgs, reports, settings, verbose=False): # Recursively process subclasses. process_cls_list(cls.__subclasses__()) + # FIXME Workaround weird new (blender 3.2) issue where some classes (like `bpy.types.Modifier`) + # are not listed by `bpy.types.ID.__base__.__subclasses__()` until they are accessed from + # `bpy.types` (eg just executing `bpy.types.Modifier`). + cls_dir = dir(bpy.types) + for cls_name in cls_dir: + getattr(bpy.types, cls_name) + # Parse everything (recursively parsing from bpy_struct "class"...). process_cls_list(bpy.types.ID.__base__.__subclasses__()) diff --git a/release/scripts/modules/rna_manual_reference.py b/release/scripts/modules/rna_manual_reference.py index af6634a791a..f9bb49cf48f 100644 --- a/release/scripts/modules/rna_manual_reference.py +++ b/release/scripts/modules/rna_manual_reference.py @@ -47,7 +47,8 @@ url_manual_mapping = ( ("bpy.types.lineartgpencilmodifier.use_offset_towards_custom_camera*", "grease_pencil/modifiers/generate/line_art.html#bpy-types-lineartgpencilmodifier-use-offset-towards-custom-camera"), ("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.sequencertoolsettings.use_snap_current_frame_to_strips*", "video_editing/sequencer/editing.html#bpy-types-sequencertoolsettings-use-snap-current-frame-to-strips"), + ("bpy.types.sequencertoolsettings.use_snap_current_frame_to_strips*", "video_editing/edit/montage/editing.html#bpy-types-sequencertoolsettings-use-snap-current-frame-to-strips"), + ("bpy.types.cycleslightsettings.use_multiple_importance_sampling*", "render/cycles/light_settings.html#bpy-types-cycleslightsettings-use-multiple-importance-sampling"), ("bpy.types.fluiddomainsettings.sndparticle_potential_max_energy*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-potential-max-energy"), ("bpy.types.fluiddomainsettings.sndparticle_potential_min_energy*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-potential-min-energy"), ("bpy.types.lineartgpencilmodifier.use_overlap_edge_type_support*", "grease_pencil/modifiers/generate/line_art.html#bpy-types-lineartgpencilmodifier-use-overlap-edge-type-support"), @@ -62,7 +63,8 @@ url_manual_mapping = ( ("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.adaptive_scrambling_distance*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-adaptive-scrambling-distance"), ("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.lineartgpencilmodifier.use_face_mark_keep_contour*", "grease_pencil/modifiers/generate/line_art.html#bpy-types-lineartgpencilmodifier-use-face-mark-keep-contour"), + ("bpy.types.rendersettings.use_sequencer_override_scene_strip*", "editors/video_sequencer/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.cyclesrendersettings.preview_scrambling_distance*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-preview-scrambling-distance"), @@ -109,6 +111,7 @@ url_manual_mapping = ( ("bpy.types.fluiddomainsettings.use_collision_border_top*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-use-collision-border-top"), ("bpy.types.gpencilsculptsettings.intersection_threshold*", "grease_pencil/modes/draw/tools/cutter.html#bpy-types-gpencilsculptsettings-intersection-threshold"), ("bpy.types.gpencilsculptsettings.use_multiframe_falloff*", "grease_pencil/multiframe.html#bpy-types-gpencilsculptsettings-use-multiframe-falloff"), + ("bpy.types.lineartgpencilmodifier.use_back_face_culling*", "grease_pencil/modifiers/generate/line_art.html#bpy-types-lineartgpencilmodifier-use-back-face-culling"), ("bpy.types.lineartgpencilmodifier.use_intersection_mask*", "grease_pencil/modifiers/generate/line_art.html#bpy-types-lineartgpencilmodifier-use-intersection-mask"), ("bpy.types.lineartgpencilmodifier.use_invert_collection*", "grease_pencil/modifiers/generate/line_art.html#bpy-types-lineartgpencilmodifier-use-invert-collection"), ("bpy.types.movietrackingsettings.use_keyframe_selection*", "movie_clip/tracking/clip/toolbar/solve.html#bpy-types-movietrackingsettings-use-keyframe-selection"), @@ -128,7 +131,7 @@ url_manual_mapping = ( ("bpy.types.lineartgpencilmodifier.use_face_mark_invert*", "grease_pencil/modifiers/generate/line_art.html#bpy-types-lineartgpencilmodifier-use-face-mark-invert"), ("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.sequencertoolsettings.snap_to_current_frame*", "video_editing/edit/montage/editing.html#bpy-types-sequencertoolsettings-snap-to-current-frame"), ("bpy.ops.object.geometry_nodes_input_attribute_toggle*", "modeling/modifiers/generate/geometry_nodes.html#bpy-ops-object-geometry-nodes-input-attribute-toggle"), ("bpy.types.animvizmotionpaths.show_keyframe_highlight*", "animation/motion_paths.html#bpy-types-animvizmotionpaths-show-keyframe-highlight"), ("bpy.types.brushgpencilsettings.pen_subdivision_steps*", "grease_pencil/modes/draw/tools/draw.html#bpy-types-brushgpencilsettings-pen-subdivision-steps"), @@ -163,6 +166,7 @@ url_manual_mapping = ( ("bpy.types.brushgpencilsettings.use_random_press_val*", "grease_pencil/modes/draw/tools/draw.html#bpy-types-brushgpencilsettings-use-random-press-val"), ("bpy.types.brushgpencilsettings.use_stroke_random_uv*", "grease_pencil/modes/draw/tools/draw.html#bpy-types-brushgpencilsettings-use-stroke-random-uv"), ("bpy.types.cyclesmaterialsettings.homogeneous_volume*", "render/cycles/material_settings.html#bpy-types-cyclesmaterialsettings-homogeneous-volume"), + ("bpy.types.cyclesobjectsettings.is_caustics_receiver*", "render/cycles/object_settings/object_data.html#bpy-types-cyclesobjectsettings-is-caustics-receiver"), ("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"), @@ -182,7 +186,7 @@ url_manual_mapping = ( ("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.sequencertoolsettings.snap_to_hold_offset*", "video_editing/edit/montage/editing.html#bpy-types-sequencertoolsettings-snap-to-hold-offset"), ("bpy.types.toolsettings.use_mesh_automerge_and_split*", "modeling/meshes/tools/tool_settings.html#bpy-types-toolsettings-use-mesh-automerge-and-split"), ("bpy.types.animvizmotionpaths.show_keyframe_numbers*", "animation/motion_paths.html#bpy-types-animvizmotionpaths-show-keyframe-numbers"), ("bpy.types.brush.cloth_constraint_softbody_strength*", "sculpt_paint/sculpting/tools/cloth.html#bpy-types-brush-cloth-constraint-softbody-strength"), @@ -223,6 +227,7 @@ url_manual_mapping = ( ("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.cyclesobjectsettings.is_caustics_caster*", "render/cycles/object_settings/object_data.html#bpy-types-cyclesobjectsettings-is-caustics-caster"), ("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"), @@ -238,6 +243,7 @@ url_manual_mapping = ( ("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.geometrynodecurveprimitivequadrilateral*", "modeling/geometry_nodes/curve_primitives/quadrilateral.html#bpy-types-geometrynodecurveprimitivequadrilateral"), ("bpy.types.lineartgpencilmodifier.smooth_tolerance*", "grease_pencil/modifiers/generate/line_art.html#bpy-types-lineartgpencilmodifier-smooth-tolerance"), ("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"), @@ -247,8 +253,8 @@ url_manual_mapping = ( ("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*", "editors/video_sequencer/preview/display/overlays.html#bpy-types-sequencerpreviewoverlay-show-annotation"), ("bpy.types.sequencerpreviewoverlay.show_safe_areas*", "editors/video_sequencer/preview/display/overlays.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.sequencertoolsettings.snap_ignore_muted*", "video_editing/edit/montage/editing.html#bpy-types-sequencertoolsettings-snap-ignore-muted"), + ("bpy.types.sequencertoolsettings.snap_ignore_sound*", "video_editing/edit/montage/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_gizmo_navigate*", "editors/video_sequencer/preview/display/gizmos.html#bpy-types-spacesequenceeditor-show-gizmo-navigate"), @@ -297,6 +303,7 @@ url_manual_mapping = ( ("bpy.types.spacedopesheeteditor.show_pose_markers*", "animation/markers.html#bpy-types-spacedopesheeteditor-show-pose-markers"), ("bpy.types.spaceoutliner.use_filter_object_camera*", "editors/outliner/interface.html#bpy-types-spaceoutliner-use-filter-object-camera"), ("bpy.types.spaceoutliner.use_filter_object_others*", "editors/outliner/interface.html#bpy-types-spaceoutliner-use-filter-object-others"), + ("bpy.types.spacesequenceeditor.overlay_frame_type*", "editors/video_sequencer/preview/sidebar.html#bpy-types-spacesequenceeditor-overlay-frame-type"), ("bpy.types.spacesequenceeditor.show_strip_overlay*", "editors/video_sequencer/sequencer/display.html#bpy-types-spacesequenceeditor-show-strip-overlay"), ("bpy.types.spaceuveditor.custom_grid_subdivisions*", "editors/uv/sidebar.html#bpy-types-spaceuveditor-custom-grid-subdivisions"), ("bpy.types.toolsettings.proportional_edit_falloff*", "editors/3dview/controls/proportional_editing.html#bpy-types-toolsettings-proportional-edit-falloff"), @@ -311,11 +318,13 @@ url_manual_mapping = ( ("bpy.types.animvizmotionpaths.show_frame_numbers*", "animation/motion_paths.html#bpy-types-animvizmotionpaths-show-frame-numbers"), ("bpy.types.brushgpencilsettings.pen_smooth_steps*", "grease_pencil/modes/draw/tools/draw.html#bpy-types-brushgpencilsettings-pen-smooth-steps"), ("bpy.types.brushgpencilsettings.show_fill_extend*", "grease_pencil/modes/draw/tools/fill.html#bpy-types-brushgpencilsettings-show-fill-extend"), + ("bpy.types.cycleslightsettings.is_caustics_light*", "render/cycles/light_settings.html#bpy-types-cycleslightsettings-is-caustics-light"), ("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.cyclesworldsettings.is_caustics_light*", "render/cycles/world_settings.html#bpy-types-cyclesworldsettings-is-caustics-light"), ("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"), @@ -341,12 +350,13 @@ url_manual_mapping = ( ("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.sequenceeditor.use_overlay_frame_lock*", "editors/video_sequencer/preview/sidebar.html#bpy-types-sequenceeditor-use-overlay-frame-lock"), ("bpy.types.sequencerpreviewoverlay.show_metadata*", "editors/video_sequencer/preview/display/overlays.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_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.proxy_render_size*", "editors/video_sequencer/preview/sidebar.html#bpy-types-spacesequenceeditor-proxy-render-size"), ("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"), @@ -384,15 +394,16 @@ url_manual_mapping = ( ("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"), ("bpy.types.rendersettings_simplify_gpencil_tint*", "render/cycles/render_settings/simplify.html#bpy-types-rendersettings-simplify-gpencil-tint"), + ("bpy.types.spaceoutliner.lib_override_view_mode*", "editors/outliner/interface.html#bpy-types-spaceoutliner-lib-override-view-mode"), ("bpy.types.spaceoutliner.use_filter_object_mesh*", "editors/outliner/interface.html#bpy-types-spaceoutliner-use-filter-object-mesh"), ("bpy.types.spaceoutliner.use_filter_view_layers*", "editors/outliner/interface.html#bpy-types-spaceoutliner-use-filter-view-layers"), - ("bpy.types.spacesequenceeditor.show_overexposed*", "video_editing/preview/sidebar.html#bpy-types-spacesequenceeditor-show-overexposed"), + ("bpy.types.spacesequenceeditor.show_overexposed*", "editors/video_sequencer/preview/sidebar.html#bpy-types-spacesequenceeditor-show-overexposed"), ("bpy.types.toolsettings.use_gpencil_draw_onback*", "grease_pencil/modes/draw/introduction.html#bpy-types-toolsettings-use-gpencil-draw-onback"), ("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.ops.sequencer.deinterlace_selected_movies*", "video_editing/edit/montage/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"), ("bpy.types.brush.use_multiplane_scrape_dynamic*", "sculpt_paint/sculpting/tools/multiplane_scrape.html#bpy-types-brush-use-multiplane-scrape-dynamic"), @@ -426,18 +437,19 @@ url_manual_mapping = ( ("bpy.types.freestylelinestyle.use_split_length*", "render/freestyle/view_layer/line_style/strokes.html#bpy-types-freestylelinestyle-use-split-length"), ("bpy.types.geometrynodedistributepointsonfaces*", "modeling/geometry_nodes/point/distribute_points_on_faces.html#bpy-types-geometrynodedistributepointsonfaces"), ("bpy.types.geometrynodesetcurvehandlepositions*", "modeling/geometry_nodes/curve/set_handle_positions.html#bpy-types-geometrynodesetcurvehandlepositions"), + ("bpy.types.greasepencil.stroke_thickness_space*", "grease_pencil/properties/strokes.html#bpy-types-greasepencil-stroke-thickness-space"), ("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"), + ("bpy.types.rendersettings.sequencer_gl_preview*", "editors/video_sequencer/preview/sidebar.html#bpy-types-rendersettings-sequencer-gl-preview"), ("bpy.types.rendersettings.simplify_subdivision*", "render/cycles/render_settings/simplify.html#bpy-types-rendersettings-simplify-subdivision"), ("bpy.types.sequencerpreviewoverlay.show_cursor*", "editors/video_sequencer/preview/display/overlays.html#bpy-types-sequencerpreviewoverlay-show-cursor"), ("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.cursor_location*", "video_editing/preview/sidebar.html#bpy-types-spacesequenceeditor-cursor-location"), - ("bpy.types.spacesequenceeditor.display_channel*", "video_editing/preview/sidebar.html#bpy-types-spacesequenceeditor-display-channel"), + ("bpy.types.spacesequenceeditor.cursor_location*", "editors/video_sequencer/preview/sidebar.html#bpy-types-spacesequenceeditor-cursor-location"), + ("bpy.types.spacesequenceeditor.display_channel*", "editors/video_sequencer/preview/sidebar.html#bpy-types-spacesequenceeditor-display-channel"), ("bpy.types.spacesequenceeditor.show_gizmo_tool*", "editors/video_sequencer/preview/display/gizmos.html#bpy-types-spacesequenceeditor-show-gizmo-tool"), - ("bpy.types.spacesequenceeditor.show_region_hud*", "video_editing/sequencer/navigating.html#bpy-types-spacesequenceeditor-show-region-hud"), + ("bpy.types.spacesequenceeditor.show_region_hud*", "editors/video_sequencer/sequencer/navigating.html#bpy-types-spacesequenceeditor-show-region-hud"), ("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"), @@ -480,6 +492,7 @@ url_manual_mapping = ( ("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.functionnodeinputspecialcharacters*", "modeling/geometry_nodes/text/special_characters.html#bpy-types-functionnodeinputspecialcharacters"), + ("bpy.types.geometrynodecurveendpointselection*", "modeling/geometry_nodes/curve/endpoint_selection.html#bpy-types-geometrynodecurveendpointselection"), ("bpy.types.geometrynodeinputmeshedgeneighbors*", "modeling/geometry_nodes/mesh/edge_neighbors.html#bpy-types-geometrynodeinputmeshedgeneighbors"), ("bpy.types.geometrynodeinputmeshfaceneighbors*", "modeling/geometry_nodes/mesh/face_neighbors.html#bpy-types-geometrynodeinputmeshfaceneighbors"), ("bpy.types.gpencilsculptguide.reference_point*", "grease_pencil/modes/draw/guides.html#bpy-types-gpencilsculptguide-reference-point"), @@ -492,7 +505,7 @@ url_manual_mapping = ( ("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.sequencertimelineoverlay.show_grid*", "editors/video_sequencer/sequencer/display.html#bpy-types-sequencertimelineoverlay-show-grid"), - ("bpy.types.sequencertoolsettings.overlap_mode*", "video_editing/sequencer/editing.html#bpy-types-sequencertoolsettings-overlap-mode"), + ("bpy.types.sequencertoolsettings.overlap_mode*", "video_editing/edit/montage/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"), ("bpy.types.spacenodeoverlay.show_context_path*", "interface/controls/nodes/introduction.html#bpy-types-spacenodeoverlay-show-context-path"), ("bpy.types.spaceoutliner.show_restrict_column*", "editors/outliner/interface.html#bpy-types-spaceoutliner-show-restrict-column"), @@ -500,6 +513,7 @@ url_manual_mapping = ( ("bpy.types.spaceuveditor.display_stretch_type*", "editors/uv/overlays.html#bpy-types-spaceuveditor-display-stretch-type"), ("bpy.types.toolsettings.transform_pivot_point*", "editors/3dview/controls/pivot_point/index.html#bpy-types-toolsettings-transform-pivot-point"), ("bpy.types.toolsettings.use_proportional_edit*", "editors/3dview/controls/proportional_editing.html#bpy-types-toolsettings-use-proportional-edit"), + ("bpy.types.toolsettings.uv_sticky_select_mode*", "editors/uv/selecting.html#bpy-types-toolsettings-uv-sticky-select-mode"), ("bpy.types.volumedisplay.interpolation_method*", "modeling/volumes/properties.html#bpy-types-volumedisplay-interpolation-method"), ("bpy.types.brushgpencilsettings.angle_factor*", "grease_pencil/modes/draw/tools/draw.html#bpy-types-brushgpencilsettings-angle-factor"), ("bpy.types.brushgpencilsettings.pen_strength*", "grease_pencil/modes/draw/tools/erase.html#bpy-types-brushgpencilsettings-pen-strength"), @@ -582,7 +596,10 @@ url_manual_mapping = ( ("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.geometrynodecurveprimitivecircle*", "modeling/geometry_nodes/curve_primitives/curve_circle.html#bpy-types-geometrynodecurveprimitivecircle"), ("bpy.types.geometrynodecurvequadraticbezier*", "modeling/geometry_nodes/curve_primitives/quadratic_bezier.html#bpy-types-geometrynodecurvequadraticbezier"), + ("bpy.types.gpencillayer.use_viewlayer_masks*", "grease_pencil/properties/layers.html#bpy-types-gpencillayer-use-viewlayer-masks"), + ("bpy.types.greasepencil.onion_keyframe_type*", "grease_pencil/properties/onion_skinning.html#bpy-types-greasepencil-onion-keyframe-type"), ("bpy.types.lineartgpencilmodifier.use_cache*", "grease_pencil/modifiers/generate/line_art.html#bpy-types-lineartgpencilmodifier-use-cache"), ("bpy.types.lineartgpencilmodifier.use_loose*", "grease_pencil/modifiers/generate/line_art.html#bpy-types-lineartgpencilmodifier-use-loose"), ("bpy.types.materialgpencilstyle.show_stroke*", "grease_pencil/materials/properties.html#bpy-types-materialgpencilstyle-show-stroke"), @@ -596,9 +613,7 @@ 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.spacenodeoverlay.show_wire_color*", "interface/controls/nodes/introduction.html#bpy-types-spacenodeoverlay-show-wire-color"), - ("bpy.types.spacesequenceeditor.display_mode*", "video_editing/preview/display_mode.html#bpy-types-spacesequenceeditor-display-mode"), - ("bpy.types.spacesequenceeditor.overlay_type*", "video_editing/preview/sidebar.html#bpy-types-spacesequenceeditor-overlay-type"), - ("bpy.types.spaceuveditor.sticky_select_mode*", "editors/uv/selecting.html#bpy-types-spaceuveditor-sticky-select-mode"), + ("bpy.types.spacesequenceeditor.display_mode*", "editors/video_sequencer/preview/display/display_mode.html#bpy-types-spacesequenceeditor-display-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"), ("bpy.types.viewlayer.pass_cryptomatte_depth*", "render/layers/passes.html#bpy-types-viewlayer-pass-cryptomatte-depth"), @@ -620,6 +635,8 @@ url_manual_mapping = ( ("bpy.types.brushgpencilsettings.show_lasso*", "grease_pencil/modes/draw/tools/draw.html#bpy-types-brushgpencilsettings-show-lasso"), ("bpy.types.compositornodeconvertcolorspace*", "compositing/types/converter/color_space.html#bpy-types-compositornodeconvertcolorspace"), ("bpy.types.cyclescurverendersettings.shape*", "render/cycles/render_settings/hair.html#bpy-types-cyclescurverendersettings-shape"), + ("bpy.types.cycleslightsettings.cast_shadow*", "render/cycles/light_settings.html#bpy-types-cycleslightsettings-cast-shadow"), + ("bpy.types.cycleslightsettings.max_bounces*", "render/cycles/light_settings.html#bpy-types-cycleslightsettings-max-bounces"), ("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"), @@ -637,11 +654,14 @@ url_manual_mapping = ( ("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.geometrynodeattributedomainsize*", "modeling/geometry_nodes/attribute/domain_size.html#bpy-types-geometrynodeattributedomainsize"), ("bpy.types.geometrynodesetsplineresolution*", "modeling/geometry_nodes/curve/set_spline_resolution.html#bpy-types-geometrynodesetsplineresolution"), ("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"), ("bpy.types.gpencilsculptguide.use_snapping*", "grease_pencil/modes/draw/guides.html#bpy-types-gpencilsculptguide-use-snapping"), ("bpy.types.gpencilsculptsettings.lock_axis*", "grease_pencil/modes/draw/drawing_planes.html#bpy-types-gpencilsculptsettings-lock-axis"), + ("bpy.types.greasepencil.ghost_before_range*", "grease_pencil/properties/onion_skinning.html#bpy-types-greasepencil-ghost-before-range"), + ("bpy.types.greasepencil.stroke_depth_order*", "grease_pencil/properties/strokes.html#bpy-types-greasepencil-stroke-depth-order"), ("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.lineartgpencilmodifier.overscan*", "grease_pencil/modifiers/generate/line_art.html#bpy-types-lineartgpencilmodifier-overscan"), @@ -658,20 +678,19 @@ url_manual_mapping = ( ("bpy.types.rendersettings.use_render_cache*", "render/output/properties/output.html#bpy-types-rendersettings-use-render-cache"), ("bpy.types.rendersettings.use_single_layer*", "render/layers/view_layer.html#bpy-types-rendersettings-use-single-layer"), ("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.spaceclipeditor.cursor_location*", "editors/clip/sidebar.html#bpy-types-spaceclipeditor-cursor-location"), ("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.spacenodeeditor.show_annotation*", "interface/controls/nodes/introduction.html#bpy-types-spacenodeeditor-show-annotation"), ("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.spacesequenceeditor.use_proxies*", "editors/video_sequencer/preview/sidebar.html#bpy-types-spacesequenceeditor-use-proxies"), ("bpy.types.spaceuveditor.edge_display_type*", "editors/uv/overlays.html#bpy-types-spaceuveditor-edge-display-type"), ("bpy.types.spaceuveditor.show_pixel_coords*", "editors/uv/sidebar.html#bpy-types-spaceuveditor-show-pixel-coords"), ("bpy.types.spaceview3d.show_reconstruction*", "editors/3dview/display/overlays.html#bpy-types-spaceview3d-show-reconstruction"), ("bpy.types.toolsettings.gpencil_selectmode*", "grease_pencil/selecting.html#bpy-types-toolsettings-gpencil-selectmode"), ("bpy.types.toolsettings.use_auto_normalize*", "sculpt_paint/weight_paint/tool_settings/options.html#bpy-types-toolsettings-use-auto-normalize"), ("bpy.types.toolsettings.use_mesh_automerge*", "modeling/meshes/tools/tool_settings.html#bpy-types-toolsettings-use-mesh-automerge"), - ("bpy.types.toolsettings.use_snap_sequencer*", "video_editing/sequencer/editing.html#bpy-types-toolsettings-use-snap-sequencer"), + ("bpy.types.toolsettings.use_snap_sequencer*", "video_editing/edit/montage/editing.html#bpy-types-toolsettings-use-snap-sequencer"), ("bpy.types.toolsettings.use_snap_translate*", "editors/3dview/controls/snapping.html#bpy-types-toolsettings-use-snap-translate"), ("bpy.types.toolsettings.use_uv_select_sync*", "editors/uv/selecting.html#bpy-types-toolsettings-use-uv-select-sync"), ("bpy.types.view3doverlay.wireframe_opacity*", "editors/3dview/display/overlays.html#bpy-types-view3doverlay-wireframe-opacity"), @@ -712,15 +731,17 @@ url_manual_mapping = ( ("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.geometrynodealigneulertovector*", "modeling/geometry_nodes/utilities/align_euler_to_vector.html#bpy-types-geometrynodealigneulertovector"), + ("bpy.types.functionnodealigneulertovector*", "modeling/geometry_nodes/utilities/align_euler_to_vector.html#bpy-types-functionnodealigneulertovector"), ("bpy.types.geometrynodeattributestatistic*", "modeling/geometry_nodes/attribute/attribute_statistic.html#bpy-types-geometrynodeattributestatistic"), - ("bpy.types.geometrynodecurvequadrilateral*", "modeling/geometry_nodes/curve_primitives/quadrilateral.html#bpy-types-geometrynodecurvequadrilateral"), + ("bpy.types.geometrynodecurveprimitiveline*", "modeling/geometry_nodes/curve_primitives/curve_line.html#bpy-types-geometrynodecurveprimitiveline"), ("bpy.types.geometrynodegeometrytoinstance*", "modeling/geometry_nodes/geometry/geometry_to_instance.html#bpy-types-geometrynodegeometrytoinstance"), ("bpy.types.geometrynodeinputmaterialindex*", "modeling/geometry_nodes/material/material_index.html#bpy-types-geometrynodeinputmaterialindex"), ("bpy.types.geometrynodeinputmeshedgeangle*", "modeling/geometry_nodes/mesh/edge_angle.html#bpy-types-geometrynodeinputmeshedgeangle"), ("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.geometrynodetranslateinstances*", "modeling/geometry_nodes/instances/translate_instances.html#bpy-types-geometrynodetranslateinstances"), + ("bpy.types.greasepencil.ghost_after_range*", "grease_pencil/properties/onion_skinning.html#bpy-types-greasepencil-ghost-after-range"), + ("bpy.types.greasepencil.use_ghosts_always*", "grease_pencil/properties/onion_skinning.html#bpy-types-greasepencil-use-ghosts-always"), ("bpy.types.imageformatsettings.color_mode*", "render/output/properties/output.html#bpy-types-imageformatsettings-color-mode"), ("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"), @@ -761,7 +782,7 @@ url_manual_mapping = ( ("bpy.ops.outliner.collection_show_inside*", "editors/outliner/editing.html#bpy-ops-outliner-collection-show-inside"), ("bpy.ops.poselib.restore_previous_action*", "animation/armatures/posing/editing/pose_library.html#bpy-ops-poselib-restore-previous-action"), ("bpy.ops.preferences.reset_default_theme*", "editors/preferences/themes.html#bpy-ops-preferences-reset-default-theme"), - ("bpy.ops.sequencer.strip_transform_clear*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-strip-transform-clear"), + ("bpy.ops.sequencer.strip_transform_clear*", "video_editing/edit/montage/editing.html#bpy-ops-sequencer-strip-transform-clear"), ("bpy.ops.spreadsheet.add_row_filter_rule*", "editors/spreadsheet.html#bpy-ops-spreadsheet-add-row-filter-rule"), ("bpy.types.animdata.action_extrapolation*", "editors/nla/sidebar.html#bpy-types-animdata-action-extrapolation"), ("bpy.types.animvizmotionpaths.frame_step*", "animation/motion_paths.html#bpy-types-animvizmotionpaths-frame-step"), @@ -795,12 +816,11 @@ url_manual_mapping = ( ("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.geometrynodeendpointselection*", "modeling/geometry_nodes/curve/endpoint_selection.html#bpy-types-geometrynodeendpointselection"), - ("bpy.types.geometrynodegeometryproximity*", "modeling/geometry_nodes/geometry/geometry_proximity.html#bpy-types-geometrynodegeometryproximity"), + ("bpy.types.geometrynodeattributetransfer*", "modeling/geometry_nodes/attribute/transfer_attribute.html#bpy-types-geometrynodeattributetransfer"), ("bpy.types.geometrynodeinputmeshfacearea*", "modeling/geometry_nodes/mesh/face_area.html#bpy-types-geometrynodeinputmeshfacearea"), ("bpy.types.geometrynodeinputsplinecyclic*", "modeling/geometry_nodes/curve/is_spline_cyclic.html#bpy-types-geometrynodeinputsplinecyclic"), ("bpy.types.geometrynodeinstancestopoints*", "modeling/geometry_nodes/instances/instances_to_points.html#bpy-types-geometrynodeinstancestopoints"), - ("bpy.types.geometrynodetransferattribute*", "modeling/geometry_nodes/attribute/transfer_attribute.html#bpy-types-geometrynodetransferattribute"), + ("bpy.types.gpencillayer.viewlayer_render*", "grease_pencil/properties/layers.html#bpy-types-gpencillayer-viewlayer-render"), ("bpy.types.layercollection.hide_viewport*", "editors/outliner/interface.html#bpy-types-layercollection-hide-viewport"), ("bpy.types.layercollection.indirect_only*", "editors/outliner/interface.html#bpy-types-layercollection-indirect-only"), ("bpy.types.material.use_sss_translucency*", "render/eevee/materials/settings.html#bpy-types-material-use-sss-translucency"), @@ -835,7 +855,7 @@ url_manual_mapping = ( ("bpy.ops.gpencil.image_to_grease_pencil*", "editors/image/editing.html#bpy-ops-gpencil-image-to-grease-pencil"), ("bpy.ops.mesh.vertices_smooth_laplacian*", "modeling/meshes/editing/vertex/laplacian_smooth.html#bpy-ops-mesh-vertices-smooth-laplacian"), ("bpy.ops.object.multires_rebuild_subdiv*", "modeling/modifiers/generate/multiresolution.html#bpy-ops-object-multires-rebuild-subdiv"), - ("bpy.ops.sequencer.select_side_of_frame*", "video_editing/sequencer/selecting.html#bpy-ops-sequencer-select-side-of-frame"), + ("bpy.ops.sequencer.select_side_of_frame*", "video_editing/edit/montage/selecting.html#bpy-ops-sequencer-select-side-of-frame"), ("bpy.types.animvizmotionpaths.frame_end*", "animation/motion_paths.html#bpy-types-animvizmotionpaths-frame-end"), ("bpy.types.armature.rigify_colors_index*", "addons/rigging/rigify/metarigs.html#bpy-types-armature-rigify-colors-index"), ("bpy.types.armature.rigify_theme_to_add*", "addons/rigging/rigify/metarigs.html#bpy-types-armature-rigify-theme-to-add"), @@ -848,6 +868,7 @@ url_manual_mapping = ( ("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.dopesheet.show_only_selected*", "editors/dope_sheet/introduction.html#bpy-types-dopesheet-show-only-selected"), ("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"), @@ -857,11 +878,13 @@ url_manual_mapping = ( ("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.geometrynodecaptureattribute*", "modeling/geometry_nodes/attribute/capture_attribute.html#bpy-types-geometrynodecaptureattribute"), + ("bpy.types.geometrynodeinputshadesmooth*", "modeling/geometry_nodes/mesh/is_shade_smooth.html#bpy-types-geometrynodeinputshadesmooth"), ("bpy.types.geometrynodeinstanceonpoints*", "modeling/geometry_nodes/instances/instance_on_points.html#bpy-types-geometrynodeinstanceonpoints"), ("bpy.types.geometrynodepointstovertices*", "modeling/geometry_nodes/point/points_to_vertices.html#bpy-types-geometrynodepointstovertices"), ("bpy.types.geometrynoderealizeinstances*", "modeling/geometry_nodes/instances/realize_instances.html#bpy-types-geometrynoderealizeinstances"), ("bpy.types.geometrynodeseparategeometry*", "modeling/geometry_nodes/geometry/separate_geometry.html#bpy-types-geometrynodeseparategeometry"), ("bpy.types.geometrynodesetmaterialindex*", "modeling/geometry_nodes/material/set_material_index.html#bpy-types-geometrynodesetmaterialindex"), + ("bpy.types.greasepencil.edit_line_color*", "grease_pencil/properties/display.html#bpy-types-greasepencil-edit-line-color"), ("bpy.types.material.preview_render_type*", "render/materials/preview.html#bpy-types-material-preview-render-type"), ("bpy.types.materialgpencilstyle.pattern*", "grease_pencil/materials/properties.html#bpy-types-materialgpencilstyle-pattern"), ("bpy.types.materialgpencilstyle.texture*", "grease_pencil/materials/properties.html#bpy-types-materialgpencilstyle-texture"), @@ -873,7 +896,7 @@ url_manual_mapping = ( ("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"), - ("bpy.types.sequenceeditor.overlay_frame*", "video_editing/preview/sidebar.html#bpy-types-sequenceeditor-overlay-frame"), + ("bpy.types.sequenceeditor.overlay_frame*", "editors/video_sequencer/preview/sidebar.html#bpy-types-sequenceeditor-overlay-frame"), ("bpy.types.shadernodebsdfhairprincipled*", "render/shader_nodes/shader/hair_principled.html#bpy-types-shadernodebsdfhairprincipled"), ("bpy.types.shadernodevectordisplacement*", "render/shader_nodes/vector/vector_displacement.html#bpy-types-shadernodevectordisplacement"), ("bpy.types.spacegrapheditor.show_cursor*", "editors/graph_editor/introduction.html#bpy-types-spacegrapheditor-show-cursor"), @@ -903,9 +926,9 @@ url_manual_mapping = ( ("bpy.ops.object.visual_transform_apply*", "scene_layout/object/editing/apply.html#bpy-ops-object-visual-transform-apply"), ("bpy.ops.outliner.collection_duplicate*", "editors/outliner/editing.html#bpy-ops-outliner-collection-duplicate"), ("bpy.ops.pose.select_constraint_target*", "animation/armatures/posing/selecting.html#bpy-ops-pose-select-constraint-target"), - ("bpy.ops.sequencer.change_effect_input*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-change-effect-input"), - ("bpy.ops.sequencer.strip_color_tag_set*", "video_editing/sequencer/sidebar/strip.html#bpy-ops-sequencer-strip-color-tag-set"), - ("bpy.ops.sequencer.strip_transform_fit*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-strip-transform-fit"), + ("bpy.ops.sequencer.change_effect_input*", "video_editing/edit/montage/editing.html#bpy-ops-sequencer-change-effect-input"), + ("bpy.ops.sequencer.strip_color_tag_set*", "editors/video_sequencer/sequencer/sidebar/strip.html#bpy-ops-sequencer-strip-color-tag-set"), + ("bpy.ops.sequencer.strip_transform_fit*", "video_editing/edit/montage/editing.html#bpy-ops-sequencer-strip-transform-fit"), ("bpy.types.armature.rigify_colors_lock*", "addons/rigging/rigify/metarigs.html#bpy-types-armature-rigify-colors-lock"), ("bpy.types.bakesettings.cage_extrusion*", "render/cycles/baking.html#bpy-types-bakesettings-cage-extrusion"), ("bpy.types.bakesettings.use_pass_color*", "render/cycles/baking.html#bpy-types-bakesettings-use-pass-color"), @@ -918,6 +941,7 @@ url_manual_mapping = ( ("bpy.types.colormanageddisplaysettings*", "render/color_management.html#bpy-types-colormanageddisplaysettings"), ("bpy.types.colorramp.hue_interpolation*", "interface/controls/templates/color_ramp.html#bpy-types-colorramp-hue-interpolation"), ("bpy.types.compositornodebilateralblur*", "compositing/types/filter/bilateral_blur.html#bpy-types-compositornodebilateralblur"), + ("bpy.types.compositornodecryptomattev2*", "compositing/types/matte/cryptomatte.html#bpy-types-compositornodecryptomattev2"), ("bpy.types.compositornodedistancematte*", "compositing/types/matte/distance_key.html#bpy-types-compositornodedistancematte"), ("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"), @@ -941,6 +965,8 @@ url_manual_mapping = ( ("bpy.types.geometrynodesplineparameter*", "modeling/geometry_nodes/curve/spline_parameter.html#bpy-types-geometrynodesplineparameter"), ("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.greasepencil.use_onion_fade*", "grease_pencil/properties/onion_skinning.html#bpy-types-greasepencil-use-onion-fade"), + ("bpy.types.greasepencil.use_onion_loop*", "grease_pencil/properties/onion_skinning.html#bpy-types-greasepencil-use-onion-loop"), ("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/view_layer/line_style/modifiers/color/material.html#bpy-types-linestyle-modifier-material"), ("bpy.types.motionpath.use_custom_color*", "animation/motion_paths.html#bpy-types-motionpath-use-custom-color"), @@ -959,9 +985,9 @@ url_manual_mapping = ( ("bpy.types.sceneeevee.volumetric_light*", "render/eevee/render_settings/volumetrics.html#bpy-types-sceneeevee-volumetric-light"), ("bpy.types.sculpt.detail_refine_method*", "sculpt_paint/sculpting/tool_settings/dyntopo.html#bpy-types-sculpt-detail-refine-method"), ("bpy.types.sculpt.symmetrize_direction*", "sculpt_paint/sculpting/tool_settings/symmetry.html#bpy-types-sculpt-symmetrize-direction"), - ("bpy.types.sequenceeditor.show_overlay*", "video_editing/preview/sidebar.html#bpy-types-sequenceeditor-show-overlay"), - ("bpy.types.sequenceeditor.use_prefetch*", "video_editing/preview/sidebar.html#bpy-types-sequenceeditor-use-prefetch"), - ("bpy.types.soundsequence.show_waveform*", "video_editing/sequencer/sidebar/strip.html#bpy-types-soundsequence-show-waveform"), + ("bpy.types.sequenceeditor.show_overlay*", "editors/video_sequencer/preview/sidebar.html#bpy-types-sequenceeditor-show-overlay"), + ("bpy.types.sequenceeditor.use_prefetch*", "editors/video_sequencer/preview/sidebar.html#bpy-types-sequenceeditor-use-prefetch"), + ("bpy.types.soundsequence.show_waveform*", "editors/video_sequencer/sequencer/sidebar/strip.html#bpy-types-soundsequence-show-waveform"), ("bpy.types.spaceclipeditor.show_stable*", "editors/clip/display/clip_display.html#bpy-types-spaceclipeditor-show-stable"), ("bpy.types.spaceoutliner.filter_invert*", "editors/outliner/interface.html#bpy-types-spaceoutliner-filter-invert"), ("bpy.types.spacetexteditor.show_margin*", "editors/text_editor.html#bpy-types-spacetexteditor-show-margin"), @@ -983,7 +1009,7 @@ url_manual_mapping = ( ("bpy.ops.object.parent_no_inverse_set*", "scene_layout/object/editing/parent.html#bpy-ops-object-parent-no-inverse-set"), ("bpy.ops.object.vertex_group_quantize*", "sculpt_paint/weight_paint/editing.html#bpy-ops-object-vertex-group-quantize"), ("bpy.ops.outliner.collection_instance*", "editors/outliner/editing.html#bpy-ops-outliner-collection-instance"), - ("bpy.ops.sequencer.change_effect_type*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-change-effect-type"), + ("bpy.ops.sequencer.change_effect_type*", "video_editing/edit/montage/editing.html#bpy-ops-sequencer-change-effect-type"), ("bpy.ops.transform.create_orientation*", "editors/3dview/controls/orientation.html#bpy-ops-transform-create-orientation"), ("bpy.ops.transform.delete_orientation*", "editors/3dview/controls/orientation.html#bpy-ops-transform-delete-orientation"), ("bpy.ops.view3d.localview_remove_from*", "editors/3dview/navigate/local_view.html#bpy-ops-view3d-localview-remove-from"), @@ -1018,6 +1044,8 @@ url_manual_mapping = ( ("bpy.types.geometrynodesetshadesmooth*", "modeling/geometry_nodes/mesh/set_shade_smooth.html#bpy-types-geometrynodesetshadesmooth"), ("bpy.types.geometrynodestringtocurves*", "modeling/geometry_nodes/text/string_to_curves.html#bpy-types-geometrynodestringtocurves"), ("bpy.types.geometrynodesubdividecurve*", "modeling/geometry_nodes/curve/subdivide_curve.html#bpy-types-geometrynodesubdividecurve"), + ("bpy.types.gpencillayer.channel_color*", "grease_pencil/properties/layers.html#bpy-types-gpencillayer-channel-color"), + ("bpy.types.gpencillayer.use_solo_mode*", "grease_pencil/properties/layers.html#bpy-types-gpencillayer-use-solo-mode"), ("bpy.types.greasepencil.use_multiedit*", "grease_pencil/multiframe.html#bpy-types-greasepencil-use-multiedit"), ("bpy.types.keyframe.handle_right_type*", "editors/graph_editor/fcurves/properties.html#bpy-types-keyframe-handle-right-type"), ("bpy.types.materialgpencilstyle.color*", "grease_pencil/materials/properties.html#bpy-types-materialgpencilstyle-color"), @@ -1025,9 +1053,10 @@ url_manual_mapping = ( ("bpy.types.movietrackingstabilization*", "movie_clip/tracking/clip/sidebar/stabilization/index.html#bpy-types-movietrackingstabilization"), ("bpy.types.object.display_bounds_type*", "scene_layout/object/properties/display.html#bpy-types-object-display-bounds-type"), ("bpy.types.regionview3d.lock_rotation*", "editors/3dview/navigate/views.html#bpy-types-regionview3d-lock-rotation"), + ("bpy.types.rendersettings.hair_subdiv*", "render/cycles/render_settings/hair.html#bpy-types-rendersettings-hair-subdiv"), ("bpy.types.scene.audio_distance_model*", "scene_layout/scene/properties.html#bpy-types-scene-audio-distance-model"), ("bpy.types.scene.audio_doppler_factor*", "scene_layout/scene/properties.html#bpy-types-scene-audio-doppler-factor"), - ("bpy.types.sequencetransform.rotation*", "video_editing/sequencer/sidebar/strip.html#bpy-types-sequencetransform-rotation"), + ("bpy.types.sequencetransform.rotation*", "editors/video_sequencer/sequencer/sidebar/strip.html#bpy-types-sequencetransform-rotation"), ("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"), @@ -1063,7 +1092,7 @@ url_manual_mapping = ( ("bpy.ops.pose.visual_transform_apply*", "animation/armatures/posing/editing/apply.html#bpy-ops-pose-visual-transform-apply"), ("bpy.ops.poselib.convert_old_poselib*", "animation/armatures/posing/editing/pose_library.html#bpy-ops-poselib-convert-old-poselib"), ("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.sequencer.view_ghost_border*", "editors/video_sequencer/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.types.animdata.action_influence*", "editors/nla/sidebar.html#bpy-types-animdata-action-influence"), ("bpy.types.armature.layers_protected*", "animation/armatures/properties/skeleton.html#bpy-types-armature-layers-protected"), @@ -1078,6 +1107,7 @@ url_manual_mapping = ( ("bpy.types.camera.passepartout_alpha*", "render/cameras.html#bpy-types-camera-passepartout-alpha"), ("bpy.types.colorrampelement.position*", "interface/controls/templates/color_ramp.html#bpy-types-colorrampelement-position"), ("bpy.types.compositornodechromamatte*", "compositing/types/matte/chroma_key.html#bpy-types-compositornodechromamatte"), + ("bpy.types.compositornodecryptomatte*", "compositing/types/matte/cryptomatte_legacy.html#bpy-types-compositornodecryptomatte"), ("bpy.types.compositornodedilateerode*", "compositing/types/filter/dilate_erode.html#bpy-types-compositornodedilateerode"), ("bpy.types.compositornodeellipsemask*", "compositing/types/matte/ellipse_mask.html#bpy-types-compositornodeellipsemask"), ("bpy.types.compositornodesplitviewer*", "compositing/types/output/split_viewer.html#bpy-types-compositornodesplitviewer"), @@ -1090,20 +1120,23 @@ url_manual_mapping = ( ("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.functionnodereplacestring*", "modeling/geometry_nodes/text/replace_string.html#bpy-types-functionnodereplacestring"), + ("bpy.types.functionnodevaluetostring*", "modeling/geometry_nodes/text/value_to_string.html#bpy-types-functionnodevaluetostring"), ("bpy.types.geometrynodecurvetopoints*", "modeling/geometry_nodes/curve/curve_to_points.html#bpy-types-geometrynodecurvetopoints"), ("bpy.types.geometrynodeinputmaterial*", "modeling/geometry_nodes/input/material.html#bpy-types-geometrynodeinputmaterial"), ("bpy.types.geometrynodeinputposition*", "modeling/geometry_nodes/input/position.html#bpy-types-geometrynodeinputposition"), - ("bpy.types.geometrynodeisshadesmooth*", "modeling/geometry_nodes/mesh/is_shade_smooth.html#bpy-types-geometrynodeisshadesmooth"), ("bpy.types.geometrynodemeshicosphere*", "modeling/geometry_nodes/mesh_primitives/icosphere.html#bpy-types-geometrynodemeshicosphere"), - ("bpy.types.geometrynodereplacestring*", "modeling/geometry_nodes/text/replace_string.html#bpy-types-geometrynodereplacestring"), ("bpy.types.geometrynoderesamplecurve*", "modeling/geometry_nodes/curve/resample_curve.html#bpy-types-geometrynoderesamplecurve"), ("bpy.types.geometrynodescaleelements*", "modeling/geometry_nodes/mesh/scale_elements.html#bpy-types-geometrynodescaleelements"), ("bpy.types.geometrynodesubdividemesh*", "modeling/geometry_nodes/mesh/subdivide_mesh.html#bpy-types-geometrynodesubdividemesh"), - ("bpy.types.geometrynodevaluetostring*", "modeling/geometry_nodes/text/value_to_string.html#bpy-types-geometrynodevaluetostring"), + ("bpy.types.greasepencil.before_color*", "grease_pencil/properties/onion_skinning.html#bpy-types-greasepencil-before-color"), + ("bpy.types.greasepencil.onion_factor*", "grease_pencil/properties/onion_skinning.html#bpy-types-greasepencil-onion-factor"), + ("bpy.types.greasepencil.pixel_factor*", "grease_pencil/properties/strokes.html#bpy-types-greasepencil-pixel-factor"), ("bpy.types.keyframe.handle_left_type*", "editors/graph_editor/fcurves/properties.html#bpy-types-keyframe-handle-left-type"), ("bpy.types.light.use_custom_distance*", "render/eevee/lighting.html#bpy-types-light-use-custom-distance"), ("bpy.types.materialgpencilstyle.flip*", "grease_pencil/materials/properties.html#bpy-types-materialgpencilstyle-flip"), ("bpy.types.materialgpencilstyle.mode*", "grease_pencil/materials/properties.html#bpy-types-materialgpencilstyle-mode"), + ("bpy.types.meshsequencecachemodifier*", "modeling/modifiers/modify/mesh_sequence_cache.html#bpy-types-meshsequencecachemodifier"), ("bpy.types.modifier.show_in_editmode*", "modeling/modifiers/introduction.html#bpy-types-modifier-show-in-editmode"), ("bpy.types.motionpath.line_thickness*", "animation/motion_paths.html#bpy-types-motionpath-line-thickness"), ("bpy.types.object.empty_display_size*", "modeling/empties.html#bpy-types-object-empty-display-size"), @@ -1116,10 +1149,11 @@ url_manual_mapping = ( ("bpy.types.sceneeevee.bokeh_max_size*", "render/eevee/render_settings/depth_of_field.html#bpy-types-sceneeevee-bokeh-max-size"), ("bpy.types.sculpt.detail_type_method*", "sculpt_paint/sculpting/tool_settings/dyntopo.html#bpy-types-sculpt-detail-type-method"), ("bpy.types.sculpt.use_smooth_shading*", "sculpt_paint/sculpting/tool_settings/dyntopo.html#bpy-types-sculpt-use-smooth-shading"), - ("bpy.types.sequenceeditor.show_cache*", "video_editing/sequencer/navigating.html#bpy-types-sequenceeditor-show-cache"), + ("bpy.types.sequenceeditor.show_cache*", "editors/video_sequencer/sequencer/navigating.html#bpy-types-sequenceeditor-show-cache"), ("bpy.types.shadernodebsdfanisotropic*", "render/shader_nodes/shader/anisotropic.html#bpy-types-shadernodebsdfanisotropic"), ("bpy.types.shadernodebsdftranslucent*", "render/shader_nodes/shader/translucent.html#bpy-types-shadernodebsdftranslucent"), ("bpy.types.shadernodebsdftransparent*", "render/shader_nodes/shader/transparent.html#bpy-types-shadernodebsdftransparent"), + ("bpy.types.shadernodetexpointdensity*", "render/shader_nodes/textures/point_density.html#bpy-types-shadernodetexpointdensity"), ("bpy.types.shadernodevectortransform*", "render/shader_nodes/vector/transform.html#bpy-types-shadernodevectortransform"), ("bpy.types.shrinkwrapgpencilmodifier*", "grease_pencil/modifiers/deform/shrinkwrap.html#bpy-types-shrinkwrapgpencilmodifier"), ("bpy.types.spaceclipeditor.show_grid*", "editors/clip/display/clip_display.html#bpy-types-spaceclipeditor-show-grid"), @@ -1158,8 +1192,8 @@ url_manual_mapping = ( ("bpy.ops.pose.user_transforms_clear*", "animation/armatures/posing/editing/clear.html#bpy-ops-pose-user-transforms-clear"), ("bpy.ops.poselib.browse_interactive*", "animation/armatures/posing/editing/pose_library.html#bpy-ops-poselib-browse-interactive"), ("bpy.ops.sculpt.set_persistent_base*", "sculpt_paint/sculpting/tools/layer.html#bpy-ops-sculpt-set-persistent-base"), - ("bpy.ops.sequencer.crossfade_sounds*", "video_editing/sequencer/strips/transitions/sound_crossfade.html#bpy-ops-sequencer-crossfade-sounds"), - ("bpy.ops.sequencer.export_subtitles*", "video_editing/preview/introduction.html#bpy-ops-sequencer-export-subtitles"), + ("bpy.ops.sequencer.crossfade_sounds*", "video_editing/edit/montage/strips/transitions/sound_crossfade.html#bpy-ops-sequencer-crossfade-sounds"), + ("bpy.ops.sequencer.export_subtitles*", "editors/video_sequencer/preview/header.html#bpy-ops-sequencer-export-subtitles"), ("bpy.ops.transform.edge_bevelweight*", "modeling/meshes/editing/edge/edge_data.html#bpy-ops-transform-edge-bevelweight"), ("bpy.ops.wm.previews_batch_generate*", "files/blend/previews.html#bpy-ops-wm-previews-batch-generate"), ("bpy.types.assetmetadata.active_tag*", "editors/asset_browser.html#bpy-types-assetmetadata-active-tag"), @@ -1187,7 +1221,9 @@ url_manual_mapping = ( ("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.functionnodestringlength*", "modeling/geometry_nodes/text/string_length.html#bpy-types-functionnodestringlength"), ("bpy.types.geometrynodefieldatindex*", "modeling/geometry_nodes/utilities/field_at_index.html#bpy-types-geometrynodefieldatindex"), + ("bpy.types.geometrynodeimagetexture*", "modeling/geometry_nodes/texture/image.html#bpy-types-geometrynodeimagetexture"), ("bpy.types.geometrynodeinputtangent*", "modeling/geometry_nodes/curve/curve_tangent.html#bpy-types-geometrynodeinputtangent"), ("bpy.types.geometrynodejoingeometry*", "modeling/geometry_nodes/geometry/join_geometry.html#bpy-types-geometrynodejoingeometry"), ("bpy.types.geometrynodemeshcylinder*", "modeling/geometry_nodes/mesh_primitives/cylinder.html#bpy-types-geometrynodemeshcylinder"), @@ -1196,8 +1232,11 @@ url_manual_mapping = ( ("bpy.types.geometrynodereversecurve*", "modeling/geometry_nodes/curve/reverse_curve.html#bpy-types-geometrynodereversecurve"), ("bpy.types.geometrynodesetcurvetilt*", "modeling/geometry_nodes/curve/set_curve_tilt.html#bpy-types-geometrynodesetcurvetilt"), ("bpy.types.geometrynodesplinelength*", "modeling/geometry_nodes/curve/spline_length.html#bpy-types-geometrynodesplinelength"), - ("bpy.types.geometrynodestringlength*", "modeling/geometry_nodes/text/string_length.html#bpy-types-geometrynodestringlength"), ("bpy.types.geometrynodevolumetomesh*", "modeling/geometry_nodes/volume/volume_to_mesh.html#bpy-types-geometrynodevolumetomesh"), + ("bpy.types.gpencillayer.line_change*", "grease_pencil/properties/layers.html#bpy-types-gpencillayer-line-change"), + ("bpy.types.gpencillayer.parent_type*", "grease_pencil/properties/layers.html#bpy-types-gpencillayer-parent-type"), + ("bpy.types.gpencillayer.tint_factor*", "grease_pencil/properties/layers.html#bpy-types-gpencillayer-tint-factor"), + ("bpy.types.greasepencil.after_color*", "grease_pencil/properties/onion_skinning.html#bpy-types-greasepencil-after-color"), ("bpy.types.image.use_half_precision*", "editors/image/image_settings.html#bpy-types-image-use-half-precision"), ("bpy.types.image.use_view_as_render*", "editors/image/image_settings.html#bpy-types-image-use-view-as-render"), ("bpy.types.imagepaint.interpolation*", "sculpt_paint/texture_paint/tool_settings/texture_slots.html#bpy-types-imagepaint-interpolation"), @@ -1208,15 +1247,16 @@ url_manual_mapping = ( ("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.hair_type*", "render/eevee/render_settings/hair.html#bpy-types-rendersettings-hair-type"), ("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.sequencetransform.offset*", "editors/video_sequencer/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"), ("bpy.types.shadernodebsdfrefraction*", "render/shader_nodes/shader/refraction.html#bpy-types-shadernodebsdfrefraction"), ("bpy.types.shadernodeoutputmaterial*", "render/shader_nodes/output/material.html#bpy-types-shadernodeoutputmaterial"), ("bpy.types.shadernodetexenvironment*", "render/shader_nodes/textures/environment.html#bpy-types-shadernodetexenvironment"), - ("bpy.types.spacesequenceeditor.show*", "video_editing/preview/introduction.html#bpy-types-spacesequenceeditor-show"), + ("bpy.types.spacesequenceeditor.show*", "editors/video_sequencer/preview/header.html#bpy-types-spacesequenceeditor-show"), ("bpy.types.spaceuveditor.show_faces*", "editors/uv/overlays.html#bpy-types-spaceuveditor-show-faces"), ("bpy.types.spaceuveditor.uv_opacity*", "editors/uv/overlays.html#bpy-types-spaceuveditor-uv-opacity"), ("bpy.types.subdividegpencilmodifier*", "grease_pencil/modifiers/generate/subdivide.html#bpy-types-subdividegpencilmodifier"), @@ -1226,6 +1266,7 @@ url_manual_mapping = ( ("bpy.types.unitsettings.length_unit*", "scene_layout/scene/properties.html#bpy-types-unitsettings-length-unit"), ("bpy.types.vertexweighteditmodifier*", "modeling/modifiers/modify/weight_edit.html#bpy-types-vertexweighteditmodifier"), ("bpy.types.volumedisplay.slice_axis*", "modeling/volumes/properties.html#bpy-types-volumedisplay-slice-axis"), + ("bpy.ops.action.markers_make_local*", "animation/markers.html#bpy-ops-action-markers-make-local"), ("bpy.ops.anim.channels_clean_empty*", "editors/nla/editing.html#bpy-ops-anim-channels-clean-empty"), ("bpy.ops.armature.select_hierarchy*", "animation/armatures/bones/selecting.html#bpy-ops-armature-select-hierarchy"), ("bpy.ops.armature.switch_direction*", "animation/armatures/bones/editing/switch_direction.html#bpy-ops-armature-switch-direction"), @@ -1234,6 +1275,7 @@ url_manual_mapping = ( ("bpy.ops.clip.setup_tracking_scene*", "movie_clip/tracking/clip/editing/clip.html#bpy-ops-clip-setup-tracking-scene"), ("bpy.ops.curve.match_texture_space*", "modeling/meshes/uv/uv_texture_spaces.html#bpy-ops-curve-match-texture-space"), ("bpy.ops.font.text_paste_from_file*", "modeling/texts/editing.html#bpy-ops-font-text-paste-from-file"), + ("bpy.ops.geometry.attribute_remove*", "modeling/geometry_nodes/attributes_reference.html#bpy-ops-geometry-attribute-remove"), ("bpy.ops.gpencil.frame_clean_loose*", "grease_pencil/modes/edit/grease_pencil_menu.html#bpy-ops-gpencil-frame-clean-loose"), ("bpy.ops.mask.primitive_circle_add*", "movie_clip/masking/scurve.html#bpy-ops-mask-primitive-circle-add"), ("bpy.ops.mask.primitive_square_add*", "movie_clip/masking/scurve.html#bpy-ops-mask-primitive-square-add"), @@ -1248,10 +1290,10 @@ url_manual_mapping = ( ("bpy.ops.preferences.theme_install*", "editors/preferences/themes.html#bpy-ops-preferences-theme-install"), ("bpy.ops.render.play_rendered_anim*", "render/output/animation_player.html#bpy-ops-render-play-rendered-anim"), ("bpy.ops.sculpt.set_pivot_position*", "sculpt_paint/sculpting/editing/sculpt.html#bpy-ops-sculpt-set-pivot-position"), - ("bpy.ops.sequencer.image_strip_add*", "video_editing/sequencer/strips/image.html#bpy-ops-sequencer-image-strip-add"), - ("bpy.ops.sequencer.movie_strip_add*", "video_editing/sequencer/strips/movie.html#bpy-ops-sequencer-movie-strip-add"), - ("bpy.ops.sequencer.reassign_inputs*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-reassign-inputs"), - ("bpy.ops.sequencer.sound_strip_add*", "video_editing/sequencer/strips/sound.html#bpy-ops-sequencer-sound-strip-add"), + ("bpy.ops.sequencer.image_strip_add*", "video_editing/edit/montage/strips/image.html#bpy-ops-sequencer-image-strip-add"), + ("bpy.ops.sequencer.movie_strip_add*", "video_editing/edit/montage/strips/movie.html#bpy-ops-sequencer-movie-strip-add"), + ("bpy.ops.sequencer.reassign_inputs*", "video_editing/edit/montage/editing.html#bpy-ops-sequencer-reassign-inputs"), + ("bpy.ops.sequencer.sound_strip_add*", "video_editing/edit/montage/strips/sound.html#bpy-ops-sequencer-sound-strip-add"), ("bpy.ops.ui.remove_override_button*", "files/linked_libraries/library_overrides.html#bpy-ops-ui-remove-override-button"), ("bpy.ops.view3d.view_center_camera*", "editors/3dview/navigate/camera_view.html#bpy-ops-view3d-view-center-camera"), ("bpy.types.animvizmotionpaths.type*", "animation/motion_paths.html#bpy-types-animvizmotionpaths-type"), @@ -1269,6 +1311,7 @@ url_manual_mapping = ( ("bpy.types.compositornodelumamatte*", "compositing/types/matte/luminance_key.html#bpy-types-compositornodelumamatte"), ("bpy.types.compositornodemovieclip*", "compositing/types/input/movie_clip.html#bpy-types-compositornodemovieclip"), ("bpy.types.compositornodenormalize*", "compositing/types/vector/normalize.html#bpy-types-compositornodenormalize"), + ("bpy.types.compositornodeposterize*", "compositing/types/color/posterize.html#bpy-types-compositornodeposterize"), ("bpy.types.compositornodepremulkey*", "compositing/types/converter/alpha_convert.html#bpy-types-compositornodepremulkey"), ("bpy.types.compositornodescenetime*", "compositing/types/input/scene_time.html#bpy-types-compositornodescenetime"), ("bpy.types.compositornodestabilize*", "compositing/types/distort/stabilize_2d.html#bpy-types-compositornodestabilize"), @@ -1280,6 +1323,7 @@ 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.envelopegpencilmodifier*", "grease_pencil/modifiers/generate/envelope.html#bpy-types-envelopegpencilmodifier"), ("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"), @@ -1287,7 +1331,9 @@ url_manual_mapping = ( ("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"), ("bpy.types.functionnodeinputvector*", "modeling/geometry_nodes/input/vector.html#bpy-types-functionnodeinputvector"), - ("bpy.types.geometrynodecurvecircle*", "modeling/geometry_nodes/curve_primitives/curve_circle.html#bpy-types-geometrynodecurvecircle"), + ("bpy.types.functionnoderandomvalue*", "modeling/geometry_nodes/utilities/random_value.html#bpy-types-functionnoderandomvalue"), + ("bpy.types.functionnoderotateeuler*", "modeling/geometry_nodes/utilities/rotate_euler.html#bpy-types-functionnoderotateeuler"), + ("bpy.types.functionnodeslicestring*", "modeling/geometry_nodes/text/slice_string.html#bpy-types-functionnodeslicestring"), ("bpy.types.geometrynodecurvelength*", "modeling/geometry_nodes/curve/curve_length.html#bpy-types-geometrynodecurvelength"), ("bpy.types.geometrynodecurvespiral*", "modeling/geometry_nodes/curve_primitives/curve_spiral.html#bpy-types-geometrynodecurvespiral"), ("bpy.types.geometrynodecurvetomesh*", "modeling/geometry_nodes/curve/curve_to_mesh.html#bpy-types-geometrynodecurvetomesh"), @@ -1295,19 +1341,19 @@ url_manual_mapping = ( ("bpy.types.geometrynodefilletcurve*", "modeling/geometry_nodes/curve/fillet_curve.html#bpy-types-geometrynodefilletcurve"), ("bpy.types.geometrynodeinputnormal*", "modeling/geometry_nodes/input/normal.html#bpy-types-geometrynodeinputnormal"), ("bpy.types.geometrynodeinputradius*", "modeling/geometry_nodes/input/radius.html#bpy-types-geometrynodeinputradius"), - ("bpy.types.geometrynodejoinstrings*", "modeling/geometry_nodes/text/join_strings.html#bpy-types-geometrynodejoinstrings"), ("bpy.types.geometrynodemeshboolean*", "modeling/geometry_nodes/mesh/mesh_boolean.html#bpy-types-geometrynodemeshboolean"), ("bpy.types.geometrynodemeshtocurve*", "modeling/geometry_nodes/mesh/mesh_to_curve.html#bpy-types-geometrynodemeshtocurve"), - ("bpy.types.geometrynoderandomvalue*", "modeling/geometry_nodes/utilities/random_value.html#bpy-types-geometrynoderandomvalue"), - ("bpy.types.geometrynoderotateeuler*", "modeling/geometry_nodes/utilities/rotate_euler.html#bpy-types-geometrynoderotateeuler"), ("bpy.types.geometrynodesamplecurve*", "modeling/geometry_nodes/curve/sample_curve.html#bpy-types-geometrynodesamplecurve"), ("bpy.types.geometrynodesetmaterial*", "modeling/geometry_nodes/material/set_material.html#bpy-types-geometrynodesetmaterial"), ("bpy.types.geometrynodesetposition*", "modeling/geometry_nodes/geometry/set_position.html#bpy-types-geometrynodesetposition"), - ("bpy.types.geometrynodeslicestring*", "modeling/geometry_nodes/text/slice_string.html#bpy-types-geometrynodeslicestring"), ("bpy.types.geometrynodetriangulate*", "modeling/geometry_nodes/mesh/triangulate.html#bpy-types-geometrynodetriangulate"), ("bpy.types.gpencillayer.blend_mode*", "grease_pencil/properties/layers.html#bpy-types-gpencillayer-blend-mode"), + ("bpy.types.gpencillayer.pass_index*", "grease_pencil/properties/layers.html#bpy-types-gpencillayer-pass-index"), + ("bpy.types.gpencillayer.tint_color*", "grease_pencil/properties/layers.html#bpy-types-gpencillayer-tint-color"), ("bpy.types.gpencillayer.use_lights*", "grease_pencil/properties/layers.html#bpy-types-gpencillayer-use-lights"), ("bpy.types.gpencilsculptguide.type*", "grease_pencil/modes/draw/guides.html#bpy-types-gpencilsculptguide-type"), + ("bpy.types.greasepencil.onion_mode*", "grease_pencil/properties/onion_skinning.html#bpy-types-greasepencil-onion-mode"), + ("bpy.types.greasepencilgrid.offset*", "grease_pencil/properties/display.html#bpy-types-greasepencilgrid-offset"), ("bpy.types.laplaciandeformmodifier*", "modeling/modifiers/deform/laplacian_deform.html#bpy-types-laplaciandeformmodifier"), ("bpy.types.laplaciansmoothmodifier*", "modeling/modifiers/deform/laplacian_smooth.html#bpy-types-laplaciansmoothmodifier"), ("bpy.types.layercollection.exclude*", "editors/outliner/interface.html#bpy-types-layercollection-exclude"), @@ -1321,13 +1367,14 @@ url_manual_mapping = ( ("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*", "editors/video_sequencer/preview/display/overlays.html#bpy-types-sequencerpreviewoverlay"), - ("bpy.types.sequencetransform.scale*", "video_editing/sequencer/sidebar/strip.html#bpy-types-sequencetransform-scale"), + ("bpy.types.sequencetransform.scale*", "editors/video_sequencer/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"), ("bpy.types.shadernodetexwhitenoise*", "render/shader_nodes/textures/white_noise.html#bpy-types-shadernodetexwhitenoise"), ("bpy.types.shadernodevolumescatter*", "render/shader_nodes/shader/volume_scatter.html#bpy-types-shadernodevolumescatter"), ("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.texturenodetexdistnoise*", "editors/texture_node/types/textures/distorted_noise.html#bpy-types-texturenodetexdistnoise"), ("bpy.types.vertexweightmixmodifier*", "modeling/modifiers/modify/weight_mix.html#bpy-types-vertexweightmixmodifier"), ("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"), @@ -1365,15 +1412,15 @@ url_manual_mapping = ( ("bpy.ops.rigidbody.mass_calculate*", "scene_layout/object/editing/rigid_body.html#bpy-ops-rigidbody-mass-calculate"), ("bpy.ops.screen.spacedata_cleanup*", "advanced/operators.html#bpy-ops-screen-spacedata-cleanup"), ("bpy.ops.sculpt.detail_flood_fill*", "sculpt_paint/sculpting/tool_settings/dyntopo.html#bpy-ops-sculpt-detail-flood-fill"), - ("bpy.ops.sequencer.duplicate_move*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-duplicate-move"), - ("bpy.ops.sequencer.select_grouped*", "video_editing/sequencer/selecting.html#bpy-ops-sequencer-select-grouped"), - ("bpy.ops.sequencer.select_handles*", "video_editing/sequencer/selecting.html#bpy-ops-sequencer-select-handles"), + ("bpy.ops.sequencer.duplicate_move*", "video_editing/edit/montage/editing.html#bpy-ops-sequencer-duplicate-move"), + ("bpy.ops.sequencer.select_grouped*", "video_editing/edit/montage/selecting.html#bpy-ops-sequencer-select-grouped"), + ("bpy.ops.sequencer.select_handles*", "video_editing/edit/montage/selecting.html#bpy-ops-sequencer-select-handles"), ("bpy.ops.uv.average_islands_scale*", "modeling/meshes/uv/editing.html#bpy-ops-uv-average-islands-scale"), ("bpy.types.armature.axes_position*", "animation/armatures/properties/display.html#bpy-types-armature-axes-position"), ("bpy.types.armature.pose_position*", "animation/armatures/properties/skeleton.html#bpy-types-armature-pose-position"), ("bpy.types.bakesettings.use_clear*", "render/cycles/baking.html#bpy-types-bakesettings-use-clear"), ("bpy.types.bone.envelope_distance*", "animation/armatures/bones/properties/deform.html#bpy-types-bone-envelope-distance"), - ("bpy.types.brightcontrastmodifier*", "video_editing/sequencer/sidebar/modifiers.html#bpy-types-brightcontrastmodifier"), + ("bpy.types.brightcontrastmodifier*", "editors/video_sequencer/sequencer/sidebar/modifiers.html#bpy-types-brightcontrastmodifier"), ("bpy.types.brush.cursor_color_add*", "sculpt_paint/brush/cursor.html#bpy-types-brush-cursor-color-add"), ("bpy.types.brush.pose_deform_type*", "sculpt_paint/sculpting/tools/pose.html#bpy-types-brush-pose-deform-type"), ("bpy.types.brush.pose_ik_segments*", "sculpt_paint/sculpting/tools/pose.html#bpy-types-brush-pose-ik-segments"), @@ -1407,16 +1454,20 @@ url_manual_mapping = ( ("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.functionnodefloattoint*", "modeling/geometry_nodes/utilities/float_to_integer.html#bpy-types-functionnodefloattoint"), + ("bpy.types.functionnodeinputcolor*", "modeling/geometry_nodes/input/color.html#bpy-types-functionnodeinputcolor"), ("bpy.types.geometrynodeconvexhull*", "modeling/geometry_nodes/geometry/convex_hull.html#bpy-types-geometrynodeconvexhull"), - ("bpy.types.geometrynodedomainsize*", "modeling/geometry_nodes/attribute/domain_size.html#bpy-types-geometrynodedomainsize"), - ("bpy.types.geometrynodefloattoint*", "modeling/geometry_nodes/utilities/float_to_integer.html#bpy-types-geometrynodefloattoint"), - ("bpy.types.geometrynodeinputcolor*", "modeling/geometry_nodes/input/color.html#bpy-types-geometrynodeinputcolor"), ("bpy.types.geometrynodeinputindex*", "modeling/geometry_nodes/input/input_index.html#bpy-types-geometrynodeinputindex"), ("bpy.types.geometrynodeisviewport*", "modeling/geometry_nodes/input/is_viewport.html#bpy-types-geometrynodeisviewport"), ("bpy.types.geometrynodemeshcircle*", "modeling/geometry_nodes/mesh_primitives/mesh_circle.html#bpy-types-geometrynodemeshcircle"), ("bpy.types.geometrynodeobjectinfo*", "modeling/geometry_nodes/input/object_info.html#bpy-types-geometrynodeobjectinfo"), + ("bpy.types.geometrynodesplitedges*", "modeling/geometry_nodes/mesh/split_edges.html#bpy-types-geometrynodesplitedges"), + ("bpy.types.geometrynodestringjoin*", "modeling/geometry_nodes/text/join_strings.html#bpy-types-geometrynodestringjoin"), + ("bpy.types.greasepencilgrid.color*", "grease_pencil/properties/display.html#bpy-types-greasepencilgrid-color"), + ("bpy.types.greasepencilgrid.lines*", "grease_pencil/properties/display.html#bpy-types-greasepencilgrid-lines"), + ("bpy.types.greasepencilgrid.scale*", "grease_pencil/properties/display.html#bpy-types-greasepencilgrid-scale"), ("bpy.types.imagepaint.use_occlude*", "sculpt_paint/texture_paint/tool_settings/options.html#bpy-types-imagepaint-use-occlude"), - ("bpy.types.imagesequence.use_flip*", "video_editing/sequencer/sidebar/strip.html#bpy-types-imagesequence-use-flip"), + ("bpy.types.imagesequence.use_flip*", "editors/video_sequencer/sequencer/sidebar/strip.html#bpy-types-imagesequence-use-flip"), ("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"), @@ -1436,9 +1487,11 @@ url_manual_mapping = ( ("bpy.types.shadernodelightfalloff*", "render/shader_nodes/color/light_falloff.html#bpy-types-shadernodelightfalloff"), ("bpy.types.shadernodeparticleinfo*", "render/shader_nodes/input/particle_info.html#bpy-types-shadernodeparticleinfo"), ("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.sound.use_memory_cache*", "editors/video_sequencer/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/modify/texture_mapping.html#bpy-types-texturegpencilmodifier"), + ("bpy.types.texturenodecoordinates*", "editors/texture_node/types/input/coordinates.html#bpy-types-texturenodecoordinates"), + ("bpy.types.texturenodetexmusgrave*", "editors/texture_node/types/textures/musgrave.html#bpy-types-texturenodetexmusgrave"), ("bpy.types.unitsettings.mass_unit*", "scene_layout/scene/properties.html#bpy-types-unitsettings-mass-unit"), ("bpy.types.unitsettings.time_unit*", "scene_layout/scene/properties.html#bpy-types-unitsettings-time-unit"), ("bpy.types.volumedisplacemodifier*", "modeling/modifiers/deform/volume_displace.html#bpy-types-volumedisplacemodifier"), @@ -1461,6 +1514,7 @@ url_manual_mapping = ( ("bpy.ops.graph.blend_to_neighbor*", "editors/graph_editor/fcurves/editing.html#bpy-ops-graph-blend-to-neighbor"), ("bpy.ops.graph.snap_cursor_value*", "editors/graph_editor/fcurves/editing.html#bpy-ops-graph-snap-cursor-value"), ("bpy.ops.image.save_all_modified*", "editors/image/editing.html#bpy-ops-image-save-all-modified"), + ("bpy.ops.marker.make_links_scene*", "animation/markers.html#bpy-ops-marker-make-links-scene"), ("bpy.ops.mesh.extrude_edges_move*", "modeling/meshes/editing/edge/extrude_edges.html#bpy-ops-mesh-extrude-edges-move"), ("bpy.ops.mesh.extrude_faces_move*", "modeling/meshes/editing/face/extrude_individual_faces.html#bpy-ops-mesh-extrude-faces-move"), ("bpy.ops.mesh.faces_shade_smooth*", "modeling/meshes/editing/face/shading.html#bpy-ops-mesh-faces-shade-smooth"), @@ -1492,7 +1546,7 @@ url_manual_mapping = ( ("bpy.ops.scene.view_layer_remove*", "render/layers/introduction.html#bpy-ops-scene-view-layer-remove"), ("bpy.ops.screen.screen_full_area*", "interface/window_system/areas.html#bpy-ops-screen-screen-full-area"), ("bpy.ops.sculpt.face_sets_create*", "sculpt_paint/sculpting/editing/face_sets.html#bpy-ops-sculpt-face-sets-create"), - ("bpy.ops.sequencer.select_linked*", "video_editing/sequencer/selecting.html#bpy-ops-sequencer-select-linked"), + ("bpy.ops.sequencer.select_linked*", "video_editing/edit/montage/selecting.html#bpy-ops-sequencer-select-linked"), ("bpy.ops.transform.rotate_normal*", "modeling/meshes/editing/mesh/normals.html#bpy-ops-transform-rotate-normal"), ("bpy.ops.transform.shrink_fatten*", "modeling/meshes/editing/mesh/transform/shrink-fatten.html#bpy-ops-transform-shrink-fatten"), ("bpy.ops.transform.vertex_random*", "modeling/meshes/editing/mesh/transform/randomize.html#bpy-ops-transform-vertex-random"), @@ -1530,13 +1584,15 @@ url_manual_mapping = ( ("bpy.types.fcurve.auto_smoothing*", "editors/graph_editor/fcurves/properties.html#bpy-types-fcurve-auto-smoothing"), ("bpy.types.fluideffectorsettings*", "physics/fluid/type/effector.html#bpy-types-fluideffectorsettings"), ("bpy.types.followtrackconstraint*", "animation/constraints/motion_tracking/follow_track.html#bpy-types-followtrackconstraint"), - ("bpy.types.geometrynodecurveline*", "modeling/geometry_nodes/curve_primitives/curve_line.html#bpy-types-geometrynodecurveline"), + ("bpy.types.functionnodeinputbool*", "modeling/geometry_nodes/input/boolean.html#bpy-types-functionnodeinputbool"), ("bpy.types.geometrynodecurvestar*", "modeling/geometry_nodes/curve_primitives/star.html#bpy-types-geometrynodecurvestar"), - ("bpy.types.geometrynodeedgesplit*", "modeling/geometry_nodes/mesh/split_edges.html#bpy-types-geometrynodeedgesplit"), ("bpy.types.geometrynodefillcurve*", "modeling/geometry_nodes/curve/fill_curve.html#bpy-types-geometrynodefillcurve"), ("bpy.types.geometrynodeflipfaces*", "modeling/geometry_nodes/mesh/flip_faces.html#bpy-types-geometrynodeflipfaces"), + ("bpy.types.geometrynodeproximity*", "modeling/geometry_nodes/geometry/geometry_proximity.html#bpy-types-geometrynodeproximity"), ("bpy.types.geometrynodetransform*", "modeling/geometry_nodes/geometry/transform.html#bpy-types-geometrynodetransform"), ("bpy.types.geometrynodetrimcurve*", "modeling/geometry_nodes/curve/trim_curve.html#bpy-types-geometrynodetrimcurve"), + ("bpy.types.gpencillayer.location*", "grease_pencil/properties/layers.html#bpy-types-gpencillayer-location"), + ("bpy.types.gpencillayer.rotation*", "grease_pencil/properties/layers.html#bpy-types-gpencillayer-rotation"), ("bpy.types.gpencilsculptsettings*", "grease_pencil/properties/index.html#bpy-types-gpencilsculptsettings"), ("bpy.types.keyframe.handle_right*", "editors/graph_editor/fcurves/properties.html#bpy-types-keyframe-handle-right"), ("bpy.types.lengthgpencilmodifier*", "grease_pencil/modifiers/generate/length.html#bpy-types-lengthgpencilmodifier"), @@ -1559,14 +1615,17 @@ url_manual_mapping = ( ("bpy.types.sculpt.gravity_object*", "sculpt_paint/sculpting/tool_settings/options.html#bpy-types-sculpt-gravity-object"), ("bpy.types.shadernodebsdfdiffuse*", "render/shader_nodes/shader/diffuse.html#bpy-types-shadernodebsdfdiffuse"), ("bpy.types.shadernodelayerweight*", "render/shader_nodes/input/layer_weight.html#bpy-types-shadernodelayerweight"), + ("bpy.types.shadernodenewgeometry*", "render/shader_nodes/input/geometry.html#bpy-types-shadernodenewgeometry"), ("bpy.types.shadernodeoutputlight*", "render/shader_nodes/output/light.html#bpy-types-shadernodeoutputlight"), ("bpy.types.shadernodeoutputworld*", "render/shader_nodes/output/world.html#bpy-types-shadernodeoutputworld"), + ("bpy.types.shadernodeshadertorgb*", "render/shader_nodes/converter/shader_to_rgb.html#bpy-types-shadernodeshadertorgb"), ("bpy.types.shadernodetexgradient*", "render/shader_nodes/textures/gradient.html#bpy-types-shadernodetexgradient"), ("bpy.types.shadernodevectorcurve*", "render/shader_nodes/vector/curves.html#bpy-types-shadernodevectorcurve"), ("bpy.types.shadernodevertexcolor*", "render/shader_nodes/input/vertex_color.html#bpy-types-shadernodevertexcolor"), ("bpy.types.smoothgpencilmodifier*", "grease_pencil/modifiers/deform/smooth.html#bpy-types-smoothgpencilmodifier"), ("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.texturenodetexvoronoi*", "editors/texture_node/types/textures/voronoi.html#bpy-types-texturenodetexvoronoi"), ("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"), @@ -1576,6 +1635,7 @@ url_manual_mapping = ( ("bpy.ops.anim.channels_collapse*", "editors/graph_editor/channels.html#bpy-ops-anim-channels-collapse"), ("bpy.ops.armature.select_mirror*", "animation/armatures/bones/selecting.html#bpy-ops-armature-select-mirror"), ("bpy.ops.curve.switch_direction*", "modeling/curves/editing/segments.html#bpy-ops-curve-switch-direction"), + ("bpy.ops.geometry.attribute_add*", "modeling/geometry_nodes/attributes_reference.html#bpy-ops-geometry-attribute-add"), ("bpy.ops.gpencil.duplicate_move*", "grease_pencil/modes/edit/grease_pencil_menu.html#bpy-ops-gpencil-duplicate-move"), ("bpy.ops.gpencil.stroke_arrange*", "grease_pencil/modes/edit/stroke_menu.html#bpy-ops-gpencil-stroke-arrange"), ("bpy.ops.graph.equalize_handles*", "editors/graph_editor/fcurves/editing.html#bpy-ops-graph-equalize-handles"), @@ -1593,7 +1653,7 @@ url_manual_mapping = ( ("bpy.ops.paint.mask_box_gesture*", "sculpt_paint/sculpting/editing/mask.html#bpy-ops-paint-mask-box-gesture"), ("bpy.ops.screen.region_quadview*", "editors/3dview/navigate/views.html#bpy-ops-screen-region-quadview"), ("bpy.ops.screen.screenshot_area*", "interface/window_system/topbar.html#bpy-ops-screen-screenshot-area"), - ("bpy.ops.sequencer.offset_clear*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-offset-clear"), + ("bpy.ops.sequencer.offset_clear*", "video_editing/edit/montage/editing.html#bpy-ops-sequencer-offset-clear"), ("bpy.ops.spreadsheet.toggle_pin*", "editors/spreadsheet.html#bpy-ops-spreadsheet-toggle-pin"), ("bpy.ops.uv.follow_active_quads*", "modeling/meshes/editing/uv.html#bpy-ops-uv-follow-active-quads"), ("bpy.types.arraygpencilmodifier*", "grease_pencil/modifiers/generate/array.html#bpy-types-arraygpencilmodifier"), @@ -1603,11 +1663,11 @@ url_manual_mapping = ( ("bpy.types.brushgpencilsettings*", "grease_pencil/modes/draw/tools/index.html#bpy-types-brushgpencilsettings"), ("bpy.types.buildgpencilmodifier*", "grease_pencil/modifiers/generate/build.html#bpy-types-buildgpencilmodifier"), ("bpy.types.camera.sensor_height*", "render/cameras.html#bpy-types-camera-sensor-height"), - ("bpy.types.colorbalancemodifier*", "video_editing/sequencer/sidebar/modifiers.html#bpy-types-colorbalancemodifier"), + ("bpy.types.colorbalancemodifier*", "editors/video_sequencer/sequencer/sidebar/modifiers.html#bpy-types-colorbalancemodifier"), ("bpy.types.colorgpencilmodifier*", "grease_pencil/modifiers/color/hue_saturation.html#bpy-types-colorgpencilmodifier"), ("bpy.types.colorramp.color_mode*", "interface/controls/templates/color_ramp.html#bpy-types-colorramp-color-mode"), ("bpy.types.compositornodefilter*", "compositing/types/filter/filter_node.html#bpy-types-compositornodefilter"), - ("bpy.types.compositornodehuesat*", "compositing/types/color/posterize.html#bpy-types-compositornodehuesat"), + ("bpy.types.compositornodehuesat*", "compositing/types/color/hue_saturation.html#bpy-types-compositornodehuesat"), ("bpy.types.compositornodeidmask*", "compositing/types/converter/id_mask.html#bpy-types-compositornodeidmask"), ("bpy.types.compositornodeinvert*", "compositing/types/color/invert.html#bpy-types-compositornodeinvert"), ("bpy.types.compositornodekeying*", "compositing/types/matte/keying.html#bpy-types-compositornodekeying"), @@ -1615,6 +1675,7 @@ url_manual_mapping = ( ("bpy.types.compositornodemixrgb*", "compositing/types/color/mix.html#bpy-types-compositornodemixrgb"), ("bpy.types.compositornodenormal*", "compositing/types/vector/normal.html#bpy-types-compositornodenormal"), ("bpy.types.compositornoderotate*", "compositing/types/distort/rotate.html#bpy-types-compositornoderotate"), + ("bpy.types.compositornodeswitch*", "compositing/types/layout/switch.html#bpy-types-compositornodeswitch"), ("bpy.types.compositornodeviewer*", "compositing/types/output/viewer.html#bpy-types-compositornodeviewer"), ("bpy.types.constraint.influence*", "animation/constraints/interface/common.html#bpy-types-constraint-influence"), ("bpy.types.curve.use_path_clamp*", "modeling/curves/properties/path_animation.html#bpy-types-curve-use-path-clamp"), @@ -1623,8 +1684,10 @@ url_manual_mapping = ( ("bpy.types.editbone.use_connect*", "animation/armatures/bones/properties/relations.html#bpy-types-editbone-use-connect"), ("bpy.types.ffmpegsettings.audio*", "render/output/properties/output.html#bpy-types-ffmpegsettings-audio"), ("bpy.types.followpathconstraint*", "animation/constraints/relationship/follow_path.html#bpy-types-followpathconstraint"), - ("bpy.types.gaussianblursequence*", "video_editing/sequencer/strips/effects/blur.html#bpy-types-gaussianblursequence"), + ("bpy.types.functionnodeinputint*", "modeling/geometry_nodes/input/integer.html#bpy-types-functionnodeinputint"), + ("bpy.types.gaussianblursequence*", "video_editing/edit/montage/strips/effects/blur.html#bpy-types-gaussianblursequence"), ("bpy.types.geometrynodeboundbox*", "modeling/geometry_nodes/geometry/bounding_box.html#bpy-types-geometrynodeboundbox"), + ("bpy.types.geometrynodecurvearc*", "modeling/geometry_nodes/curve_primitives/arc.html#bpy-types-geometrynodecurvearc"), ("bpy.types.geometrynodedualmesh*", "modeling/geometry_nodes/mesh/dual_mesh.html#bpy-types-geometrynodedualmesh"), ("bpy.types.geometrynodematerial*", "-1"), ("bpy.types.geometrynodemeshcone*", "modeling/geometry_nodes/mesh_primitives/cone.html#bpy-types-geometrynodemeshcone"), @@ -1649,7 +1712,7 @@ url_manual_mapping = ( ("bpy.types.preferencesfilepaths*", "editors/preferences/file_paths.html#bpy-types-preferencesfilepaths"), ("bpy.types.rigidbodyobject.mass*", "physics/rigid_body/properties/settings.html#bpy-types-rigidbodyobject-mass"), ("bpy.types.scene.background_set*", "scene_layout/scene/properties.html#bpy-types-scene-background-set"), - ("bpy.types.sequence.frame_start*", "video_editing/sequencer/sidebar/strip.html#bpy-types-sequence-frame-start"), + ("bpy.types.sequence.frame_start*", "editors/video_sequencer/sequencer/sidebar/strip.html#bpy-types-sequence-frame-start"), ("bpy.types.shadernodebackground*", "render/shader_nodes/shader/background.html#bpy-types-shadernodebackground"), ("bpy.types.shadernodebsdfglossy*", "render/shader_nodes/shader/glossy.html#bpy-types-shadernodebsdfglossy"), ("bpy.types.shadernodebsdfvelvet*", "render/shader_nodes/shader/velvet.html#bpy-types-shadernodebsdfvelvet"), @@ -1665,14 +1728,18 @@ url_manual_mapping = ( ("bpy.types.simpledeformmodifier*", "modeling/modifiers/deform/simple_deform.html#bpy-types-simpledeformmodifier"), ("bpy.types.spaceclipeditor.show*", "editors/clip/display/index.html#bpy-types-spaceclipeditor-show"), ("bpy.types.spacedopesheeteditor*", "editors/dope_sheet/index.html#bpy-types-spacedopesheeteditor"), - ("bpy.types.speedcontrolsequence*", "video_editing/sequencer/strips/effects/speed_control.html#bpy-types-speedcontrolsequence"), + ("bpy.types.speedcontrolsequence*", "video_editing/edit/montage/strips/effects/speed_control.html#bpy-types-speedcontrolsequence"), ("bpy.types.texturenodecurvetime*", "editors/texture_node/types/input/time.html#bpy-types-texturenodecurvetime"), + ("bpy.types.texturenodetexclouds*", "editors/texture_node/types/textures/clouds.html#bpy-types-texturenodetexclouds"), + ("bpy.types.texturenodetexmarble*", "editors/texture_node/types/textures/marble.html#bpy-types-texturenodetexmarble"), + ("bpy.types.texturenodetexstucci*", "editors/texture_node/types/textures/stucci.html#bpy-types-texturenodetexstucci"), + ("bpy.types.texturenodetranslate*", "editors/texture_node/types/distort/translate.html#bpy-types-texturenodetranslate"), ("bpy.types.transformorientation*", "editors/3dview/controls/orientation.html#bpy-types-transformorientation"), ("bpy.types.unifiedpaintsettings*", "sculpt_paint/brush/brush.html#bpy-types-unifiedpaintsettings"), ("bpy.types.viewlayer.use_strand*", "render/layers/introduction.html#bpy-types-viewlayer-use-strand"), ("bpy.types.volume.sequence_mode*", "modeling/volumes/properties.html#bpy-types-volume-sequence-mode"), ("bpy.types.volumetomeshmodifier*", "modeling/modifiers/generate/volume_to_mesh.html#bpy-types-volumetomeshmodifier"), - ("bpy.types.whitebalancemodifier*", "video_editing/sequencer/sidebar/modifiers.html#bpy-types-whitebalancemodifier"), + ("bpy.types.whitebalancemodifier*", "editors/video_sequencer/sequencer/sidebar/modifiers.html#bpy-types-whitebalancemodifier"), ("bpy.ops.anim.channels_ungroup*", "editors/graph_editor/channels.html#bpy-ops-anim-channels-ungroup"), ("bpy.ops.armature.extrude_move*", "animation/armatures/bones/editing/extrude.html#bpy-ops-armature-extrude-move"), ("bpy.ops.armature.parent_clear*", "animation/armatures/bones/editing/parenting.html#bpy-ops-armature-parent-clear"), @@ -1681,6 +1748,7 @@ url_manual_mapping = ( ("bpy.ops.curve.handle_type_set*", "modeling/curves/editing/control_points.html#bpy-ops-curve-handle-type-set"), ("bpy.ops.curve.spline_type_set*", "modeling/curves/editing/curve.html#bpy-ops-curve-spline-type-set"), ("bpy.ops.file.unpack_libraries*", "files/blend/packed_data.html#bpy-ops-file-unpack-libraries"), + ("bpy.ops.gpencil.layer_isolate*", "grease_pencil/properties/layers.html#bpy-ops-gpencil-layer-isolate"), ("bpy.ops.gpencil.move_to_layer*", "grease_pencil/modes/edit/stroke_menu.html#bpy-ops-gpencil-move-to-layer"), ("bpy.ops.gpencil.stroke_sample*", "grease_pencil/modes/edit/stroke_menu.html#bpy-ops-gpencil-stroke-sample"), ("bpy.ops.gpencil.stroke_smooth*", "grease_pencil/modes/edit/point_menu.html#bpy-ops-gpencil-stroke-smooth"), @@ -1713,12 +1781,12 @@ url_manual_mapping = ( ("bpy.ops.screen.repeat_history*", "interface/undo_redo.html#bpy-ops-screen-repeat-history"), ("bpy.ops.script.execute_preset*", "interface/window_system/tabs_panels.html#bpy-ops-script-execute-preset"), ("bpy.ops.sculpt.face_sets_init*", "sculpt_paint/sculpting/editing/face_sets.html#bpy-ops-sculpt-face-sets-init"), - ("bpy.ops.sequencer.change_path*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-change-path"), - ("bpy.ops.sequencer.refresh_all*", "video_editing/sequencer/navigating.html#bpy-ops-sequencer-refresh-all"), - ("bpy.ops.sequencer.select_less*", "video_editing/sequencer/selecting.html#bpy-ops-sequencer-select-less"), - ("bpy.ops.sequencer.select_more*", "video_editing/sequencer/selecting.html#bpy-ops-sequencer-select-more"), - ("bpy.ops.sequencer.select_side*", "video_editing/sequencer/selecting.html#bpy-ops-sequencer-select-side"), - ("bpy.ops.sequencer.swap_inputs*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-swap-inputs"), + ("bpy.ops.sequencer.change_path*", "video_editing/edit/montage/editing.html#bpy-ops-sequencer-change-path"), + ("bpy.ops.sequencer.refresh_all*", "editors/video_sequencer/sequencer/navigating.html#bpy-ops-sequencer-refresh-all"), + ("bpy.ops.sequencer.select_less*", "video_editing/edit/montage/selecting.html#bpy-ops-sequencer-select-less"), + ("bpy.ops.sequencer.select_more*", "video_editing/edit/montage/selecting.html#bpy-ops-sequencer-select-more"), + ("bpy.ops.sequencer.select_side*", "video_editing/edit/montage/selecting.html#bpy-ops-sequencer-select-side"), + ("bpy.ops.sequencer.swap_inputs*", "video_editing/edit/montage/editing.html#bpy-ops-sequencer-swap-inputs"), ("bpy.ops.surface.primitive*add*", "modeling/surfaces/primitives.html#bpy-ops-surface-primitive-add"), ("bpy.ops.text.resolve_conflict*", "editors/text_editor.html#bpy-ops-text-resolve-conflict"), ("bpy.ops.transform.edge_crease*", "modeling/meshes/editing/edge/edge_data.html#bpy-ops-transform-edge-crease"), @@ -1747,11 +1815,9 @@ url_manual_mapping = ( ("bpy.types.dashgpencilmodifier*", "grease_pencil/modifiers/generate/dash.html#bpy-types-dashgpencilmodifier"), ("bpy.types.fluiddomainsettings*", "physics/fluid/type/domain/index.html#bpy-types-fluiddomainsettings"), ("bpy.types.functionnodecompare*", "modeling/geometry_nodes/utilities/compare.html#bpy-types-functionnodecompare"), - ("bpy.types.geometrynodeboolean*", "modeling/geometry_nodes/input/boolean.html#bpy-types-geometrynodeboolean"), ("bpy.types.geometrynodeinputid*", "modeling/geometry_nodes/input/id.html#bpy-types-geometrynodeinputid"), - ("bpy.types.geometrynodeinteger*", "modeling/geometry_nodes/input/integer.html#bpy-types-geometrynodeinteger"), ("bpy.types.geometrynoderaycast*", "modeling/geometry_nodes/geometry/raycast.html#bpy-types-geometrynoderaycast"), - ("bpy.types.geonodeimagetexture*", "modeling/geometry_nodes/texture/image.html#bpy-types-geonodeimagetexture"), + ("bpy.types.gpencillayer.parent*", "grease_pencil/properties/layers.html#bpy-types-gpencillayer-parent"), ("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"), @@ -1773,14 +1839,20 @@ url_manual_mapping = ( ("bpy.types.shadernodelightpath*", "render/shader_nodes/input/light_path.html#bpy-types-shadernodelightpath"), ("bpy.types.shadernodemixshader*", "render/shader_nodes/shader/mix.html#bpy-types-shadernodemixshader"), ("bpy.types.shadernodenormalmap*", "render/shader_nodes/vector/normal_map.html#bpy-types-shadernodenormalmap"), + ("bpy.types.shadernodeoutputaov*", "render/shader_nodes/output/aov.html#bpy-types-shadernodeoutputaov"), ("bpy.types.shadernodepointinfo*", "render/shader_nodes/input/point_info.html#bpy-types-shadernodepointinfo"), ("bpy.types.shadernodewireframe*", "render/shader_nodes/input/wireframe.html#bpy-types-shadernodewireframe"), - ("bpy.types.spacesequenceeditor*", "video_editing/index.html#bpy-types-spacesequenceeditor"), + ("bpy.types.spacesequenceeditor*", "editors/video_sequencer/index.html#bpy-types-spacesequenceeditor"), ("bpy.types.spline.resolution_u*", "modeling/curves/properties/active_spline.html#bpy-types-spline-resolution-u"), ("bpy.types.spline.use_bezier_u*", "modeling/curves/properties/active_spline.html#bpy-types-spline-use-bezier-u"), ("bpy.types.spline.use_cyclic_u*", "modeling/curves/properties/active_spline.html#bpy-types-spline-use-cyclic-u"), ("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.texturenodedistance*", "editors/texture_node/types/converter/distance.html#bpy-types-texturenodedistance"), + ("bpy.types.texturenodetexblend*", "editors/texture_node/types/textures/blend.html#bpy-types-texturenodetexblend"), + ("bpy.types.texturenodetexmagic*", "editors/texture_node/types/textures/magic.html#bpy-types-texturenodetexmagic"), + ("bpy.types.texturenodetexnoise*", "editors/texture_node/types/textures/noise.html#bpy-types-texturenodetexnoise"), + ("bpy.types.texturenodevaltonor*", "editors/texture_node/types/converter/value_to_normal.html#bpy-types-texturenodevaltonor"), ("bpy.types.texturenodevaltorgb*", "editors/texture_node/types/converter/rgb_to_bw.html#bpy-types-texturenodevaltorgb"), ("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"), @@ -1834,24 +1906,24 @@ url_manual_mapping = ( ("bpy.ops.preferences.autoexec*", "editors/preferences/save_load.html#bpy-ops-preferences-autoexec"), ("bpy.ops.scene.view_layer_add*", "render/layers/introduction.html#bpy-ops-scene-view-layer-add"), ("bpy.ops.sculpt.face_set_edit*", "sculpt_paint/sculpting/editing/face_sets.html#bpy-ops-sculpt-face-set-edit"), - ("bpy.ops.sequencer.gap_insert*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-gap-insert"), - ("bpy.ops.sequencer.gap_remove*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-gap-remove"), - ("bpy.ops.sequencer.rendersize*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-rendersize"), - ("bpy.ops.sequencer.select_all*", "video_editing/sequencer/selecting.html#bpy-ops-sequencer-select-all"), - ("bpy.ops.sequencer.select_box*", "video_editing/sequencer/selecting.html#bpy-ops-sequencer-select-box"), + ("bpy.ops.sequencer.gap_insert*", "video_editing/edit/montage/editing.html#bpy-ops-sequencer-gap-insert"), + ("bpy.ops.sequencer.gap_remove*", "video_editing/edit/montage/editing.html#bpy-ops-sequencer-gap-remove"), + ("bpy.ops.sequencer.rendersize*", "video_editing/edit/montage/editing.html#bpy-ops-sequencer-rendersize"), + ("bpy.ops.sequencer.select_all*", "video_editing/edit/montage/selecting.html#bpy-ops-sequencer-select-all"), + ("bpy.ops.sequencer.select_box*", "video_editing/edit/montage/selecting.html#bpy-ops-sequencer-select-box"), ("bpy.ops.sound.bake_animation*", "scene_layout/scene/properties.html#bpy-ops-sound-bake-animation"), ("bpy.ops.transform.edge_slide*", "modeling/meshes/editing/edge/edge_slide.html#bpy-ops-transform-edge-slide"), ("bpy.ops.transform.vert_slide*", "modeling/meshes/editing/vertex/slide_vertices.html#bpy-ops-transform-vert-slide"), ("bpy.ops.uv.project_from_view*", "modeling/meshes/editing/uv.html#bpy-ops-uv-project-from-view"), ("bpy.ops.wm.memory_statistics*", "advanced/operators.html#bpy-ops-wm-memory-statistics"), ("bpy.ops.wm.recover_auto_save*", "files/blend/open_save.html#bpy-ops-wm-recover-auto-save"), - ("bpy.types.adjustmentsequence*", "video_editing/sequencer/strips/adjustment.html#bpy-types-adjustmentsequence"), - ("bpy.types.alphaundersequence*", "video_editing/sequencer/strips/effects/alpha_over_under_overdrop.html#bpy-types-alphaundersequence"), + ("bpy.types.adjustmentsequence*", "video_editing/edit/montage/strips/adjustment.html#bpy-types-adjustmentsequence"), + ("bpy.types.alphaundersequence*", "video_editing/edit/montage/strips/effects/alpha_over_under_overdrop.html#bpy-types-alphaundersequence"), ("bpy.types.animvizmotionpaths*", "animation/motion_paths.html#bpy-types-animvizmotionpaths"), ("bpy.types.armature.show_axes*", "animation/armatures/properties/display.html#bpy-types-armature-show-axes"), ("bpy.types.armatureconstraint*", "animation/constraints/relationship/armature.html#bpy-types-armatureconstraint"), ("bpy.types.compositornodeblur*", "compositing/types/filter/blur_node.html#bpy-types-compositornodeblur"), - ("bpy.types.compositornodecomb*", "compositing/types/converter/combine_separate.html#bpy-types-compositornodecomb"), + ("bpy.types.compositornodecomb*", "editors/texture_node/types/color/combine_separate.html#bpy-types-compositornodecomb"), ("bpy.types.compositornodecrop*", "compositing/types/distort/crop.html#bpy-types-compositornodecrop"), ("bpy.types.compositornodeflip*", "compositing/types/distort/flip.html#bpy-types-compositornodeflip"), ("bpy.types.compositornodemask*", "compositing/types/input/mask.html#bpy-types-compositornodemask"), @@ -1868,11 +1940,12 @@ url_manual_mapping = ( ("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/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.gammacrosssequence*", "video_editing/edit/montage/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"), + ("bpy.types.gpencillayer.scale*", "grease_pencil/properties/layers.html#bpy-types-gpencillayer-scale"), ("bpy.types.gpencilsculptguide*", "grease_pencil/modes/draw/guides.html#bpy-types-gpencilsculptguide"), - ("bpy.types.huecorrectmodifier*", "video_editing/sequencer/sidebar/modifiers.html#bpy-types-huecorrectmodifier"), + ("bpy.types.huecorrectmodifier*", "editors/video_sequencer/sequencer/sidebar/modifiers.html#bpy-types-huecorrectmodifier"), ("bpy.types.image.is_multiview*", "editors/image/image_settings.html#bpy-types-image-is-multiview"), ("bpy.types.imagepaint.stencil*", "sculpt_paint/texture_paint/tool_settings/mask.html#bpy-types-imagepaint-stencil"), ("bpy.types.meshdeformmodifier*", "modeling/modifiers/deform/mesh_deform.html#bpy-types-meshdeformmodifier"), @@ -1890,7 +1963,6 @@ url_manual_mapping = ( ("bpy.types.shadernodebsdfhair*", "render/shader_nodes/shader/hair.html#bpy-types-shadernodebsdfhair"), ("bpy.types.shadernodebsdftoon*", "render/shader_nodes/shader/toon.html#bpy-types-shadernodebsdftoon"), ("bpy.types.shadernodeemission*", "render/shader_nodes/shader/emission.html#bpy-types-shadernodeemission"), - ("bpy.types.shadernodegeometry*", "render/shader_nodes/input/geometry.html#bpy-types-shadernodegeometry"), ("bpy.types.shadernodehairinfo*", "render/shader_nodes/input/hair_info.html#bpy-types-shadernodehairinfo"), ("bpy.types.shadernodepointinfo*", "render/shader_nodes/input/point_info.html#bpy-types-shadernodepointinfo"), ("bpy.types.shadernodemaprange*", "render/shader_nodes/converter/map_range.html#bpy-types-shadernodemaprange"), @@ -1903,7 +1975,9 @@ url_manual_mapping = ( ("bpy.types.shadernodetexnoise*", "render/shader_nodes/textures/noise.html#bpy-types-shadernodetexnoise"), ("bpy.types.shrinkwrapmodifier*", "modeling/modifiers/deform/shrinkwrap.html#bpy-types-shrinkwrapmodifier"), ("bpy.types.splineikconstraint*", "animation/constraints/tracking/spline_ik.html#bpy-types-splineikconstraint"), + ("bpy.types.texturenodechecker*", "editors/texture_node/types/patterns/checker.html#bpy-types-texturenodechecker"), ("bpy.types.texturenodetexture*", "editors/texture_node/types/input/texture.html#bpy-types-texturenodetexture"), + ("bpy.types.texturenodetexwood*", "editors/texture_node/types/textures/wood.html#bpy-types-texturenodetexwood"), ("bpy.types.view3dshading.type*", "editors/3dview/display/shading.html#bpy-types-view3dshading-type"), ("bpy.types.volume.frame_start*", "modeling/volumes/properties.html#bpy-types-volume-frame-start"), ("bpy.types.volume.is_sequence*", "modeling/volumes/properties.html#bpy-types-volume-is-sequence"), @@ -1960,9 +2034,9 @@ url_manual_mapping = ( ("bpy.ops.poselib.pose_remove*", "animation/armatures/posing/editing/pose_library.html#bpy-ops-poselib-pose-remove"), ("bpy.ops.poselib.pose_rename*", "animation/armatures/posing/editing/pose_library.html#bpy-ops-poselib-pose-rename"), ("bpy.ops.preferences.keyitem*", "editors/preferences/keymap.html#bpy-ops-preferences-keyitem"), - ("bpy.ops.sequencer.swap_data*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-swap-data"), + ("bpy.ops.sequencer.swap_data*", "video_editing/edit/montage/editing.html#bpy-ops-sequencer-swap-data"), ("bpy.ops.transform.push_pull*", "modeling/meshes/editing/mesh/transform/push_pull.html#bpy-ops-transform-push-pull"), - ("bpy.ops.transform.seq_slide*", "video_editing/sequencer/editing.html#bpy-ops-transform-seq-slide"), + ("bpy.ops.transform.seq_slide*", "video_editing/edit/montage/editing.html#bpy-ops-transform-seq-slide"), ("bpy.ops.transform.trackball*", "scene_layout/object/editing/transform/rotate.html#bpy-ops-transform-trackball"), ("bpy.ops.transform.transform*", "scene_layout/object/editing/transform/align_transform_orientation.html#bpy-ops-transform-transform"), ("bpy.ops.transform.translate*", "scene_layout/object/editing/transform/move.html#bpy-ops-transform-translate"), @@ -1971,7 +2045,7 @@ url_manual_mapping = ( ("bpy.ops.uv.minimize_stretch*", "modeling/meshes/uv/editing.html#bpy-ops-uv-minimize-stretch"), ("bpy.ops.uv.select_edge_ring*", "editors/uv/selecting.html#bpy-ops-uv-select-edge-ring"), ("bpy.ops.wm.save_as_mainfile*", "files/blend/open_save.html#bpy-ops-wm-save-as-mainfile"), - ("bpy.types.alphaoversequence*", "video_editing/sequencer/strips/effects/alpha_over_under_overdrop.html#bpy-types-alphaoversequence"), + ("bpy.types.alphaoversequence*", "video_editing/edit/montage/strips/effects/alpha_over_under_overdrop.html#bpy-types-alphaoversequence"), ("bpy.types.armatureeditbones*", "animation/armatures/bones/editing/index.html#bpy-types-armatureeditbones"), ("bpy.types.brush.pose_offset*", "sculpt_paint/sculpting/tools/pose.html#bpy-types-brush-pose-offset"), ("bpy.types.brush.rake_factor*", "sculpt_paint/sculpting/tools/snake_hook.html#bpy-types-brush-rake-factor"), @@ -1982,7 +2056,7 @@ url_manual_mapping = ( ("bpy.types.collisionmodifier*", "physics/collision.html#bpy-types-collisionmodifier"), ("bpy.types.collisionsettings*", "physics/collision.html#bpy-types-collisionsettings"), ("bpy.types.compositornodergb*", "compositing/types/input/rgb.html#bpy-types-compositornodergb"), - ("bpy.types.compositornodesep*", "compositing/types/converter/combine_separate.html#bpy-types-compositornodesep"), + ("bpy.types.compositornodesep*", "editors/texture_node/types/color/combine_separate.html#bpy-types-compositornodesep"), ("bpy.types.curve.bevel_depth*", "modeling/curves/properties/geometry.html#bpy-types-curve-bevel-depth"), ("bpy.types.curve.use_stretch*", "modeling/curves/properties/shape.html#bpy-types-curve-use-stretch"), ("bpy.types.edgesplitmodifier*", "modeling/modifiers/generate/edge_split.html#bpy-types-edgesplitmodifier"), @@ -2001,7 +2075,7 @@ url_manual_mapping = ( ("bpy.types.mesh.use_mirror_y*", "modeling/meshes/tools/tool_settings.html#bpy-types-mesh-use-mirror-y"), ("bpy.types.mesh.use_mirror_z*", "modeling/meshes/tools/tool_settings.html#bpy-types-mesh-use-mirror-z"), ("bpy.types.meshcachemodifier*", "modeling/modifiers/modify/mesh_cache.html#bpy-types-meshcachemodifier"), - ("bpy.types.movieclipsequence*", "video_editing/sequencer/strips/clip.html#bpy-types-movieclipsequence"), + ("bpy.types.movieclipsequence*", "video_editing/edit/montage/strips/clip.html#bpy-types-movieclipsequence"), ("bpy.types.object.dimensions*", "scene_layout/object/properties/transforms.html#bpy-types-object-dimensions"), ("bpy.types.object.is_holdout*", "scene_layout/object/properties/visibility.html#bpy-types-object-is-holdout"), ("bpy.types.object.pass_index*", "scene_layout/object/properties/relations.html#bpy-types-object-pass-index"), @@ -2012,7 +2086,7 @@ url_manual_mapping = ( ("bpy.types.scene.frame_start*", "render/output/properties/frame_range.html#bpy-types-scene-frame-start"), ("bpy.types.sceneeevee.shadow*", "render/eevee/render_settings/shadows.html#bpy-types-sceneeevee-shadow"), ("bpy.types.screen.use_follow*", "editors/timeline.html#bpy-types-screen-use-follow"), - ("bpy.types.sequencetransform*", "video_editing/sequencer/sidebar/strip.html#bpy-types-sequencetransform"), + ("bpy.types.sequencetransform*", "editors/video_sequencer/sequencer/sidebar/strip.html#bpy-types-sequencetransform"), ("bpy.types.shadernodecombine*", "render/shader_nodes/converter/combine_separate.html#bpy-types-shadernodecombine"), ("bpy.types.shadernodefresnel*", "render/shader_nodes/input/fresnel.html#bpy-types-shadernodefresnel"), ("bpy.types.shadernodeholdout*", "render/shader_nodes/shader/holdout.html#bpy-types-shadernodeholdout"), @@ -2024,8 +2098,10 @@ url_manual_mapping = ( ("bpy.types.texturenodebricks*", "editors/texture_node/types/patterns/bricks.html#bpy-types-texturenodebricks"), ("bpy.types.texturenodemixrgb*", "editors/texture_node/types/color/mix_rgb.html#bpy-types-texturenodemixrgb"), ("bpy.types.texturenodeoutput*", "editors/texture_node/types/output/output.html#bpy-types-texturenodeoutput"), + ("bpy.types.texturenoderotate*", "editors/texture_node/types/distort/rotate.html#bpy-types-texturenoderotate"), + ("bpy.types.texturenodeviewer*", "editors/texture_node/types/output/viewer.html#bpy-types-texturenodeviewer"), ("bpy.types.tracktoconstraint*", "animation/constraints/tracking/track_to.html#bpy-types-tracktoconstraint"), - ("bpy.types.transformsequence*", "video_editing/sequencer/strips/effects/transform.html#bpy-types-transformsequence"), + ("bpy.types.transformsequence*", "video_editing/edit/montage/strips/effects/transform.html#bpy-types-transformsequence"), ("bpy.types.uvprojectmodifier*", "modeling/modifiers/modify/uv_project.html#bpy-types-uvprojectmodifier"), ("bpy.types.viewlayer.samples*", "render/layers/introduction.html#bpy-types-viewlayer-samples"), ("bpy.types.viewlayer.use_sky*", "render/layers/introduction.html#bpy-types-viewlayer-use-sky"), @@ -2069,6 +2145,7 @@ url_manual_mapping = ( ("bpy.ops.pose.select_linked*", "animation/armatures/posing/selecting.html#bpy-ops-pose-select-linked"), ("bpy.ops.pose.select_mirror*", "animation/armatures/posing/selecting.html#bpy-ops-pose-select-mirror"), ("bpy.ops.poselib.apply_pose*", "animation/armatures/properties/pose_library.html#bpy-ops-poselib-apply-pose"), + ("bpy.ops.screen.marker_jump*", "animation/markers.html#bpy-ops-screen-marker-jump"), ("bpy.ops.screen.repeat_last*", "interface/undo_redo.html#bpy-ops-screen-repeat-last"), ("bpy.ops.sculpt.mask_expand*", "sculpt_paint/sculpting/editing/mask.html#bpy-ops-sculpt-mask-expand"), ("bpy.ops.sculpt.mask_filter*", "sculpt_paint/sculpting/editing/mask.html#bpy-ops-sculpt-mask-filter"), @@ -2085,7 +2162,7 @@ url_manual_mapping = ( ("bpy.types.bone.tail_radius*", "animation/armatures/bones/properties/deform.html#bpy-types-bone-tail-radius"), ("bpy.types.brush.cloth_mass*", "sculpt_paint/sculpting/tools/cloth.html#bpy-types-brush-cloth-mass"), ("bpy.types.brushtextureslot*", "sculpt_paint/brush/texture.html#bpy-types-brushtextureslot"), - ("bpy.types.colormixsequence*", "video_editing/sequencer/strips/effects/color_mix.html#bpy-types-colormixsequence"), + ("bpy.types.colormixsequence*", "video_editing/edit/montage/strips/effects/color_mix.html#bpy-types-colormixsequence"), ("bpy.types.curve.dimensions*", "modeling/curves/properties/shape.html#bpy-types-curve-dimensions"), ("bpy.types.curve.taper_mode*", "modeling/curves/properties/geometry.html#bpy-types-curve-taper-mode"), ("bpy.types.curve.twist_mode*", "modeling/curves/properties/shape.html#bpy-types-curve-twist-mode"), @@ -2099,14 +2176,15 @@ url_manual_mapping = ( ("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/view_layer/line_set.html#bpy-types-freestylelineset"), + ("bpy.types.greasepencilgrid*", "grease_pencil/properties/display.html#bpy-types-greasepencilgrid"), ("bpy.types.image.alpha_mode*", "editors/image/image_settings.html#bpy-types-image-alpha-mode"), ("bpy.types.mask.frame_start*", "movie_clip/masking/sidebar.html#bpy-types-mask-frame-start"), ("bpy.types.motionpath.color*", "animation/motion_paths.html#bpy-types-motionpath-color"), ("bpy.types.motionpath.lines*", "animation/motion_paths.html#bpy-types-motionpath-lines"), - ("bpy.types.multicamsequence*", "video_editing/sequencer/strips/effects/multicam.html#bpy-types-multicamsequence"), - ("bpy.types.multiplysequence*", "video_editing/sequencer/strips/effects/multiply.html#bpy-types-multiplysequence"), + ("bpy.types.multicamsequence*", "video_editing/edit/montage/strips/effects/multicam.html#bpy-types-multicamsequence"), + ("bpy.types.multiplysequence*", "video_editing/edit/montage/strips/effects/multiply.html#bpy-types-multiplysequence"), ("bpy.types.multiresmodifier*", "modeling/modifiers/generate/multiresolution.html#bpy-types-multiresmodifier"), - ("bpy.types.overdropsequence*", "video_editing/sequencer/strips/effects/alpha_over_under_overdrop.html#bpy-types-overdropsequence"), + ("bpy.types.overdropsequence*", "video_editing/edit/montage/strips/effects/alpha_over_under_overdrop.html#bpy-types-overdropsequence"), ("bpy.types.paint.show_brush*", "sculpt_paint/brush/cursor.html#bpy-types-paint-show-brush"), ("bpy.types.paint.use_cavity*", "sculpt_paint/texture_paint/tool_settings/mask.html#bpy-types-paint-use-cavity"), ("bpy.types.particlesettings*", "physics/particles/index.html#bpy-types-particlesettings"), @@ -2116,8 +2194,8 @@ url_manual_mapping = ( ("bpy.types.scene.frame_step*", "render/output/properties/frame_range.html#bpy-types-scene-frame-step"), ("bpy.types.sceneeevee.bloom*", "render/eevee/render_settings/bloom.html#bpy-types-sceneeevee-bloom"), ("bpy.types.sculpt.show_mask*", "sculpt_paint/sculpting/editing/mask.html#bpy-types-sculpt-show-mask"), - ("bpy.types.sequence.channel*", "video_editing/sequencer/sidebar/strip.html#bpy-types-sequence-channel"), - ("bpy.types.sequencemodifier*", "video_editing/sequencer/sidebar/modifiers.html#bpy-types-sequencemodifier"), + ("bpy.types.sequence.channel*", "editors/video_sequencer/sequencer/sidebar/strip.html#bpy-types-sequence-channel"), + ("bpy.types.sequencemodifier*", "editors/video_sequencer/sequencer/sidebar/modifiers.html#bpy-types-sequencemodifier"), ("bpy.types.shaderfxcolorize*", "grease_pencil/visual_effects/colorize.html#bpy-types-shaderfxcolorize"), ("bpy.types.shaderfxpixelate*", "grease_pencil/visual_effects/pixelate.html#bpy-types-shaderfxpixelate"), ("bpy.types.shadernodeinvert*", "render/shader_nodes/color/invert.html#bpy-types-shadernodeinvert"), @@ -2136,11 +2214,12 @@ url_manual_mapping = ( ("bpy.types.spaceview3d.lock*", "editors/3dview/sidebar.html#bpy-types-spaceview3d-lock"), ("bpy.types.spaceview3d.show*", "editors/3dview/display/index.html#bpy-types-spaceview3d-show"), ("bpy.types.sphfluidsettings*", "physics/particles/emitter/physics/fluid.html#bpy-types-sphfluidsettings"), - ("bpy.types.subtractsequence*", "video_editing/sequencer/strips/effects/subtract.html#bpy-types-subtractsequence"), + ("bpy.types.subtractsequence*", "video_editing/edit/montage/strips/effects/subtract.html#bpy-types-subtractsequence"), ("bpy.types.text.indentation*", "editors/text_editor.html#bpy-types-text-indentation"), ("bpy.types.texture.contrast*", "render/materials/legacy_textures/colors.html#bpy-types-texture-contrast"), ("bpy.types.texturenodegroup*", "editors/texture_node/types/groups.html#bpy-types-texturenodegroup"), ("bpy.types.texturenodeimage*", "editors/texture_node/types/input/image.html#bpy-types-texturenodeimage"), + ("bpy.types.texturenodescale*", "editors/texture_node/types/distort/scale.html#bpy-types-texturenodescale"), ("bpy.types.viewlayer.use_ao*", "render/layers/introduction.html#bpy-types-viewlayer-use-ao"), ("bpy.ops.armature.dissolve*", "animation/armatures/bones/editing/delete.html#bpy-ops-armature-dissolve"), ("bpy.ops.armature.separate*", "animation/armatures/bones/editing/separate_bones.html#bpy-ops-armature-separate"), @@ -2205,7 +2284,6 @@ url_manual_mapping = ( ("bpy.types.floorconstraint*", "animation/constraints/relationship/floor.html#bpy-types-floorconstraint"), ("bpy.types.fmodifiercycles*", "editors/graph_editor/fcurves/modifiers.html#bpy-types-fmodifiercycles"), ("bpy.types.fmodifierlimits*", "editors/graph_editor/fcurves/modifiers.html#bpy-types-fmodifierlimits"), - ("bpy.types.geometrynodearc*", "modeling/geometry_nodes/curve_primitives/arc.html#bpy-types-geometrynodearc"), ("bpy.types.imagepaint.mode*", "sculpt_paint/texture_paint/tool_settings/texture_slots.html#bpy-types-imagepaint-mode"), ("bpy.types.latticemodifier*", "modeling/modifiers/deform/lattice.html#bpy-types-latticemodifier"), ("bpy.types.materiallineart*", "render/materials/line_art.html#bpy-types-materiallineart"), @@ -2244,6 +2322,7 @@ url_manual_mapping = ( ("bpy.ops.gpencil.dissolve*", "grease_pencil/modes/edit/grease_pencil_menu.html#bpy-ops-gpencil-dissolve"), ("bpy.ops.graph.frame_jump*", "editors/graph_editor/fcurves/editing.html#bpy-ops-graph-frame-jump"), ("bpy.ops.graph.sound_bake*", "editors/graph_editor/fcurves/editing.html#bpy-ops-graph-sound-bake"), + ("bpy.ops.marker.duplicate*", "animation/markers.html#bpy-ops-marker-duplicate"), ("bpy.ops.mask.select_less*", "movie_clip/masking/selecting.html#bpy-ops-mask-select-less"), ("bpy.ops.mask.select_more*", "movie_clip/masking/selecting.html#bpy-ops-mask-select-more"), ("bpy.ops.mesh.convex_hull*", "modeling/meshes/editing/mesh/convex_hull.html#bpy-ops-mesh-convex-hull"), @@ -2260,10 +2339,10 @@ url_manual_mapping = ( ("bpy.ops.scene.view_layer*", "render/layers/introduction.html#bpy-ops-scene-view-layer"), ("bpy.ops.screen.redo_last*", "interface/undo_redo.html#bpy-ops-screen-redo-last"), ("bpy.ops.sculpt.mask_init*", "sculpt_paint/sculpting/editing/mask.html#bpy-ops-sculpt-mask-init"), - ("bpy.ops.sequencer.delete*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-delete"), - ("bpy.ops.sequencer.reload*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-reload"), - ("bpy.ops.sequencer.unlock*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-unlock"), - ("bpy.ops.sequencer.unmute*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-unmute"), + ("bpy.ops.sequencer.delete*", "video_editing/edit/montage/editing.html#bpy-ops-sequencer-delete"), + ("bpy.ops.sequencer.reload*", "video_editing/edit/montage/editing.html#bpy-ops-sequencer-reload"), + ("bpy.ops.sequencer.unlock*", "video_editing/edit/montage/editing.html#bpy-ops-sequencer-unlock"), + ("bpy.ops.sequencer.unmute*", "video_editing/edit/montage/editing.html#bpy-ops-sequencer-unmute"), ("bpy.ops.transform.mirror*", "scene_layout/object/editing/mirror.html#bpy-ops-transform-mirror"), ("bpy.ops.transform.resize*", "scene_layout/object/editing/transform/scale.html#bpy-ops-transform-resize"), ("bpy.ops.transform.rotate*", "scene_layout/object/editing/transform/rotate.html#bpy-ops-transform-rotate"), @@ -2277,7 +2356,7 @@ url_manual_mapping = ( ("bpy.ops.wm.save_mainfile*", "files/blend/open_save.html#bpy-ops-wm-save-mainfile"), ("bpy.types.bone.show_wire*", "animation/armatures/bones/properties/display.html#bpy-types-bone-show-wire"), ("bpy.types.brush.hardness*", "sculpt_paint/sculpting/tool_settings/brush_settings.html#bpy-types-brush-hardness"), - ("bpy.types.curvesmodifier*", "video_editing/sequencer/sidebar/modifiers.html#bpy-types-curvesmodifier"), + ("bpy.types.curvesmodifier*", "editors/video_sequencer/sequencer/sidebar/modifiers.html#bpy-types-curvesmodifier"), ("bpy.types.ffmpegsettings*", "render/output/properties/output.html#bpy-types-ffmpegsettings"), ("bpy.types.fmodifiernoise*", "editors/graph_editor/fcurves/modifiers.html#bpy-types-fmodifiernoise"), ("bpy.types.image.filepath*", "editors/image/image_settings.html#bpy-types-image-filepath"), @@ -2299,7 +2378,7 @@ url_manual_mapping = ( ("bpy.types.shadernodebump*", "render/shader_nodes/vector/bump.html#bpy-types-shadernodebump"), ("bpy.types.shadernodemath*", "render/shader_nodes/converter/math.html#bpy-types-shadernodemath"), ("bpy.types.smoothmodifier*", "modeling/modifiers/deform/smooth.html#bpy-types-smoothmodifier"), - ("bpy.types.sound.use_mono*", "video_editing/sequencer/sidebar/strip.html#bpy-types-sound-use-mono"), + ("bpy.types.sound.use_mono*", "editors/video_sequencer/sequencer/sidebar/strip.html#bpy-types-sound-use-mono"), ("bpy.types.spline.order_u*", "modeling/curves/properties/active_spline.html#bpy-types-spline-order-u"), ("bpy.types.timelinemarker*", "animation/markers.html#bpy-types-timelinemarker"), ("bpy.types.usersolidlight*", "editors/preferences/lights.html#bpy-types-usersolidlight"), @@ -2346,7 +2425,7 @@ url_manual_mapping = ( ("bpy.ops.pose.select_all*", "animation/armatures/posing/selecting.html#bpy-ops-pose-select-all"), ("bpy.ops.rigidbody.world*", "physics/rigid_body/world.html#bpy-ops-rigidbody-world"), ("bpy.ops.sculpt.optimize*", "sculpt_paint/sculpting/editing/sculpt.html#bpy-ops-sculpt-optimize"), - ("bpy.ops.sequencer.split*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-split"), + ("bpy.ops.sequencer.split*", "video_editing/edit/montage/editing.html#bpy-ops-sequencer-split"), ("bpy.ops.transform.shear*", "modeling/meshes/editing/mesh/transform/shear.html#bpy-ops-transform-shear"), ("bpy.ops.uv.cube_project*", "modeling/meshes/editing/uv.html#bpy-ops-uv-cube-project"), ("bpy.ops.uv.pack_islands*", "modeling/meshes/uv/editing.html#bpy-ops-uv-pack-islands"), @@ -2366,8 +2445,8 @@ url_manual_mapping = ( ("bpy.types.clothmodifier*", "physics/cloth/index.html#bpy-types-clothmodifier"), ("bpy.types.clothsettings*", "physics/cloth/settings/index.html#bpy-types-clothsettings"), ("bpy.types.cloudstexture*", "render/materials/legacy_textures/types/clouds.html#bpy-types-cloudstexture"), - ("bpy.types.colorsequence*", "video_editing/sequencer/strips/color.html#bpy-types-colorsequence"), - ("bpy.types.crosssequence*", "video_editing/sequencer/strips/transitions/cross.html#bpy-types-crosssequence"), + ("bpy.types.colorsequence*", "video_editing/edit/montage/strips/color.html#bpy-types-colorsequence"), + ("bpy.types.crosssequence*", "video_editing/edit/montage/strips/transitions/cross.html#bpy-types-crosssequence"), ("bpy.types.curve.extrude*", "modeling/curves/properties/geometry.html#bpy-types-curve-extrude"), ("bpy.types.curvemodifier*", "modeling/modifiers/deform/curve.html#bpy-types-curvemodifier"), ("bpy.types.editbone.head*", "animation/armatures/bones/properties/transform.html#bpy-types-editbone-head"), @@ -2375,11 +2454,11 @@ url_manual_mapping = ( ("bpy.types.editbone.roll*", "animation/armatures/bones/properties/transform.html#bpy-types-editbone-roll"), ("bpy.types.editbone.tail*", "animation/armatures/bones/properties/transform.html#bpy-types-editbone-tail"), ("bpy.types.fieldsettings*", "physics/forces/force_fields/index.html#bpy-types-fieldsettings"), - ("bpy.types.imagesequence*", "video_editing/sequencer/strips/image.html#bpy-types-imagesequence"), + ("bpy.types.imagesequence*", "video_editing/edit/montage/strips/image.html#bpy-types-imagesequence"), ("bpy.types.marbletexture*", "render/materials/legacy_textures/types/marble.html#bpy-types-marbletexture"), ("bpy.types.modifier.name*", "modeling/modifiers/introduction.html#bpy-types-modifier-name"), ("bpy.types.modifier.show*", "modeling/modifiers/introduction.html#bpy-types-modifier-show"), - ("bpy.types.moviesequence*", "video_editing/sequencer/strips/movie.html#bpy-types-moviesequence"), + ("bpy.types.moviesequence*", "video_editing/edit/montage/strips/movie.html#bpy-types-moviesequence"), ("bpy.types.movietracking*", "movie_clip/tracking/index.html#bpy-types-movietracking"), ("bpy.types.nlastrip.mute*", "editors/nla/sidebar.html#bpy-types-nlastrip-mute"), ("bpy.types.nlastrip.name*", "editors/nla/sidebar.html#bpy-types-nlastrip-name"), @@ -2390,17 +2469,18 @@ url_manual_mapping = ( ("bpy.types.particlebrush*", "physics/particles/mode.html#bpy-types-particlebrush"), ("bpy.types.scene.gravity*", "physics/forces/gravity.html#bpy-types-scene-gravity"), ("bpy.types.sceneeevee.gi*", "render/eevee/render_settings/indirect_lighting.html#bpy-types-sceneeevee-gi"), - ("bpy.types.scenesequence*", "video_editing/sequencer/strips/scene.html#bpy-types-scenesequence"), + ("bpy.types.scenesequence*", "video_editing/edit/montage/strips/scene.html#bpy-types-scenesequence"), ("bpy.types.screwmodifier*", "modeling/modifiers/generate/screw.html#bpy-types-screwmodifier"), - ("bpy.types.sequence.name*", "video_editing/sequencer/sidebar/strip.html#bpy-types-sequence-name"), - ("bpy.types.sequenceproxy*", "video_editing/sequencer/sidebar/proxy.html#bpy-types-sequenceproxy"), + ("bpy.types.sequence.name*", "editors/video_sequencer/sequencer/sidebar/strip.html#bpy-types-sequence-name"), + ("bpy.types.sequenceproxy*", "editors/video_sequencer/sequencer/sidebar/proxy.html#bpy-types-sequenceproxy"), ("bpy.types.shaderfxswirl*", "grease_pencil/visual_effects/swirl.html#bpy-types-shaderfxswirl"), ("bpy.types.shadernodergb*", "render/shader_nodes/input/rgb.html#bpy-types-shadernodergb"), - ("bpy.types.soundsequence*", "video_editing/sequencer/strips/sound.html#bpy-types-soundsequence"), + ("bpy.types.soundsequence*", "video_editing/edit/montage/strips/sound.html#bpy-types-soundsequence"), ("bpy.types.spaceoutliner*", "editors/outliner/index.html#bpy-types-spaceoutliner"), ("bpy.types.spacetimeline*", "editors/timeline.html#bpy-types-spacetimeline"), ("bpy.types.spaceuveditor*", "editors/uv/index.html#bpy-types-spaceuveditor"), ("bpy.types.stuccitexture*", "render/materials/legacy_textures/types/stucci.html#bpy-types-stuccitexture"), + ("bpy.types.texturenodeat*", "editors/texture_node/types/distort/at.html#bpy-types-texturenodeat"), ("bpy.types.view3doverlay*", "editors/3dview/display/overlays.html#bpy-types-view3doverlay"), ("bpy.types.viewlayer.use*", "render/layers/view_layer.html#bpy-types-viewlayer-use"), ("bpy.types.volumedisplay*", "modeling/volumes/properties.html#bpy-types-volumedisplay"), @@ -2436,11 +2516,11 @@ url_manual_mapping = ( ("bpy.ops.pose.propagate*", "animation/armatures/posing/editing/propagate.html#bpy-ops-pose-propagate"), ("bpy.ops.pose.push_rest*", "animation/armatures/posing/editing/in_betweens.html#bpy-ops-pose-push-rest"), ("bpy.ops.pose.rot_clear*", "animation/armatures/posing/editing/clear.html#bpy-ops-pose-rot-clear"), - ("bpy.ops.sequencer.lock*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-lock"), - ("bpy.ops.sequencer.mute*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-mute"), - ("bpy.ops.sequencer.slip*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-slip"), - ("bpy.ops.sequencer.snap*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-snap"), - ("bpy.ops.sequencer.swap*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-swap"), + ("bpy.ops.sequencer.lock*", "video_editing/edit/montage/editing.html#bpy-ops-sequencer-lock"), + ("bpy.ops.sequencer.mute*", "video_editing/edit/montage/editing.html#bpy-ops-sequencer-mute"), + ("bpy.ops.sequencer.slip*", "video_editing/edit/montage/editing.html#bpy-ops-sequencer-slip"), + ("bpy.ops.sequencer.snap*", "video_editing/edit/montage/editing.html#bpy-ops-sequencer-snap"), + ("bpy.ops.sequencer.swap*", "video_editing/edit/montage/editing.html#bpy-ops-sequencer-swap"), ("bpy.ops.transform.bend*", "modeling/meshes/editing/mesh/transform/bend.html#bpy-ops-transform-bend"), ("bpy.ops.transform.tilt*", "modeling/curves/editing/control_points.html#bpy-ops-transform-tilt"), ("bpy.ops.uv.snap_cursor*", "modeling/meshes/uv/editing.html#bpy-ops-uv-snap-cursor"), @@ -2451,7 +2531,7 @@ url_manual_mapping = ( ("bpy.types.castmodifier*", "modeling/modifiers/deform/cast.html#bpy-types-castmodifier"), ("bpy.types.curve.offset*", "modeling/curves/properties/geometry.html#bpy-types-curve-offset"), ("bpy.types.geometrynode*", "modeling/geometry_nodes/index.html#bpy-types-geometrynode"), - ("bpy.types.glowsequence*", "video_editing/sequencer/strips/effects/glow.html#bpy-types-glowsequence"), + ("bpy.types.glowsequence*", "video_editing/edit/montage/strips/effects/glow.html#bpy-types-glowsequence"), ("bpy.types.gpencillayer*", "grease_pencil/properties/layers.html#bpy-types-gpencillayer"), ("bpy.types.hookmodifier*", "modeling/modifiers/deform/hooks.html#bpy-types-hookmodifier"), ("bpy.types.image.source*", "editors/image/image_settings.html#bpy-types-image-source"), @@ -2459,23 +2539,23 @@ url_manual_mapping = ( ("bpy.types.latticepoint*", "animation/lattice.html#bpy-types-latticepoint"), ("bpy.types.magictexture*", "render/materials/legacy_textures/types/magic.html#bpy-types-magictexture"), ("bpy.types.maskmodifier*", "modeling/modifiers/generate/mask.html#bpy-types-maskmodifier"), - ("bpy.types.masksequence*", "video_editing/sequencer/strips/mask.html#bpy-types-masksequence"), + ("bpy.types.masksequence*", "video_editing/edit/montage/strips/mask.html#bpy-types-masksequence"), ("bpy.types.materialslot*", "render/materials/assignment.html#bpy-types-materialslot"), - ("bpy.types.metasequence*", "video_editing/sequencer/meta.html#bpy-types-metasequence"), + ("bpy.types.metasequence*", "video_editing/edit/montage/meta.html#bpy-types-metasequence"), ("bpy.types.object.color*", "scene_layout/object/properties/display.html#bpy-types-object-color"), ("bpy.types.object.delta*", "scene_layout/object/properties/transforms.html#bpy-types-object-delta"), ("bpy.types.object.empty*", "modeling/empties.html#bpy-types-object-empty"), ("bpy.types.object.scale*", "scene_layout/object/properties/transforms.html#bpy-types-object-scale"), ("bpy.types.particleedit*", "physics/particles/mode.html#bpy-types-particleedit"), ("bpy.types.scene.camera*", "scene_layout/scene/properties.html#bpy-types-scene-camera"), - ("bpy.types.sequencecrop*", "video_editing/sequencer/sidebar/strip.html#bpy-types-sequencecrop"), + ("bpy.types.sequencecrop*", "editors/video_sequencer/sequencer/sidebar/strip.html#bpy-types-sequencecrop"), ("bpy.types.shaderfxblur*", "grease_pencil/visual_effects/blur.html#bpy-types-shaderfxblur"), ("bpy.types.shaderfxflip*", "grease_pencil/visual_effects/flip.html#bpy-types-shaderfxflip"), ("bpy.types.shaderfxglow*", "grease_pencil/visual_effects/glow.html#bpy-types-shaderfxglow"), ("bpy.types.shaderfxwave*", "grease_pencil/visual_effects/wave_distortion.html#bpy-types-shaderfxwave"), ("bpy.types.skinmodifier*", "modeling/modifiers/generate/skin.html#bpy-types-skinmodifier"), ("bpy.types.spaceconsole*", "editors/python_console.html#bpy-types-spaceconsole"), - ("bpy.types.textsequence*", "video_editing/sequencer/strips/text.html#bpy-types-textsequence"), + ("bpy.types.textsequence*", "video_editing/edit/montage/strips/text.html#bpy-types-textsequence"), ("bpy.types.unitsettings*", "scene_layout/scene/properties.html#bpy-types-unitsettings"), ("bpy.types.vertexcolors*", "sculpt_paint/vertex_paint/index.html#bpy-types-vertexcolors"), ("bpy.types.view3dcursor*", "editors/3dview/3d_cursor.html#bpy-types-view3dcursor"), @@ -2483,7 +2563,7 @@ url_manual_mapping = ( ("bpy.types.warpmodifier*", "modeling/modifiers/deform/warp.html#bpy-types-warpmodifier"), ("bpy.types.wavemodifier*", "modeling/modifiers/deform/wave.html#bpy-types-wavemodifier"), ("bpy.types.weldmodifier*", "modeling/modifiers/generate/weld.html#bpy-types-weldmodifier"), - ("bpy.types.wipesequence*", "video_editing/sequencer/strips/transitions/wipe.html#bpy-types-wipesequence"), + ("bpy.types.wipesequence*", "video_editing/edit/montage/strips/transitions/wipe.html#bpy-types-wipesequence"), ("bpy.ops.armature.fill*", "animation/armatures/bones/editing/fill_between_joints.html#bpy-ops-armature-fill"), ("bpy.ops.asset.tag_add*", "editors/asset_browser.html#bpy-ops-asset-tag-add"), ("bpy.ops.clip.prefetch*", "movie_clip/tracking/clip/editing/clip.html#bpy-ops-clip-prefetch"), @@ -2494,6 +2574,9 @@ url_manual_mapping = ( ("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"), ("bpy.ops.image.save_as*", "editors/image/editing.html#bpy-ops-image-save-as"), + ("bpy.ops.marker.delete*", "animation/markers.html#bpy-ops-marker-delete"), + ("bpy.ops.marker.rename*", "animation/markers.html#bpy-ops-marker-rename"), + ("bpy.ops.marker.select*", "animation/markers.html#bpy-ops-marker-select"), ("bpy.ops.material.copy*", "render/materials/assignment.html#bpy-ops-material-copy"), ("bpy.ops.mesh.decimate*", "modeling/meshes/editing/mesh/cleanup.html#bpy-ops-mesh-decimate"), ("bpy.ops.mesh.dissolve*", "modeling/meshes/editing/mesh/delete.html#bpy-ops-mesh-dissolve"), @@ -2513,7 +2596,7 @@ url_manual_mapping = ( ("bpy.ops.wm.obj_export*", "files/import_export/obj.html#bpy-ops-wm-obj-export"), ("bpy.ops.wm.properties*", "files/data_blocks.html#bpy-ops-wm-properties"), ("bpy.ops.wm.usd_export*", "files/import_export/usd.html#bpy-ops-wm-usd-export"), - ("bpy.types.addsequence*", "video_editing/sequencer/strips/effects/add.html#bpy-types-addsequence"), + ("bpy.types.addsequence*", "video_editing/edit/montage/strips/effects/add.html#bpy-types-addsequence"), ("bpy.types.brush.cloth*", "sculpt_paint/sculpting/tools/cloth.html#bpy-types-brush-cloth"), ("bpy.types.camera.show*", "render/cameras.html#bpy-types-camera-show"), ("bpy.types.consoleline*", "editors/python_console.html#bpy-types-consoleline"), @@ -2588,6 +2671,7 @@ url_manual_mapping = ( ("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.marker.move*", "animation/markers.html#bpy-ops-marker-move"), ("bpy.ops.mesh.bisect*", "modeling/meshes/editing/mesh/bisect.html#bpy-ops-mesh-bisect"), ("bpy.ops.mesh.delete*", "modeling/meshes/editing/mesh/delete.html#bpy-ops-mesh-delete"), ("bpy.ops.nla.move_up*", "editors/nla/editing.html#bpy-ops-nla-move-up"), @@ -2636,6 +2720,7 @@ url_manual_mapping = ( ("bpy.ops.image.pack*", "editors/image/editing.html#bpy-ops-image-pack"), ("bpy.ops.image.save*", "editors/image/editing.html#bpy-ops-image-save"), ("bpy.ops.image.tile*", "modeling/meshes/uv/workflows/udims.html#bpy-ops-image-tile"), + ("bpy.ops.marker.add*", "animation/markers.html#bpy-ops-marker-add"), ("bpy.ops.mesh.bevel*", "modeling/meshes/editing/edge/bevel.html#bpy-ops-mesh-bevel"), ("bpy.ops.mesh.inset*", "modeling/meshes/editing/face/inset_faces.html#bpy-ops-mesh-inset"), ("bpy.ops.mesh.knife*", "modeling/meshes/tools/knife.html#bpy-ops-mesh-knife"), @@ -2689,6 +2774,7 @@ url_manual_mapping = ( ("bpy.types.textbox*", "modeling/texts/properties.html#bpy-types-textbox"), ("bpy.types.texture*", "render/materials/legacy_textures/index.html#bpy-types-texture"), ("bpy.ops.armature*", "animation/armatures/index.html#bpy-ops-armature"), + ("bpy.ops.geometry*", "modeling/index.html#bpy-ops-geometry"), ("bpy.ops.material*", "render/materials/index.html#bpy-ops-material"), ("bpy.ops.nla.bake*", "animation/actions.html#bpy-ops-nla-bake"), ("bpy.ops.nla.snap*", "editors/nla/editing.html#bpy-ops-nla-snap"), diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index a1913945364..6408873f13e 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -1026,7 +1026,7 @@ def km_markers(params): *_template_items_select_actions(params, "marker.select_all"), ("marker.delete", {"type": 'X', "value": 'PRESS'}, None), ("marker.delete", {"type": 'DEL', "value": 'PRESS'}, None), - ("marker.rename", {"type": 'M', "value": 'PRESS', "ctrl": True}, None), + op_panel("TOPBAR_PT_name_marker", {"type": 'F2', "value": 'PRESS'}, [("keep_open", False)]), ("marker.move", {"type": 'G', "value": 'PRESS'}, None), ("marker.camera_bind", {"type": 'B', "value": 'PRESS', "ctrl": True}, None), ]) @@ -1387,7 +1387,8 @@ def km_view3d(params): ("view3d.localview", {"type": 'NUMPAD_SLASH', "value": 'PRESS'}, None), ("view3d.localview", {"type": 'SLASH', "value": 'PRESS'}, None), ("view3d.localview", {"type": 'MOUSESMARTZOOM', "value": 'ANY'}, None), - ("view3d.localview_remove_from", {"type": 'M', "value": 'PRESS'}, None), + ("view3d.localview_remove_from", {"type": 'NUMPAD_SLASH', "value": 'PRESS', "alt": True}, None), + ("view3d.localview_remove_from", {"type": 'SLASH', "value": 'PRESS', "alt": True}, None), # Navigation. ("view3d.rotate", {"type": 'MOUSEROTATE', "value": 'ANY'}, None), *(( @@ -1832,7 +1833,6 @@ def km_graph_editor(params): *_template_items_proportional_editing( params, connected=False, toggle_data_path='tool_settings.use_proportional_fcurve'), ("marker.add", {"type": 'M', "value": 'PRESS'}, None), - ("marker.rename", {"type": 'M', "value": 'PRESS', "ctrl": True}, None), *_template_items_context_menu("GRAPH_MT_context_menu", params.context_menu_event), ]) @@ -2486,7 +2486,6 @@ def km_dopesheet(params): *_template_items_proportional_editing( params, connected=False, toggle_data_path='tool_settings.use_proportional_action'), ("marker.add", {"type": 'M', "value": 'PRESS'}, None), - ("marker.rename", {"type": 'M', "value": 'PRESS', "ctrl": True}, None), ("marker.camera_bind", {"type": 'B', "value": 'PRESS', "ctrl": True}, None), *_template_items_context_menu("DOPESHEET_MT_context_menu", params.context_menu_event), *_template_items_change_frame(params), @@ -2510,7 +2509,8 @@ def km_nla_generic(_params): *_template_space_region_type_toggle( sidebar_key={"type": 'N', "value": 'PRESS'}, ), - ("nla.tweakmode_enter", {"type": 'TAB', "value": 'PRESS'}, None), + ("nla.tweakmode_enter", {"type": 'TAB', "value": 'PRESS'}, + {"properties": [("use_upper_stack_evaluation", False)]}), ("nla.tweakmode_exit", {"type": 'TAB', "value": 'PRESS'}, None), ("nla.tweakmode_enter", {"type": 'TAB', "value": 'PRESS', "shift": True}, {"properties": [("isolate_action", True)]}), @@ -2620,7 +2620,6 @@ def km_nla_editor(params): ("transform.transform", {"type": 'S', "value": 'PRESS'}, {"properties": [("mode", 'TIME_SCALE')]}), ("marker.add", {"type": 'M', "value": 'PRESS'}, None), - ("marker.rename", {"type": 'M', "value": 'PRESS', "ctrl": True}, None), *_template_items_context_menu("NLA_MT_context_menu", params.context_menu_event), *_template_items_change_frame(params), ]) @@ -2929,7 +2928,6 @@ def km_sequencer(params): ("transform.transform", {"type": 'E', "value": 'PRESS'}, {"properties": [("mode", 'TIME_EXTEND')]}), ("marker.add", {"type": 'M', "value": 'PRESS'}, None), - ("marker.rename", {"type": 'M', "value": 'PRESS', "ctrl": True}, None), ("sequencer.select_side_of_frame", {"type": 'LEFT_BRACKET', "value": 'PRESS'}, {"properties": [("side", 'LEFT')]}), ("sequencer.select_side_of_frame", {"type": 'RIGHT_BRACKET', "value": 'PRESS'}, @@ -7372,7 +7370,7 @@ def km_3d_view_tool_sculpt_mask_by_color(params): "3D View Tool: Sculpt, Mask by Color", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("sculpt.mask_by_color", {"type": params.tool_mouse, "value": 'CLICK'}, None) + ("sculpt.mask_by_color", {"type": params.tool_mouse, "value": 'PRESS'}, None) ]}, ) diff --git a/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py b/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py index 4c1b905ac05..64039f200e9 100644 --- a/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py +++ b/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py @@ -836,7 +836,7 @@ def km_markers(params): ("marker.select_all", {"type": 'I', "value": 'PRESS', "ctrl": True}, {"properties": [("action", 'INVERT')]}), ("marker.delete", {"type": 'BACK_SPACE', "value": 'PRESS'}, None), ("marker.delete", {"type": 'DEL', "value": 'PRESS'}, None), - ("marker.rename", {"type": 'RET', "value": 'PRESS'}, None), + op_panel("TOPBAR_PT_name_marker", {"type": 'RET', "value": 'PRESS'}, [("keep_open", False)]), ("marker.move", {"type": 'W', "value": 'PRESS'}, None), ]) @@ -937,7 +937,6 @@ def km_graph_editor(params): ("wm.context_menu_enum", {"type": 'X', "value": 'PRESS'}, {"properties": [("data_path", 'space_data.auto_snap')]}), ("marker.add", {"type": 'M', "value": 'PRESS'}, None), - ("marker.rename", {"type": 'RET', "value": 'PRESS'}, None), op_menu_pie("GRAPH_MT_snap_pie", {"type": 'X', "value": 'PRESS', "shift": True}), ]) @@ -1438,7 +1437,6 @@ def km_dopesheet(params): ("wm.context_toggle", {"type": 'B', "value": 'PRESS'}, {"properties": [("data_path", 'tool_settings.use_proportional_action')]}), ("marker.add", {"type": 'M', "value": 'PRESS'}, None), - ("marker.rename", {"type": 'RET', "value": 'PRESS'}, None), ("anim.start_frame_set", {"type": 'LEFT_ARROW', "value": 'PRESS', "ctrl": True}, None), ("anim.end_frame_set", {"type": 'RIGHT_ARROW', "value": 'PRESS', "ctrl": True}, None), ]) @@ -1548,7 +1546,6 @@ def km_nla_editor(params): *_template_items_context_menu("NLA_MT_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}), op_menu_pie("NLA_MT_snap_pie", {"type": 'X', "value": 'PRESS', "shift": True}), ("marker.add", {"type": 'M', "value": 'PRESS'}, None), - ("marker.rename", {"type": 'RET', "value": 'PRESS'}, None), ]) return keymap @@ -1835,7 +1832,6 @@ def km_sequencer(params): {"properties": [("mode", 'TIME_EXTEND')]}), *_template_items_context_menu("SEQUENCER_MT_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}), ("marker.add", {"type": 'M', "value": 'PRESS'}, None), - ("marker.rename", {"type": 'RET', "value": 'PRESS'}, None), # Tools op_tool_cycle("builtin.select_box", {"type": 'Q', "value": 'PRESS'}), op_tool_cycle("builtin.blade", {"type": 'B', "value": 'PRESS'}), diff --git a/release/scripts/startup/bl_operators/geometry_nodes.py b/release/scripts/startup/bl_operators/geometry_nodes.py index c3892e988c5..f435efc76fc 100644 --- a/release/scripts/startup/bl_operators/geometry_nodes.py +++ b/release/scripts/startup/bl_operators/geometry_nodes.py @@ -26,8 +26,9 @@ def geometry_node_group_empty_new(): def geometry_modifier_poll(context): ob = context.object - # Test object support for geometry node modifier (No curves object support yet) - if not ob or ob.type not in {'MESH', 'POINTCLOUD', 'VOLUME', 'CURVE', 'FONT'}: + + # Test object support for geometry node modifier + if not ob or ob.type not in {'MESH', 'POINTCLOUD', 'VOLUME', 'CURVE', 'FONT', 'CURVES'}: return False return True @@ -82,32 +83,7 @@ class NewGeometryNodeTreeAssign(Operator): return {'FINISHED'} -class CopyGeometryNodeTreeAssign(Operator): - """Copy the active geometry node group and assign it to the active modifier""" - - bl_idname = "node.copy_geometry_node_group_assign" - bl_label = "Copy Geometry Node Group" - bl_options = {'REGISTER', 'UNDO'} - - @classmethod - def poll(cls, context): - return geometry_modifier_poll(context) - - def execute(self, context): - modifier = context.object.modifiers.active - if modifier is None: - return {'CANCELLED'} - - group = modifier.node_group - if group is None: - return {'CANCELLED'} - - modifier.node_group = group.copy() - return {'FINISHED'} - - classes = ( NewGeometryNodesModifier, NewGeometryNodeTreeAssign, - CopyGeometryNodeTreeAssign, ) diff --git a/release/scripts/startup/bl_operators/node.py b/release/scripts/startup/bl_operators/node.py index b8368cd1970..60a684ae5e8 100644 --- a/release/scripts/startup/bl_operators/node.py +++ b/release/scripts/startup/bl_operators/node.py @@ -73,7 +73,11 @@ class NodeAddOperator: for n in tree.nodes: n.select = False - node = tree.nodes.new(type=node_type) + try: + node = tree.nodes.new(type=node_type) + except RuntimeError as e: + self.report({'ERROR'}, str(e)) + return None for setting in self.settings: # XXX catch exceptions here? diff --git a/release/scripts/startup/bl_ui/properties_data_mesh.py b/release/scripts/startup/bl_ui/properties_data_mesh.py index 1bfd82cc790..1ba7a4a5413 100644 --- a/release/scripts/startup/bl_ui/properties_data_mesh.py +++ b/release/scripts/startup/bl_ui/properties_data_mesh.py @@ -599,20 +599,21 @@ class MESH_UL_color_attributes(UIList, ColorAttributesListBase): split.emboss = 'NONE' split.prop(attribute, "name", text="") - active_render = _index == data.color_attributes.render_color_index + sub = split.row() + sub.alignment = 'RIGHT' + sub.active = False + sub.label(text="%s â–¶ %s" % (domain_name, data_type.name)) - props = split.operator( + active_render = _index == data.color_attributes.render_color_index + + row = layout.row() + row.emboss = 'NONE' + prop = row.operator( "geometry.color_attribute_render_set", text="", icon='RESTRICT_RENDER_OFF' if active_render else 'RESTRICT_RENDER_ON', ) - - props.name = attribute.name - - sub = split.row() - sub.alignment = 'RIGHT' - sub.active = False - sub.label(text="%s â–¶ %s" % (domain_name, data_type.name)) + prop.name = attribute.name class MESH_UL_color_attributes_selector(UIList, ColorAttributesListBase): diff --git a/release/scripts/startup/bl_ui/space_nla.py b/release/scripts/startup/bl_ui/space_nla.py index 99324ac5d50..f0e991c768d 100644 --- a/release/scripts/startup/bl_ui/space_nla.py +++ b/release/scripts/startup/bl_ui/space_nla.py @@ -150,6 +150,23 @@ class NLA_MT_marker(Menu): marker_menu_generic(layout, context) +class NLA_MT_marker_select(Menu): + bl_label = 'Select' + + def draw(self, context): + layout = self.layout + + layout.operator("marker.select_all", text="All").action = 'SELECT' + layout.operator("marker.select_all", text="None").action = 'DESELECT' + layout.operator("marker.select_all", text="Invert").action = 'INVERT' + + layout.separator() + + layout.operator("marker.select_leftright", text="Before Current Frame").mode = 'LEFT' + layout.operator("marker.select_leftright", text="After Current Frame").mode = 'RIGHT' + + + class NLA_MT_edit(Menu): bl_label = "Edit" @@ -197,7 +214,8 @@ class NLA_MT_edit(Menu): layout.operator("nla.tweakmode_exit", text="Stop Tweaking Strip Actions") else: layout.operator("nla.tweakmode_enter", text="Start Editing Stashed Action").isolate_action = True - layout.operator("nla.tweakmode_enter", text="Start Tweaking Strip Actions") + layout.operator("nla.tweakmode_enter", text="Start Tweaking Strip Actions (Full Stack)").use_upper_stack_evaluation = True + layout.operator("nla.tweakmode_enter", text="Start Tweaking Strip Actions (Lower Stack)").use_upper_stack_evaluation = False class NLA_MT_add(Menu): @@ -271,7 +289,8 @@ class NLA_MT_context_menu(Menu): layout.operator("nla.tweakmode_exit", text="Stop Tweaking Strip Actions") else: layout.operator("nla.tweakmode_enter", text="Start Editing Stashed Action").isolate_action = True - layout.operator("nla.tweakmode_enter", text="Start Tweaking Strip Actions") + layout.operator("nla.tweakmode_enter", text="Start Tweaking Strip Actions (Full Stack)").use_upper_stack_evaluation = True + layout.operator("nla.tweakmode_enter", text="Start Tweaking Strip Actions (Lower Stack)").use_upper_stack_evaluation = False layout.separator() @@ -312,6 +331,7 @@ classes = ( NLA_MT_view, NLA_MT_select, NLA_MT_marker, + NLA_MT_marker_select, NLA_MT_add, NLA_MT_edit_transform, NLA_MT_snap_pie, diff --git a/release/scripts/startup/bl_ui/space_node.py b/release/scripts/startup/bl_ui/space_node.py index d7d905cb820..d7c6bf63423 100644 --- a/release/scripts/startup/bl_ui/space_node.py +++ b/release/scripts/startup/bl_ui/space_node.py @@ -149,7 +149,7 @@ class NODE_HT_header(Header): active_modifier = ob.modifiers.active if active_modifier and active_modifier.type == 'NODES': if active_modifier.node_group: - row.template_ID(active_modifier, "node_group", new="node.copy_geometry_node_group_assign") + row.template_ID(active_modifier, "node_group", new="object.geometry_node_tree_copy_assign") else: row.template_ID(active_modifier, "node_group", new="node.new_geometry_node_group_assign") else: @@ -709,6 +709,7 @@ class NODE_PT_overlay(Panel): if snode.tree_type == 'GeometryNodeTree': col.separator() col.prop(overlay, "show_timing", text="Timings") + col.prop(overlay, "show_named_attributes", text="Named Attributes") class NODE_UL_interface_sockets(bpy.types.UIList): diff --git a/release/scripts/startup/bl_ui/space_time.py b/release/scripts/startup/bl_ui/space_time.py index 5296900fa30..13ab6e67b00 100644 --- a/release/scripts/startup/bl_ui/space_time.py +++ b/release/scripts/startup/bl_ui/space_time.py @@ -187,11 +187,17 @@ def marker_menu_generic(layout, context): layout.separator() - layout.operator("marker.rename", text="Rename Marker") + props = layout.operator("wm.call_panel", text="Rename Marker") + props.name = "TOPBAR_PT_name_marker" + props.keep_open = False layout.operator("marker.move", text="Move Marker") layout.separator() + layout.menu('NLA_MT_marker_select') + + layout.separator() + layout.operator("marker.camera_bind") layout.separator() diff --git a/release/scripts/startup/bl_ui/space_topbar.py b/release/scripts/startup/bl_ui/space_topbar.py index 55dcb4a20eb..2cf50bdbf95 100644 --- a/release/scripts/startup/bl_ui/space_topbar.py +++ b/release/scripts/startup/bl_ui/space_topbar.py @@ -851,6 +851,64 @@ class TOPBAR_PT_name(Panel): row.label(text="No active item") +class TOPBAR_PT_name_marker(Panel): + bl_space_type = 'TOPBAR' # dummy + bl_region_type = 'HEADER' + bl_label = "Rename Marker" + bl_ui_units_x = 14 + + @staticmethod + def is_using_pose_markers(context): + sd = context.space_data + return (sd.type == 'DOPESHEET_EDITOR' and sd.mode in {'ACTION', 'SHAPEKEY'} and + sd.show_pose_markers and sd.action) + + @staticmethod + def get_selected_marker(context): + if TOPBAR_PT_name_marker.is_using_pose_markers(context): + markers = context.space_data.action.pose_markers + else: + markers = context.scene.timeline_markers + + for marker in markers: + if marker.select: + return marker + return None + + @staticmethod + def row_with_icon(layout, icon): + row = layout.row() + row.activate_init = True + row.label(icon=icon) + return row + + def draw(self, context): + layout = self.layout + + layout.label(text="Marker Name") + + scene = context.scene + if scene.tool_settings.lock_markers: + row = self.row_with_icon(layout, 'ERROR') + label = "Markers are locked" + row.label(text=label) + return + + marker = self.get_selected_marker(context) + if marker is None: + row = self.row_with_icon(layout, 'ERROR') + row.label(text="No active marker") + return + + icon = 'TIME' + if marker.camera is not None: + icon = 'CAMERA_DATA' + elif self.is_using_pose_markers(context): + icon = 'ARMATURE_DATA' + row = self.row_with_icon(layout, icon) + row.prop(marker, "name", text="") + + classes = ( TOPBAR_HT_upper_bar, TOPBAR_MT_file_context_menu, @@ -877,6 +935,7 @@ classes = ( TOPBAR_PT_gpencil_layers, TOPBAR_PT_gpencil_primitive, TOPBAR_PT_name, + TOPBAR_PT_name_marker, ) if __name__ == "__main__": # only for live edit. diff --git a/release/scripts/startup/bl_ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py index 035c4fd1352..b954d726ca3 100644 --- a/release/scripts/startup/bl_ui/space_userpref.py +++ b/release/scripts/startup/bl_ui/space_userpref.py @@ -2281,7 +2281,8 @@ class USERPREF_PT_experimental_prototypes(ExperimentalPanel, Panel): ), ) - +# Keep this as tweaks can be useful to restore. +""" class USERPREF_PT_experimental_tweaks(ExperimentalPanel, Panel): bl_label = "Tweaks" @@ -2292,6 +2293,7 @@ class USERPREF_PT_experimental_tweaks(ExperimentalPanel, Panel): ), ) +""" class USERPREF_PT_experimental_debugging(ExperimentalPanel, Panel): bl_label = "Debugging" @@ -2413,7 +2415,7 @@ classes = ( USERPREF_PT_experimental_new_features, USERPREF_PT_experimental_prototypes, - USERPREF_PT_experimental_tweaks, + # USERPREF_PT_experimental_tweaks, USERPREF_PT_experimental_debugging, # Add dynamically generated editor theme panels last, diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py index 2e924d5b4c9..74f20aca072 100644 --- a/release/scripts/startup/bl_ui/space_view3d.py +++ b/release/scripts/startup/bl_ui/space_view3d.py @@ -2722,6 +2722,9 @@ class VIEW3D_MT_object_apply(Menu): text_ctxt=i18n_contexts.default, ).target = 'MESH' layout.operator("object.duplicates_make_real") + layout.operator("object.parent_inverse_apply", + text="Parent Inverse", + text_ctxt=i18n_contexts.default) class VIEW3D_MT_object_parent(Menu): @@ -6746,8 +6749,10 @@ class VIEW3D_PT_snapping(Panel): col.prop(tool_settings, "use_snap_backface_culling") if obj: - if object_mode == 'EDIT': - col.prop(tool_settings, "use_snap_self") + if object_mode == 'EDIT' and obj.type not in {'LATTICE', 'META', 'FONT'}: + sub = col.column() + sub.active = not (tool_settings.use_proportional_edit and obj.type == 'MESH') + sub.prop(tool_settings, "use_snap_self") if object_mode in {'OBJECT', 'POSE', 'EDIT', 'WEIGHT_PAINT'}: col.prop(tool_settings, "use_snap_align_rotation") diff --git a/source/blender/blenfont/BLF_api.h b/source/blender/blenfont/BLF_api.h index 058b0f120f7..e5e2b1711b1 100644 --- a/source/blender/blenfont/BLF_api.h +++ b/source/blender/blenfont/BLF_api.h @@ -109,7 +109,7 @@ typedef bool (*BLF_GlyphBoundsFn)(const char *str, size_t str_step_ofs, const struct rcti *glyph_step_bounds, int glyph_advance_x, - const struct rctf *glyph_bounds, + const struct rcti *glyph_bounds, const int glyph_bearing[2], void *user_data); @@ -151,9 +151,9 @@ size_t BLF_width_to_rstrlen( void BLF_boundbox_ex(int fontid, const char *str, size_t str_len, - struct rctf *box, + struct rcti *box, struct ResultBLF *r_info) ATTR_NONNULL(2); -void BLF_boundbox(int fontid, const char *str, size_t str_len, struct rctf *box) ATTR_NONNULL(); +void BLF_boundbox(int fontid, const char *str, size_t str_len, struct rcti *box) ATTR_NONNULL(); /** * The next both function return the width and height @@ -173,9 +173,9 @@ float BLF_height(int fontid, const char *str, size_t str_len) ATTR_WARN_UNUSED_R * Return dimensions of the font without any sample text. */ int BLF_height_max(int fontid) ATTR_WARN_UNUSED_RESULT; -float BLF_width_max(int fontid) ATTR_WARN_UNUSED_RESULT; -float BLF_descender(int fontid) ATTR_WARN_UNUSED_RESULT; -float BLF_ascender(int fontid) ATTR_WARN_UNUSED_RESULT; +int BLF_width_max(int fontid) ATTR_WARN_UNUSED_RESULT; +int BLF_descender(int fontid) ATTR_WARN_UNUSED_RESULT; +int BLF_ascender(int fontid) ATTR_WARN_UNUSED_RESULT; /** * The following function return the width and height of the string, but @@ -195,7 +195,7 @@ float BLF_fixed_width(int fontid) ATTR_WARN_UNUSED_RESULT; * have to be enable/disable using BLF_enable/disable. */ void BLF_rotation(int fontid, float angle); -void BLF_clipping(int fontid, float xmin, float ymin, float xmax, float ymax); +void BLF_clipping(int fontid, int xmin, int ymin, int xmax, int ymax); void BLF_wordwrap(int fontid, int wrap_width); #if BLF_BLUR_ENABLE diff --git a/source/blender/blenfont/intern/blf.c b/source/blender/blenfont/intern/blf.c index 2b5a2cdf606..a944ab332bd 100644 --- a/source/blender/blenfont/intern/blf.c +++ b/source/blender/blenfont/intern/blf.c @@ -342,9 +342,9 @@ void BLF_position(int fontid, float x, float y, float z) } } - font->pos[0] = x; - font->pos[1] = y; - font->pos[2] = z; + font->pos[0] = round_fl_to_int(x); + font->pos[1] = round_fl_to_int(y); + font->pos[2] = round_fl_to_int(z); } } @@ -488,7 +488,7 @@ static void blf_draw_gl__start(FontBLF *font) GPU_matrix_mul(font->m); } - GPU_matrix_translate_3fv(font->pos); + GPU_matrix_translate_3f(font->pos[0], font->pos[1], font->pos[2]); if (font->flags & BLF_ASPECT) { GPU_matrix_scale_3fv(font->aspect); @@ -589,9 +589,10 @@ size_t BLF_width_to_strlen( if (font) { const float xa = (font->flags & BLF_ASPECT) ? font->aspect[0] : 1.0f; size_t ret; - ret = blf_font_width_to_strlen(font, str, str_len, width / xa, r_width); + int width_result; + ret = blf_font_width_to_strlen(font, str, str_len, width / xa, &width_result); if (r_width) { - *r_width *= xa; + *r_width = (float)width_result * xa; } return ret; } @@ -610,9 +611,10 @@ size_t BLF_width_to_rstrlen( if (font) { const float xa = (font->flags & BLF_ASPECT) ? font->aspect[0] : 1.0f; size_t ret; - ret = blf_font_width_to_rstrlen(font, str, str_len, width / xa, r_width); + int width_result; + ret = blf_font_width_to_rstrlen(font, str, str_len, width / xa, &width_result); if (r_width) { - *r_width *= xa; + *r_width = (float)width_result * xa; } return ret; } @@ -624,7 +626,7 @@ size_t BLF_width_to_rstrlen( } void BLF_boundbox_ex( - int fontid, const char *str, const size_t str_len, rctf *r_box, struct ResultBLF *r_info) + int fontid, const char *str, const size_t str_len, rcti *r_box, struct ResultBLF *r_info) { FontBLF *font = blf_get(fontid); @@ -640,7 +642,7 @@ void BLF_boundbox_ex( } } -void BLF_boundbox(int fontid, const char *str, const size_t str_len, rctf *r_box) +void BLF_boundbox(int fontid, const char *str, const size_t str_len, rcti *r_box) { BLF_boundbox_ex(fontid, str, str_len, r_box, NULL); } @@ -716,7 +718,7 @@ int BLF_height_max(int fontid) return 0; } -float BLF_width_max(int fontid) +int BLF_width_max(int fontid) { FontBLF *font = blf_get(fontid); @@ -724,10 +726,10 @@ float BLF_width_max(int fontid) return blf_font_width_max(font); } - return 0.0f; + return 0; } -float BLF_descender(int fontid) +int BLF_descender(int fontid) { FontBLF *font = blf_get(fontid); @@ -735,10 +737,10 @@ float BLF_descender(int fontid) return blf_font_descender(font); } - return 0.0f; + return 0; } -float BLF_ascender(int fontid) +int BLF_ascender(int fontid) { FontBLF *font = blf_get(fontid); @@ -758,7 +760,7 @@ void BLF_rotation(int fontid, float angle) } } -void BLF_clipping(int fontid, float xmin, float ymin, float xmax, float ymax) +void BLF_clipping(int fontid, int xmin, int ymin, int xmax, int ymax) { FontBLF *font = blf_get(fontid); @@ -889,7 +891,7 @@ void BLF_state_print(int fontid) printf(" name: '%s'\n", font->name); printf(" size: %f\n", font->size); printf(" dpi: %u\n", font->dpi); - printf(" pos: %.6f %.6f %.6f\n", UNPACK3(font->pos)); + printf(" pos: %d %d %d\n", UNPACK3(font->pos)); printf(" aspect: (%d) %.6f %.6f %.6f\n", (font->flags & BLF_ROTATION) != 0, UNPACK3(font->aspect)); diff --git a/source/blender/blenfont/intern/blf_font.c b/source/blender/blenfont/intern/blf_font.c index 60ff5f6470f..f93cb8b2d64 100644 --- a/source/blender/blenfont/intern/blf_font.c +++ b/source/blender/blenfont/intern/blf_font.c @@ -57,34 +57,26 @@ static SpinLock blf_glyph_cache_mutex; /* May be set to #UI_widgetbase_draw_cache_flush. */ static void (*blf_draw_cache_flush)(void) = NULL; +static ft_pix blf_font_height_max_ft_pix(struct FontBLF *font); +static ft_pix blf_font_width_max_ft_pix(struct FontBLF *font); + /* -------------------------------------------------------------------- */ /** \name FreeType Utilities (Internal) * \{ */ -/** - * Convert a FreeType 26.6 value representing an unscaled design size to pixels. - * This is an exact copy of the scaling done inside FT_Get_Kerning when called - * with #FT_KERNING_DEFAULT, including arbitrary resizing for small fonts. - */ -static int blf_unscaled_F26Dot6_to_pixels(FontBLF *font, FT_Pos value) +/* Convert a FreeType 26.6 value representing an unscaled design size to factional pixels. */ +static ft_pix blf_unscaled_F26Dot6_to_pixels(FontBLF *font, FT_Pos value) { /* Scale value by font size using integer-optimized multiplication. */ FT_Long scaled = FT_MulFix(value, font->face->size->metrics.x_scale); - /* FreeType states that this '25' has been determined heuristically. */ + /* Copied from FreeType's FT_Get_Kerning (with FT_KERNING_DEFAULT), scaling down */ + /* kerning distances at small ppem values so that they don't become too big. */ if (font->face->size->metrics.x_ppem < 25) { scaled = FT_MulDiv(scaled, font->face->size->metrics.x_ppem, 25); } - /* Copies of internal FreeType macros needed here. */ -#define FT_PIX_FLOOR(x) ((x) & ~63) -#define FT_PIX_ROUND(x) FT_PIX_FLOOR((x) + 32) - - /* Round to even 64ths, then divide by 64. */ - return (int)FT_PIX_ROUND(scaled) >> 6; - -#undef FT_PIX_FLOOR -#undef FT_PIX_ROUND + return (ft_pix)scaled; } /** \} */ @@ -146,12 +138,12 @@ void blf_batch_draw_begin(FontBLF *font) if (simple_shader) { /* Offset is applied to each glyph. */ - g_batch.ofs[0] = floorf(font->pos[0]); - g_batch.ofs[1] = floorf(font->pos[1]); + g_batch.ofs[0] = font->pos[0]; + g_batch.ofs[1] = font->pos[1]; } else { /* Offset is baked in modelview mat. */ - zero_v2(g_batch.ofs); + zero_v2_int(g_batch.ofs); } if (g_batch.active) { @@ -293,36 +285,39 @@ BLI_INLINE GlyphBLF *blf_glyph_from_utf8_and_step( return blf_glyph_ensure(font, gc, charcode); } -BLI_INLINE int blf_kerning(FontBLF *font, const GlyphBLF *g_prev, const GlyphBLF *g) +BLI_INLINE ft_pix blf_kerning(FontBLF *font, const GlyphBLF *g_prev, const GlyphBLF *g) { - if (!FT_HAS_KERNING(font->face) || g_prev == NULL) { - return 0; - } + ft_pix adjustment = 0; - FT_Vector delta = {KERNING_ENTRY_UNSET}; + /* Small adjust if there is hinting. */ + adjustment += g->lsb_delta - ((g_prev) ? g_prev->rsb_delta : 0); - /* Get unscaled kerning value from our cache if ASCII. */ - 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 (FT_HAS_KERNING(font->face) && g_prev) { + FT_Vector delta = {KERNING_ENTRY_UNSET}; - /* If not ASCII or not found in cache, ask FreeType for kerning. */ - if (UNLIKELY(delta.x == KERNING_ENTRY_UNSET)) { - /* Note that this function sets delta values to zero on any error. */ - FT_Get_Kerning(font->face, g_prev->idx, g->idx, FT_KERNING_UNSCALED, &delta); - } + /* Get unscaled kerning value from our cache if ASCII. */ + 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 ASCII we save this value to our cache for quicker access next time. */ - 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 not ASCII or not found in cache, ask FreeType for kerning. */ + if (UNLIKELY(delta.x == KERNING_ENTRY_UNSET)) { + /* Note that this function sets delta values to zero on any error. */ + FT_Get_Kerning(font->face, g_prev->idx, g->idx, FT_KERNING_UNSCALED, &delta); + } + + /* If ASCII we save this value to our cache for quicker access next time. */ + 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. */ - return blf_unscaled_F26Dot6_to_pixels(font, delta.x); + if (delta.x != 0) { + /* Convert unscaled design units to pixels and move pen. */ + adjustment += blf_unscaled_F26Dot6_to_pixels(font, delta.x); + } } - return 0; + return adjustment; } /** \} */ @@ -336,10 +331,10 @@ static void blf_font_draw_ex(FontBLF *font, const char *str, const size_t str_len, struct ResultBLF *r_info, - int pen_y) + ft_pix pen_y) { GlyphBLF *g, *g_prev = NULL; - int pen_x = 0; + ft_pix pen_x = 0; size_t i = 0; if (str_len == 0) { @@ -358,9 +353,9 @@ static void blf_font_draw_ex(FontBLF *font, pen_x += blf_kerning(font, g_prev, g); /* do not return this loop if clipped, we want every character tested */ - blf_glyph_draw(font, gc, g, (float)pen_x, (float)pen_y); + blf_glyph_draw(font, gc, g, ft_pix_to_int_floor(pen_x), ft_pix_to_int_floor(pen_y)); - pen_x += g->advance_i; + pen_x = ft_pix_round_advance(pen_x, g->advance_x); g_prev = g; } @@ -368,7 +363,7 @@ static void blf_font_draw_ex(FontBLF *font, if (r_info) { r_info->lines = 1; - r_info->width = pen_x; + r_info->width = ft_pix_to_int(pen_x); } } void blf_font_draw(FontBLF *font, const char *str, const size_t str_len, struct ResultBLF *r_info) @@ -382,7 +377,9 @@ int blf_font_draw_mono(FontBLF *font, const char *str, const size_t str_len, int { GlyphBLF *g; int col, columns = 0; - int pen_x = 0, pen_y = 0; + ft_pix pen_x = 0, pen_y = 0; + ft_pix cwidth_fpx = ft_pix_from_int(cwidth); + size_t i = 0; GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); @@ -396,7 +393,7 @@ int blf_font_draw_mono(FontBLF *font, const char *str, const size_t str_len, int continue; } /* do not return this loop if clipped, we want every character tested */ - blf_glyph_draw(font, gc, g, (float)pen_x, (float)pen_y); + blf_glyph_draw(font, gc, g, ft_pix_to_int_floor(pen_x), ft_pix_to_int_floor(pen_y)); col = BLI_wcwidth((char32_t)g->c); if (col < 0) { @@ -404,7 +401,7 @@ int blf_font_draw_mono(FontBLF *font, const char *str, const size_t str_len, int } columns += col; - pen_x += cwidth * col; + pen_x += cwidth_fpx * col; } blf_batch_draw_end(); @@ -425,11 +422,11 @@ static void blf_font_draw_buffer_ex(FontBLF *font, const char *str, const size_t str_len, struct ResultBLF *r_info, - int pen_y) + ft_pix pen_y) { GlyphBLF *g, *g_prev = NULL; - int pen_x = (int)font->pos[0]; - int pen_y_basis = (int)font->pos[1] + pen_y; + ft_pix pen_x = ft_pix_from_int(font->pos[0]); + ft_pix pen_y_basis = ft_pix_from_int(font->pos[1]) + pen_y; size_t i = 0; /* buffer specific vars */ @@ -449,18 +446,18 @@ static void blf_font_draw_buffer_ex(FontBLF *font, } pen_x += blf_kerning(font, g_prev, g); - chx = pen_x + ((int)g->pos[0]); - chy = pen_y_basis + g->dims[1]; + chx = ft_pix_to_int(pen_x + ft_pix_from_int(g->pos[0])); + chy = ft_pix_to_int(pen_y_basis + ft_pix_from_int(g->dims[1])); if (g->pitch < 0) { - pen_y = pen_y_basis + (g->dims[1] - g->pos[1]); + pen_y = pen_y_basis + ft_pix_from_int(g->dims[1] - g->pos[1]); } else { - pen_y = pen_y_basis - (g->dims[1] - g->pos[1]); + pen_y = pen_y_basis - ft_pix_from_int(g->dims[1] - g->pos[1]); } - if ((chx + g->dims[0]) >= 0 && chx < buf_info->dims[0] && (pen_y + g->dims[1]) >= 0 && - pen_y < buf_info->dims[1]) { + if ((chx + g->dims[0]) >= 0 && chx < buf_info->dims[0] && + (ft_pix_to_int(pen_y) + g->dims[1]) >= 0 && ft_pix_to_int(pen_y) < buf_info->dims[1]) { /* don't draw beyond the buffer bounds */ int width_clip = g->dims[0]; int height_clip = g->dims[1]; @@ -469,17 +466,20 @@ static void blf_font_draw_buffer_ex(FontBLF *font, if (width_clip + chx > buf_info->dims[0]) { width_clip -= chx + width_clip - buf_info->dims[0]; } - if (height_clip + pen_y > buf_info->dims[1]) { - height_clip -= pen_y + height_clip - buf_info->dims[1]; + if (height_clip + ft_pix_to_int(pen_y) > buf_info->dims[1]) { + height_clip -= ft_pix_to_int(pen_y) + height_clip - buf_info->dims[1]; } /* drawing below the image? */ if (pen_y < 0) { - yb_start += (g->pitch < 0) ? -pen_y : pen_y; - height_clip += pen_y; + yb_start += (g->pitch < 0) ? -ft_pix_to_int(pen_y) : ft_pix_to_int(pen_y); + height_clip += ft_pix_to_int(pen_y); pen_y = 0; } + /* Avoid conversions in the pixel writing loop. */ + const int pen_y_px = ft_pix_to_int(pen_y); + if (buf_info->fbuf) { int yb = yb_start; for (y = ((chy >= 0) ? 0 : -chy); y < height_clip; y++) { @@ -488,7 +488,7 @@ static void blf_font_draw_buffer_ex(FontBLF *font, if (a_byte) { const float a = (a_byte / 255.0f) * b_col_float[3]; const size_t buf_ofs = (((size_t)(chx + x) + - ((size_t)(pen_y + y) * (size_t)buf_info->dims[0])) * + ((size_t)(pen_y_px + y) * (size_t)buf_info->dims[0])) * (size_t)buf_info->ch); float *fbuf = buf_info->fbuf + buf_ofs; @@ -519,7 +519,7 @@ static void blf_font_draw_buffer_ex(FontBLF *font, if (a_byte) { const float a = (a_byte / 255.0f) * b_col_float[3]; const size_t buf_ofs = (((size_t)(chx + x) + - ((size_t)(pen_y + y) * (size_t)buf_info->dims[0])) * + ((size_t)(pen_y_px + y) * (size_t)buf_info->dims[0])) * (size_t)buf_info->ch); unsigned char *cbuf = buf_info->cbuf + buf_ofs; @@ -542,13 +542,13 @@ static void blf_font_draw_buffer_ex(FontBLF *font, } } - pen_x += g->advance_i; + pen_x = ft_pix_round_advance(pen_x, g->advance_x); g_prev = g; } if (r_info) { r_info->lines = 1; - r_info->width = pen_x; + r_info->width = ft_pix_to_int(pen_x); } } @@ -573,23 +573,24 @@ void blf_font_draw_buffer(FontBLF *font, * \{ */ static bool blf_font_width_to_strlen_glyph_process( - FontBLF *font, GlyphBLF *g_prev, GlyphBLF *g, int *pen_x, const int width_i) + FontBLF *font, GlyphBLF *g_prev, GlyphBLF *g, ft_pix *pen_x, const int width_i) { if (UNLIKELY(g == NULL)) { return false; /* continue the calling loop. */ } *pen_x += blf_kerning(font, g_prev, g); - *pen_x += g->advance_i; + *pen_x = ft_pix_round_advance(*pen_x, g->advance_x); /* When true, break the calling loop. */ - return (*pen_x >= width_i); + return (ft_pix_to_int(*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) + FontBLF *font, const char *str, const size_t str_len, int width, int *r_width) { GlyphBLF *g, *g_prev; - int pen_x, width_new; + ft_pix pen_x; + ft_pix width_new; size_t i, i_prev; GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); @@ -605,7 +606,7 @@ size_t blf_font_width_to_strlen( } if (r_width) { - *r_width = (float)width_new; + *r_width = ft_pix_to_int(width_new); } blf_glyph_cache_release(font); @@ -613,15 +614,14 @@ 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) + FontBLF *font, const char *str, const size_t str_len, int width, int *r_width) { GlyphBLF *g, *g_prev; - int pen_x, width_new; + ft_pix pen_x, width_new; size_t i, i_prev, i_tmp; const char *s, *s_prev; GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); - const int width_i = (int)width; i = BLI_strnlen(str, str_len); s = BLI_str_find_prev_char_utf8(&str[i], str); @@ -642,13 +642,13 @@ size_t blf_font_width_to_rstrlen( BLI_assert(i_tmp == i); } - if (blf_font_width_to_strlen_glyph_process(font, g_prev, g, &pen_x, width_i)) { + if (blf_font_width_to_strlen_glyph_process(font, g_prev, g, &pen_x, width)) { break; } } if (r_width) { - *r_width = (float)width_new; + *r_width = ft_pix_to_int(width_new); } blf_glyph_cache_release(font); @@ -665,19 +665,18 @@ static void blf_font_boundbox_ex(FontBLF *font, GlyphCacheBLF *gc, const char *str, const size_t str_len, - rctf *box, + rcti *box, struct ResultBLF *r_info, - int pen_y) + ft_pix pen_y) { GlyphBLF *g, *g_prev = NULL; - int pen_x = 0; + ft_pix pen_x = 0; size_t i = 0; - rctf gbox; - box->xmin = 32000.0f; - box->xmax = -32000.0f; - box->ymin = 32000.0f; - box->ymax = -32000.0f; + ft_pix box_xmin = ft_pix_from_int(32000); + ft_pix box_xmax = ft_pix_from_int(-32000); + ft_pix box_ymin = ft_pix_from_int(32000); + ft_pix box_ymax = ft_pix_from_int(-32000); while ((i < str_len) && str[i]) { g = blf_glyph_from_utf8_and_step(font, gc, str, str_len, &i); @@ -686,44 +685,50 @@ static void blf_font_boundbox_ex(FontBLF *font, continue; } pen_x += blf_kerning(font, g_prev, g); + const ft_pix pen_x_next = ft_pix_round_advance(pen_x, g->advance_x); - gbox.xmin = (float)pen_x; - gbox.xmax = (float)pen_x + g->advance; - gbox.ymin = g->box.ymin + (float)pen_y; - gbox.ymax = g->box.ymax + (float)pen_y; + const ft_pix gbox_xmin = pen_x; + const ft_pix gbox_xmax = pen_x_next; + const ft_pix gbox_ymin = g->box_ymin + pen_y; + const ft_pix gbox_ymax = g->box_ymax + pen_y; - if (gbox.xmin < box->xmin) { - box->xmin = gbox.xmin; + if (gbox_xmin < box_xmin) { + box_xmin = gbox_xmin; } - if (gbox.ymin < box->ymin) { - box->ymin = gbox.ymin; + if (gbox_ymin < box_ymin) { + box_ymin = gbox_ymin; } - if (gbox.xmax > box->xmax) { - box->xmax = gbox.xmax; + if (gbox_xmax > box_xmax) { + box_xmax = gbox_xmax; } - if (gbox.ymax > box->ymax) { - box->ymax = gbox.ymax; + if (gbox_ymax > box_ymax) { + box_ymax = gbox_ymax; } - pen_x += g->advance_i; + pen_x = pen_x_next; g_prev = g; } - if (box->xmin > box->xmax) { - box->xmin = 0.0f; - box->ymin = 0.0f; - box->xmax = 0.0f; - box->ymax = 0.0f; + if (box_xmin > box_xmax) { + box_xmin = 0; + box_ymin = 0; + box_xmax = 0; + box_ymax = 0; } + box->xmin = ft_pix_to_int_floor(box_xmin); + box->xmax = ft_pix_to_int_ceil(box_xmax); + box->ymin = ft_pix_to_int_floor(box_ymin); + box->ymax = ft_pix_to_int_ceil(box_ymax); + if (r_info) { r_info->lines = 1; - r_info->width = pen_x; + r_info->width = ft_pix_to_int(pen_x); } } void blf_font_boundbox( - FontBLF *font, const char *str, const size_t str_len, rctf *r_box, struct ResultBLF *r_info) + FontBLF *font, const char *str, const size_t str_len, rcti *r_box, struct ResultBLF *r_info) { GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); blf_font_boundbox_ex(font, gc, str, str_len, r_box, r_info, 0); @@ -738,7 +743,7 @@ void blf_font_width_and_height(FontBLF *font, struct ResultBLF *r_info) { float xa, ya; - rctf box; + rcti box; if (font->flags & BLF_ASPECT) { xa = font->aspect[0]; @@ -755,8 +760,8 @@ void blf_font_width_and_height(FontBLF *font, else { blf_font_boundbox(font, str, str_len, &box, r_info); } - *r_width = (BLI_rctf_size_x(&box) * xa); - *r_height = (BLI_rctf_size_y(&box) * ya); + *r_width = ((float)BLI_rcti_size_x(&box) * xa); + *r_height = ((float)BLI_rcti_size_y(&box) * ya); } float blf_font_width(FontBLF *font, @@ -765,7 +770,7 @@ float blf_font_width(FontBLF *font, struct ResultBLF *r_info) { float xa; - rctf box; + rcti box; if (font->flags & BLF_ASPECT) { xa = font->aspect[0]; @@ -780,7 +785,7 @@ float blf_font_width(FontBLF *font, else { blf_font_boundbox(font, str, str_len, &box, r_info); } - return BLI_rctf_size_x(&box) * xa; + return (float)BLI_rcti_size_x(&box) * xa; } float blf_font_height(FontBLF *font, @@ -789,7 +794,7 @@ float blf_font_height(FontBLF *font, struct ResultBLF *r_info) { float ya; - rctf box; + rcti box; if (font->flags & BLF_ASPECT) { ya = font->aspect[1]; @@ -804,7 +809,7 @@ float blf_font_height(FontBLF *font, else { blf_font_boundbox(font, str, str_len, &box, r_info); } - return BLI_rctf_size_y(&box) * ya; + return (float)BLI_rcti_size_y(&box) * ya; } float blf_font_fixed_width(FontBLF *font) @@ -822,12 +827,12 @@ static void blf_font_boundbox_foreach_glyph_ex(FontBLF *font, BLF_GlyphBoundsFn user_fn, void *user_data, struct ResultBLF *r_info, - int pen_y) + ft_pix pen_y) { GlyphBLF *g, *g_prev = NULL; - int pen_x = 0; + ft_pix pen_x = 0; size_t i = 0, i_curr; - rcti gbox; + rcti gbox_px; if (str_len == 0) { /* early output. */ @@ -842,15 +847,23 @@ static void blf_font_boundbox_foreach_glyph_ex(FontBLF *font, continue; } pen_x += blf_kerning(font, g_prev, g); + const ft_pix pen_x_next = ft_pix_round_advance(pen_x, g->advance_x); + + gbox_px.xmin = ft_pix_to_int_floor(pen_x); + gbox_px.xmax = ft_pix_to_int_ceil(pen_x_next); + gbox_px.ymin = ft_pix_to_int_floor(pen_y); + gbox_px.ymax = gbox_px.ymin - g->dims[1]; + const int advance_x_px = gbox_px.xmax - gbox_px.xmin; - gbox.xmin = pen_x; - gbox.xmax = gbox.xmin + MIN2(g->advance_i, g->dims[0]); - gbox.ymin = pen_y; - gbox.ymax = gbox.ymin - g->dims[1]; + pen_x = pen_x_next; - pen_x += g->advance_i; + rcti box_px; + box_px.xmin = ft_pix_to_int_floor(g->box_xmin); + box_px.xmax = ft_pix_to_int_ceil(g->box_xmax); + box_px.ymin = ft_pix_to_int_floor(g->box_ymin); + box_px.ymax = ft_pix_to_int_ceil(g->box_ymax); - if (user_fn(str, i_curr, &gbox, g->advance_i, &g->box, g->pos, user_data) == false) { + if (user_fn(str, i_curr, &gbox_px, advance_x_px, &box_px, g->pos, user_data) == false) { break; } @@ -859,7 +872,7 @@ static void blf_font_boundbox_foreach_glyph_ex(FontBLF *font, if (r_info) { r_info->lines = 1; - r_info->width = pen_x; + r_info->width = ft_pix_to_int(pen_x); } } void blf_font_boundbox_foreach_glyph(FontBLF *font, @@ -897,22 +910,26 @@ static void blf_font_wrap_apply(FontBLF *font, GlyphCacheBLF *gc, const char *str, const size_t str_len, - int pen_y, + ft_pix pen_y, void *userdata), void *userdata) { GlyphBLF *g, *g_prev = NULL; - int pen_x = 0, pen_y = 0; + ft_pix pen_x = 0; + ft_pix pen_y = 0; size_t i = 0; int lines = 0; - int pen_x_next = 0; + ft_pix pen_x_next = 0; + + /* Space between lines needs to be aligned to the pixel grid (T97310). */ + ft_pix line_height = FT_PIX_FLOOR(blf_font_height_max_ft_pix(font)); GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); struct WordWrapVars { - int wrap_width; + ft_pix wrap_width; size_t start, last[2]; - } wrap = {font->wrap_width != -1 ? font->wrap_width : INT_MAX, 0, {0, 0}}; + } wrap = {font->wrap_width != -1 ? ft_pix_from_int(font->wrap_width) : INT_MAX, 0, {0, 0}}; // printf("%s wrapping (%d, %d) `%s`:\n", __func__, str_len, strlen(str), str); while ((i < str_len) && str[i]) { @@ -936,7 +953,7 @@ static void blf_font_wrap_apply(FontBLF *font, * * This is _only_ done when we know for sure the character is ascii (newline or a space). */ - pen_x_next = pen_x + g->advance_i; + pen_x_next = ft_pix_round_advance(pen_x, g->advance_x); if (UNLIKELY((pen_x_next >= wrap.wrap_width) && (wrap.start != wrap.last[0]))) { do_draw = true; } @@ -964,7 +981,7 @@ static void blf_font_wrap_apply(FontBLF *font, wrap.start = wrap.last[0]; i = wrap.last[1]; pen_x = 0; - pen_y -= blf_font_height_max(font); + pen_y -= line_height; g_prev = NULL; lines += 1; continue; @@ -979,7 +996,7 @@ static void blf_font_wrap_apply(FontBLF *font, if (r_info) { r_info->lines = lines; /* width of last line only (with wrapped lines) */ - r_info->width = pen_x_next; + r_info->width = ft_pix_to_int(pen_x_next); } blf_glyph_cache_release(font); @@ -990,7 +1007,7 @@ static void blf_font_draw__wrap_cb(FontBLF *font, GlyphCacheBLF *gc, const char *str, const size_t str_len, - int pen_y, + ft_pix pen_y, void *UNUSED(userdata)) { blf_font_draw_ex(font, gc, str, str_len, NULL, pen_y); @@ -1008,22 +1025,22 @@ static void blf_font_boundbox_wrap_cb(FontBLF *font, GlyphCacheBLF *gc, const char *str, const size_t str_len, - int pen_y, + ft_pix pen_y, void *userdata) { - rctf *box = userdata; - rctf box_single; + rcti *box = userdata; + rcti box_single; blf_font_boundbox_ex(font, gc, str, str_len, &box_single, NULL, pen_y); - BLI_rctf_union(box, &box_single); + BLI_rcti_union(box, &box_single); } void blf_font_boundbox__wrap( - FontBLF *font, const char *str, const size_t str_len, rctf *box, struct ResultBLF *r_info) + FontBLF *font, const char *str, const size_t str_len, rcti *box, struct ResultBLF *r_info) { - box->xmin = 32000.0f; - box->xmax = -32000.0f; - box->ymin = 32000.0f; - box->ymax = -32000.0f; + box->xmin = 32000; + box->xmax = -32000; + box->ymin = 32000; + box->ymax = -32000; blf_font_wrap_apply(font, str, str_len, r_info, blf_font_boundbox_wrap_cb, box); } @@ -1033,7 +1050,7 @@ static void blf_font_draw_buffer__wrap_cb(FontBLF *font, GlyphCacheBLF *gc, const char *str, const size_t str_len, - int pen_y, + ft_pix pen_y, void *UNUSED(userdata)) { blf_font_draw_buffer_ex(font, gc, str, str_len, NULL, pen_y); @@ -1084,44 +1101,56 @@ int blf_font_count_missing_chars(FontBLF *font, /** \name Font Query: Attributes * \{ */ -int blf_font_height_max(FontBLF *font) +static ft_pix blf_font_height_max_ft_pix(FontBLF *font) { - int 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))); + ft_pix height_max; + FT_Face face = font->face; + if (FT_IS_SCALABLE(face)) { + height_max = ft_pix_from_int((int)(face->ascender - face->descender) * + (int)face->size->metrics.y_ppem) / + (ft_pix)face->units_per_EM; } else { - height_max = (int)(((float)font->face->size->metrics.height) / 64.0f); + height_max = (ft_pix)face->size->metrics.height; } /* can happen with size 1 fonts */ - return MAX2(height_max, 1); + return MAX2(height_max, ft_pix_from_int(1)); } -int blf_font_width_max(FontBLF *font) +int blf_font_height_max(FontBLF *font) +{ + return ft_pix_to_int(blf_font_height_max_ft_pix(font)); +} + +static ft_pix blf_font_width_max_ft_pix(FontBLF *font) { - int 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))); + ft_pix width_max; + const FT_Face face = font->face; + if (FT_IS_SCALABLE(face)) { + width_max = ft_pix_from_int((int)(face->bbox.xMax - face->bbox.xMin) * + (int)face->size->metrics.x_ppem) / + (ft_pix)face->units_per_EM; } else { - width_max = (int)(((float)font->face->size->metrics.max_advance) / 64.0f); + width_max = (ft_pix)face->size->metrics.max_advance; } /* can happen with size 1 fonts */ - return MAX2(width_max, 1); + return MAX2(width_max, ft_pix_from_int(1)); +} + +int blf_font_width_max(FontBLF *font) +{ + return ft_pix_to_int(blf_font_width_max_ft_pix(font)); } -float blf_font_descender(FontBLF *font) +int blf_font_descender(FontBLF *font) { - return ((float)font->face->size->metrics.descender) / 64.0f; + return ft_pix_to_int((ft_pix)font->face->size->metrics.descender); } -float blf_font_ascender(FontBLF *font) +int blf_font_ascender(FontBLF *font) { - return ((float)font->face->size->metrics.ascender) / 64.0f; + return ft_pix_to_int((ft_pix)font->face->size->metrics.ascender); } char *blf_display_name(FontBLF *font) @@ -1170,8 +1199,8 @@ static void blf_font_fill(FontBLF *font) font->aspect[0] = 1.0f; font->aspect[1] = 1.0f; font->aspect[2] = 1.0f; - font->pos[0] = 0.0f; - font->pos[1] = 0.0f; + font->pos[0] = 0; + font->pos[1] = 0; font->angle = 0.0f; for (int i = 0; i < 16; i++) { @@ -1184,10 +1213,10 @@ static void blf_font_fill(FontBLF *font) font->color[2] = 0; font->color[3] = 255; - font->clip_rec.xmin = 0.0f; - font->clip_rec.xmax = 0.0f; - font->clip_rec.ymin = 0.0f; - font->clip_rec.ymax = 0.0f; + font->clip_rec.xmin = 0; + font->clip_rec.xmax = 0; + font->clip_rec.ymin = 0; + font->clip_rec.ymax = 0; font->flags = 0; font->dpi = 0; font->size = 0; diff --git a/source/blender/blenfont/intern/blf_glyph.c b/source/blender/blenfont/intern/blf_glyph.c index 28531c5afc1..2694b179a11 100644 --- a/source/blender/blenfont/intern/blf_glyph.c +++ b/source/blender/blenfont/intern/blf_glyph.c @@ -183,8 +183,7 @@ static GlyphBLF *blf_glyph_cache_add_glyph( GlyphBLF *g = (GlyphBLF *)MEM_callocN(sizeof(GlyphBLF), "blf_glyph_get"); g->c = charcode; g->idx = glyph_index; - g->advance = ((float)glyph->advance.x) / 64.0f; - g->advance_i = (int)g->advance; + g->advance_x = (ft_pix)glyph->advance.x; g->pos[0] = glyph->bitmap_left; g->pos[1] = glyph->bitmap_top; g->dims[0] = (int)glyph->bitmap.width; @@ -193,10 +192,14 @@ static GlyphBLF *blf_glyph_cache_add_glyph( FT_BBox bbox; FT_Outline_Get_CBox(&(glyph->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; + g->box_xmin = (ft_pix)bbox.xMin; + g->box_xmax = (ft_pix)bbox.xMax; + g->box_ymin = (ft_pix)bbox.yMin; + g->box_ymax = (ft_pix)bbox.yMax; + + /* Used to improve advance when hinting is enabled. */ + g->lsb_delta = (ft_pix)glyph->lsb_delta; + g->rsb_delta = (ft_pix)glyph->rsb_delta; const int buffer_size = (int)(glyph->bitmap.width * glyph->bitmap.rows); if (buffer_size != 0) { @@ -488,28 +491,29 @@ void blf_glyph_free(GlyphBLF *g) /** \name Glyph Bounds Calculation * \{ */ -static void blf_glyph_calc_rect(rctf *rect, GlyphBLF *g, float x, float y) +static void blf_glyph_calc_rect(rcti *rect, GlyphBLF *g, const int x, const int y) { - rect->xmin = floorf(x + (float)g->pos[0]); - rect->xmax = rect->xmin + (float)g->dims[0]; - rect->ymin = floorf(y + (float)g->pos[1]); - rect->ymax = rect->ymin - (float)g->dims[1]; + rect->xmin = x + g->pos[0]; + rect->xmax = rect->xmin + g->dims[0]; + rect->ymin = y + g->pos[1]; + rect->ymax = rect->ymin - g->dims[1]; } -static void blf_glyph_calc_rect_test(rctf *rect, GlyphBLF *g, float x, float y) +static void blf_glyph_calc_rect_test(rcti *rect, GlyphBLF *g, const int x, const int y) { /* Intentionally check with `g->advance`, because this is the * width used by BLF_width. This allows that the text slightly * overlaps the clipping border to achieve better alignment. */ - rect->xmin = floorf(x); - rect->xmax = rect->xmin + MIN2(g->advance, (float)g->dims[0]); - rect->ymin = floorf(y); - rect->ymax = rect->ymin - (float)g->dims[1]; + rect->xmin = x; + rect->xmax = rect->xmin + MIN2(ft_pix_to_int(g->advance_x), g->dims[0]); + rect->ymin = y; + rect->ymax = rect->ymin - g->dims[1]; } -static void blf_glyph_calc_rect_shadow(rctf *rect, GlyphBLF *g, float x, float y, FontBLF *font) +static void blf_glyph_calc_rect_shadow( + rcti *rect, GlyphBLF *g, const int x, const int y, FontBLF *font) { - blf_glyph_calc_rect(rect, g, x + (float)font->shadow_x, y + (float)font->shadow_y); + blf_glyph_calc_rect(rect, g, x + font->shadow_x, y + font->shadow_y); } /** \} */ @@ -521,18 +525,18 @@ static void blf_glyph_calc_rect_shadow(rctf *rect, GlyphBLF *g, float x, float y static void blf_texture_draw(const unsigned char color[4], const int glyph_size[2], const int offset, - float x1, - float y1, - float x2, - float y2) + const int x1, + const int y1, + const int x2, + const int y2) { /* Only one vertex per glyph, geometry shader expand it into a quad. */ /* TODO: Get rid of Geom Shader because it's not optimal AT ALL for the GPU. */ copy_v4_fl4(GPU_vertbuf_raw_step(&g_batch.pos_step), - x1 + g_batch.ofs[0], - y1 + g_batch.ofs[1], - x2 + g_batch.ofs[0], - y2 + g_batch.ofs[1]); + (float)(x1 + g_batch.ofs[0]), + (float)(y1 + g_batch.ofs[1]), + (float)(x2 + g_batch.ofs[0]), + (float)(y2 + g_batch.ofs[1])); copy_v4_v4_uchar(GPU_vertbuf_raw_step(&g_batch.col_step), color); copy_v2_v2_int(GPU_vertbuf_raw_step(&g_batch.glyph_size_step), glyph_size); *((int *)GPU_vertbuf_raw_step(&g_batch.offset_step)) = offset; @@ -547,10 +551,10 @@ static void blf_texture_draw(const unsigned char color[4], static void blf_texture5_draw(const unsigned char color_in[4], const int glyph_size[2], const int offset, - float x1, - float y1, - float x2, - float y2) + const int x1, + const int y1, + const int x2, + const int y2) { int glyph_size_flag[2]; /* flag the x and y component signs for 5x5 blurring */ @@ -563,10 +567,10 @@ static void blf_texture5_draw(const unsigned char color_in[4], static void blf_texture3_draw(const unsigned char color_in[4], const int glyph_size[2], const int offset, - float x1, - float y1, - float x2, - float y2) + const int x1, + const int y1, + const int x2, + const int y2) { int glyph_size_flag[2]; /* flag the x component sign for 3x3 blurring */ @@ -576,7 +580,7 @@ static void blf_texture3_draw(const unsigned char color_in[4], blf_texture_draw(color_in, glyph_size_flag, offset, x1, y1, x2, y2); } -void blf_glyph_draw(FontBLF *font, GlyphCacheBLF *gc, GlyphBLF *g, float x, float y) +void blf_glyph_draw(FontBLF *font, GlyphCacheBLF *gc, GlyphBLF *g, const int x, const int y) { if ((!g->dims[0]) || (!g->dims[1])) { return; @@ -615,11 +619,11 @@ void blf_glyph_draw(FontBLF *font, GlyphCacheBLF *gc, GlyphBLF *g, float x, floa } if (font->flags & BLF_CLIPPING) { - rctf rect_test; + rcti rect_test; blf_glyph_calc_rect_test(&rect_test, g, x, y); - BLI_rctf_translate(&rect_test, font->pos[0], font->pos[1]); + BLI_rcti_translate(&rect_test, font->pos[0], font->pos[1]); - if (!BLI_rctf_inside_rctf(&font->clip_rec, &rect_test)) { + if (!BLI_rcti_inside_rcti(&font->clip_rec, &rect_test)) { return; } } @@ -630,7 +634,7 @@ void blf_glyph_draw(FontBLF *font, GlyphCacheBLF *gc, GlyphBLF *g, float x, floa } if (font->flags & BLF_SHADOW) { - rctf rect_ofs; + rcti rect_ofs; blf_glyph_calc_rect_shadow(&rect_ofs, g, x, y, font); if (font->shadow == 0) { @@ -662,7 +666,7 @@ void blf_glyph_draw(FontBLF *font, GlyphCacheBLF *gc, GlyphBLF *g, float x, floa } } - rctf rect; + rcti rect; blf_glyph_calc_rect(&rect, g, x, y); #if BLF_BLUR_ENABLE diff --git a/source/blender/blenfont/intern/blf_internal.h b/source/blender/blenfont/intern/blf_internal.h index 1018dc09ff8..7754f960043 100644 --- a/source/blender/blenfont/intern/blf_internal.h +++ b/source/blender/blenfont/intern/blf_internal.h @@ -67,18 +67,18 @@ void blf_font_draw_buffer__wrap(struct FontBLF *font, size_t str_len, struct ResultBLF *r_info); size_t blf_font_width_to_strlen( - struct FontBLF *font, const char *str, size_t str_len, float width, float *r_width); + struct FontBLF *font, const char *str, size_t str_len, int width, int *r_width); size_t blf_font_width_to_rstrlen( - struct FontBLF *font, const char *str, size_t str_len, float width, float *r_width); + struct FontBLF *font, const char *str, size_t str_len, int width, int *r_width); void blf_font_boundbox(struct FontBLF *font, const char *str, size_t str_len, - struct rctf *r_box, + struct rcti *r_box, struct ResultBLF *r_info); void blf_font_boundbox__wrap(struct FontBLF *font, const char *str, size_t str_len, - struct rctf *r_box, + struct rcti *r_box, struct ResultBLF *r_info); void blf_font_width_and_height(struct FontBLF *font, const char *str, @@ -97,8 +97,8 @@ float blf_font_height(struct FontBLF *font, float blf_font_fixed_width(struct FontBLF *font); int blf_font_height_max(struct FontBLF *font); int blf_font_width_max(struct FontBLF *font); -float blf_font_descender(struct FontBLF *font); -float blf_font_ascender(struct FontBLF *font); +int blf_font_descender(struct FontBLF *font); +int blf_font_ascender(struct FontBLF *font); char *blf_display_name(struct FontBLF *font); @@ -109,7 +109,7 @@ void blf_font_boundbox_foreach_glyph(struct FontBLF *font, size_t str_step_ofs, const struct rcti *glyph_step_bounds, int glyph_advance_x, - const struct rctf *glyph_bounds, + const struct rcti *glyph_bounds, const int glyph_bearing[2], void *user_data), void *user_data, @@ -133,7 +133,7 @@ struct GlyphBLF *blf_glyph_ensure(struct FontBLF *font, struct GlyphCacheBLF *gc void blf_glyph_free(struct GlyphBLF *g); void blf_glyph_draw( - struct FontBLF *font, struct GlyphCacheBLF *gc, struct GlyphBLF *g, float x, float y); + struct FontBLF *font, struct GlyphCacheBLF *gc, struct GlyphBLF *g, int x, int y); #ifdef WIN32 /* blf_font_win32_compat.c */ diff --git a/source/blender/blenfont/intern/blf_internal_types.h b/source/blender/blenfont/intern/blf_internal_types.h index 23dc2fda38c..62bce36dda0 100644 --- a/source/blender/blenfont/intern/blf_internal_types.h +++ b/source/blender/blenfont/intern/blf_internal_types.h @@ -10,6 +10,78 @@ #include "GPU_texture.h" #include "GPU_vertex_buffer.h" +/* -------------------------------------------------------------------- */ +/** \name Sub-Pixel Offset & Utilities + * + * Free-type uses fixed point precision for sub-pixel offsets. + * Utility functions here avoid exposing the details in the BLF API. + * \{ */ + +/** + * This is an internal type that represents sub-pixel positioning, + * users of this type are to use `ft_pix_*` functions to keep scaling/rounding in one place. + */ +typedef int32_t ft_pix; + +/* Macros copied from `include/freetype/internal/ftobjs.h`. */ + +/* FIXME(@campbellbarton): Follow rounding from Blender 3.1x and older. + * This is what users will expect and changing this creates wider spaced text. + * Use this macro to communicate that rounding should be used, using floor is to avoid + * user visible changes, which can be reviewed and handled separately. */ +#define USE_LEGACY_SPACING + +#define FT_PIX_FLOOR(x) ((x) & ~63) +#define FT_PIX_ROUND(x) FT_PIX_FLOOR((x) + 32) +#define FT_PIX_CEIL(x) ((x) + 63) + +#ifdef USE_LEGACY_SPACING +# define FT_PIX_DEFAULT_ROUNDING(x) FT_PIX_FLOOR(x) +#else +# define FT_PIX_DEFAULT_ROUNDING(x) FT_PIX_ROUND(x) +#endif + +BLI_INLINE int ft_pix_to_int(ft_pix v) +{ +#ifdef USE_LEGACY_SPACING + return (int)(v >> 6); +#else + return (int)(FT_PIX_DEFAULT_ROUNDING(v) >> 6); +#endif +} + +BLI_INLINE int ft_pix_to_int_floor(ft_pix v) +{ + return (int)(v >> 6); /* No need for explicit floor as the bits are removed when shifting. */ +} + +BLI_INLINE int ft_pix_to_int_ceil(ft_pix v) +{ + return (int)(FT_PIX_CEIL(v) >> 6); +} + +BLI_INLINE ft_pix ft_pix_from_int(int v) +{ + return v * 64; +} + +BLI_INLINE ft_pix ft_pix_from_float(float v) +{ + return lroundf(v * 64.0f); +} + +BLI_INLINE ft_pix ft_pix_round_advance(ft_pix v, ft_pix step) +{ + /* See #USE_LEGACY_SPACING, rounding logic could change here. */ + return FT_PIX_DEFAULT_ROUNDING(v) + FT_PIX_DEFAULT_ROUNDING(step); +} + +#undef FT_PIX_ROUND +#undef FT_PIX_CEIL +#undef FT_PIX_DEFAULT_ROUNDING + +/** \} */ + #define BLF_BATCH_DRAW_LEN_MAX 2048 /* in glyph */ /* Number of characters in GlyphCacheBLF.glyph_ascii_table. */ @@ -28,7 +100,7 @@ typedef struct BatchBLF { struct GPUVertBufRaw pos_step, col_step, offset_step, glyph_size_step; unsigned int pos_loc, col_loc, offset_loc, glyph_size_loc; unsigned int glyph_len; - float ofs[2]; /* copy of font->pos */ + int ofs[2]; /* copy of font->pos */ float mat[4][4]; /* previous call modelmatrix. */ bool enabled, active, simple_shader; struct GlyphCacheBLF *glyph_cache; @@ -86,12 +158,16 @@ typedef struct GlyphBLF { FT_UInt idx; /* glyph box. */ - rctf box; + ft_pix box_xmin; + ft_pix box_xmax; + ft_pix box_ymin; + ft_pix box_ymax; + + ft_pix advance_x; - /* advance size. */ - float advance; - /* avoid conversion to int while drawing */ - int advance_i; + /* The difference in bearings when hinting is active, zero otherwise. */ + ft_pix lsb_delta; + ft_pix rsb_delta; /* position inside the texture where this glyph is store. */ int offset; @@ -154,7 +230,7 @@ typedef struct FontBLF { float aspect[3]; /* initial position for draw the text. */ - float pos[3]; + int pos[3]; /* angle in radians. */ float angle; @@ -183,7 +259,7 @@ typedef struct FontBLF { float m[16]; /* clipping rectangle. */ - rctf clip_rec; + rcti clip_rec; /* the width to wrap the text, see BLF_WORD_WRAP */ int wrap_width; diff --git a/source/blender/blenfont/intern/blf_thumbs.c b/source/blender/blenfont/intern/blf_thumbs.c index 0e265fb7553..a75072f854f 100644 --- a/source/blender/blenfont/intern/blf_thumbs.c +++ b/source/blender/blenfont/intern/blf_thumbs.c @@ -64,7 +64,7 @@ void BLF_thumb_preview(const char *filepath, /* Always create the image with a white font, * the caller can theme how it likes */ memcpy(font->buf_info.col_init, font_color, sizeof(font->buf_info.col_init)); - font->pos[1] = (float)h; + font->pos[1] = h; font_size_curr = font_size; @@ -84,7 +84,7 @@ void BLF_thumb_preview(const char *filepath, font_size_curr -= (font_size_curr / font_shrink); font_shrink += 1; - font->pos[1] -= blf_font_ascender(font) * 1.1f; + font->pos[1] -= (int)((float)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_animsys.h b/source/blender/blenkernel/BKE_animsys.h index ded64b68f79..91ecfe09f38 100644 --- a/source/blender/blenkernel/BKE_animsys.h +++ b/source/blender/blenkernel/BKE_animsys.h @@ -7,6 +7,7 @@ * \ingroup bke */ +#include "BLI_bitmap.h" #include "BLI_sys_types.h" /* for bool */ #ifdef __cplusplus @@ -258,16 +259,20 @@ struct NlaKeyframingContext *BKE_animsys_get_nla_keyframing_context( * \param count: Number of values in the array. * \param index: Index of the element about to be updated, or -1. * \param[out] r_force_all: Set to true if all channels must be inserted. May be NULL. - * \return False if correction fails due to a division by zero, - * or null r_force_all when all channels are required. + * \param[out] r_successful_remaps: Bits will be enabled for indices that are both intended to be + * remapped and succeeded remapping. With both, it allows caller to check successfully remapped + * indices without having to explicitly check whether the index was intended to be remapped. */ -bool BKE_animsys_nla_remap_keyframe_values(struct NlaKeyframingContext *context, +void BKE_animsys_nla_remap_keyframe_values(struct NlaKeyframingContext *context, struct PointerRNA *prop_ptr, struct PropertyRNA *prop, float *values, int count, int index, - bool *r_force_all); + const struct AnimationEvalContext *anim_eval_context, + bool *r_force_all, + BLI_bitmap *r_successful_remaps); + /** * Free all cached contexts from the list. */ diff --git a/source/blender/blenkernel/BKE_attribute.h b/source/blender/blenkernel/BKE_attribute.h index 62469a28eb1..f3a29736bc8 100644 --- a/source/blender/blenkernel/BKE_attribute.h +++ b/source/blender/blenkernel/BKE_attribute.h @@ -65,7 +65,7 @@ struct CustomDataLayer *BKE_id_attribute_find(const struct ID *id, int type, AttributeDomain domain); -AttributeDomain BKE_id_attribute_domain(struct ID *id, const struct CustomDataLayer *layer); +AttributeDomain BKE_id_attribute_domain(const struct ID *id, const struct CustomDataLayer *layer); int BKE_id_attribute_data_length(struct ID *id, struct CustomDataLayer *layer); bool BKE_id_attribute_required(struct ID *id, struct CustomDataLayer *layer); bool BKE_id_attribute_rename(struct ID *id, diff --git a/source/blender/blenkernel/BKE_curve_to_mesh.hh b/source/blender/blenkernel/BKE_curve_to_mesh.hh index a49cb6eb7f5..6e657542e0f 100644 --- a/source/blender/blenkernel/BKE_curve_to_mesh.hh +++ b/source/blender/blenkernel/BKE_curve_to_mesh.hh @@ -2,7 +2,7 @@ #pragma once -struct CurveEval; +struct CurvesGeometry; struct Mesh; /** \file @@ -21,11 +21,13 @@ namespace blender::bke { * 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, bool fill_caps); +Mesh *curve_to_mesh_sweep(const CurvesGeometry &main, + const CurvesGeometry &profile, + bool fill_caps); /** * 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); +Mesh *curve_to_wire_mesh(const CurvesGeometry &curve); } // namespace blender::bke diff --git a/source/blender/blenkernel/BKE_curves.hh b/source/blender/blenkernel/BKE_curves.hh index 9fd023edcf2..282e2a40bd0 100644 --- a/source/blender/blenkernel/BKE_curves.hh +++ b/source/blender/blenkernel/BKE_curves.hh @@ -76,6 +76,11 @@ class CurvesGeometryRuntime { mutable Vector<float3> evaluated_position_cache; mutable std::mutex position_cache_mutex; mutable bool position_cache_dirty = true; + /** + * The evaluated positions result, using a separate span in case all curves are poly curves, + * in which case a separate array of evaluated positions is unnecessary. + */ + mutable Span<float3> evaluated_positions_span; /** * Cache of lengths along each evaluated curve for for each evaluated point. If a curve is @@ -178,6 +183,13 @@ class CurvesGeometry : public ::CurvesGeometry { MutableSpan<int> resolution_for_write(); /** + * The angle used to rotate evaluated normals around the tangents after their calculation. + * Call #tag_normals_changed after changes. + */ + VArray<float> tilt() const; + MutableSpan<float> tilt_for_write(); + + /** * Which method to use for calculating the normals of evaluated points (#NormalMode). * Call #tag_normals_changed after changes. */ @@ -316,6 +328,10 @@ class CurvesGeometry : public ::CurvesGeometry { * calculated. That can be ensured with #ensure_evaluated_offsets. */ void interpolate_to_evaluated(int curve_index, GSpan src, GMutableSpan dst) const; + /** + * Evaluate generic data for curve control points to the standard evaluated points of the curves. + */ + void interpolate_to_evaluated(GSpan src, GMutableSpan dst) const; private: /** @@ -377,6 +393,7 @@ namespace curves { */ inline int curve_segment_size(const int points_num, const bool cyclic) { + BLI_assert(points_num > 0); return cyclic ? points_num : points_num - 1; } @@ -433,6 +450,13 @@ bool segment_is_vector(Span<int8_t> handle_types_left, bool last_cylic_segment_is_vector(Span<int8_t> handle_types_left, Span<int8_t> handle_types_right); /** + * Return true if the handle types at the index are free (#BEZIER_HANDLE_FREE) or vector + * (#BEZIER_HANDLE_VECTOR). In these cases, directional continuitity from the previous and next + * evaluated segments is assumed not to be desired. + */ +bool point_is_sharp(Span<int8_t> handle_types_left, Span<int8_t> handle_types_right, int index); + +/** * Calculate offsets into the curve's evaluated points for each control point. While most control * point edges generate the number of edges specified by the resolution, vector segments only * generate one edge. @@ -681,8 +705,7 @@ inline IndexRange CurvesGeometry::lengths_range_for_curve(const int curve_index, BLI_assert(cyclic == this->cyclic()[curve_index]); const IndexRange points = this->evaluated_points_for_curve(curve_index); const int start = points.start() + curve_index; - const int size = curves::curve_segment_size(points.size(), cyclic); - return {start, size}; + return {start, points.is_empty() ? 0 : curves::curve_segment_size(points.size(), cyclic)}; } inline Span<float> CurvesGeometry::evaluated_lengths_for_curve(const int curve_index, @@ -703,4 +726,22 @@ inline float CurvesGeometry::evaluated_length_total_for_curve(const int curve_in /** \} */ +/* -------------------------------------------------------------------- */ +/** \name Bezier Inline Methods + * \{ */ + +namespace curves::bezier { + +inline bool point_is_sharp(const Span<int8_t> handle_types_left, + const Span<int8_t> handle_types_right, + const int index) +{ + return ELEM(handle_types_left[index], BEZIER_HANDLE_VECTOR, BEZIER_HANDLE_FREE) || + ELEM(handle_types_right[index], BEZIER_HANDLE_VECTOR, BEZIER_HANDLE_FREE); +} + +} // namespace curves::bezier + +/** \} */ + } // namespace blender::bke diff --git a/source/blender/blenkernel/BKE_global.h b/source/blender/blenkernel/BKE_global.h index d82e7460071..2070584a8a0 100644 --- a/source/blender/blenkernel/BKE_global.h +++ b/source/blender/blenkernel/BKE_global.h @@ -195,12 +195,13 @@ enum { G_DEBUG_XR = (1 << 19), /* XR/OpenXR messages */ G_DEBUG_XR_TIME = (1 << 20), /* XR/OpenXR timing messages */ - G_DEBUG_GHOST = (1 << 21), /* Debug GHOST module. */ + G_DEBUG_GHOST = (1 << 21), /* Debug GHOST module. */ + G_DEBUG_WINTAB = (1 << 22), /* Debug Wintab. */ }; #define G_DEBUG_ALL \ (G_DEBUG | G_DEBUG_FFMPEG | G_DEBUG_PYTHON | G_DEBUG_EVENTS | G_DEBUG_WM | G_DEBUG_JOBS | \ - G_DEBUG_FREESTYLE | G_DEBUG_DEPSGRAPH | G_DEBUG_IO | G_DEBUG_GHOST) + G_DEBUG_FREESTYLE | G_DEBUG_DEPSGRAPH | G_DEBUG_IO | G_DEBUG_GHOST | G_DEBUG_WINTAB) /** #Global.fileflags */ enum { diff --git a/source/blender/blenkernel/BKE_image.h b/source/blender/blenkernel/BKE_image.h index fba2512fa49..fff3b2a8f89 100644 --- a/source/blender/blenkernel/BKE_image.h +++ b/source/blender/blenkernel/BKE_image.h @@ -329,7 +329,7 @@ void BKE_image_merge(struct Main *bmain, struct Image *dest, struct Image *sourc bool BKE_image_scale(struct Image *image, int width, int height); /** - * Check if texture has alpha (planes == 32 || planes == 16). + * Check if texture has alpha `planes == 32 || planes == 16`. */ bool BKE_image_has_alpha(struct Image *image); @@ -350,13 +350,13 @@ void BKE_image_get_tile_label(struct Image *ima, * Checks whether the given filepath refers to a UDIM tiled texture. * If yes, the range from the lowest to the highest tile is returned. * - * `filepath` may be modified to ensure a UDIM token is present. - * `tiles` may be filled even if the result ultimately is false! + * \param filepath: may be modified to ensure a UDIM token is present. + * \param tiles: may be filled even if the result ultimately is false! */ bool BKE_image_get_tile_info(char *filepath, struct ListBase *tiles, - int *tile_start, - int *tile_range); + int *r_tile_start, + int *r_tile_range); struct ImageTile *BKE_image_add_tile(struct Image *ima, int tile_number, const char *label); bool BKE_image_remove_tile(struct Image *ima, struct ImageTile *tile); @@ -385,7 +385,7 @@ typedef enum { void BKE_image_ensure_tile_token(char *filename); /** - * When provided with an absolute virtual filepath, check to see if at least + * When provided with an absolute virtual `filepath`, check to see if at least * one concrete file exists. * Note: This function requires directory traversal and may be inefficient in time-critical, * or iterative, code paths. @@ -394,8 +394,8 @@ bool BKE_image_tile_filepath_exists(const char *filepath); /** * Retrieves the UDIM token format and returns the pattern from the provided `filepath`. - * The returned pattern is typically passed to either `BKE_image_get_tile_number_from_filepath` or - * `BKE_image_set_filepath_from_tile_number`. + * The returned pattern is typically passed to either #BKE_image_get_tile_number_from_filepath or + * #BKE_image_set_filepath_from_tile_number. */ char *BKE_image_get_tile_strformat(const char *filepath, eUDIM_TILE_FORMAT *r_tile_format); bool BKE_image_get_tile_number_from_filepath(const char *filepath, diff --git a/source/blender/blenkernel/BKE_layer.h b/source/blender/blenkernel/BKE_layer.h index cad6f6d6645..3e4f2fe154e 100644 --- a/source/blender/blenkernel/BKE_layer.h +++ b/source/blender/blenkernel/BKE_layer.h @@ -594,9 +594,9 @@ void BKE_view_layer_rename_lightgroup(ViewLayer *view_layer, ViewLayerLightgroup *lightgroup, const char *name); -void BKE_lightgroup_membership_get(struct LightgroupMembership *lgm, char *value); +void BKE_lightgroup_membership_get(struct LightgroupMembership *lgm, char *name); int BKE_lightgroup_membership_length(struct LightgroupMembership *lgm); -void BKE_lightgroup_membership_set(struct LightgroupMembership **lgm, const char *value); +void BKE_lightgroup_membership_set(struct LightgroupMembership **lgm, const char *name); #ifdef __cplusplus } diff --git a/source/blender/blenkernel/BKE_mesh_tangent.h b/source/blender/blenkernel/BKE_mesh_tangent.h index 5d9f850dd24..27061a5766e 100644 --- a/source/blender/blenkernel/BKE_mesh_tangent.h +++ b/source/blender/blenkernel/BKE_mesh_tangent.h @@ -28,7 +28,7 @@ void BKE_mesh_calc_loop_tangent_single_ex(const struct MVert *mverts, int numPolys, struct ReportList *reports); /** - * Wrapper around BKE_mesh_calc_loop_tangent_single_ex, which takes care of most boiling code. + * Wrapper around BKE_mesh_calc_loop_tangent_single_ex, which takes care of most boilerplate code. * \note * - There must be a valid loop's CD_NORMALS available. * - The mesh should be made of only tris and quads! diff --git a/source/blender/blenkernel/BKE_object.h b/source/blender/blenkernel/BKE_object.h index eaeb6e6a2e4..faf878dfc2a 100644 --- a/source/blender/blenkernel/BKE_object.h +++ b/source/blender/blenkernel/BKE_object.h @@ -270,6 +270,17 @@ void BKE_object_apply_mat4(struct Object *ob, const float mat[4][4], bool use_compat, bool use_parent); + +/** + * Use parent's world location and rotation as the child's origin. The parent inverse will + * become identity when the parent has no shearing. Otherwise, it is non-identity and contains the + * object's local matrix data that cannot be decomposed into location, rotation and scale. + * + * Assumes the object's world matrix has no shear. + * Assumes parent exists. + */ +void BKE_object_apply_parent_inverse(struct Object *ob); + void BKE_object_matrix_local_get(struct Object *ob, float r_mat[4][4]); bool BKE_object_pose_context_check(const struct Object *ob); diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h index 1b296277b8f..4ae37095411 100644 --- a/source/blender/blenkernel/BKE_paint.h +++ b/source/blender/blenkernel/BKE_paint.h @@ -30,7 +30,9 @@ struct EdgeSet; struct EnumPropertyItem; struct GHash; struct GridPaintMask; +struct Image; struct ImagePool; +struct ImageUser; struct ListBase; struct MLoop; struct MLoopTri; @@ -42,6 +44,7 @@ struct Object; struct PBVH; struct Paint; struct PaintCurve; +struct PaintModeSettings; struct Palette; struct PaletteColor; struct Scene; @@ -649,6 +652,11 @@ typedef struct SculptSession { */ bool sticky_shading_color; + /** + * Last used painting canvas key. + */ + char *last_paint_canvas_key; + } SculptSession; void BKE_sculptsession_free(struct Object *ob); @@ -725,6 +733,20 @@ enum { SCULPT_MASK_LAYER_CALC_LOOP = (1 << 1), }; +/* paint_canvas.cc */ +/** + * Create a key that can be used to compare with previous ones to identify changes. + * The resulting 'string' is owned by the caller. + */ +char *BKE_paint_canvas_key_get(struct PaintModeSettings *settings, struct Object *ob); + +bool BKE_paint_canvas_image_get(struct PaintModeSettings *settings, + struct Object *ob, + struct Image **r_image, + struct ImageUser **r_image_user); +int BKE_paint_canvas_uvmap_layer_index_get(const struct PaintModeSettings *settings, + struct Object *ob); + #ifdef __cplusplus } #endif diff --git a/source/blender/blenkernel/BKE_pbvh.h b/source/blender/blenkernel/BKE_pbvh.h index 775847f27f8..bb918fcfdcb 100644 --- a/source/blender/blenkernel/BKE_pbvh.h +++ b/source/blender/blenkernel/BKE_pbvh.h @@ -31,10 +31,13 @@ struct MLoopTri; struct MPoly; struct MVert; struct Mesh; +struct MeshElemMap; struct PBVH; struct PBVHNode; struct SubdivCCG; struct TaskParallelSettings; +struct Image; +struct ImageUser; struct MeshElemMap; typedef struct PBVH PBVH; @@ -48,6 +51,15 @@ typedef struct { float (*color)[4]; } PBVHColorBufferNode; +typedef struct PBVHPixelsNode { + /** + * Contains triangle/pixel data used during texture painting. + * + * Contains #blender::bke::pbvh::pixels::NodeData. + */ + void *node_data; +} PBVHPixelsNode; + typedef enum { PBVH_Leaf = 1 << 0, @@ -66,6 +78,8 @@ typedef enum { PBVH_UpdateTopology = 1 << 13, PBVH_UpdateColor = 1 << 14, + PBVH_RebuildPixels = 1 << 15, + } PBVHNodeFlags; typedef struct PBVHFrustumPlanes { @@ -127,6 +141,12 @@ void BKE_pbvh_build_bmesh(PBVH *pbvh, struct BMLog *log, int cd_vert_node_offset, int cd_face_node_offset); + +void BKE_pbvh_build_pixels(PBVH *pbvh, + const struct MLoop *mloop, + struct CustomData *ldata, + struct Image *image, + struct ImageUser *image_user); void BKE_pbvh_free(PBVH *pbvh); /* Hierarchical Search in the BVH, two methods: @@ -287,6 +307,7 @@ bool BKE_pbvh_node_fully_masked_get(PBVHNode *node); void BKE_pbvh_node_fully_unmasked_set(PBVHNode *node, int fully_masked); bool BKE_pbvh_node_fully_unmasked_get(PBVHNode *node); +void BKE_pbvh_mark_rebuild_pixels(PBVH *pbvh); void BKE_pbvh_vert_mark_update(PBVH *pbvh, int index); void BKE_pbvh_node_get_grids(PBVH *pbvh, diff --git a/source/blender/blenkernel/BKE_pbvh_pixels.hh b/source/blender/blenkernel/BKE_pbvh_pixels.hh new file mode 100644 index 00000000000..35eb340d0a1 --- /dev/null +++ b/source/blender/blenkernel/BKE_pbvh_pixels.hh @@ -0,0 +1,184 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2022 Blender Foundation. All rights reserved. */ + +#pragma once + +#include "BLI_math.h" +#include "BLI_math_vec_types.hh" +#include "BLI_rect.h" +#include "BLI_vector.hh" + +#include "DNA_image_types.h" +#include "DNA_meshdata_types.h" + +#include "BKE_image.h" +#include "BKE_image_wrappers.hh" + +#include "IMB_imbuf_types.h" + +namespace blender::bke::pbvh::pixels { + +struct TrianglePaintInput { + int3 vert_indices; + /** + * Delta barycentric coordinates between 2 neighbouring UV's in the U direction. + * + * Only the first two coordinates are stored. The third should be recalculated + */ + float2 delta_barycentric_coord_u; + + /** + * Initially only the vert indices are known. + * + * delta_barycentric_coord_u is initialized in a later stage as it requires image tile + * dimensions. + */ + TrianglePaintInput(const int3 vert_indices) + : vert_indices(vert_indices), delta_barycentric_coord_u(0.0f, 0.0f) + { + } +}; + +/** + * Data shared between pixels that belong to the same triangle. + * + * Data is stored as a list of structs, grouped by usage to improve performance (improves CPU + * cache prefetching). + */ +struct Triangles { + /** Data accessed by the inner loop of the painting brush. */ + Vector<TrianglePaintInput> paint_input; + + public: + void append(const int3 vert_indices) + { + this->paint_input.append(TrianglePaintInput(vert_indices)); + } + + TrianglePaintInput &get_paint_input(const int index) + { + return paint_input[index]; + } + + const TrianglePaintInput &get_paint_input(const int index) const + { + return paint_input[index]; + } + + void clear() + { + paint_input.clear(); + } + + uint64_t size() const + { + return paint_input.size(); + } + + uint64_t mem_size() const + { + return paint_input.size() * sizeof(TrianglePaintInput); + } +}; + +/** + * Encode sequential pixels to reduce memory footprint. + */ +struct PackedPixelRow { + /** Barycentric coordinate of the first pixel. */ + float2 start_barycentric_coord; + /** Image coordinate starting of the first pixel. */ + ushort2 start_image_coordinate; + /** Number of sequential pixels encoded in this package. */ + ushort num_pixels; + /** Reference to the pbvh triangle index. */ + ushort triangle_index; +}; + +/** + * Node pixel data containing the pixels for a single UDIM tile. + */ +struct UDIMTilePixels { + /** UDIM Tile number. */ + short tile_number; + + struct { + bool dirty : 1; + } flags; + + /* Dirty region of the tile in image space. */ + rcti dirty_region; + + Vector<PackedPixelRow> pixel_rows; + + UDIMTilePixels() + { + flags.dirty = false; + BLI_rcti_init_minmax(&dirty_region); + } + + void mark_dirty(const PackedPixelRow &pixel_row) + { + int2 start_image_coord(pixel_row.start_image_coordinate.x, pixel_row.start_image_coordinate.y); + BLI_rcti_do_minmax_v(&dirty_region, start_image_coord); + BLI_rcti_do_minmax_v(&dirty_region, start_image_coord + int2(pixel_row.num_pixels + 1, 0)); + flags.dirty = true; + } + + void clear_dirty() + { + BLI_rcti_init_minmax(&dirty_region); + flags.dirty = false; + } +}; + +struct NodeData { + struct { + bool dirty : 1; + } flags; + + Vector<UDIMTilePixels> tiles; + Triangles triangles; + + NodeData() + { + flags.dirty = false; + } + + UDIMTilePixels *find_tile_data(const image::ImageTileWrapper &image_tile) + { + for (UDIMTilePixels &tile : tiles) { + if (tile.tile_number == image_tile.get_tile_number()) { + return &tile; + } + } + return nullptr; + } + + void mark_region(Image &image, const image::ImageTileWrapper &image_tile, ImBuf &image_buffer) + { + UDIMTilePixels *tile = find_tile_data(image_tile); + if (tile && tile->flags.dirty) { + BKE_image_partial_update_mark_region( + &image, image_tile.image_tile, &image_buffer, &tile->dirty_region); + tile->clear_dirty(); + } + } + + void clear_data() + { + tiles.clear(); + triangles.clear(); + } + + static void free_func(void *instance) + { + NodeData *node_data = static_cast<NodeData *>(instance); + MEM_delete(node_data); + } +}; + +NodeData &BKE_pbvh_pixels_node_data_get(PBVHNode &node); +void BKE_pbvh_pixels_mark_image_dirty(PBVHNode &node, Image &image, ImageUser &image_user); + +} // namespace blender::bke::pbvh::pixels diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index aca8cdf916e..710e2900d78 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -126,7 +126,7 @@ set(SRC intern/editlattice.c intern/editmesh.c intern/editmesh_bvh.c - intern/editmesh_cache.c + intern/editmesh_cache.cc intern/editmesh_tangent.c intern/effect.c intern/fcurve.c @@ -203,12 +203,12 @@ set(SRC intern/mesh_normals.cc intern/mesh_remap.c intern/mesh_remesh_voxel.cc - intern/mesh_runtime.c + intern/mesh_runtime.cc intern/mesh_sample.cc intern/mesh_tangent.c intern/mesh_tessellate.c intern/mesh_validate.cc - intern/mesh_wrapper.c + intern/mesh_wrapper.cc intern/modifier.c intern/movieclip.c intern/multires.c @@ -235,6 +235,7 @@ set(SRC intern/outliner_treehash.c intern/packedFile.c intern/paint.c + intern/paint_canvas.cc intern/paint_toolslots.c intern/particle.c intern/particle_child.c @@ -243,6 +244,7 @@ set(SRC intern/pbvh.cc intern/pbvh.c intern/pbvh_bmesh.c + intern/pbvh_pixels.cc intern/pointcache.c intern/pointcloud.cc intern/preferences.c diff --git a/source/blender/blenkernel/intern/anim_path.c b/source/blender/blenkernel/intern/anim_path.c index 1f8c6df6147..a8c25069c19 100644 --- a/source/blender/blenkernel/intern/anim_path.c +++ b/source/blender/blenkernel/intern/anim_path.c @@ -295,10 +295,12 @@ bool BKE_where_on_path(const Object *ob, key_curve_tangent_weights(frac, w, KEY_BSPLINE); - interp_v3_v3v3v3v3(r_dir, p0->vec, p1->vec, p2->vec, p3->vec, w); + if (r_dir) { + interp_v3_v3v3v3v3(r_dir, p0->vec, p1->vec, p2->vec, p3->vec, w); - /* Make compatible with #vec_to_quat. */ - negate_v3(r_dir); + /* Make compatible with #vec_to_quat. */ + negate_v3(r_dir); + } //} const ListBase *nurbs = BKE_curve_editNurbs_get(cu); diff --git a/source/blender/blenkernel/intern/anim_sys.c b/source/blender/blenkernel/intern/anim_sys.c index 8a5bf2b81dd..54fee079947 100644 --- a/source/blender/blenkernel/intern/anim_sys.c +++ b/source/blender/blenkernel/intern/anim_sys.c @@ -1531,6 +1531,192 @@ static NlaEvalChannel *nlaevalchan_verify(PointerRNA *ptr, NlaEvalData *nlaeval, /* ---------------------- */ +/** \returns true if a solution exists and the output was written to. */ +static bool nla_blend_get_inverted_lower_value(const int blendmode, + const float strip_value, + const float blended_value, + const float influence, + float *r_lower_value) +{ + if (IS_EQF(influence, 0.0f)) { + *r_lower_value = blended_value; + return true; + } + + switch (blendmode) { + case NLASTRIP_MODE_ADD: + /* Simply subtract the scaled value on to the stack. */ + *r_lower_value = blended_value - (strip_value * influence); + return true; + + case NLASTRIP_MODE_SUBTRACT: + /* Simply add the scaled value from the stack. */ + *r_lower_value = blended_value + (strip_value * influence); + return true; + + case NLASTRIP_MODE_MULTIPLY: { + /* Check for division by zero. */ + const float denominator = (influence * strip_value + (1.0f - influence)); + if (IS_EQF(denominator, 0.0f)) { + /* For 0/0, any r_lower_value is a solution. We'll just choose 1. + * + * Any r_lower_value is a solution. In this case, ideally we would insert redundant + * keyframes, those that don't change the fcurve's shape. Otherwise, we're likely messing + * up interpolation for the animator, requiring further cleanup on their part. + */ + if (IS_EQF(blended_value, 0.0f)) { + /* When denominator==0: + * + * denominator = (inf * strip_value + (1.0f - inf)) + * 0 = inf * strip_value + (1-inf) + * -inf * strip_value = 1 - inf + * -strip_value = (1 - inf) / inf + * strip_value = (inf - 1) / inf + * strip_value = 1 - (1/inf) + * + * For blending, nla_blend_value(), this results in: + * + * blended_value = inf * (lower_value * strip_value) + (1 - inf) * lower_value; + * = inf * (lower_value * (1 - (1/inf))) + ... + * = inf * (1 - (1/inf)) * lower_value + ... + * = (inf - (inf/inf)) * lower_value + ... + * = -(inf - 1) * lower_value + (1 - inf) * lower_value; + * blended_value = 0 + * + * Effectively, blended_value will equal 0 no matter what lower_value is. Put another + * way, when (blended_value==0 and denominator==0), then lower_value can be any value and + * blending will give us back blended_value=0. We have infinite solutions for this case. + */ + *r_lower_value = 1; + return true; + } + /* No solution for division by zero. */ + return false; + } + /* Math: + * blended_value = inf * (lower_value * strip_value) + (1 - inf) * lower_value + * = lower_value * (inf * strip_value + (1-inf)) + * lower_value = blended_value / (inf * strip_value + (1-inf)) + * lower_value = blended_value / denominator + */ + *r_lower_value = blended_value / denominator; + return true; + } + case NLASTRIP_MODE_COMBINE: + BLI_assert_msg(0, "Use nla_combine_get_inverted_lower_value()"); + return false; + + case NLASTRIP_MODE_REPLACE: + + /* No solution if lower strip has 0 influence. */ + if (IS_EQF(influence, 1.0f)) { + return false; + } + + /* Math: + * + * blended_value = lower_value * (1.0f - inf) + (strip_value * inf) + * blended_value - (strip_value * inf) = lower_value * (1.0f - inf) + * blended_value - (strip_value * inf) / (1.0f - inf) = lower_value + * + * lower_value = blended_value - (strip_value * inf) / (1.0f - inf) + */ + *r_lower_value = (blended_value - (strip_value * influence)) / (1.0f - influence); + return true; + } + + BLI_assert_msg(0, "invalid blend mode"); + return false; +} + +/** \returns true if solution exists and output written to. */ +static bool nla_combine_get_inverted_lower_value(const int mix_mode, + float base_value, + const float strip_value, + const float blended_value, + const float influence, + float *r_lower_value) +{ + if (IS_EQF(influence, 0.0f)) { + *r_lower_value = blended_value; + return true; + } + + /* Perform blending. */ + switch (mix_mode) { + case NEC_MIX_ADD: + case NEC_MIX_AXIS_ANGLE: + *r_lower_value = blended_value - (strip_value - base_value) * influence; + return true; + case NEC_MIX_MULTIPLY: + /* Division by zero. */ + if (IS_EQF(strip_value, 0.0f)) { + /* Resolve 0/0 to 1. + * + * Any r_lower_value is a solution. In this case, ideally we would insert redundant + * keyframes, those that don't change the fcurve's shape. Otherwise, we're likely messing + * up interpolation for the animator, requiring further cleanup on their part. + */ + if (IS_EQF(blended_value, 0.0f)) { + /* For blending, nla_combine_value(), when strip_value==0: + * + * blended_value = lower_value * powf(strip_value / base_value, infl); + * blended_value = lower_value * powf(0, infl); + * blended_value = lower_value * 0; + * blended_value = 0; + * + * Effectively, blended_value will equal 0 no matter what lower_value is. Put another + * way, when (blended_value==0 and strip_value==0), then lower_value can be any value and + * blending will give us back blended_value=0. We have infinite solutions for this case. + */ + *r_lower_value = 1.0f; + return true; + } + /* No solution. */ + return false; + } + + if (IS_EQF(base_value, 0.0f)) { + base_value = 1.0f; + } + + *r_lower_value = blended_value / powf(strip_value / base_value, influence); + return true; + + case NEC_MIX_QUATERNION: + BLI_assert_msg(0, "Use nla_combine_quaternion_get_inverted_lower_values()"); + return false; + } + + BLI_assert_msg(0, "Mixmode not implemented"); + return false; +} + +static void nla_combine_quaternion_get_inverted_lower_values(const float strip_values[4], + const float blended_values[4], + const float influence, + float r_lower_value[4]) +{ + if (IS_EQF(influence, 0.0f)) { + normalize_qt_qt(r_lower_value, blended_values); + return; + } + + /* blended_value = lower_values @ strip_values^infl + * blended_value @ inv(strip_values^inf) = lower_values + * + * Returns: lower_values = blended_value @ inv(strip_values^inf) */ + float tmp_strip_values[4], tmp_blended[4]; + + normalize_qt_qt(tmp_strip_values, strip_values); + normalize_qt_qt(tmp_blended, blended_values); + + pow_qt_fl_normalized(tmp_strip_values, influence); + invert_qt_normalized(tmp_strip_values); + + mul_qt_qtqt(r_lower_value, tmp_blended, tmp_strip_values); +} + /* Blend the lower nla stack value and upper strip value of a channel according to mode and * influence. */ static float nla_blend_value(const int blendmode, @@ -1772,6 +1958,25 @@ static void nlaevalchan_assert_blendOrcombine_compatible(NlaEvalChannelSnapshot BLI_assert(lower_necs->length == blended_necs->length); } +/** Check each remap domain of blended values individually in case animator had a non-combine NLA + * strip with a subset of quaternion channels and remapping through any of them failed and thus + * potentially has undefined values. + * + * \returns true if case occured and handled. Returns false if case didn't occur. + */ +static bool nlaevalchan_combine_quaternion_handle_undefined_blend_values( + NlaEvalChannelSnapshot *blended_necs, NlaEvalChannelSnapshot *upper_or_lower_necs) +{ + for (int j = 0; j < 4; j++) { + if (!BLI_BITMAP_TEST_BOOL(blended_necs->remap_domain.ptr, j)) { + BLI_bitmap_set_all(upper_or_lower_necs->remap_domain.ptr, false, 4); + return true; + } + } + + return false; +} + /* Assert that the channels given can be blended or combined together as a quaternion. */ static void nlaevalchan_assert_blendOrcombine_compatible_quaternion( NlaEvalChannelSnapshot *lower_necs, @@ -1787,14 +1992,12 @@ static void nlaevalchan_copy_values(NlaEvalChannelSnapshot *dst, NlaEvalChannelS memcpy(dst->values, src->values, src->length * sizeof(float)); } -/** - * Copies lower necs to blended necs if upper necs is NULL or has zero influence. - * \return true if copied. - */ -static bool nlaevalchan_blendOrcombine_try_copy_lower(NlaEvalChannelSnapshot *lower_necs, - NlaEvalChannelSnapshot *upper_necs, - const float upper_influence, - NlaEvalChannelSnapshot *r_blended_necs) +/** Copies from lower necs to blended necs if upper necs is NULL or has zero influence. + * \return true if copied. */ +static bool nlaevalchan_blendOrcombine_try_copy_from_lower(NlaEvalChannelSnapshot *lower_necs, + NlaEvalChannelSnapshot *upper_necs, + const float upper_influence, + NlaEvalChannelSnapshot *r_blended_necs) { const bool has_influence = !IS_EQF(upper_influence, 0.0f); if (upper_necs != NULL && has_influence) { @@ -1805,10 +2008,35 @@ static bool nlaevalchan_blendOrcombine_try_copy_lower(NlaEvalChannelSnapshot *lo return true; } -/** - * Based on blend-mode, blend lower necs with upper necs into blended necs. +/** Copies to lower necs from blended necs if upper necs is NULL or has zero influence. If + * successful, copies blended_necs remap domains to lower_necs. * - * Each upper value's blend domain determines whether to blend or to copy directly from lower. + * Does not check upper value blend domains. + * + * \return true if copied. */ +static bool nlaevalchan_blendOrcombine_try_copy_to_lower(NlaEvalChannelSnapshot *blended_necs, + NlaEvalChannelSnapshot *upper_necs, + const float upper_influence, + NlaEvalChannelSnapshot *r_lower_necs) +{ + const bool has_influence = !IS_EQF(upper_influence, 0.0f); + if (upper_necs != NULL && has_influence) { + return false; + } + + nlaevalchan_copy_values(r_lower_necs, blended_necs); + + /* Must copy remap domain to handle case where some blended values are out of domain. */ + BLI_bitmap_copy_all( + r_lower_necs->remap_domain.ptr, blended_necs->remap_domain.ptr, r_lower_necs->length); + + return true; +} + +/** Based on blendmode, blend lower necs with upper necs into blended necs. + * + * Each upper value's blend domain determines whether to blend or to copy directly + * from lower. */ static void nlaevalchan_blend_value(NlaEvalChannelSnapshot *lower_necs, NlaEvalChannelSnapshot *upper_necs, @@ -1817,7 +2045,7 @@ static void nlaevalchan_blend_value(NlaEvalChannelSnapshot *lower_necs, NlaEvalChannelSnapshot *r_blended_necs) { nlaevalchan_assert_blendOrcombine_compatible(lower_necs, upper_necs, r_blended_necs); - if (nlaevalchan_blendOrcombine_try_copy_lower( + if (nlaevalchan_blendOrcombine_try_copy_from_lower( lower_necs, upper_necs, upper_influence, r_blended_necs)) { return; } @@ -1846,7 +2074,7 @@ static void nlaevalchan_combine_value(NlaEvalChannelSnapshot *lower_necs, NlaEvalChannelSnapshot *r_blended_necs) { nlaevalchan_assert_blendOrcombine_compatible(lower_necs, upper_necs, r_blended_necs); - if (nlaevalchan_blendOrcombine_try_copy_lower( + if (nlaevalchan_blendOrcombine_try_copy_from_lower( lower_necs, upper_necs, upper_influence, r_blended_necs)) { return; } @@ -1879,7 +2107,7 @@ static void nlaevalchan_combine_quaternion(NlaEvalChannelSnapshot *lower_necs, NlaEvalChannelSnapshot *r_blended_necs) { nlaevalchan_assert_blendOrcombine_compatible_quaternion(lower_necs, upper_necs, r_blended_necs); - if (nlaevalchan_blendOrcombine_try_copy_lower( + if (nlaevalchan_blendOrcombine_try_copy_from_lower( lower_necs, upper_necs, upper_influence, r_blended_necs)) { return; } @@ -2033,14 +2261,8 @@ static void nlaevalchan_combine_quaternion_get_inverted_upper_evalchan( nlaevalchan_assert_nonNull(r_upper_necs); nlaevalchan_assert_blendOrcombine_compatible_quaternion(lower_necs, r_upper_necs, blended_necs); - /* Must check each domain index individually in case animator had a non-combine NLA strip with a - * subset of quaternion channels and remapping through any of them failed and thus potentially - * has undefined values. */ - for (int j = 0; j < 4; j++) { - if (!BLI_BITMAP_TEST_BOOL(blended_necs->remap_domain.ptr, j)) { - BLI_bitmap_set_all(r_upper_necs->remap_domain.ptr, false, 4); - return; - } + if (nlaevalchan_combine_quaternion_handle_undefined_blend_values(blended_necs, r_upper_necs)) { + return; } const bool success = nla_combine_quaternion_get_inverted_strip_values( @@ -2109,6 +2331,173 @@ static void nlaevalchan_blendOrcombine_get_inverted_upper_evalchan( } } +static void nlaevalchan_blend_value_get_inverted_lower_evalchan( + NlaEvalChannelSnapshot *blended_necs, + NlaEvalChannelSnapshot *upper_necs, + const int upper_blendmode, + const float upper_influence, + NlaEvalChannelSnapshot *r_lower_necs) +{ + nlaevalchan_assert_blendOrcombine_compatible(r_lower_necs, upper_necs, blended_necs); + + if (nlaevalchan_blendOrcombine_try_copy_to_lower( + blended_necs, upper_necs, upper_influence, r_lower_necs)) { + return; + } + + const int length = r_lower_necs->length; + + for (int j = 0; j < length; j++) { + if (!BLI_BITMAP_TEST_BOOL(blended_necs->remap_domain.ptr, j)) { + BLI_BITMAP_DISABLE(r_lower_necs->remap_domain.ptr, j); + continue; + } + + /* If upper value was not blended, then the blended value was directly copied from the lower + * value. */ + if (!BLI_BITMAP_TEST_BOOL(upper_necs->blend_domain.ptr, j)) { + r_lower_necs->values[j] = blended_necs->values[j]; + BLI_BITMAP_ENABLE(r_lower_necs->remap_domain.ptr, j); + continue; + } + + const bool success = nla_blend_get_inverted_lower_value(upper_blendmode, + upper_necs->values[j], + blended_necs->values[j], + upper_influence, + &r_lower_necs->values[j]); + + BLI_BITMAP_SET(r_lower_necs->remap_domain.ptr, j, success); + } +} + +static void nlaevalchan_combine_value_get_inverted_lower_evalchan( + NlaEvalChannelSnapshot *blended_necs, + NlaEvalChannelSnapshot *upper_necs, + const float upper_influence, + NlaEvalChannelSnapshot *r_lower_necs) +{ + nlaevalchan_assert_blendOrcombine_compatible(r_lower_necs, upper_necs, blended_necs); + + if (nlaevalchan_blendOrcombine_try_copy_to_lower( + blended_necs, upper_necs, upper_influence, r_lower_necs)) { + return; + } + + float *base_values = r_lower_necs->channel->base_snapshot.values; + const int mix_mode = r_lower_necs->channel->mix_mode; + const int length = r_lower_necs->length; + + for (int j = 0; j < length; j++) { + if (!BLI_BITMAP_TEST_BOOL(blended_necs->remap_domain.ptr, j)) { + BLI_BITMAP_DISABLE(r_lower_necs->remap_domain.ptr, j); + continue; + } + + /* If upper value was not blended, then the blended value was directly copied from the lower + * value. */ + if (!BLI_BITMAP_TEST_BOOL(upper_necs->blend_domain.ptr, j)) { + r_lower_necs->values[j] = blended_necs->values[j]; + BLI_BITMAP_ENABLE(r_lower_necs->remap_domain.ptr, j); + continue; + } + + const bool success = nla_combine_get_inverted_lower_value(mix_mode, + base_values[j], + upper_necs->values[j], + blended_necs->values[j], + upper_influence, + &r_lower_necs->values[j]); + + BLI_BITMAP_SET(r_lower_necs->remap_domain.ptr, j, success); + } +} + +static void nlaevalchan_combine_quaternion_get_inverted_lower_evalchan( + NlaEvalChannelSnapshot *blended_necs, + NlaEvalChannelSnapshot *upper_necs, + const float upper_influence, + NlaEvalChannelSnapshot *r_lower_necs) +{ + nlaevalchan_assert_blendOrcombine_compatible_quaternion(r_lower_necs, upper_necs, blended_necs); + + if (nlaevalchan_combine_quaternion_handle_undefined_blend_values(blended_necs, r_lower_necs)) { + return; + } + + if (nlaevalchan_blendOrcombine_try_copy_to_lower( + blended_necs, upper_necs, upper_influence, r_lower_necs)) { + return; + } + + /* If upper value was not blended, then the blended value was directly copied from the lower + * value. */ + if (!BLI_BITMAP_TEST_BOOL(upper_necs->blend_domain.ptr, 0)) { + memcpy(r_lower_necs->values, blended_necs->values, 4 * sizeof(float)); + BLI_bitmap_set_all(r_lower_necs->remap_domain.ptr, true, 4); + return; + } + + nla_combine_quaternion_get_inverted_lower_values( + upper_necs->values, blended_necs->values, upper_influence, r_lower_necs->values); + + BLI_bitmap_set_all(r_lower_necs->remap_domain.ptr, true, 4); +} + +/** Based on blendmode and mix mode, solve for the lower values such that when lower blended or + * combined with upper then we get blended values as a result. + * + * Only processes blended values in the remap domain. Successfully remapped lower values are placed + * in the remap domain so caller knows which values are usable. + * + * \param blended_necs: Never NULL. + * \param upper_necs: Can be NULL. + * \param upper_blendmode: Enum value in eNlaStrip_Blend_Mode. + * \param upper_influence: Value in range [0, 1]. + * \param r_lower_necs: Never NULL. + */ +static void nlaevalchan_blendOrCombine_get_inverted_lower_evalchan( + NlaEvalChannelSnapshot *blended_necs, + NlaEvalChannelSnapshot *upper_necs, + const int upper_blendmode, + const float upper_influence, + NlaEvalChannelSnapshot *r_lower_necs) + +{ + nlaevalchan_assert_nonNull(r_lower_necs); + + switch (upper_blendmode) { + case NLASTRIP_MODE_COMBINE: { + switch (r_lower_necs->channel->mix_mode) { + case NEC_MIX_QUATERNION: { + nlaevalchan_combine_quaternion_get_inverted_lower_evalchan( + blended_necs, upper_necs, upper_influence, r_lower_necs); + return; + } + case NEC_MIX_ADD: + case NEC_MIX_AXIS_ANGLE: + case NEC_MIX_MULTIPLY: { + nlaevalchan_combine_value_get_inverted_lower_evalchan( + blended_necs, upper_necs, upper_influence, r_lower_necs); + return; + } + } + BLI_assert_msg(0, "Mix mode should've been handled"); + return; + } + case NLASTRIP_MODE_ADD: + case NLASTRIP_MODE_SUBTRACT: + case NLASTRIP_MODE_MULTIPLY: + case NLASTRIP_MODE_REPLACE: { + nlaevalchan_blend_value_get_inverted_lower_evalchan( + blended_necs, upper_necs, upper_blendmode, upper_influence, r_lower_necs); + return; + } + } + + BLI_assert_msg(0, "Blend mode should've been handled"); +} + /* ---------------------- */ /* F-Modifier stack joining/separation utilities - * should we generalize these for BLI_listbase.h interface? */ @@ -2223,7 +2612,8 @@ static void nlasnapshot_from_action(PointerRNA *ptr, } /* evaluate action-clip strip */ -static void nlastrip_evaluate_actionclip(PointerRNA *ptr, +static void nlastrip_evaluate_actionclip(const int evaluation_mode, + PointerRNA *ptr, NlaEvalData *channels, ListBase *modifiers, NlaEvalStrip *nes, @@ -2247,22 +2637,49 @@ static void nlastrip_evaluate_actionclip(PointerRNA *ptr, /* join this strip's modifiers to the parent's modifiers (own modifiers first) */ nlaeval_fmodifiers_join_stacks(&tmp_modifiers, &strip->modifiers, modifiers); - NlaEvalSnapshot strip_snapshot; - nlaeval_snapshot_init(&strip_snapshot, channels, NULL); + switch (evaluation_mode) { + case STRIP_EVAL_BLEND: { + + NlaEvalSnapshot strip_snapshot; + nlaeval_snapshot_init(&strip_snapshot, channels, NULL); + + nlasnapshot_from_action( + ptr, channels, &tmp_modifiers, strip->act, strip->strip_time, &strip_snapshot); + nlasnapshot_blend( + channels, snapshot, &strip_snapshot, strip->blendmode, strip->influence, snapshot); - nlasnapshot_from_action( - ptr, channels, &tmp_modifiers, strip->act, strip->strip_time, &strip_snapshot); - nlasnapshot_blend( - channels, snapshot, &strip_snapshot, strip->blendmode, strip->influence, snapshot); + nlaeval_snapshot_free_data(&strip_snapshot); + + break; + } + case STRIP_EVAL_BLEND_GET_INVERTED_LOWER_SNAPSHOT: { - nlaeval_snapshot_free_data(&strip_snapshot); + NlaEvalSnapshot strip_snapshot; + nlaeval_snapshot_init(&strip_snapshot, channels, NULL); + + nlasnapshot_from_action( + ptr, channels, &tmp_modifiers, strip->act, strip->strip_time, &strip_snapshot); + nlasnapshot_blend_get_inverted_lower_snapshot( + channels, snapshot, &strip_snapshot, strip->blendmode, strip->influence, snapshot); + + nlaeval_snapshot_free_data(&strip_snapshot); + + break; + } + case STRIP_EVAL_NOBLEND: { + nlasnapshot_from_action( + ptr, channels, &tmp_modifiers, strip->act, strip->strip_time, snapshot); + break; + } + } /* unlink this strip's modifiers from the parent's modifiers again */ nlaeval_fmodifiers_split_stacks(&strip->modifiers, modifiers); } /* evaluate transition strip */ -static void nlastrip_evaluate_transition(PointerRNA *ptr, +static void nlastrip_evaluate_transition(const int evaluation_mode, + PointerRNA *ptr, NlaEvalData *channels, ListBase *modifiers, NlaEvalStrip *nes, @@ -2294,49 +2711,126 @@ static void nlastrip_evaluate_transition(PointerRNA *ptr, s2 = nes->strip->next; } - /* prepare template for 'evaluation strip' - * - based on the transition strip's evaluation strip data - * - strip_mode is NES_TIME_TRANSITION_* based on which endpoint - * - strip_time is the 'normalized' (i.e. in-strip) time for evaluation, - * which doubles up as an additional weighting factor for the strip influences - * which allows us to appear to be 'interpolating' between the two extremes - */ - tmp_nes = *nes; - - /* evaluate these strips into a temp-buffer (tmp_channels) */ - /* FIXME: modifier evaluation here needs some work... */ - /* first strip */ - tmp_nes.strip_mode = NES_TIME_TRANSITION_START; - tmp_nes.strip = s1; - tmp_nes.strip_time = s1->strip_time; - nlaeval_snapshot_init(&snapshot1, channels, snapshot); - nlastrip_evaluate( - ptr, channels, &tmp_modifiers, &tmp_nes, &snapshot1, anim_eval_context, flush_to_original); + switch (evaluation_mode) { + case STRIP_EVAL_BLEND: { - /* second strip */ - tmp_nes.strip_mode = NES_TIME_TRANSITION_END; - tmp_nes.strip = s2; - tmp_nes.strip_time = s2->strip_time; - nlaeval_snapshot_init(&snapshot2, channels, snapshot); - nlastrip_evaluate( - ptr, channels, &tmp_modifiers, &tmp_nes, &snapshot2, anim_eval_context, flush_to_original); + /* prepare template for 'evaluation strip' + * - based on the transition strip's evaluation strip data + * - strip_mode is NES_TIME_TRANSITION_* based on which endpoint + * - strip_time is the 'normalized' (i.e. in-strip) time for evaluation, + * which doubles up as an additional weighting factor for the strip influences + * which allows us to appear to be 'interpolating' between the two extremes + */ + tmp_nes = *nes; + + /* evaluate these strips into a temp-buffer (tmp_channels) */ + /* FIXME: modifier evaluation here needs some work... */ + /* first strip */ + tmp_nes.strip_mode = NES_TIME_TRANSITION_START; + tmp_nes.strip = s1; + tmp_nes.strip_time = s1->strip_time; + nlaeval_snapshot_init(&snapshot1, channels, snapshot); + nlasnapshot_blend_strip(ptr, + channels, + &tmp_modifiers, + &tmp_nes, + &snapshot1, + anim_eval_context, + flush_to_original); + + /* second strip */ + tmp_nes.strip_mode = NES_TIME_TRANSITION_END; + tmp_nes.strip = s2; + tmp_nes.strip_time = s2->strip_time; + nlaeval_snapshot_init(&snapshot2, channels, snapshot); + nlasnapshot_blend_strip(ptr, + channels, + &tmp_modifiers, + &tmp_nes, + &snapshot2, + anim_eval_context, + flush_to_original); + + /** Replace \a snapshot2 NULL channels with base or default values so all channels blend. */ + nlasnapshot_ensure_channels(channels, &snapshot2); + /** Mark all \a snapshot2 channel's values to blend. */ + nlasnapshot_enable_all_blend_domain(&snapshot2); + nlasnapshot_blend( + channels, &snapshot1, &snapshot2, NLASTRIP_MODE_REPLACE, nes->strip_time, snapshot); + + nlaeval_snapshot_free_data(&snapshot1); + nlaeval_snapshot_free_data(&snapshot2); + + break; + } + case STRIP_EVAL_BLEND_GET_INVERTED_LOWER_SNAPSHOT: { + /* No support for remapping values through a transition. Mark all channel values affected by + * transition as non-remappable. */ + tmp_nes = *nes; + + /* Process first strip. */ + tmp_nes.strip = s1; + tmp_nes.strip_time = s1->strip_time; + nlaeval_snapshot_init(&snapshot1, channels, snapshot); + nlasnapshot_blend_strip_no_blend( + ptr, channels, &tmp_modifiers, &tmp_nes, &snapshot1, anim_eval_context); + + /* Remove channel values affected by transition from the remap domain. */ + LISTBASE_FOREACH (NlaEvalChannel *, nec, &channels->channels) { + NlaEvalChannelSnapshot *necs = nlaeval_snapshot_get(&snapshot1, nec->index); + if (necs == NULL) { + continue; + } + NlaEvalChannelSnapshot *output_necs = nlaeval_snapshot_ensure_channel(snapshot, nec); + for (int i = 0; i < necs->length; i++) { + if (BLI_BITMAP_TEST_BOOL(necs->blend_domain.ptr, i)) { + BLI_BITMAP_DISABLE(output_necs->remap_domain.ptr, i); + } + } + } + + nlaeval_snapshot_free_data(&snapshot1); + + /* Process second strip. */ + tmp_nes.strip = s2; + tmp_nes.strip_time = s2->strip_time; + nlaeval_snapshot_init(&snapshot2, channels, snapshot); + nlasnapshot_blend_strip_no_blend( + ptr, channels, &tmp_modifiers, &tmp_nes, &snapshot2, anim_eval_context); + + /* Remove channel values affected by transition from the remap domain. */ + LISTBASE_FOREACH (NlaEvalChannel *, nec, &channels->channels) { + NlaEvalChannelSnapshot *necs = nlaeval_snapshot_get(&snapshot2, nec->index); + if (necs == NULL) { + continue; + } + NlaEvalChannelSnapshot *output_necs = nlaeval_snapshot_ensure_channel(snapshot, nec); + for (int i = 0; i < necs->length; i++) { + if (BLI_BITMAP_TEST_BOOL(necs->blend_domain.ptr, i)) { + BLI_BITMAP_DISABLE(output_necs->remap_domain.ptr, i); + } + } + } - /** Replace \a snapshot2 NULL channels with base or default values so all channels blend. */ - nlasnapshot_ensure_channels(channels, &snapshot2); - /** Mark all \a snapshot2 channel's values to blend. */ - nlasnapshot_enable_all_blend_domain(&snapshot2); - nlasnapshot_blend( - channels, &snapshot1, &snapshot2, NLASTRIP_MODE_REPLACE, nes->strip_time, snapshot); + nlaeval_snapshot_free_data(&snapshot2); - nlaeval_snapshot_free_data(&snapshot1); - nlaeval_snapshot_free_data(&snapshot2); + break; + } + case STRIP_EVAL_NOBLEND: { + BLI_assert( + !"This case shouldn't occur. Transitions assumed to not reference other " + "transitions. "); + break; + } + } /* unlink this strip's modifiers from the parent's modifiers again */ nlaeval_fmodifiers_split_stacks(&nes->strip->modifiers, modifiers); } /* evaluate meta-strip */ -static void nlastrip_evaluate_meta(PointerRNA *ptr, +static void nlastrip_evaluate_meta(const int evaluation_mode, + PointerRNA *ptr, NlaEvalData *channels, ListBase *modifiers, NlaEvalStrip *nes, @@ -2366,12 +2860,31 @@ static void nlastrip_evaluate_meta(PointerRNA *ptr, evaltime); tmp_nes = nlastrips_ctime_get_strip(NULL, &strip->strips, -1, &child_context, flush_to_original); + /* Assert currently supported modes. If new mode added, then assertion marks potentially missed + * area. + * + * Note: In the future if support is ever added to metastrips to support nested tracks, then + * STRIP_EVAL_BLEND and STRIP_EVAL_BLEND_GET_INVERTED_LOWER_SNAPSHOT cases are no longer + * equivalent. The output of nlastrips_ctime_get_strip() may return a list of strips. The only + * case difference should be the evaluation order. + */ + BLI_assert(ELEM(evaluation_mode, + STRIP_EVAL_BLEND, + STRIP_EVAL_BLEND_GET_INVERTED_LOWER_SNAPSHOT, + STRIP_EVAL_NOBLEND)); + /* directly evaluate child strip into accumulation buffer... * - there's no need to use a temporary buffer (as it causes issues [T40082]) */ if (tmp_nes) { - nlastrip_evaluate( - ptr, channels, &tmp_modifiers, tmp_nes, snapshot, &child_context, flush_to_original); + nlastrip_evaluate(evaluation_mode, + ptr, + channels, + &tmp_modifiers, + tmp_nes, + snapshot, + &child_context, + flush_to_original); /* free temp eval-strip */ MEM_freeN(tmp_nes); @@ -2381,7 +2894,8 @@ static void nlastrip_evaluate_meta(PointerRNA *ptr, nlaeval_fmodifiers_split_stacks(&strip->modifiers, modifiers); } -void nlastrip_evaluate(PointerRNA *ptr, +void nlastrip_evaluate(const int evaluation_mode, + PointerRNA *ptr, NlaEvalData *channels, ListBase *modifiers, NlaEvalStrip *nes, @@ -2406,15 +2920,27 @@ void nlastrip_evaluate(PointerRNA *ptr, /* actions to take depend on the type of strip */ switch (strip->type) { case NLASTRIP_TYPE_CLIP: /* action-clip */ - nlastrip_evaluate_actionclip(ptr, channels, modifiers, nes, snapshot); + nlastrip_evaluate_actionclip(evaluation_mode, ptr, channels, modifiers, nes, snapshot); break; case NLASTRIP_TYPE_TRANSITION: /* transition */ - nlastrip_evaluate_transition( - ptr, channels, modifiers, nes, snapshot, anim_eval_context, flush_to_original); + nlastrip_evaluate_transition(evaluation_mode, + ptr, + channels, + modifiers, + nes, + snapshot, + anim_eval_context, + flush_to_original); break; case NLASTRIP_TYPE_META: /* meta */ - nlastrip_evaluate_meta( - ptr, channels, modifiers, nes, snapshot, anim_eval_context, flush_to_original); + nlastrip_evaluate_meta(evaluation_mode, + ptr, + channels, + modifiers, + nes, + snapshot, + anim_eval_context, + flush_to_original); break; default: /* do nothing */ @@ -2425,6 +2951,53 @@ void nlastrip_evaluate(PointerRNA *ptr, strip->flag &= ~NLASTRIP_FLAG_EDIT_TOUCHED; } +void nlasnapshot_blend_strip(PointerRNA *ptr, + NlaEvalData *channels, + ListBase *modifiers, + NlaEvalStrip *nes, + NlaEvalSnapshot *snapshot, + const struct AnimationEvalContext *anim_eval_context, + const bool flush_to_original) +{ + nlastrip_evaluate(STRIP_EVAL_BLEND, + ptr, + channels, + modifiers, + nes, + snapshot, + anim_eval_context, + flush_to_original); +} + +void nlasnapshot_blend_strip_get_inverted_lower_snapshot( + PointerRNA *ptr, + NlaEvalData *channels, + ListBase *modifiers, + NlaEvalStrip *nes, + NlaEvalSnapshot *snapshot, + const struct AnimationEvalContext *anim_eval_context) +{ + nlastrip_evaluate(STRIP_EVAL_BLEND_GET_INVERTED_LOWER_SNAPSHOT, + ptr, + channels, + modifiers, + nes, + snapshot, + anim_eval_context, + false); +} + +void nlasnapshot_blend_strip_no_blend(PointerRNA *ptr, + NlaEvalData *channels, + ListBase *modifiers, + NlaEvalStrip *nes, + NlaEvalSnapshot *snapshot, + const struct AnimationEvalContext *anim_eval_context) +{ + nlastrip_evaluate( + STRIP_EVAL_NOBLEND, ptr, channels, modifiers, nes, snapshot, anim_eval_context, false); +} + void nladata_flush_channels(PointerRNA *ptr, NlaEvalData *channels, NlaEvalSnapshot *snapshot, @@ -2524,8 +3097,14 @@ static void animsys_evaluate_nla_domain(PointerRNA *ptr, NlaEvalData *channels, { GSet *touched_actions = BLI_gset_ptr_new(__func__); - if (adt->action) { - nla_eval_domain_action(ptr, channels, adt->action, touched_actions); + /* Include domain of Action Track. */ + if ((adt->flag & ADT_NLA_EDIT_ON) == 0) { + if (adt->action) { + nla_eval_domain_action(ptr, channels, adt->action, touched_actions); + } + } + else if (adt->tmpact && (adt->flag & ADT_NLA_EVAL_UPPER_TRACKS)) { + nla_eval_domain_action(ptr, channels, adt->tmpact, touched_actions); } /* NLA Data - Animation Data for Strips */ @@ -2634,7 +3213,8 @@ static void animsys_create_action_track_strip(const AnimData *adt, const bool tweaking = (adt->flag & ADT_NLA_EDIT_ON) != 0; const bool soloing = (adt->flag & ADT_NLA_SOLO_TRACK) != 0; - const bool actionstrip_evaluated = r_action_strip->act && !soloing && !tweaking; + const bool eval_upper = !tweaking || (adt->flag & ADT_NLA_EVAL_UPPER_TRACKS) != 0; + const bool actionstrip_evaluated = r_action_strip->act && !soloing && eval_upper; if (!actionstrip_evaluated) { r_action_strip->flag |= NLASTRIP_FLAG_MUTED; } @@ -2779,13 +3359,13 @@ static bool animsys_evaluate_nla_for_flush(NlaEvalData *echannels, /* Per strip, evaluate and accumulate on top of existing channels. */ for (nes = estrips.first; nes; nes = nes->next) { - nlastrip_evaluate(ptr, - echannels, - NULL, - nes, - &echannels->eval_snapshot, - anim_eval_context, - flush_to_original); + nlasnapshot_blend_strip(ptr, + echannels, + NULL, + nes, + &echannels->eval_snapshot, + anim_eval_context, + flush_to_original); } /* Free temporary evaluation data that's not used elsewhere. */ @@ -2816,6 +3396,7 @@ static void animsys_evaluate_nla_for_keyframing(PointerRNA *ptr, short track_index = 0; bool has_strips = false; + ListBase *upper_estrips = &r_context->upper_estrips; ListBase lower_estrips = {NULL, NULL}; NlaEvalStrip *nes; @@ -2845,6 +3426,30 @@ static void animsys_evaluate_nla_for_keyframing(PointerRNA *ptr, } } + /* Get the upper stack of strips to evaluate at current time (influence calculated here). + * Var nlt exists only if tweak strip exists. */ + if (nlt) { + + /* Skip tweaked strip. */ + nlt = nlt->next; + track_index++; + + for (; nlt; nlt = nlt->next, track_index++) { + + if (!is_nlatrack_evaluatable(adt, nlt)) { + continue; + } + + if (nlt->strips.first) { + has_strips = true; + } + + /* Get strip to evaluate for this channel. */ + nes = nlastrips_ctime_get_strip( + upper_estrips, &nlt->strips, track_index, anim_eval_context, false); + } + } + /** NOTE: Although we early out, we can still keyframe to the non-pushed action since the * keyframe remap function detects (r_context->strip.act == NULL) and will keyframe without * remapping. @@ -2856,6 +3461,10 @@ static void animsys_evaluate_nla_for_keyframing(PointerRNA *ptr, /* Write r_context->eval_strip. */ if (adt->flag & ADT_NLA_EDIT_ON) { + /* Append action_track_strip to upper estrips. */ + NlaStrip *action_strip = &r_context->action_track_strip; + animsys_create_action_track_strip(adt, false, action_strip); + nlastrips_ctime_get_strip_single(upper_estrips, action_strip, anim_eval_context, false); NlaStrip *tweak_strip = &r_context->strip; animsys_create_tweak_strip(adt, true, tweak_strip); @@ -2885,13 +3494,13 @@ static void animsys_evaluate_nla_for_keyframing(PointerRNA *ptr, /* For each strip, evaluate then accumulate on top of existing channels. */ for (nes = lower_estrips.first; nes; nes = nes->next) { - nlastrip_evaluate(ptr, - &r_context->lower_eval_data, - NULL, - nes, - &r_context->lower_eval_data.eval_snapshot, - anim_eval_context, - false); + nlasnapshot_blend_strip(ptr, + &r_context->lower_eval_data, + NULL, + nes, + &r_context->lower_eval_data.eval_snapshot, + anim_eval_context, + false); } /* Free temporary evaluation data that's not used elsewhere. */ @@ -3009,6 +3618,41 @@ void nlasnapshot_blend_get_inverted_upper_snapshot(NlaEvalData *eval_data, } } +/** Using \a blended_snapshot and \a upper_snapshot, we can solve for the \a r_lower_snapshot. + * + * Only channels that exist within \a blended_snapshot are processed. + * Only blended values within the \a remap_domain are processed. + * + * Writes to \a r_upper_snapshot NlaEvalChannelSnapshot->remap_domain to match remapping success. + * + * Assumes caller marked upper values that are in the \a blend_domain. This determines whether the + * blended value came directly from the lower snapshot or a result of blending. + **/ +void nlasnapshot_blend_get_inverted_lower_snapshot(NlaEvalData *eval_data, + NlaEvalSnapshot *blended_snapshot, + NlaEvalSnapshot *upper_snapshot, + const short upper_blendmode, + const float upper_influence, + NlaEvalSnapshot *r_lower_snapshot) +{ + nlaeval_snapshot_ensure_size(r_lower_snapshot, eval_data->num_channels); + + LISTBASE_FOREACH (NlaEvalChannel *, nec, &eval_data->channels) { + NlaEvalChannelSnapshot *blended_necs = nlaeval_snapshot_get(blended_snapshot, nec->index); + if (blended_necs == NULL) { + /* We assume the caller only wants a subset of channels to be inverted, those that exist + * within \a blended_snapshot. */ + continue; + } + + NlaEvalChannelSnapshot *upper_necs = nlaeval_snapshot_get(upper_snapshot, nec->index); + NlaEvalChannelSnapshot *result_necs = nlaeval_snapshot_ensure_channel(r_lower_snapshot, nec); + + nlaevalchan_blendOrCombine_get_inverted_lower_evalchan( + blended_necs, upper_necs, upper_blendmode, upper_influence, result_necs); + } +} + /* ---------------------- */ NlaKeyframingContext *BKE_animsys_get_nla_keyframing_context( @@ -3023,9 +3667,11 @@ NlaKeyframingContext *BKE_animsys_get_nla_keyframing_context( return NULL; } - /* No remapping if editing an ordinary Replace action with full influence. */ + /* No remapping if editing an ordinary Replace action with full influence and upper tracks not + * evaluated. */ if (!(adt->flag & ADT_NLA_EDIT_ON) && - (adt->act_blendmode == NLASTRIP_MODE_REPLACE && adt->act_influence == 1.0f)) { + (adt->act_blendmode == NLASTRIP_MODE_REPLACE && adt->act_influence == 1.0f) && + (adt->flag & ADT_NLA_EVAL_UPPER_TRACKS) == 0) { return NULL; } @@ -3047,39 +3693,60 @@ NlaKeyframingContext *BKE_animsys_get_nla_keyframing_context( return ctx; } -bool BKE_animsys_nla_remap_keyframe_values(struct NlaKeyframingContext *context, +void BKE_animsys_nla_remap_keyframe_values(struct NlaKeyframingContext *context, struct PointerRNA *prop_ptr, struct PropertyRNA *prop, float *values, int count, int index, - bool *r_force_all) + const struct AnimationEvalContext *anim_eval_context, + bool *r_force_all, + BLI_bitmap *r_successful_remaps) { + BLI_bitmap_set_all(r_successful_remaps, false, count); + if (r_force_all != NULL) { *r_force_all = false; } + BLI_bitmap *remap_domain = BLI_BITMAP_NEW(count, __func__); + for (int i = 0; i < count; i++) { + if (!ELEM(index, i, -1)) { + continue; + } + + BLI_BITMAP_ENABLE(remap_domain, i); + } + /* No context means no correction. */ if (context == NULL || context->strip.act == NULL) { - return true; + BLI_bitmap_copy_all(r_successful_remaps, remap_domain, count); + MEM_freeN(remap_domain); + return; } /* If the strip is not evaluated, it is the same as zero influence. */ if (context->eval_strip == NULL) { - return false; + MEM_freeN(remap_domain); + return; } - /* Full influence Replace strips also require no correction. */ + /* Full influence Replace strips also require no correction if there are no upper tracks + * evaluating. */ int blend_mode = context->strip.blendmode; float influence = context->strip.influence; - if (blend_mode == NLASTRIP_MODE_REPLACE && influence == 1.0f) { - return true; + if (blend_mode == NLASTRIP_MODE_REPLACE && influence == 1.0f && + BLI_listbase_is_empty(&context->upper_estrips)) { + BLI_bitmap_copy_all(r_successful_remaps, remap_domain, count); + MEM_freeN(remap_domain); + return; } /* Zero influence is division by zero. */ if (influence <= 0.0f) { - return false; + MEM_freeN(remap_domain); + return; } /** Create \a blended_snapshot and fill with input \a values. */ @@ -3097,12 +3764,38 @@ bool BKE_animsys_nla_remap_keyframe_values(struct NlaKeyframingContext *context, if (nec->base_snapshot.length != count) { BLI_assert_msg(0, "invalid value count"); nlaeval_snapshot_free_data(&blended_snapshot); - return false; + MEM_freeN(remap_domain); + return; } NlaEvalChannelSnapshot *blended_necs = nlaeval_snapshot_ensure_channel(&blended_snapshot, nec); memcpy(blended_necs->values, values, sizeof(float) * count); - BLI_bitmap_set_all(blended_necs->remap_domain.ptr, true, count); + + /* Force all channels to be remapped for quaternions in a Combine strip, otherwise it will + * always fail. See nlaevalchan_combine_quaternion_handle_undefined_blend_values(). + */ + const bool can_force_all = r_force_all != NULL; + if (blended_necs->channel->mix_mode == NEC_MIX_QUATERNION && + blend_mode == NLASTRIP_MODE_COMBINE && can_force_all) { + + *r_force_all = true; + index = -1; + BLI_bitmap_set_all(remap_domain, true, 4); + } + + BLI_bitmap_copy_all(blended_necs->remap_domain.ptr, remap_domain, count); + + /* Need to send id_ptr instead of prop_ptr so fcurve RNA paths resolve properly. */ + PointerRNA id_ptr; + RNA_id_pointer_create(prop_ptr->owner_id, &id_ptr); + + /* Per iteration, remove effect of upper strip which gives output of nla stack below it. */ + LISTBASE_FOREACH_BACKWARD (NlaEvalStrip *, nes, &context->upper_estrips) { + /* This will disable blended_necs->remap_domain bits if an upper strip is not invertible + * (full replace, multiply zero, or transition). Then there is no remap solution. */ + nlasnapshot_blend_strip_get_inverted_lower_snapshot( + &id_ptr, eval_data, NULL, nes, &blended_snapshot, anim_eval_context); + } /** Remove lower NLA stack effects. */ nlasnapshot_blend_get_inverted_upper_snapshot(eval_data, @@ -3112,40 +3805,25 @@ bool BKE_animsys_nla_remap_keyframe_values(struct NlaKeyframingContext *context, influence, &blended_snapshot); - /** Write results into \a values. */ - bool successful_remap = true; - if (blended_necs->channel->mix_mode == NEC_MIX_QUATERNION && - blend_mode == NLASTRIP_MODE_COMBINE) { - - if (r_force_all != NULL) { - *r_force_all = true; - index = -1; - } - else { - successful_remap = false; - } - } - + /* Write results into \a values for successfully remapped values. */ for (int i = 0; i < count; i++) { - if (!ELEM(index, i, -1)) { - continue; - } if (!BLI_BITMAP_TEST_BOOL(blended_necs->remap_domain.ptr, i)) { - successful_remap = false; + continue; } - values[i] = blended_necs->values[i]; } - nlaeval_snapshot_free_data(&blended_snapshot); + BLI_bitmap_copy_all(r_successful_remaps, blended_necs->remap_domain.ptr, blended_necs->length); - return successful_remap; + nlaeval_snapshot_free_data(&blended_snapshot); + MEM_freeN(remap_domain); } void BKE_animsys_free_nla_keyframing_context_cache(struct ListBase *cache) { LISTBASE_FOREACH (NlaKeyframingContext *, ctx, cache) { MEM_SAFE_FREE(ctx->eval_strip); + BLI_freelistN(&ctx->upper_estrips); nlaeval_free(&ctx->lower_eval_data); } diff --git a/source/blender/blenkernel/intern/armature_update.c b/source/blender/blenkernel/intern/armature_update.c index 361ab176abd..2db4c086e04 100644 --- a/source/blender/blenkernel/intern/armature_update.c +++ b/source/blender/blenkernel/intern/armature_update.c @@ -405,8 +405,8 @@ static void splineik_evaluate_bone( if (pchan->bone->length < FLT_EPSILON) { /* Only move the bone position with zero length bones. */ - float bone_pos[4], dir[3], rad; - BKE_where_on_path(ik_data->tar, state->curve_position, bone_pos, dir, NULL, &rad, NULL); + float bone_pos[4], rad; + BKE_where_on_path(ik_data->tar, state->curve_position, bone_pos, NULL, NULL, &rad, NULL); apply_curve_transform(ik_data, ob, rad, bone_pos, &rad); @@ -445,13 +445,13 @@ static void splineik_evaluate_bone( /* Step 1: determine the positions for the endpoints of the bone. */ if (point_start < 1.0f) { - float vec[4], dir[3], rad; + float vec[4], rad; radius = 0.0f; /* Calculate head position. */ if (point_start == 0.0f) { /* Start of the path. We have no previous tail position to copy. */ - BKE_where_on_path(ik_data->tar, point_start, vec, dir, NULL, &rad, NULL); + BKE_where_on_path(ik_data->tar, point_start, vec, NULL, NULL, &rad, NULL); } else { copy_v3_v3(vec, state->prev_tail_loc); @@ -486,7 +486,7 @@ static void splineik_evaluate_bone( } else { /* Scale to fit curve end position. */ - if (BKE_where_on_path(ik_data->tar, point_end, vec, dir, NULL, &rad, NULL)) { + if (BKE_where_on_path(ik_data->tar, point_end, vec, NULL, NULL, &rad, NULL)) { state->prev_tail_radius = rad; copy_v3_v3(state->prev_tail_loc, vec); copy_v3_v3(pose_tail, vec); diff --git a/source/blender/blenkernel/intern/attribute.c b/source/blender/blenkernel/intern/attribute.c index d8c7c3c6dd7..307868ce6d9 100644 --- a/source/blender/blenkernel/intern/attribute.c +++ b/source/blender/blenkernel/intern/attribute.c @@ -292,7 +292,7 @@ int BKE_id_attributes_length(const ID *id, AttributeDomainMask domain_mask, Cust return length; } -AttributeDomain BKE_id_attribute_domain(ID *id, const CustomDataLayer *layer) +AttributeDomain BKE_id_attribute_domain(const ID *id, const CustomDataLayer *layer) { DomainInfo info[ATTR_DOMAIN_NUM]; get_domains(id, info); @@ -451,7 +451,7 @@ CustomDataLayer *BKE_id_attribute_from_index(ID *id, for (int i = 0; i < customdata->totlayer; i++) { if (!(layer_mask & CD_TYPE_AS_MASK(customdata->layers[i].type)) || - (CD_TYPE_AS_MASK(customdata->layers[i].type) & CD_FLAG_TEMPORARY)) { + (customdata->layers[i].flag & CD_FLAG_TEMPORARY)) { continue; } @@ -504,7 +504,7 @@ int BKE_id_attribute_to_index(const struct ID *id, CustomDataLayer *layer_iter = cdata->layers + j; if (!(CD_TYPE_AS_MASK(layer_iter->type) & layer_mask) || - (CD_TYPE_AS_MASK(layer_iter->type) & CD_FLAG_TEMPORARY)) { + (layer_iter->flag & CD_FLAG_TEMPORARY)) { continue; } @@ -541,8 +541,7 @@ CustomDataLayer *BKE_id_attribute_subset_active_get(const ID *id, for (int j = 0; j < cdata->totlayer; j++) { CustomDataLayer *layer = cdata->layers + j; - if (!(CD_TYPE_AS_MASK(layer->type) & mask) || - (CD_TYPE_AS_MASK(layer->type) & CD_FLAG_TEMPORARY)) { + if (!(CD_TYPE_AS_MASK(layer->type) & mask) || (layer->flag & CD_FLAG_TEMPORARY)) { continue; } @@ -581,8 +580,7 @@ void BKE_id_attribute_subset_active_set(ID *id, for (int j = 0; j < cdata->totlayer; j++) { CustomDataLayer *layer_iter = cdata->layers + j; - if (!(CD_TYPE_AS_MASK(layer_iter->type) & mask) || - (CD_TYPE_AS_MASK(layer_iter->type) & CD_FLAG_TEMPORARY)) { + if (!(CD_TYPE_AS_MASK(layer_iter->type) & mask) || (layer_iter->flag & CD_FLAG_TEMPORARY)) { continue; } diff --git a/source/blender/blenkernel/intern/attribute_access.cc b/source/blender/blenkernel/intern/attribute_access.cc index d0420b4170a..0ae9fa4356b 100644 --- a/source/blender/blenkernel/intern/attribute_access.cc +++ b/source/blender/blenkernel/intern/attribute_access.cc @@ -746,15 +746,14 @@ bool CustomDataAttributes::create_by_move(const AttributeIDRef &attribute_id, bool CustomDataAttributes::remove(const AttributeIDRef &attribute_id) { - bool result = false; for (const int i : IndexRange(data.totlayer)) { const CustomDataLayer &layer = data.layers[i]; if (custom_data_layer_matches_attribute_id(layer, attribute_id)) { CustomData_free_layer(&data, layer.type, size_, i); - result = true; + return true; } } - return result; + return false; } void CustomDataAttributes::reallocate(const int size) diff --git a/source/blender/blenkernel/intern/blendfile_link_append.c b/source/blender/blenkernel/intern/blendfile_link_append.c index be2abdbfb69..da48365a43e 100644 --- a/source/blender/blenkernel/intern/blendfile_link_append.c +++ b/source/blender/blenkernel/intern/blendfile_link_append.c @@ -1187,6 +1187,11 @@ void BKE_blendfile_append(BlendfileLinkAppendContext *lapp_context, ReportList * BLI_assert(ID_IS_LINKED(id)); BLI_assert(id->newid != NULL); + /* Do NOT delete a linked data that was already linked before this append. */ + if (id->tag & LIB_TAG_PRE_EXISTING) { + continue; + } + id->tag |= LIB_TAG_DOIT; item->new_id = id->newid; } diff --git a/source/blender/blenkernel/intern/camera.c b/source/blender/blenkernel/intern/camera.c index 3897df9f05f..32925168437 100644 --- a/source/blender/blenkernel/intern/camera.c +++ b/source/blender/blenkernel/intern/camera.c @@ -698,12 +698,12 @@ static bool camera_frame_fit_calc_from_data(CameraParams *params, add_v3_v3v3(plane_isect_1_other, plane_isect_1, plane_isect_1_no); add_v3_v3v3(plane_isect_2_other, plane_isect_2, plane_isect_2_no); - if (!isect_line_line_v3(plane_isect_1, - plane_isect_1_other, - plane_isect_2, - plane_isect_2_other, - plane_isect_pt_1, - plane_isect_pt_2) != 0) { + if (isect_line_line_v3(plane_isect_1, + plane_isect_1_other, + plane_isect_2, + plane_isect_2_other, + plane_isect_pt_1, + plane_isect_pt_2) == 0) { return false; } diff --git a/source/blender/blenkernel/intern/collection.c b/source/blender/blenkernel/intern/collection.c index 939f7c7b3ff..0edc16e822c 100644 --- a/source/blender/blenkernel/intern/collection.c +++ b/source/blender/blenkernel/intern/collection.c @@ -360,7 +360,7 @@ IDTypeInfo IDType_ID_GR = { .name = "Collection", .name_plural = "collections", .translation_context = BLT_I18NCONTEXT_ID_COLLECTION, - .flags = IDTYPE_FLAGS_NO_ANIMDATA, + .flags = IDTYPE_FLAGS_NO_ANIMDATA | IDTYPE_FLAGS_APPEND_IS_REUSABLE, .asset_type_info = NULL, .init_data = collection_init_data, diff --git a/source/blender/blenkernel/intern/constraint.c b/source/blender/blenkernel/intern/constraint.c index e85524d4bcb..35f2f94bc91 100644 --- a/source/blender/blenkernel/intern/constraint.c +++ b/source/blender/blenkernel/intern/constraint.c @@ -1495,7 +1495,7 @@ static void followpath_get_tarmat(struct Depsgraph *UNUSED(depsgraph), if (VALID_CONS_TARGET(ct) && (ct->tar->type == OB_CURVES_LEGACY)) { Curve *cu = ct->tar->data; - float vec[4], dir[3], radius; + float vec[4], radius; float curvetime; unit_m4(ct->matrix); @@ -1532,7 +1532,7 @@ static void followpath_get_tarmat(struct Depsgraph *UNUSED(depsgraph), if (BKE_where_on_path(ct->tar, curvetime, vec, - dir, + NULL, (data->followflag & FOLLOWPATH_FOLLOW) ? quat : NULL, &radius, NULL)) { /* quat_pt is quat or NULL. */ @@ -3886,7 +3886,7 @@ static void clampto_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *tar /* get targetmatrix */ if (data->tar->runtime.curve_cache && data->tar->runtime.curve_cache->anim_path_accum_length) { - float vec[4], dir[3], totmat[4][4]; + float vec[4], totmat[4][4]; float curvetime; short clamp_axis; @@ -3969,7 +3969,7 @@ static void clampto_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *tar } /* 3. position on curve */ - if (BKE_where_on_path(ct->tar, curvetime, vec, dir, NULL, NULL, NULL)) { + if (BKE_where_on_path(ct->tar, curvetime, vec, NULL, NULL, NULL, NULL)) { unit_m4(totmat); copy_v3_v3(totmat[3], vec); diff --git a/source/blender/blenkernel/intern/curve.cc b/source/blender/blenkernel/intern/curve.cc index 2d72ad28d18..6815b4d7486 100644 --- a/source/blender/blenkernel/intern/curve.cc +++ b/source/blender/blenkernel/intern/curve.cc @@ -665,7 +665,7 @@ Nurb *BKE_nurb_duplicate(const Nurb *nu) if (newnu == nullptr) { return nullptr; } - memcpy(newnu, nu, sizeof(Nurb)); + *newnu = blender::dna::shallow_copy(*nu); if (nu->bezt) { newnu->bezt = (BezTriple *)MEM_malloc_arrayN(nu->pntsu, sizeof(BezTriple), "duplicateNurb2"); @@ -699,7 +699,7 @@ Nurb *BKE_nurb_duplicate(const Nurb *nu) Nurb *BKE_nurb_copy(Nurb *src, int pntsu, int pntsv) { Nurb *newnu = (Nurb *)MEM_mallocN(sizeof(Nurb), "copyNurb"); - memcpy(newnu, src, sizeof(Nurb)); + *newnu = blender::dna::shallow_copy(*src); if (pntsu == 1) { SWAP(int, pntsu, pntsv); diff --git a/source/blender/blenkernel/intern/curve_bezier.cc b/source/blender/blenkernel/intern/curve_bezier.cc index dfe462d8566..13695525616 100644 --- a/source/blender/blenkernel/intern/curve_bezier.cc +++ b/source/blender/blenkernel/intern/curve_bezier.cc @@ -281,6 +281,11 @@ static void interpolate_to_evaluated(const Span<T> src, BLI_assert(!src.is_empty()); BLI_assert(evaluated_offsets.size() == src.size()); BLI_assert(evaluated_offsets.last() == dst.size()); + if (src.size() == 1) { + BLI_assert(dst.size() == 1); + dst.first() = src.first(); + return; + } linear_interpolation(src.first(), src[1], dst.take_front(evaluated_offsets.first())); diff --git a/source/blender/blenkernel/intern/curve_eval.cc b/source/blender/blenkernel/intern/curve_eval.cc index 6e09d1e8f10..d1ec9499298 100644 --- a/source/blender/blenkernel/intern/curve_eval.cc +++ b/source/blender/blenkernel/intern/curve_eval.cc @@ -385,8 +385,8 @@ std::unique_ptr<CurveEval> curves_to_curve_eval(const Curves &curves) VArray_Span<float> nurbs_weights{ src_component.attribute_get_for_read<float>("nurbs_weight", ATTR_DOMAIN_POINT, 0.0f)}; - VArray_Span<int> nurbs_orders{ - src_component.attribute_get_for_read<int>("nurbs_order", ATTR_DOMAIN_CURVE, 4)}; + VArray_Span<int8_t> nurbs_orders{ + src_component.attribute_get_for_read<int8_t>("nurbs_order", ATTR_DOMAIN_CURVE, 4)}; VArray_Span<int8_t> nurbs_knots_modes{ src_component.attribute_get_for_read<int8_t>("knots_mode", ATTR_DOMAIN_CURVE, 0)}; @@ -402,7 +402,8 @@ std::unique_ptr<CurveEval> curves_to_curve_eval(const Curves &curves) const IndexRange point_range = geometry.points_for_curve(curve_index); std::unique_ptr<Spline> spline; - switch (curve_types[curve_index]) { + /* #CurveEval does not support catmull rom curves, so convert those to poly splines. */ + switch (std::max<int8_t>(1, curve_types[curve_index])) { case CURVE_TYPE_POLY: { spline = std::make_unique<PolySpline>(); spline->resize(point_range.size()); diff --git a/source/blender/blenkernel/intern/curve_poly.cc b/source/blender/blenkernel/intern/curve_poly.cc index b0ed62d38dd..2db7cd71ad3 100644 --- a/source/blender/blenkernel/intern/curve_poly.cc +++ b/source/blender/blenkernel/intern/curve_poly.cc @@ -6,7 +6,7 @@ #include <algorithm> -#include "BLI_math_vector.h" +#include "BLI_math_rotation.hh" #include "BLI_math_vector.hh" #include "BKE_curves.hh" @@ -54,20 +54,6 @@ void calculate_tangents(const Span<float3> positions, } } -static float3 rotate_direction_around_axis(const float3 &direction, - const float3 &axis, - const float angle) -{ - BLI_ASSERT_UNIT_V3(direction); - BLI_ASSERT_UNIT_V3(axis); - - const float3 axis_scaled = axis * math::dot(direction, axis); - const float3 diff = direction - axis_scaled; - const float3 cross = math::cross(axis, diff); - - return axis_scaled + diff * std::cos(angle) + cross * std::sin(angle); -} - void calculate_normals_z_up(const Span<float3> tangents, MutableSpan<float3> normals) { BLI_assert(normals.size() == tangents.size()); @@ -98,7 +84,7 @@ static float3 calculate_next_normal(const float3 &last_normal, const float angle = angle_normalized_v3v3(last_tangent, current_tangent); if (angle != 0.0) { const float3 axis = math::normalize(math::cross(last_tangent, current_tangent)); - return rotate_direction_around_axis(last_normal, axis, angle); + return math::rotate_direction_around_axis(last_normal, axis, angle); } return last_normal; } @@ -147,7 +133,7 @@ void calculate_normals_minimum(const Span<float3> tangents, const float angle_step = correction_angle / normals.size(); for (const int i : normals.index_range()) { const float angle = angle_step * i; - normals[i] = rotate_direction_around_axis(normals[i], tangents[i], angle); + normals[i] = math::rotate_direction_around_axis(normals[i], tangents[i], angle); } } diff --git a/source/blender/blenkernel/intern/curve_to_mesh_convert.cc b/source/blender/blenkernel/intern/curve_to_mesh_convert.cc index 9b22a4c9726..c48d155f5ce 100644 --- a/source/blender/blenkernel/intern/curve_to_mesh_convert.cc +++ b/source/blender/blenkernel/intern/curve_to_mesh_convert.cc @@ -9,65 +9,15 @@ #include "BKE_attribute_access.hh" #include "BKE_attribute_math.hh" +#include "BKE_curves.hh" #include "BKE_geometry_set.hh" #include "BKE_material.h" #include "BKE_mesh.h" -#include "BKE_spline.hh" #include "BKE_curve_to_mesh.hh" 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<MVert> r_verts, - MutableSpan<MEdge> r_edges, - const int vert_offset, - const int edge_offset) -{ - const int eval_size = spline.evaluated_points_size(); - for (const int i : IndexRange(eval_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 + eval_size - 1; - edge.v2 = vert_offset; - edge.flag = ME_LOOSEEDGE; - } - - Span<float3> positions = spline.evaluated_positions(); - Span<float3> tangents = spline.evaluated_tangents(); - Span<float3> normals = spline.evaluated_normals(); - VArray<float> radii = spline.interpolate_to_evaluated(spline.radii()); - for (const int i : IndexRange(eval_size)) { - float4x4 point_matrix = float4x4::from_normalized_axis_data( - positions[i], normals[i], tangents[i]); - point_matrix.apply_scale(radii[i]); - - MVert &vert = r_verts[vert_offset + i]; - copy_v3_v3(vert.co, point_matrix * profile_vert); - } -} - static void mark_edges_sharp(MutableSpan<MEdge> edges) { for (MEdge &edge : edges) { @@ -75,36 +25,50 @@ static void mark_edges_sharp(MutableSpan<MEdge> edges) } } -static void spline_extrude_to_mesh_data(const ResultInfo &info, - const bool fill_caps, - MutableSpan<MVert> r_verts, - MutableSpan<MEdge> r_edges, - MutableSpan<MLoop> r_loops, - MutableSpan<MPoly> r_polys) +static void fill_mesh_topology(const int vert_offset, + const int edge_offset, + const int poly_offset, + const int loop_offset, + const int main_point_num, + const int profile_point_num, + const bool main_cyclic, + const bool profile_cyclic, + const bool fill_caps, + MutableSpan<MEdge> edges, + MutableSpan<MLoop> loops, + MutableSpan<MPoly> 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); + const int main_segment_num = curves::curve_segment_size(main_point_num, main_cyclic); + const int profile_segment_num = curves::curve_segment_size(profile_point_num, profile_cyclic); + + if (profile_point_num == 1) { + for (const int i : IndexRange(main_point_num - 1)) { + MEdge &edge = edges[edge_offset + i]; + edge.v1 = vert_offset + i; + edge.v2 = vert_offset + i + 1; + edge.flag = ME_LOOSEEDGE; + } + + if (main_cyclic && main_segment_num > 1) { + MEdge &edge = edges[edge_offset + main_segment_num - 1]; + edge.v1 = vert_offset + main_point_num - 1; + edge.v2 = vert_offset; + edge.flag = ME_LOOSEEDGE; + } 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 main_edges_start = edge_offset; + for (const int i_profile : IndexRange(profile_point_num)) { + const int profile_edge_offset = main_edges_start + i_profile * main_segment_num; + for (const int i_ring : IndexRange(main_segment_num)) { + const int i_next_ring = (i_ring == main_point_num - 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_vert_offset = vert_offset + profile_point_num * i_ring; + const int next_ring_vert_offset = vert_offset + profile_point_num * i_next_ring; - MEdge &edge = r_edges[profile_edge_offset + i_ring]; + MEdge &edge = 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; @@ -112,16 +76,15 @@ static void spline_extrude_to_mesh_data(const ResultInfo &info, } /* 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 profile_edges_start = main_edges_start + profile_point_num * main_segment_num; + for (const int i_ring : IndexRange(main_point_num)) { + const int ring_vert_offset = vert_offset + profile_point_num * 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; + const int ring_edge_offset = profile_edges_start + i_ring * profile_segment_num; + for (const int i_profile : IndexRange(profile_segment_num)) { + const int i_next_profile = (i_profile == profile_point_num - 1) ? 0 : i_profile + 1; - MEdge &edge = r_edges[ring_edge_offset + i_profile]; + MEdge &edge = 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; @@ -129,368 +92,410 @@ static void spline_extrude_to_mesh_data(const ResultInfo &info, } /* 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; + for (const int i_ring : IndexRange(main_segment_num)) { + const int i_next_ring = (i_ring == main_point_num - 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_vert_offset = vert_offset + profile_point_num * i_ring; + const int next_ring_vert_offset = vert_offset + profile_point_num * 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_edge_start = profile_edges_start + profile_segment_num * i_ring; + const int next_ring_edge_offset = profile_edges_start + profile_segment_num * 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; + const int ring_poly_offset = poly_offset + i_ring * profile_segment_num; + const int ring_loop_offset = loop_offset + i_ring * profile_segment_num * 4; - for (const int i_profile : IndexRange(info.profile_edge_len)) { + for (const int i_profile : IndexRange(profile_segment_num)) { 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 i_next_profile = (i_profile == profile_point_num - 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; + const int main_edge_start = main_edges_start + main_segment_num * i_profile; + const int next_main_edge_start = main_edges_start + main_segment_num * i_next_profile; - MPoly &poly = r_polys[ring_poly_offset + i_profile]; + MPoly &poly = 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]; + MLoop &loop_a = 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]; + MLoop &loop_b = 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_b.e = next_main_edge_start + i_ring; + MLoop &loop_c = 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]; + MLoop &loop_d = loops[ring_segment_loop_offset + 3]; loop_d.v = next_ring_vert_offset + i_profile; - loop_d.e = spline_edge_start + i_ring; + loop_d.e = main_edge_start + i_ring; } } - const bool has_caps = fill_caps && profile.is_cyclic() && !spline.is_cyclic(); + const bool has_caps = fill_caps && !main_cyclic && profile_cyclic; if (has_caps) { - 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; + const int poly_size = main_segment_num * profile_segment_num; + const int cap_loop_offset = loop_offset + poly_size * 4; + const int cap_poly_offset = poly_offset + poly_size; - MPoly &poly_start = r_polys[cap_poly_offset]; + MPoly &poly_start = 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]; + poly_start.totloop = profile_segment_num; + MPoly &poly_end = polys[cap_poly_offset + 1]; + poly_end.loopstart = cap_loop_offset + profile_segment_num; + poly_end.totloop = profile_segment_num; + + const int last_ring_index = main_point_num - 1; + const int last_ring_vert_offset = vert_offset + profile_point_num * last_ring_index; + const int last_ring_edge_offset = profile_edges_start + profile_segment_num * last_ring_index; + + for (const int i : IndexRange(profile_segment_num)) { + const int i_inv = profile_segment_num - i - 1; + MLoop &loop_start = loops[cap_loop_offset + i]; + loop_start.v = vert_offset + i_inv; + loop_start.e = profile_edges_start + + ((i == (profile_segment_num - 1)) ? (profile_segment_num - 1) : (i_inv - 1)); + MLoop &loop_end = loops[cap_loop_offset + profile_segment_num + 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)); + mark_edges_sharp(edges.slice(profile_edges_start, profile_segment_num)); + mark_edges_sharp(edges.slice(last_ring_edge_offset, profile_segment_num)); } +} - /* Calculate the positions of each profile ring profile along the spline. */ - Span<float3> positions = spline.evaluated_positions(); - Span<float3> tangents = spline.evaluated_tangents(); - Span<float3> normals = spline.evaluated_normals(); - Span<float3> profile_positions = profile.evaluated_positions(); - - VArray<float> 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]); - } +static void mark_bezier_vector_edges_sharp(const int profile_point_num, + const int main_segment_num, + const Span<int> control_point_offsets, + const Span<int8_t> handle_types_left, + const Span<int8_t> handle_types_right, + MutableSpan<MEdge> edges) +{ + const int main_edges_start = 0; + if (curves::bezier::point_is_sharp(handle_types_left, handle_types_right, 0)) { + mark_edges_sharp(edges.slice(main_edges_start, main_segment_num)); } - /* Mark edge loops from sharp vector control points sharp. */ - if (profile.type() == CURVE_TYPE_BEZIER) { - const BezierSpline &bezier_spline = static_cast<const BezierSpline &>(profile); - Span<int> 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)); - } + for (const int i : IndexRange(profile_point_num).drop_front(1)) { + if (curves::bezier::point_is_sharp(handle_types_left, handle_types_right, i)) { + mark_edges_sharp(edges.slice( + main_edges_start + main_segment_num * control_point_offsets[i - 1], main_segment_num)); } } } -static inline int spline_extrude_vert_size(const Spline &curve, const Spline &profile) +static void fill_mesh_positions(const int main_point_num, + const int profile_point_num, + const Span<float3> main_positions, + const Span<float3> profile_positions, + const Span<float3> tangents, + const Span<float3> normals, + const Span<float> radii, + MutableSpan<MVert> mesh_positions) { - return curve.evaluated_points_size() * profile.evaluated_points_size(); -} + if (profile_point_num == 1) { + for (const int i_ring : IndexRange(main_point_num)) { + float4x4 point_matrix = float4x4::from_normalized_axis_data( + main_positions[i_ring], normals[i_ring], tangents[i_ring]); + if (!radii.is_empty()) { + point_matrix.apply_scale(radii[i_ring]); + } -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(); -} + MVert &vert = mesh_positions[i_ring]; + copy_v3_v3(vert.co, point_matrix * profile_positions.first()); + } + } + else { + for (const int i_ring : IndexRange(main_point_num)) { + float4x4 point_matrix = float4x4::from_normalized_axis_data( + main_positions[i_ring], normals[i_ring], tangents[i_ring]); + if (!radii.is_empty()) { + point_matrix.apply_scale(radii[i_ring]); + } -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 bool has_caps = fill_caps && profile.is_cyclic() && !curve.is_cyclic(); - const int caps = has_caps ? profile.evaluated_edges_size() * 2 : 0; - return tube + caps; + const int ring_vert_start = i_ring * profile_point_num; + for (const int i_profile : IndexRange(profile_point_num)) { + MVert &vert = mesh_positions[ring_vert_start + i_profile]; + copy_v3_v3(vert.co, point_matrix * profile_positions[i_profile]); + } + } + } } -static inline int spline_extrude_poly_size(const Spline &curve, - const Spline &profile, - const bool fill_caps) +struct CurvesInfo { + const CurvesGeometry &main; + const CurvesGeometry &profile; + + /* Make sure these are spans because they are potentially accessed many times. */ + VArray_Span<bool> main_cyclic; + VArray_Span<bool> profile_cyclic; + + /* TODO: Remove once these are cached on #CurvesGeometry. */ + std::array<int, CURVE_TYPES_NUM> main_type_counts; + std::array<int, CURVE_TYPES_NUM> profile_type_counts; +}; +static CurvesInfo get_curves_info(const CurvesGeometry &main, const CurvesGeometry &profile) { - const int tube = curve.evaluated_edges_size() * profile.evaluated_edges_size(); - const bool has_caps = fill_caps && profile.is_cyclic() && !curve.is_cyclic(); - const int caps = has_caps ? 2 : 0; - return tube + caps; + return {main, + profile, + main.cyclic(), + profile.cyclic(), + main.count_curve_types(), + profile.count_curve_types()}; } struct ResultOffsets { + /** The total number of curve combinations. */ + int total; + + /** Offsets into the result mesh for each combination. */ Array<int> vert; Array<int> edge; Array<int> loop; Array<int> poly; + + /* The indices of the main and profile curves that form each combination. */ + Array<int> main_indices; + Array<int> profile_indices; }; -static ResultOffsets calculate_result_offsets(Span<SplinePtr> profiles, - Span<SplinePtr> curves, - const bool fill_caps) +static ResultOffsets calculate_result_offsets(const CurvesInfo &info, const bool fill_caps) { - const int total = profiles.size() * curves.size(); - Array<int> vert(total + 1); - Array<int> edge(total + 1); - Array<int> loop(total + 1); - Array<int> poly(total + 1); + ResultOffsets result; + result.total = info.main.curves_num() * info.profile.curves_num(); + result.vert.reinitialize(result.total + 1); + result.edge.reinitialize(result.total + 1); + result.loop.reinitialize(result.total + 1); + result.poly.reinitialize(result.total + 1); + + result.main_indices.reinitialize(result.total); + result.profile_indices.reinitialize(result.total); + + info.main.ensure_evaluated_offsets(); + info.profile.ensure_evaluated_offsets(); 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); + for (const int i_main : info.main.curves_range()) { + const bool main_cyclic = info.main_cyclic[i_main]; + const int main_point_num = info.main.evaluated_points_for_curve(i_main).size(); + const int main_segment_num = curves::curve_segment_size(main_point_num, main_cyclic); + for (const int i_profile : info.profile.curves_range()) { + result.vert[mesh_index] = vert_offset; + result.edge[mesh_index] = edge_offset; + result.loop[mesh_index] = loop_offset; + result.poly[mesh_index] = poly_offset; + + result.main_indices[mesh_index] = i_main; + result.profile_indices[mesh_index] = i_profile; + + const bool profile_cyclic = info.profile_cyclic[i_profile]; + const int profile_point_num = info.profile.evaluated_points_for_curve(i_profile).size(); + const int profile_segment_num = curves::curve_segment_size(profile_point_num, + profile_cyclic); + + const bool has_caps = fill_caps && !main_cyclic && profile_cyclic; + const int tube_face_num = main_segment_num * profile_segment_num; + + vert_offset += main_point_num * profile_point_num; + + /* 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. */ + edge_offset += main_point_num * profile_segment_num + main_segment_num * profile_point_num; + + /* Add two cap N-gons for every ending. */ + poly_offset += tube_face_num + (has_caps ? 2 : 0); + + /* All faces on the tube are quads, and all cap faces are N-gons with an edge for each + * profile edge. */ + loop_offset += tube_face_num * 4 + (has_caps ? profile_segment_num * 2 : 0); + 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)}; + result.vert.last() = vert_offset; + result.edge.last() = edge_offset; + result.loop.last() = loop_offset; + result.poly.last() = poly_offset; + + return result; } -static AttributeDomain get_result_attribute_domain(const MeshComponent &component, - const AttributeIDRef &attribute_id) +static AttributeDomain get_attribute_domain_for_mesh(const MeshComponent &mesh, + 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)) { + if (!mesh.attribute_is_builtin(attribute_id)) { return ATTR_DOMAIN_POINT; } - std::optional<AttributeMetaData> meta_data = component.attribute_get_meta_data(attribute_id); + std::optional<AttributeMetaData> meta_data = mesh.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 ATTR_DOMAIN_POINT; } 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<ResultAttributeData> create_attribute_and_get_span( - MeshComponent &component, - const AttributeIDRef &attribute_id, - AttributeMetaData meta_data, - Vector<OutputAttribute> &r_attributes) +static bool should_add_attribute_to_mesh(const CurveComponent &curve_component, + const MeshComponent &mesh_component, + const AttributeIDRef &id) { - 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; + /* The position attribute has special non-generic evaluation. */ + if (id.is_named() && id.name() == "position") { + return false; + } + /* Don't propagate built-in curves attributes that are not built-in on meshes. */ + if (curve_component.attribute_is_builtin(id) && !mesh_component.attribute_is_builtin(id)) { + return false; } + if (!id.should_be_kept()) { + return false; + } + return true; +} - GMutableSpan span = attribute.as_span(); - r_attributes.append(std::move(attribute)); - return std::make_optional<ResultAttributeData>({span, domain}); +static GSpan evaluated_attribute_if_necessary(const GVArray &src, + const CurvesGeometry &curves, + const Span<int> type_counts, + Vector<std::byte> &buffer) +{ + if (type_counts[CURVE_TYPE_POLY] == curves.curves_num() && src.is_span()) { + return src.get_internal_span(); + } + buffer.reinitialize(curves.evaluated_points_num() * src.type().size()); + GMutableSpan eval{src.type(), buffer.data(), curves.evaluated_points_num()}; + curves.interpolate_to_evaluated(src.get_internal_span(), eval); + return eval; } -/** - * 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<std::optional<ResultAttributeData>> curve_point_attributes; - Vector<std::optional<ResultAttributeData>> 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<std::optional<ResultAttributeData>> profile_point_attributes; - Vector<std::optional<ResultAttributeData>> 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<OutputAttribute> attributes; +/** Information at a specific combination of main and profile curves. */ +struct CombinationInfo { + int i_main; + int i_profile; + + IndexRange main_points; + IndexRange profile_points; + + bool main_cyclic; + bool profile_cyclic; + + int main_segment_num; + int profile_segment_num; + + IndexRange vert_range; + IndexRange edge_range; + IndexRange poly_range; + IndexRange loop_range; }; -static ResultAttributes create_result_attributes(const CurveEval &curve, - const CurveEval &profile, - MeshComponent &mesh_component) +template<typename Fn> +static void foreach_curve_combination(const CurvesInfo &info, + const ResultOffsets &offsets, + const Fn &fn) { - Set<AttributeIDRef> 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; + threading::parallel_for(IndexRange(offsets.total), 512, [&](IndexRange range) { + for (const int i : range) { + const int i_main = offsets.main_indices[i]; + const int i_profile = offsets.profile_indices[i]; + + const IndexRange main_points = info.main.evaluated_points_for_curve(i_main); + const IndexRange profile_points = info.profile.evaluated_points_for_curve(i_profile); + + const bool main_cyclic = info.main_cyclic[i_main]; + const bool profile_cyclic = info.profile_cyclic[i_profile]; + + /* Pass all information in a struct to avoid repeating arguments in many lambdas. + * The idea is that inlining `fn` will help avoid accessing unnecessary information, + * though that may or may not happen in practice. */ + fn(CombinationInfo{i_main, + i_profile, + main_points, + profile_points, + main_cyclic, + profile_cyclic, + curves::curve_segment_size(main_points.size(), main_cyclic), + curves::curve_segment_size(profile_points.size(), profile_cyclic), + offsets_to_range(offsets.vert.as_span(), i), + offsets_to_range(offsets.edge.as_span(), i), + offsets_to_range(offsets.poly.as_span(), i), + offsets_to_range(offsets.loop.as_span(), i)}); + } + }); } template<typename T> -static void copy_curve_point_data_to_mesh_verts(const Span<T> src, - const ResultInfo &info, - MutableSpan<T> dst) +static void copy_main_point_data_to_mesh_verts(const Span<T> src, + const int profile_point_num, + MutableSpan<T> 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]); + for (const int i_ring : src.index_range()) { + const int ring_vert_start = i_ring * profile_point_num; + dst.slice(ring_vert_start, profile_point_num).fill(src[i_ring]); } } template<typename T> -static void copy_curve_point_data_to_mesh_edges(const Span<T> src, - const ResultInfo &info, - MutableSpan<T> dst) +static void copy_main_point_data_to_mesh_edges(const Span<T> src, + const int profile_point_num, + const int main_segment_num, + const int profile_segment_num, + MutableSpan<T> 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]); + const int edges_start = profile_point_num * main_segment_num; + for (const int i_ring : src.index_range()) { + const int ring_edge_start = edges_start + profile_segment_num * i_ring; + dst.slice(ring_edge_start, profile_segment_num).fill(src[i_ring]); } } template<typename T> -static void copy_curve_point_data_to_mesh_faces(const Span<T> src, - const ResultInfo &info, - MutableSpan<T> dst) +static void copy_main_point_data_to_mesh_faces(const Span<T> src, + const int main_segment_num, + const int profile_segment_num, + MutableSpan<T> 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]); + for (const int i_ring : IndexRange(main_segment_num)) { + const int ring_face_start = profile_segment_num * i_ring; + dst.slice(ring_face_start, profile_segment_num).fill(src[i_ring]); } } -static void copy_curve_point_attribute_to_mesh(const GSpan src, - const ResultInfo &info, - ResultAttributeData &dst) +static void copy_main_point_domain_attribute_to_mesh(const CurvesInfo &curves_info, + const ResultOffsets &offsets, + const AttributeDomain dst_domain, + const GSpan src_all, + GMutableSpan dst_all) { - GVArray 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) { + attribute_math::convert_to_static_type(src_all.type(), [&](auto dummy) { using T = decltype(dummy); - switch (dst.domain) { + const Span<T> src = src_all.typed<T>(); + MutableSpan<T> dst = dst_all.typed<T>(); + switch (dst_domain) { case ATTR_DOMAIN_POINT: - copy_curve_point_data_to_mesh_verts(interpolated.typed<T>(), info, dst.data.typed<T>()); + foreach_curve_combination(curves_info, offsets, [&](const CombinationInfo &info) { + copy_main_point_data_to_mesh_verts( + src.slice(info.main_points), info.profile_points.size(), dst.slice(info.vert_range)); + }); break; case ATTR_DOMAIN_EDGE: - copy_curve_point_data_to_mesh_edges(interpolated.typed<T>(), info, dst.data.typed<T>()); + foreach_curve_combination(curves_info, offsets, [&](const CombinationInfo &info) { + copy_main_point_data_to_mesh_edges(src.slice(info.main_points), + info.profile_points.size(), + info.main_segment_num, + info.profile_segment_num, + dst.slice(info.edge_range)); + }); break; case ATTR_DOMAIN_FACE: - copy_curve_point_data_to_mesh_faces(interpolated.typed<T>(), info, dst.data.typed<T>()); + foreach_curve_combination(curves_info, offsets, [&](const CombinationInfo &info) { + copy_main_point_data_to_mesh_faces(src.slice(info.main_points), + info.main_segment_num, + info.profile_segment_num, + dst.slice(info.poly_range)); + }); break; case ATTR_DOMAIN_CORNER: /* Unsupported for now, since there are no builtin attributes to convert into. */ @@ -504,12 +509,12 @@ static void copy_curve_point_attribute_to_mesh(const GSpan src, template<typename T> static void copy_profile_point_data_to_mesh_verts(const Span<T> src, - const ResultInfo &info, + const int main_point_num, MutableSpan<T> 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)) { + for (const int i_ring : IndexRange(main_point_num)) { + const int profile_vert_start = i_ring * src.size(); + for (const int i_profile : src.index_range()) { dst[profile_vert_start + i_profile] = src[i_profile]; } } @@ -517,46 +522,59 @@ static void copy_profile_point_data_to_mesh_verts(const Span<T> src, template<typename T> static void copy_profile_point_data_to_mesh_edges(const Span<T> src, - const ResultInfo &info, + const int main_segment_num, MutableSpan<T> 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]); + for (const int i_profile : src.index_range()) { + const int profile_edge_offset = i_profile * main_segment_num; + dst.slice(profile_edge_offset, main_segment_num).fill(src[i_profile]); } } template<typename T> static void copy_profile_point_data_to_mesh_faces(const Span<T> src, - const ResultInfo &info, + const int main_segment_num, + const int profile_segment_num, MutableSpan<T> 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)) { + for (const int i_ring : IndexRange(main_segment_num)) { + const int profile_face_start = i_ring * profile_segment_num; + for (const int i_profile : IndexRange(profile_segment_num)) { 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) +static void copy_profile_point_domain_attribute_to_mesh(const CurvesInfo &curves_info, + const ResultOffsets &offsets, + const AttributeDomain dst_domain, + const GSpan src_all, + GMutableSpan dst_all) { - GVArray 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) { + attribute_math::convert_to_static_type(src_all.type(), [&](auto dummy) { using T = decltype(dummy); - switch (dst.domain) { + const Span<T> src = src_all.typed<T>(); + MutableSpan<T> dst = dst_all.typed<T>(); + switch (dst_domain) { case ATTR_DOMAIN_POINT: - copy_profile_point_data_to_mesh_verts(interpolated.typed<T>(), info, dst.data.typed<T>()); + foreach_curve_combination(curves_info, offsets, [&](const CombinationInfo &info) { + copy_profile_point_data_to_mesh_verts( + src.slice(info.profile_points), info.main_points.size(), dst.slice(info.vert_range)); + }); break; case ATTR_DOMAIN_EDGE: - copy_profile_point_data_to_mesh_edges(interpolated.typed<T>(), info, dst.data.typed<T>()); + foreach_curve_combination(curves_info, offsets, [&](const CombinationInfo &info) { + copy_profile_point_data_to_mesh_edges( + src.slice(info.profile_points), info.main_segment_num, dst.slice(info.edge_range)); + }); break; case ATTR_DOMAIN_FACE: - copy_profile_point_data_to_mesh_faces(interpolated.typed<T>(), info, dst.data.typed<T>()); + foreach_curve_combination(curves_info, offsets, [&](const CombinationInfo &info) { + copy_profile_point_data_to_mesh_faces(src.slice(info.profile_points), + info.main_segment_num, + info.profile_segment_num, + dst.slice(info.poly_range)); + }); break; case ATTR_DOMAIN_CORNER: /* Unsupported for now, since there are no builtin attributes to convert into. */ @@ -568,198 +586,236 @@ static void copy_profile_point_attribute_to_mesh(const GSpan src, }); } -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<typename T> -static void copy_spline_data_to_mesh(Span<T> src, Span<int> offsets, MutableSpan<T> dst) +static void copy_indices_to_offset_ranges(const VArray<T> &src, + const Span<int> curve_indices, + const Span<int> mesh_offsets, + MutableSpan<T> dst) { - for (const int i : IndexRange(src.size())) { - dst.slice(offsets[i], offsets[i + 1] - offsets[i]).fill(src[i]); - } + /* This unnecessarily instantiates the "is single" case (which should be handled elsewhere if + * it's ever used for attributes), but the alternative is duplicating the function for spans and + * other virtual arrays. */ + devirtualize_varray(src, [&](const auto &src) { + threading::parallel_for(curve_indices.index_range(), 512, [&](IndexRange range) { + for (const int i : range) { + dst.slice(offsets_to_range(mesh_offsets, i)).fill(src[curve_indices[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) +static void copy_curve_domain_attribute_to_mesh(const ResultOffsets &mesh_offsets, + const Span<int> curve_indices, + const AttributeDomain dst_domain, + const GVArray &src, + GMutableSpan dst) { + Span<int> offsets; + switch (dst_domain) { + case ATTR_DOMAIN_POINT: + offsets = mesh_offsets.vert; + break; + case ATTR_DOMAIN_EDGE: + offsets = mesh_offsets.edge; + break; + case ATTR_DOMAIN_FACE: + offsets = mesh_offsets.poly; + break; + case ATTR_DOMAIN_CORNER: + offsets = mesh_offsets.loop; + break; + default: + BLI_assert_unreachable(); + return; + } 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<T>(), offsets.vert, dst_attribute.data.typed<T>()); - break; - case ATTR_DOMAIN_EDGE: - copy_spline_data_to_mesh(src.typed<T>(), offsets.edge, dst_attribute.data.typed<T>()); - break; - case ATTR_DOMAIN_FACE: - copy_spline_data_to_mesh(src.typed<T>(), offsets.poly, dst_attribute.data.typed<T>()); - break; - case ATTR_DOMAIN_CORNER: - copy_spline_data_to_mesh(src.typed<T>(), offsets.loop, dst_attribute.data.typed<T>()); - break; - default: - BLI_assert_unreachable(); - break; - } + copy_indices_to_offset_ranges(src.typed<T>(), curve_indices, offsets, dst.typed<T>()); }); } -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); - } -} - -Mesh *curve_to_mesh_sweep(const CurveEval &curve, const CurveEval &profile, const bool fill_caps) +Mesh *curve_to_mesh_sweep(const CurvesGeometry &main, + const CurvesGeometry &profile, + const bool fill_caps) { - Span<SplinePtr> profiles = profile.splines(); - Span<SplinePtr> curves = curve.splines(); + const CurvesInfo curves_info = get_curves_info(main, profile); - const ResultOffsets offsets = calculate_result_offsets(profiles, curves, fill_caps); + const ResultOffsets offsets = calculate_result_offsets(curves_info, 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); + MutableSpan<MVert> verts(mesh->mvert, mesh->totvert); + MutableSpan<MEdge> edges(mesh->medge, mesh->totedge); + MutableSpan<MLoop> loops(mesh->mloop, mesh->totloop); + MutableSpan<MPoly> polys(mesh->mpoly, mesh->totpoly); + + foreach_curve_combination(curves_info, offsets, [&](const CombinationInfo &info) { + fill_mesh_topology(info.vert_range.start(), + info.edge_range.start(), + info.poly_range.start(), + info.loop_range.start(), + info.main_points.size(), + info.profile_points.size(), + info.main_cyclic, + info.profile_cyclic, + fill_caps, + edges, + loops, + polys); + }); + + const Span<float3> main_positions = main.evaluated_positions(); + const Span<float3> tangents = main.evaluated_tangents(); + const Span<float3> normals = main.evaluated_normals(); + const Span<float3> profile_positions = profile.evaluated_positions(); + + Vector<std::byte> eval_buffer; + + Curves main_id = {nullptr}; + main_id.geometry = reinterpret_cast<const ::CurvesGeometry &>(main); + CurveComponent main_component; + main_component.replace(&main_id, GeometryOwnershipType::Editable); + + Curves profile_id = {nullptr}; + profile_id.geometry = reinterpret_cast<const ::CurvesGeometry &>(profile); + CurveComponent profile_component; + profile_component.replace(&profile_id, GeometryOwnershipType::Editable); + + Span<float> radii = {}; + if (main_component.attribute_exists("radius")) { + radii = evaluated_attribute_if_necessary( + main_component.attribute_get_for_read<float>("radius", ATTR_DOMAIN_POINT, 1.0f), + main, + curves_info.main_type_counts, + eval_buffer) + .typed<float>(); + } + + foreach_curve_combination(curves_info, offsets, [&](const CombinationInfo &info) { + fill_mesh_positions(info.main_points.size(), + info.profile_points.size(), + main_positions.slice(info.main_points), + profile_positions.slice(info.profile_points), + tangents.slice(info.main_points), + normals.slice(info.main_points), + radii.is_empty() ? radii : radii.slice(info.main_points), + verts.slice(info.vert_range)); + }); + + if (curves_info.profile_type_counts[CURVE_TYPE_BEZIER] > 0) { + const VArray<int8_t> curve_types = profile.curve_types(); + const VArray_Span<int8_t> handle_types_left{profile.handle_types_left()}; + const VArray_Span<int8_t> handle_types_right{profile.handle_types_right()}; + + foreach_curve_combination(curves_info, offsets, [&](const CombinationInfo &info) { + if (curve_types[info.i_profile] == CURVE_TYPE_BEZIER) { + const IndexRange points = profile.points_for_curve(info.i_profile); + mark_bezier_vector_edges_sharp(points.size(), + info.main_segment_num, + profile.bezier_evaluated_offsets_for_curve(info.i_profile), + handle_types_left.slice(points), + handle_types_right.slice(points), + edges.slice(info.edge_range)); + } + }); + } + + Set<AttributeIDRef> main_attributes; - /* Create the mesh component for retrieving attributes at this scope, since output attributes - * can keep a reference to the component for updating after retrieving write access. */ MeshComponent mesh_component; mesh_component.replace(mesh, GeometryOwnershipType::Editable); - ResultAttributes attributes = create_result_attributes(curve, profile, mesh_component); - 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); - } - }); + main_component.attribute_foreach([&](const AttributeIDRef &id, + const AttributeMetaData meta_data) { + if (!should_add_attribute_to_mesh(main_component, mesh_component, id)) { + return true; } + main_attributes.add_new(id); + + const AttributeDomain src_domain = meta_data.domain; + const CustomDataType type = meta_data.data_type; + GVArray src = main_component.attribute_try_get_for_read(id, src_domain, type); + + const AttributeDomain dst_domain = get_attribute_domain_for_mesh(mesh_component, id); + OutputAttribute dst = mesh_component.attribute_try_get_for_output_only(id, dst_domain, type); + if (!dst) { + return true; + } + + if (src_domain == ATTR_DOMAIN_POINT) { + copy_main_point_domain_attribute_to_mesh( + curves_info, + offsets, + dst_domain, + evaluated_attribute_if_necessary(src, main, curves_info.main_type_counts, eval_buffer), + dst.as_span()); + } + else if (src_domain == ATTR_DOMAIN_CURVE) { + copy_curve_domain_attribute_to_mesh( + offsets, offsets.main_indices, dst_domain, src, dst.as_span()); + } + + dst.save(); + return true; }); - copy_spline_domain_attributes_to_mesh(curve, profile, offsets, attributes); + profile_component.attribute_foreach([&](const AttributeIDRef &id, + const AttributeMetaData meta_data) { + if (main_attributes.contains(id)) { + return true; + } + if (!should_add_attribute_to_mesh(profile_component, mesh_component, id)) { + return true; + } + const AttributeDomain src_domain = meta_data.domain; + const CustomDataType type = meta_data.data_type; + GVArray src = profile_component.attribute_try_get_for_read(id, src_domain, type); + + const AttributeDomain dst_domain = get_attribute_domain_for_mesh(mesh_component, id); + OutputAttribute dst = mesh_component.attribute_try_get_for_output_only(id, dst_domain, type); + if (!dst) { + return true; + } + + if (src_domain == ATTR_DOMAIN_POINT) { + copy_profile_point_domain_attribute_to_mesh( + curves_info, + offsets, + dst_domain, + evaluated_attribute_if_necessary( + src, profile, curves_info.profile_type_counts, eval_buffer), + dst.as_span()); + } + else if (src_domain == ATTR_DOMAIN_CURVE) { + copy_curve_domain_attribute_to_mesh( + offsets, offsets.profile_indices, dst_domain, src, dst.as_span()); + } - for (OutputAttribute &output_attribute : attributes.attributes) { - output_attribute.save(); - } + dst.save(); + return true; + }); return mesh; } -static CurveEval get_curve_single_vert() +static CurvesGeometry get_curve_single_vert() { - CurveEval curve; - std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>(); - spline->resize(1.0f); - spline->positions().fill(float3(0)); - spline->radii().fill(1.0f); - spline->tilts().fill(0.0f); - curve.add_spline(std::move(spline)); - - return curve; + CurvesGeometry curves(1, 1); + curves.offsets_for_write().last() = 1; + curves.positions_for_write().fill(float3(0)); + + return curves; } -Mesh *curve_to_wire_mesh(const CurveEval &curve) +Mesh *curve_to_wire_mesh(const CurvesGeometry &curve) { - static const CurveEval vert_curve = get_curve_single_vert(); + static const CurvesGeometry vert_curve = get_curve_single_vert(); return curve_to_mesh_sweep(curve, vert_curve, false); } diff --git a/source/blender/blenkernel/intern/curves.cc b/source/blender/blenkernel/intern/curves.cc index ebbdff55a55..5b51fb8af31 100644 --- a/source/blender/blenkernel/intern/curves.cc +++ b/source/blender/blenkernel/intern/curves.cc @@ -27,6 +27,7 @@ #include "BKE_anim_data.h" #include "BKE_curves.hh" #include "BKE_customdata.h" +#include "BKE_geometry_set.hh" #include "BKE_global.h" #include "BKE_idtype.h" #include "BKE_lib_id.h" @@ -282,13 +283,11 @@ Curves *BKE_curves_copy_for_eval(Curves *curves_src, bool reference) return result; } -static Curves *curves_evaluate_modifiers(struct Depsgraph *depsgraph, - struct Scene *scene, - Object *object, - Curves *curves_input) +static void curves_evaluate_modifiers(struct Depsgraph *depsgraph, + struct Scene *scene, + Object *object, + GeometrySet &geometry_set) { - Curves *curves = curves_input; - /* Modifier evaluation modes. */ const bool use_render = (DEG_get_mode(depsgraph) == DAG_EVAL_RENDER); const int required_mode = use_render ? eModifierMode_Render : eModifierMode_Realtime; @@ -308,27 +307,10 @@ static Curves *curves_evaluate_modifiers(struct Depsgraph *depsgraph, continue; } - if ((mti->type == eModifierTypeType_OnlyDeform) && - (mti->flags & eModifierTypeFlag_AcceptsVertexCosOnly)) { - /* Ensure we are not modifying the input. */ - if (curves == curves_input) { - curves = BKE_curves_copy_for_eval(curves, true); - } - - /* Created deformed coordinates array on demand. */ - blender::bke::CurvesGeometry &geometry = blender::bke::CurvesGeometry::wrap( - curves->geometry); - MutableSpan<float3> positions = geometry.positions_for_write(); - - mti->deformVerts(md, - &mectx, - nullptr, - reinterpret_cast<float(*)[3]>(positions.data()), - curves->geometry.point_size); + if (mti->modifyGeometrySet != nullptr) { + mti->modifyGeometrySet(md, &mectx, &geometry_set); } } - - return curves; } void BKE_curves_data_update(struct Depsgraph *depsgraph, struct Scene *scene, Object *object) @@ -338,11 +320,20 @@ void BKE_curves_data_update(struct Depsgraph *depsgraph, struct Scene *scene, Ob /* Evaluate modifiers. */ Curves *curves = static_cast<Curves *>(object->data); - Curves *curves_eval = curves_evaluate_modifiers(depsgraph, scene, object, curves); + GeometrySet geometry_set = GeometrySet::create_with_curves(curves, + GeometryOwnershipType::ReadOnly); + curves_evaluate_modifiers(depsgraph, scene, object, geometry_set); /* Assign evaluated object. */ - const bool is_owned = (curves != curves_eval); - BKE_object_eval_assign_data(object, &curves_eval->id, is_owned); + Curves *curves_eval = const_cast<Curves *>(geometry_set.get_curves_for_read()); + if (curves_eval == nullptr) { + curves_eval = blender::bke::curves_new_nomain(0, 0); + BKE_object_eval_assign_data(object, &curves_eval->id, true); + } + else { + BKE_object_eval_assign_data(object, &curves_eval->id, false); + } + object->runtime.geometry_set_eval = new GeometrySet(std::move(geometry_set)); } /* Draw Cache */ diff --git a/source/blender/blenkernel/intern/curves_geometry.cc b/source/blender/blenkernel/intern/curves_geometry.cc index 5c89dfd4df5..8e97884516c 100644 --- a/source/blender/blenkernel/intern/curves_geometry.cc +++ b/source/blender/blenkernel/intern/curves_geometry.cc @@ -12,6 +12,7 @@ #include "BLI_bounds.hh" #include "BLI_index_mask_ops.hh" #include "BLI_length_parameterize.hh" +#include "BLI_math_rotation.hh" #include "DNA_curves_types.h" @@ -22,6 +23,7 @@ namespace blender::bke { static const std::string ATTR_POSITION = "position"; static const std::string ATTR_RADIUS = "radius"; +static const std::string ATTR_TILT = "tilt"; static const std::string ATTR_CURVE_TYPE = "curve_type"; static const std::string ATTR_CYCLIC = "cyclic"; static const std::string ATTR_RESOLUTION = "resolution"; @@ -330,6 +332,15 @@ MutableSpan<int8_t> CurvesGeometry::normal_mode_for_write() return get_mutable_attribute<int8_t>(*this, ATTR_DOMAIN_CURVE, ATTR_NORMAL_MODE); } +VArray<float> CurvesGeometry::tilt() const +{ + return get_varray_attribute<float>(*this, ATTR_DOMAIN_POINT, ATTR_TILT, 0.0f); +} +MutableSpan<float> CurvesGeometry::tilt_for_write() +{ + return get_mutable_attribute<float>(*this, ATTR_DOMAIN_POINT, ATTR_TILT); +} + VArray<int8_t> CurvesGeometry::handle_types_left() const { return get_varray_attribute<int8_t>(*this, ATTR_DOMAIN_POINT, ATTR_HANDLE_TYPE_LEFT, 0); @@ -577,18 +588,25 @@ void CurvesGeometry::ensure_nurbs_basis_cache() const Span<float3> CurvesGeometry::evaluated_positions() const { if (!this->runtime->position_cache_dirty) { - return this->runtime->evaluated_position_cache; + return this->runtime->evaluated_positions_span; } /* A double checked lock. */ std::scoped_lock lock{this->runtime->position_cache_mutex}; if (!this->runtime->position_cache_dirty) { - return this->runtime->evaluated_position_cache; + return this->runtime->evaluated_positions_span; } threading::isolate_task([&]() { + if (this->is_single_type(CURVE_TYPE_POLY)) { + this->runtime->evaluated_positions_span = this->positions(); + this->runtime->evaluated_position_cache.clear_and_make_inline(); + return; + } + this->runtime->evaluated_position_cache.resize(this->evaluated_points_num()); MutableSpan<float3> evaluated_positions = this->runtime->evaluated_position_cache; + this->runtime->evaluated_positions_span = evaluated_positions; VArray<int8_t> types = this->curve_types(); VArray<bool> cyclic = this->cyclic(); @@ -645,7 +663,7 @@ Span<float3> CurvesGeometry::evaluated_positions() const }); this->runtime->position_cache_dirty = false; - return this->runtime->evaluated_position_cache; + return this->runtime->evaluated_positions_span; } Span<float3> CurvesGeometry::evaluated_tangents() const @@ -710,6 +728,15 @@ Span<float3> CurvesGeometry::evaluated_tangents() const return this->runtime->evaluated_tangent_cache; } +static void rotate_directions_around_axes(MutableSpan<float3> directions, + const Span<float3> axes, + const Span<float> angles) +{ + for (const int i : directions.index_range()) { + directions[i] = math::rotate_direction_around_axis(directions[i], axes[i], angles[i]); + } +} + Span<float3> CurvesGeometry::evaluated_normals() const { if (!this->runtime->normal_cache_dirty) { @@ -726,11 +753,16 @@ Span<float3> CurvesGeometry::evaluated_normals() const const Span<float3> evaluated_tangents = this->evaluated_tangents(); const VArray<bool> cyclic = this->cyclic(); const VArray<int8_t> normal_mode = this->normal_mode(); + const VArray<int8_t> types = this->curve_types(); + const VArray<float> tilt = this->tilt(); this->runtime->evaluated_normal_cache.resize(this->evaluated_points_num()); MutableSpan<float3> evaluated_normals = this->runtime->evaluated_normal_cache; threading::parallel_for(this->curves_range(), 128, [&](IndexRange curves_range) { + /* Reuse a buffer for the evaluated tilts. */ + Vector<float> evaluated_tilts; + for (const int curve_index : curves_range) { const IndexRange evaluated_points = this->evaluated_points_for_curve(curve_index); if (UNLIKELY(evaluated_points.is_empty())) { @@ -747,6 +779,27 @@ Span<float3> CurvesGeometry::evaluated_normals() const evaluated_normals.slice(evaluated_points)); break; } + + /* If the "tilt" attribute exists, rotate the normals around the tangents by the + * evaluated angles. We can avoid copying the tilts to evaluate them for poly curves. */ + if (!(tilt.is_single() && tilt.get_internal_single() == 0.0f)) { + const IndexRange points = this->points_for_curve(curve_index); + Span<float> curve_tilt = tilt.get_internal_span().slice(points); + if (types[curve_index] == CURVE_TYPE_POLY) { + rotate_directions_around_axes(evaluated_normals.slice(evaluated_points), + evaluated_tangents.slice(evaluated_points), + curve_tilt); + } + else { + evaluated_tilts.clear(); + evaluated_tilts.resize(evaluated_points.size()); + this->interpolate_to_evaluated( + curve_index, curve_tilt, evaluated_tilts.as_mutable_span()); + rotate_directions_around_axes(evaluated_normals.slice(evaluated_points), + evaluated_tangents.slice(evaluated_points), + evaluated_tilts.as_span()); + } + } } }); }); @@ -787,6 +840,48 @@ void CurvesGeometry::interpolate_to_evaluated(const int curve_index, BLI_assert_unreachable(); } +void CurvesGeometry::interpolate_to_evaluated(const GSpan src, GMutableSpan dst) const +{ + BLI_assert(!this->runtime->offsets_cache_dirty); + BLI_assert(!this->runtime->nurbs_basis_cache_dirty); + const VArray<int8_t> types = this->curve_types(); + const VArray<int> resolution = this->resolution(); + const VArray<bool> cyclic = this->cyclic(); + const VArray<int8_t> nurbs_orders = this->nurbs_orders(); + const Span<float> nurbs_weights = this->nurbs_weights(); + + threading::parallel_for(this->curves_range(), 512, [&](IndexRange curves_range) { + for (const int curve_index : curves_range) { + const IndexRange points = this->points_for_curve(curve_index); + const IndexRange evaluated_points = this->evaluated_points_for_curve(curve_index); + switch (types[curve_index]) { + case CURVE_TYPE_CATMULL_ROM: + curves::catmull_rom::interpolate_to_evaluated(src.slice(points), + cyclic[curve_index], + resolution[curve_index], + dst.slice(evaluated_points)); + continue; + case CURVE_TYPE_POLY: + dst.slice(evaluated_points).copy_from(src.slice(points)); + continue; + case CURVE_TYPE_BEZIER: + curves::bezier::interpolate_to_evaluated( + src.slice(points), + this->runtime->bezier_evaluated_offsets.as_span().slice(points), + dst.slice(evaluated_points)); + continue; + case CURVE_TYPE_NURBS: + curves::nurbs::interpolate_to_evaluated(this->runtime->nurbs_basis_cache[curve_index], + nurbs_orders[curve_index], + nurbs_weights.slice(points), + src.slice(points), + dst.slice(evaluated_points)); + continue; + } + } + }); +} + void CurvesGeometry::ensure_evaluated_lengths() const { if (!this->runtime->length_cache_dirty) { diff --git a/source/blender/blenkernel/intern/editmesh.c b/source/blender/blenkernel/intern/editmesh.c index 114d33b1a3d..d176bf41254 100644 --- a/source/blender/blenkernel/intern/editmesh.c +++ b/source/blender/blenkernel/intern/editmesh.c @@ -214,18 +214,14 @@ float (*BKE_editmesh_vert_coords_alloc(struct Depsgraph *depsgraph, Object *ob, int *r_vert_len))[3] { - Mesh *cage; - BLI_bitmap *visit_bitmap; - struct CageUserData data; - float(*cos_cage)[3]; + Mesh *cage = editbmesh_get_eval_cage(depsgraph, scene, ob, em, &CD_MASK_BAREMESH); + float(*cos_cage)[3] = MEM_callocN(sizeof(*cos_cage) * em->bm->totvert, "bmbvh cos_cage"); - cage = editbmesh_get_eval_cage(depsgraph, scene, ob, em, &CD_MASK_BAREMESH); - cos_cage = MEM_callocN(sizeof(*cos_cage) * em->bm->totvert, "bmbvh cos_cage"); - - /* when initializing cage verts, we only want the first cage coordinate for each vertex, - * so that e.g. mirror or array use original vertex coordinates and not mirrored or duplicate */ - visit_bitmap = BLI_BITMAP_NEW(em->bm->totvert, __func__); + /* When initializing cage verts, we only want the first cage coordinate for each vertex, + * so that e.g. mirror or array use original vertex coordinates and not mirrored or duplicate. */ + BLI_bitmap *visit_bitmap = BLI_BITMAP_NEW(em->bm->totvert, __func__); + struct CageUserData data; data.totvert = em->bm->totvert; data.cos_cage = cos_cage; data.visit_bitmap = visit_bitmap; diff --git a/source/blender/blenkernel/intern/editmesh_cache.c b/source/blender/blenkernel/intern/editmesh_cache.cc index f80f300d149..438d287fb28 100644 --- a/source/blender/blenkernel/intern/editmesh_cache.c +++ b/source/blender/blenkernel/intern/editmesh_cache.cc @@ -8,7 +8,9 @@ #include "MEM_guardedalloc.h" +#include "BLI_bounds.hh" #include "BLI_math_vector.h" +#include "BLI_span.hh" #include "DNA_mesh_types.h" @@ -21,23 +23,21 @@ void BKE_editmesh_cache_ensure_poly_normals(BMEditMesh *em, EditMeshData *emd) { - if (!(emd->vertexCos && (emd->polyNos == NULL))) { + if (!(emd->vertexCos && (emd->polyNos == nullptr))) { return; } BMesh *bm = em->bm; - const float(*vertexCos)[3]; - float(*polyNos)[3]; - BMFace *efa; BMIter fiter; int i; BM_mesh_elem_index_ensure(bm, BM_VERT); - polyNos = MEM_mallocN(sizeof(*polyNos) * bm->totface, __func__); + float(*polyNos)[3] = static_cast<float(*)[3]>( + MEM_mallocN(sizeof(*polyNos) * bm->totface, __func__)); - vertexCos = emd->vertexCos; + const float(*vertexCos)[3] = emd->vertexCos; BM_ITER_MESH_INDEX (efa, &fiter, bm, BM_FACES_OF_MESH, i) { BM_elem_index_set(efa, i); /* set_inline */ @@ -50,7 +50,7 @@ void BKE_editmesh_cache_ensure_poly_normals(BMEditMesh *em, EditMeshData *emd) void BKE_editmesh_cache_ensure_vert_normals(BMEditMesh *em, EditMeshData *emd) { - if (!(emd->vertexCos && (emd->vertexNos == NULL))) { + if (!(emd->vertexCos && (emd->vertexNos == nullptr))) { return; } @@ -58,14 +58,14 @@ void BKE_editmesh_cache_ensure_vert_normals(BMEditMesh *em, EditMeshData *emd) const float(*vertexCos)[3], (*polyNos)[3]; float(*vertexNos)[3]; - /* calculate vertex normals from poly normals */ + /* Calculate vertex normals from poly normals. */ BKE_editmesh_cache_ensure_poly_normals(em, emd); BM_mesh_elem_index_ensure(bm, BM_FACE); polyNos = emd->polyNos; vertexCos = emd->vertexCos; - vertexNos = MEM_callocN(sizeof(*vertexNos) * bm->totvert, __func__); + vertexNos = static_cast<float(*)[3]>(MEM_callocN(sizeof(*vertexNos) * bm->totvert, __func__)); BM_verts_calc_normal_vcos(bm, polyNos, vertexCos, vertexNos); @@ -74,17 +74,17 @@ void BKE_editmesh_cache_ensure_vert_normals(BMEditMesh *em, EditMeshData *emd) void BKE_editmesh_cache_ensure_poly_centers(BMEditMesh *em, EditMeshData *emd) { - if (emd->polyCos != NULL) { + if (emd->polyCos != nullptr) { return; } BMesh *bm = em->bm; - float(*polyCos)[3]; BMFace *efa; BMIter fiter; int i; - polyCos = MEM_mallocN(sizeof(*polyCos) * bm->totface, __func__); + float(*polyCos)[3] = static_cast<float(*)[3]>( + MEM_mallocN(sizeof(*polyCos) * bm->totface, __func__)); if (emd->vertexCos) { const float(*vertexCos)[3]; @@ -116,18 +116,20 @@ bool BKE_editmesh_cache_calc_minmax(struct BMEditMesh *em, float min[3], float max[3]) { + using namespace blender; BMesh *bm = em->bm; - BMVert *eve; - BMIter iter; - int i; if (bm->totvert) { if (emd->vertexCos) { - BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, i) { - minmax_v3v3_v3(min, max, emd->vertexCos[i]); - } + Span<float3> vert_coords(reinterpret_cast<const float3 *>(emd->vertexCos), bm->totvert); + std::optional<bounds::MinMaxResult<float3>> bounds = bounds::min_max(vert_coords); + BLI_assert(bounds.has_value()); + copy_v3_v3(min, math::min(bounds->min, float3(min))); + copy_v3_v3(max, math::max(bounds->max, float3(max))); } else { + BMVert *eve; + BMIter iter; BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) { minmax_v3v3_v3(min, max, eve->co); } diff --git a/source/blender/blenkernel/intern/geometry_component_curves.cc b/source/blender/blenkernel/intern/geometry_component_curves.cc index 0bcab0aae7a..4796135b32f 100644 --- a/source/blender/blenkernel/intern/geometry_component_curves.cc +++ b/source/blender/blenkernel/intern/geometry_component_curves.cc @@ -462,14 +462,14 @@ static ComponentAttributeProviders create_attribute_providers_for_curve() static BuiltinCustomDataLayerProvider nurbs_order("nurbs_order", ATTR_DOMAIN_CURVE, - CD_PROP_INT32, - CD_PROP_INT32, + CD_PROP_INT8, + CD_PROP_INT8, BuiltinAttributeProvider::Creatable, BuiltinAttributeProvider::Writable, BuiltinAttributeProvider::Deletable, curve_access, - make_array_read_attribute<int>, - make_array_write_attribute<int>, + make_array_read_attribute<int8_t>, + make_array_write_attribute<int8_t>, tag_component_topology_changed); static BuiltinCustomDataLayerProvider normal_mode("normal_mode", @@ -545,6 +545,7 @@ static ComponentAttributeProviders create_attribute_providers_for_curve() &handle_type_left, &normal_mode, &nurbs_order, + &nurbs_knots_mode, &nurbs_weight, &curve_type, &resolution, diff --git a/source/blender/blenkernel/intern/geometry_set_instances.cc b/source/blender/blenkernel/intern/geometry_set_instances.cc index 44ffd64e475..d3c3f41779a 100644 --- a/source/blender/blenkernel/intern/geometry_set_instances.cc +++ b/source/blender/blenkernel/intern/geometry_set_instances.cc @@ -50,7 +50,12 @@ GeometrySet object_get_evaluated_geometry_set(const Object &object) return geometry_set; } if (object.runtime.geometry_set_eval != nullptr) { - return *object.runtime.geometry_set_eval; + GeometrySet geometry_set = *object.runtime.geometry_set_eval; + /* Ensure that subdivision is performed on the CPU. */ + if (geometry_set.has_mesh()) { + add_final_mesh_as_geometry_component(object, geometry_set); + } + return geometry_set; } /* Otherwise, construct a new geometry set with the component based on the object type. */ diff --git a/source/blender/blenkernel/intern/gpencil_geom.cc b/source/blender/blenkernel/intern/gpencil_geom.cc index e1a79986719..041696fa8d3 100644 --- a/source/blender/blenkernel/intern/gpencil_geom.cc +++ b/source/blender/blenkernel/intern/gpencil_geom.cc @@ -981,7 +981,7 @@ bool BKE_gpencil_stroke_shrink(bGPDstroke *gps, const float dist, const short mo * \{ */ bool BKE_gpencil_stroke_smooth_point(bGPDstroke *gps, - int i, + int point_index, float influence, int iterations, const bool smooth_caps, @@ -995,7 +995,7 @@ bool BKE_gpencil_stroke_smooth_point(bGPDstroke *gps, /* Overview of the algorithm here and in the following smooth functions: * The smooth functions return the new attribute in question for a single point. - * The result is stored in r_gps->points[i], while the data is read from gps. + * The result is stored in r_gps->points[point_index], while the data is read from gps. * To get a correct result, duplicate the stroke point data and read from the copy, * while writing to the real stroke. Not doing that will result in acceptable, but * asymmetric results. @@ -1004,16 +1004,16 @@ bool BKE_gpencil_stroke_smooth_point(bGPDstroke *gps, * the parameter "iterations" set to 1 or 2. (2 matches the old algorithm). */ - const bGPDspoint *pt = &gps->points[i]; + const bGPDspoint *pt = &gps->points[point_index]; const bool is_cyclic = (gps->flag & GP_STROKE_CYCLIC) != 0; /* If smooth_caps is false, the caps will not be translated by smoothing. */ - if (!smooth_caps && !is_cyclic && ELEM(i, 0, gps->totpoints - 1)) { - copy_v3_v3(&r_gps->points[i].x, &pt->x); + if (!smooth_caps && !is_cyclic && ELEM(point_index, 0, gps->totpoints - 1)) { + copy_v3_v3(&r_gps->points[point_index].x, &pt->x); return true; } /* This function uses a binomial kernel, which is the discrete version of gaussian blur. - * The weight for a vertex at the relative index i is + * The weight for a vertex at the relative index point_index is * w = nCr(n, j + n/2) / 2^n = (n/1 * (n-1)/2 * ... * (n-j-n/2)/(j+n/2)) / 2^n * All weights together sum up to 1 * This is equivalent to doing multiple iterations of averaging neighbors, @@ -1044,8 +1044,8 @@ bool BKE_gpencil_stroke_smooth_point(bGPDstroke *gps, 0.0; double total_w = 0.0; for (int step = iterations; step > 0; step--) { - int before = i - step; - int after = i + step; + int before = point_index - step; + int after = point_index + step; float w_before = (float)(w - w2); float w_after = (float)(w - w2); @@ -1056,13 +1056,13 @@ bool BKE_gpencil_stroke_smooth_point(bGPDstroke *gps, else { if (before < 0) { if (!smooth_caps) { - w_before *= -before / (float)i; + w_before *= -before / (float)point_index; } before = 0; } if (after > gps->totpoints - 1) { if (!smooth_caps) { - w_after *= (after - (gps->totpoints - 1)) / (float)(gps->totpoints - 1 - i); + w_after *= (after - (gps->totpoints - 1)) / (float)(gps->totpoints - 1 - point_index); } after = gps->totpoints - 1; } @@ -1089,7 +1089,7 @@ bool BKE_gpencil_stroke_smooth_point(bGPDstroke *gps, add_v3_v3(sco, &pt->x); /* Based on influence factor, blend between original and optimal smoothed coordinate. */ - interp_v3_v3v3(&r_gps->points[i].x, &pt->x, sco, influence); + interp_v3_v3v3(&r_gps->points[point_index].x, &pt->x, sco, influence); return true; } @@ -1101,7 +1101,7 @@ bool BKE_gpencil_stroke_smooth_point(bGPDstroke *gps, * \{ */ bool BKE_gpencil_stroke_smooth_strength( - bGPDstroke *gps, int i, float influence, int iterations, bGPDstroke *r_gps) + bGPDstroke *gps, int point_index, float influence, int iterations, bGPDstroke *r_gps) { /* If nothing to do, return early */ if (gps->totpoints <= 2 || iterations <= 0) { @@ -1110,15 +1110,15 @@ bool BKE_gpencil_stroke_smooth_strength( /* See BKE_gpencil_stroke_smooth_point for details on the algorithm. */ - const bGPDspoint *pt = &gps->points[i]; + const bGPDspoint *pt = &gps->points[point_index]; const bool is_cyclic = (gps->flag & GP_STROKE_CYCLIC) != 0; float strength = 0.0f; const int n_half = (iterations * iterations) / 4 + iterations; double w = 1.0; double total_w = 0.0; for (int step = iterations; step > 0; step--) { - int before = i - step; - int after = i + step; + int before = point_index - step; + int after = point_index + step; float w_before = (float)w; float w_after = (float)w; @@ -1147,7 +1147,7 @@ bool BKE_gpencil_stroke_smooth_strength( strength /= total_w; /* Based on influence factor, blend between original and optimal smoothed value. */ - r_gps->points[i].strength = pt->strength + strength * influence; + r_gps->points[point_index].strength = pt->strength + strength * influence; return true; } @@ -1159,7 +1159,7 @@ bool BKE_gpencil_stroke_smooth_strength( * \{ */ bool BKE_gpencil_stroke_smooth_thickness( - bGPDstroke *gps, int i, float influence, int iterations, bGPDstroke *r_gps) + bGPDstroke *gps, int point_index, float influence, int iterations, bGPDstroke *r_gps) { /* If nothing to do, return early */ if (gps->totpoints <= 2 || iterations <= 0) { @@ -1168,15 +1168,15 @@ bool BKE_gpencil_stroke_smooth_thickness( /* See BKE_gpencil_stroke_smooth_point for details on the algorithm. */ - const bGPDspoint *pt = &gps->points[i]; + const bGPDspoint *pt = &gps->points[point_index]; const bool is_cyclic = (gps->flag & GP_STROKE_CYCLIC) != 0; float pressure = 0.0f; const int n_half = (iterations * iterations) / 4 + iterations; double w = 1.0; double total_w = 0.0; for (int step = iterations; step > 0; step--) { - int before = i - step; - int after = i + step; + int before = point_index - step; + int after = point_index + step; float w_before = (float)w; float w_after = (float)w; @@ -1205,7 +1205,7 @@ bool BKE_gpencil_stroke_smooth_thickness( pressure /= total_w; /* Based on influence factor, blend between original and optimal smoothed value. */ - r_gps->points[i].pressure = pt->pressure + pressure * influence; + r_gps->points[point_index].pressure = pt->pressure + pressure * influence; return true; } @@ -1216,8 +1216,11 @@ bool BKE_gpencil_stroke_smooth_thickness( /** \name Stroke Smooth UV * \{ */ -bool BKE_gpencil_stroke_smooth_uv( - struct bGPDstroke *gps, int i, float influence, int iterations, struct bGPDstroke *r_gps) +bool BKE_gpencil_stroke_smooth_uv(struct bGPDstroke *gps, + int point_index, + float influence, + int iterations, + struct bGPDstroke *r_gps) { /* If nothing to do, return early */ if (gps->totpoints <= 2 || iterations <= 0) { @@ -1226,13 +1229,13 @@ bool BKE_gpencil_stroke_smooth_uv( /* See BKE_gpencil_stroke_smooth_point for details on the algorithm. */ - const bGPDspoint *pt = &gps->points[i]; + const bGPDspoint *pt = &gps->points[point_index]; const bool is_cyclic = (gps->flag & GP_STROKE_CYCLIC) != 0; /* If don't change the caps. */ - if (!is_cyclic && ELEM(i, 0, gps->totpoints - 1)) { - r_gps->points[i].uv_rot = pt->uv_rot; - r_gps->points[i].uv_fac = pt->uv_fac; + if (!is_cyclic && ELEM(point_index, 0, gps->totpoints - 1)) { + r_gps->points[point_index].uv_rot = pt->uv_rot; + r_gps->points[point_index].uv_fac = pt->uv_fac; return true; } @@ -1242,8 +1245,8 @@ bool BKE_gpencil_stroke_smooth_uv( double w = 1.0; double total_w = 0.0; for (int step = iterations; step > 0; step--) { - int before = i - step; - int after = i + step; + int before = point_index - step; + int after = point_index + step; float w_before = (float)w; float w_after = (float)w; @@ -1253,11 +1256,11 @@ bool BKE_gpencil_stroke_smooth_uv( } else { if (before < 0) { - w_before *= -before / (float)i; + w_before *= -before / (float)point_index; before = 0; } if (after > gps->totpoints - 1) { - w_after *= (after - (gps->totpoints - 1)) / (float)(gps->totpoints - 1 - i); + w_after *= (after - (gps->totpoints - 1)) / (float)(gps->totpoints - 1 - point_index); after = gps->totpoints - 1; } } @@ -1281,8 +1284,8 @@ bool BKE_gpencil_stroke_smooth_uv( uv_fac /= total_w; /* Based on influence factor, blend between original and optimal smoothed value. */ - r_gps->points[i].uv_rot = pt->uv_rot + uv_rot * influence; - r_gps->points[i].uv_fac = pt->uv_fac + uv_fac * influence; + r_gps->points[point_index].uv_rot = pt->uv_rot + uv_rot * influence; + r_gps->points[point_index].uv_fac = pt->uv_fac + uv_fac * influence; return true; } diff --git a/source/blender/blenkernel/intern/image.cc b/source/blender/blenkernel/intern/image.cc index b1385eab9cf..53ec148fd2d 100644 --- a/source/blender/blenkernel/intern/image.cc +++ b/source/blender/blenkernel/intern/image.cc @@ -1806,9 +1806,9 @@ void BKE_image_stamp_buf(Scene *scene, int channels) { struct StampData stamp_data; - float w, h, pad; + int w, h, pad; int x, y, y_ofs; - float h_fixed; + int h_fixed; const int mono = blf_mono_font_render; /* XXX */ struct ColorManagedDisplay *display; const char *display_device; @@ -1816,20 +1816,20 @@ void BKE_image_stamp_buf(Scene *scene, /* vars for calculating wordwrap */ struct { struct ResultBLF info; - rctf rect; + rcti rect; } wrap; /* this could be an argument if we want to operate on non linear float imbuf's * for now though this is only used for renders which use scene settings */ #define TEXT_SIZE_CHECK(str, w, h) \ - ((str[0]) && ((void)(h = h_fixed), (w = BLF_width(mono, str, sizeof(str))))) + ((str[0]) && ((void)(h = h_fixed), (w = (int)BLF_width(mono, str, sizeof(str))))) /* must enable BLF_WORD_WRAP before using */ #define TEXT_SIZE_CHECK_WORD_WRAP(str, w, h) \ ((str[0]) && (BLF_boundbox_ex(mono, str, sizeof(str), &wrap.rect, &wrap.info), \ (void)(h = h_fixed * wrap.info.lines), \ - (w = BLI_rctf_size_x(&wrap.rect)))) + (w = BLI_rcti_size_x(&wrap.rect)))) #define BUFF_MARGIN_X 2 #define BUFF_MARGIN_Y 1 @@ -3096,7 +3096,7 @@ void BKE_image_get_tile_label(Image *ima, ImageTile *tile, char *label, int len_ } } -bool BKE_image_get_tile_info(char *filepath, ListBase *tiles, int *tile_start, int *tile_range) +bool BKE_image_get_tile_info(char *filepath, ListBase *tiles, int *r_tile_start, int *r_tile_range) { char filename[FILE_MAXFILE], dirname[FILE_MAXDIR]; BLI_split_dirfile(filepath, dirname, filename, sizeof(dirname), sizeof(filename)); @@ -3106,7 +3106,7 @@ bool BKE_image_get_tile_info(char *filepath, ListBase *tiles, int *tile_start, i eUDIM_TILE_FORMAT tile_format; char *udim_pattern = BKE_image_get_tile_strformat(filename, &tile_format); - bool is_udim = true; + bool all_valid_udim = true; int min_udim = IMA_UDIM_MAX + 1; int max_udim = 0; int id; @@ -3124,7 +3124,7 @@ bool BKE_image_get_tile_info(char *filepath, ListBase *tiles, int *tile_start, i } if (id < 1001 || id > IMA_UDIM_MAX) { - is_udim = false; + all_valid_udim = false; break; } @@ -3135,11 +3135,14 @@ bool BKE_image_get_tile_info(char *filepath, ListBase *tiles, int *tile_start, i BLI_filelist_free(dirs, dirs_num); MEM_SAFE_FREE(udim_pattern); - if (is_udim && min_udim <= IMA_UDIM_MAX) { + /* Ensure that all discovered UDIMs are valid and that there's at least 2 files in total. + * Downstream code checks the range value to determine tiled-ness; it's important we match that + * expectation here too (T97366). */ + if (all_valid_udim && min_udim <= IMA_UDIM_MAX && max_udim > min_udim) { BLI_join_dirfile(filepath, FILE_MAX, dirname, filename); - *tile_start = min_udim; - *tile_range = max_udim - min_udim + 1; + *r_tile_start = min_udim; + *r_tile_range = max_udim - min_udim + 1; return true; } return false; diff --git a/source/blender/blenkernel/intern/lattice_deform_test.cc b/source/blender/blenkernel/intern/lattice_deform_test.cc index 1b1bca5fc53..58aadf652b7 100644 --- a/source/blender/blenkernel/intern/lattice_deform_test.cc +++ b/source/blender/blenkernel/intern/lattice_deform_test.cc @@ -69,7 +69,7 @@ static void test_lattice_deform_free(LatticeDeformTestContext *ctx) TEST(lattice_deform_performance, performance_no_dvert_1) { const int32_t num_items = 1; - LatticeDeformTestContext ctx = {{{nullptr}}}; + LatticeDeformTestContext ctx = {dna::shallow_zero_initialize()}; RandomNumberGenerator rng; test_lattice_deform_init(&ctx, &rng, num_items); test_lattice_deform(&ctx, num_items); @@ -78,7 +78,7 @@ TEST(lattice_deform_performance, performance_no_dvert_1) TEST(lattice_deform_performance, performance_no_dvert_1000) { const int32_t num_items = 1000; - LatticeDeformTestContext ctx = {{{nullptr}}}; + LatticeDeformTestContext ctx = {dna::shallow_zero_initialize()}; RandomNumberGenerator rng; test_lattice_deform_init(&ctx, &rng, num_items); test_lattice_deform(&ctx, num_items); @@ -87,7 +87,7 @@ TEST(lattice_deform_performance, performance_no_dvert_1000) TEST(lattice_deform_performance, performance_no_dvert_10000) { const int32_t num_items = 10000; - LatticeDeformTestContext ctx = {{{nullptr}}}; + LatticeDeformTestContext ctx = {dna::shallow_zero_initialize()}; RandomNumberGenerator rng; test_lattice_deform_init(&ctx, &rng, num_items); test_lattice_deform(&ctx, num_items); @@ -96,7 +96,7 @@ TEST(lattice_deform_performance, performance_no_dvert_10000) TEST(lattice_deform_performance, performance_no_dvert_100000) { const int32_t num_items = 100000; - LatticeDeformTestContext ctx = {{{nullptr}}}; + LatticeDeformTestContext ctx = {dna::shallow_zero_initialize()}; RandomNumberGenerator rng; test_lattice_deform_init(&ctx, &rng, num_items); test_lattice_deform(&ctx, num_items); @@ -105,7 +105,7 @@ TEST(lattice_deform_performance, performance_no_dvert_100000) TEST(lattice_deform_performance, performance_no_dvert_1000000) { const int32_t num_items = 1000000; - LatticeDeformTestContext ctx = {{{nullptr}}}; + LatticeDeformTestContext ctx = {dna::shallow_zero_initialize()}; RandomNumberGenerator rng; test_lattice_deform_init(&ctx, &rng, num_items); test_lattice_deform(&ctx, num_items); @@ -114,7 +114,7 @@ TEST(lattice_deform_performance, performance_no_dvert_1000000) TEST(lattice_deform_performance, performance_no_dvert_10000000) { const int32_t num_items = 10000000; - LatticeDeformTestContext ctx = {{{nullptr}}}; + LatticeDeformTestContext ctx = {dna::shallow_zero_initialize()}; RandomNumberGenerator rng; test_lattice_deform_init(&ctx, &rng, num_items); test_lattice_deform(&ctx, num_items); diff --git a/source/blender/blenkernel/intern/layer.c b/source/blender/blenkernel/intern/layer.c index 1cc1839d2d0..f0ccd305690 100644 --- a/source/blender/blenkernel/intern/layer.c +++ b/source/blender/blenkernel/intern/layer.c @@ -1658,7 +1658,10 @@ static void layer_collection_local_sync(ViewLayer *view_layer, if (visible) { LISTBASE_FOREACH (CollectionObject *, cob, &layer_collection->collection->gobject) { - BLI_assert(cob->ob); + if (cob->ob == NULL) { + continue; + } + Base *base = BKE_view_layer_base_find(view_layer, cob->ob); base->local_collections_bits |= local_collections_uuid; } diff --git a/source/blender/blenkernel/intern/lib_remap.c b/source/blender/blenkernel/intern/lib_remap.c index 1a6fcf5ff43..2b449ad50bb 100644 --- a/source/blender/blenkernel/intern/lib_remap.c +++ b/source/blender/blenkernel/intern/lib_remap.c @@ -317,7 +317,8 @@ static void libblock_remap_data_preprocess(ID *id_owner, */ static void libblock_remap_data_postprocess_object_update(Main *bmain, Object *old_ob, - Object *new_ob) + Object *new_ob, + const bool do_sync_collection) { if (new_ob == NULL) { /* In case we unlinked old_ob (new_ob is NULL), the object has already @@ -331,7 +332,9 @@ static void libblock_remap_data_postprocess_object_update(Main *bmain, BKE_collections_object_remove_duplicates(bmain); } - BKE_main_collection_sync_remap(bmain); + if (do_sync_collection) { + BKE_main_collection_sync_remap(bmain); + } if (old_ob == NULL) { for (Object *ob = bmain->objects.first; ob != NULL; ob = ob->id.next) { @@ -567,7 +570,8 @@ static void libblock_remap_foreach_idpair_cb(ID *old_id, ID *new_id, void *user_ * Maybe we should do a per-ID callback for this instead? */ switch (GS(old_id->name)) { case ID_OB: - libblock_remap_data_postprocess_object_update(bmain, (Object *)old_id, (Object *)new_id); + libblock_remap_data_postprocess_object_update( + bmain, (Object *)old_id, (Object *)new_id, true); break; case ID_GR: libblock_remap_data_postprocess_collection_update( @@ -719,7 +723,7 @@ static void libblock_relink_foreach_idpair_cb(ID *old_id, ID *new_id, void *user case ID_OB: if (!is_object_update_processed) { libblock_remap_data_postprocess_object_update( - bmain, (Object *)old_id, (Object *)new_id); + bmain, (Object *)old_id, (Object *)new_id, true); is_object_update_processed = true; } break; @@ -781,11 +785,14 @@ void BKE_libblock_relink_multiple(Main *bmain, (Collection *)id_iter : ((Scene *)id_iter)->master_collection; /* No choice but to check whole objects once, and all children collections. */ - libblock_remap_data_postprocess_collection_update(bmain, owner_collection, NULL, NULL); if (!is_object_update_processed) { - libblock_remap_data_postprocess_object_update(bmain, NULL, NULL); + /* We only want to affect Object pointers here, not Collection ones, LayerCollections + * will be resynced as part of the call to + * `libblock_remap_data_postprocess_collection_update` below. */ + libblock_remap_data_postprocess_object_update(bmain, NULL, NULL, false); is_object_update_processed = true; } + libblock_remap_data_postprocess_collection_update(bmain, owner_collection, NULL, NULL); break; } default: diff --git a/source/blender/blenkernel/intern/material.c b/source/blender/blenkernel/intern/material.c index bc569956f66..4caaf314888 100644 --- a/source/blender/blenkernel/intern/material.c +++ b/source/blender/blenkernel/intern/material.c @@ -1423,13 +1423,15 @@ static bool fill_texpaint_slots_cb(bNode *node, void *userdata) case SH_NODE_TEX_IMAGE: { TexPaintSlot *slot = &ma->texpaintslot[index]; slot->ima = (Image *)node->id; - slot->interp = ((NodeTexImage *)node->storage)->interpolation; + NodeTexImage *storage = (NodeTexImage *)node->storage; + slot->interp = storage->interpolation; + slot->image_user = &storage->iuser; /* for new renderer, we need to traverse the treeback in search of a UV node */ bNode *uvnode = nodetree_uv_node_recursive(node); if (uvnode) { - NodeShaderUVMap *storage = (NodeShaderUVMap *)uvnode->storage; - slot->uvname = storage->uv_map; + NodeShaderUVMap *uv_storage = (NodeShaderUVMap *)uvnode->storage; + slot->uvname = uv_storage->uv_map; /* set a value to index so UI knows that we have a valid pointer for the mesh */ slot->valid = true; } diff --git a/source/blender/blenkernel/intern/mesh.cc b/source/blender/blenkernel/intern/mesh.cc index 5afc3c0be3b..25d97d0bd3c 100644 --- a/source/blender/blenkernel/intern/mesh.cc +++ b/source/blender/blenkernel/intern/mesh.cc @@ -218,7 +218,7 @@ static void mesh_blend_write(BlendWriter *writer, ID *id, const void *id_address mesh->mface = nullptr; mesh->totface = 0; memset(&mesh->fdata, 0, sizeof(mesh->fdata)); - memset(&mesh->runtime, 0, sizeof(mesh->runtime)); + mesh->runtime = blender::dna::shallow_zero_initialize(); flayers = flayers_buff; /* Do not store actual geometry data in case this is a library override ID. */ @@ -329,7 +329,7 @@ static void mesh_blend_read_data(BlendDataReader *reader, ID *id) mesh->texflag &= ~ME_AUTOSPACE_EVALUATED; mesh->edit_mesh = nullptr; - memset(&mesh->runtime, 0, sizeof(mesh->runtime)); + mesh->runtime = blender::dna::shallow_zero_initialize(); BKE_mesh_runtime_init_data(mesh); /* happens with old files */ diff --git a/source/blender/blenkernel/intern/mesh_convert.cc b/source/blender/blenkernel/intern/mesh_convert.cc index 6abaa471877..ff953ef5b46 100644 --- a/source/blender/blenkernel/intern/mesh_convert.cc +++ b/source/blender/blenkernel/intern/mesh_convert.cc @@ -25,6 +25,7 @@ #include "BLI_utildefines.h" #include "BKE_DerivedMesh.h" +#include "BKE_curves.hh" #include "BKE_deform.h" #include "BKE_displist.h" #include "BKE_editmesh.h" @@ -700,7 +701,7 @@ void BKE_mesh_to_curve_nurblist(const Mesh *me, ListBase *nurblist, const int ed VertLink *vl; /* create new 'nurb' within the curve */ - nu = MEM_cnew<Nurb>("MeshNurb"); + nu = MEM_new<Nurb>("MeshNurb", blender::dna::shallow_zero_initialize()); nu->pntsu = totpoly; nu->pntsv = 1; @@ -970,8 +971,7 @@ static Mesh *mesh_new_from_evaluated_curve_type_object(const Object *evaluated_o } const Curves *curves = get_evaluated_curves_from_object(evaluated_object); if (curves) { - std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*curves); - return blender::bke::curve_to_wire_mesh(*curve); + return blender::bke::curve_to_wire_mesh(blender::bke::CurvesGeometry::wrap(curves->geometry)); } return nullptr; } @@ -1446,8 +1446,7 @@ void BKE_mesh_nomain_to_mesh(Mesh *mesh_src, /* mesh_src might depend on mesh_dst, so we need to do everything with a local copy */ /* TODO(Sybren): the above claim came from 2.7x derived-mesh code (DM_to_mesh); * check whether it is still true with Mesh */ - Mesh tmp; - memcpy(&tmp, mesh_dst, sizeof(tmp)); + Mesh tmp = blender::dna::shallow_copy(*mesh_dst); int totvert, totedge /*, totface */ /* UNUSED */, totloop, totpoly; bool did_shapekeys = false; eCDAllocType alloctype = CD_DUPLICATE; diff --git a/source/blender/blenkernel/intern/mesh_remap.c b/source/blender/blenkernel/intern/mesh_remap.c index 9a4c01ec7aa..2e634a14872 100644 --- a/source/blender/blenkernel/intern/mesh_remap.c +++ b/source/blender/blenkernel/intern/mesh_remap.c @@ -1350,7 +1350,7 @@ void BKE_mesh_remap_calc_loops_from_mesh(const int mode, if (need_lnors_dst) { short(*custom_nors_dst)[2] = CustomData_get_layer(ldata_dst, CD_CUSTOMLOOPNORMAL); - /* Cache poly nors into a temp CDLayer. */ + /* Cache loop normals into a temporary custom data layer. */ loop_nors_dst = CustomData_get_layer(ldata_dst, CD_NORMAL); const bool do_loop_nors_dst = (loop_nors_dst == NULL); if (!loop_nors_dst) { diff --git a/source/blender/blenkernel/intern/mesh_remesh_voxel.cc b/source/blender/blenkernel/intern/mesh_remesh_voxel.cc index 3c7992ec3d7..7be4a6f2f94 100644 --- a/source/blender/blenkernel/intern/mesh_remesh_voxel.cc +++ b/source/blender/blenkernel/intern/mesh_remesh_voxel.cc @@ -23,11 +23,13 @@ #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" +#include "BKE_attribute.h" #include "BKE_bvhutils.h" #include "BKE_customdata.h" #include "BKE_editmesh.h" #include "BKE_lib_id.h" #include "BKE_mesh.h" +#include "BKE_mesh_mapping.h" #include "BKE_mesh_remesh_voxel.h" /* own include */ #include "BKE_mesh_runtime.h" @@ -365,30 +367,139 @@ void BKE_remesh_reproject_vertex_paint(Mesh *target, const Mesh *source) BVHTreeFromMesh bvhtree = {nullptr}; BKE_bvhtree_from_mesh_get(&bvhtree, source, BVHTREE_FROM_VERTS, 2); - int tot_color_layer = CustomData_number_of_layers(&source->vdata, CD_PROP_COLOR); + int i = 0; + const CustomDataLayer *layer; - for (int layer_n = 0; layer_n < tot_color_layer; layer_n++) { - const char *layer_name = CustomData_get_layer_name(&source->vdata, CD_PROP_COLOR, layer_n); - CustomData_add_layer_named( - &target->vdata, CD_PROP_COLOR, CD_CALLOC, nullptr, target->totvert, layer_name); + MeshElemMap *source_lmap = nullptr; + int *source_lmap_mem = nullptr; + MeshElemMap *target_lmap = nullptr; + int *target_lmap_mem = nullptr; - MPropCol *target_color = (MPropCol *)CustomData_get_layer_n( - &target->vdata, CD_PROP_COLOR, layer_n); + while ((layer = BKE_id_attribute_from_index( + const_cast<ID *>(&source->id), i++, ATTR_DOMAIN_MASK_COLOR, CD_MASK_COLOR_ALL))) { + AttributeDomain domain = BKE_id_attribute_domain(&source->id, layer); + + CustomData *target_cdata = domain == ATTR_DOMAIN_POINT ? &target->vdata : &target->ldata; + const CustomData *source_cdata = domain == ATTR_DOMAIN_POINT ? &source->vdata : &source->ldata; + + /* Check attribute exists in target. */ + int layer_i = CustomData_get_named_layer_index(target_cdata, layer->type, layer->name); + if (layer_i == -1) { + int elem_num = domain == ATTR_DOMAIN_POINT ? target->totvert : target->totloop; + + CustomData_add_layer_named( + target_cdata, layer->type, CD_CALLOC, nullptr, elem_num, layer->name); + layer_i = CustomData_get_named_layer_index(target_cdata, layer->type, layer->name); + } + + size_t data_size = CustomData_sizeof(layer->type); + void *target_data = target_cdata->layers[layer_i].data; + void *source_data = layer->data; MVert *target_verts = (MVert *)CustomData_get_layer(&target->vdata, CD_MVERT); - const MPropCol *source_color = (const MPropCol *)CustomData_get_layer_n( - &source->vdata, CD_PROP_COLOR, layer_n); - for (int i = 0; i < target->totvert; i++) { - BVHTreeNearest nearest; - nearest.index = -1; - nearest.dist_sq = FLT_MAX; - BLI_bvhtree_find_nearest( - bvhtree.tree, target_verts[i].co, &nearest, bvhtree.nearest_callback, &bvhtree); - if (nearest.index != -1) { - copy_v4_v4(target_color[i].color, source_color[nearest.index].color); + + if (domain == ATTR_DOMAIN_POINT) { + for (int i = 0; i < target->totvert; i++) { + BVHTreeNearest nearest; + nearest.index = -1; + nearest.dist_sq = FLT_MAX; + BLI_bvhtree_find_nearest( + bvhtree.tree, target_verts[i].co, &nearest, bvhtree.nearest_callback, &bvhtree); + + if (nearest.index != -1) { + memcpy(POINTER_OFFSET(target_data, (size_t)i * data_size), + POINTER_OFFSET(source_data, (size_t)nearest.index * data_size), + data_size); + } + } + } + else { + /* Lazily init vertex -> loop maps. */ + if (!source_lmap) { + const MPoly *source_polys = (MPoly *)CustomData_get_layer(&source->pdata, CD_MPOLY); + const MLoop *source_loops = (MLoop *)CustomData_get_layer(&source->ldata, CD_MLOOP); + const MPoly *target_polys = (MPoly *)CustomData_get_layer(&target->pdata, CD_MPOLY); + const MLoop *target_loops = (MLoop *)CustomData_get_layer(&target->ldata, CD_MLOOP); + + BKE_mesh_vert_loop_map_create(&source_lmap, + &source_lmap_mem, + source_polys, + source_loops, + source->totvert, + source->totpoly, + source->totloop); + + BKE_mesh_vert_loop_map_create(&target_lmap, + &target_lmap_mem, + target_polys, + target_loops, + target->totvert, + target->totpoly, + target->totloop); + } + + for (int i = 0; i < target->totvert; i++) { + BVHTreeNearest nearest; + nearest.index = -1; + nearest.dist_sq = FLT_MAX; + BLI_bvhtree_find_nearest( + bvhtree.tree, target_verts[i].co, &nearest, bvhtree.nearest_callback, &bvhtree); + + if (nearest.index == -1) { + continue; + } + + MeshElemMap *source_loops = source_lmap + nearest.index; + MeshElemMap *target_loops = target_lmap + i; + + if (target_loops->count == 0 || source_loops->count == 0) { + continue; + } + + /* + * Average color data for loops around the source vertex into + * the first target loop around the target vertex + */ + + CustomData_interp(source_cdata, + target_cdata, + source_loops->indices, + nullptr, + nullptr, + source_loops->count, + target_loops->indices[0]); + + void *elem = POINTER_OFFSET(target_data, (size_t)target_loops->indices[0] * data_size); + + /* Copy to rest of target loops. */ + for (int j = 1; j < target_loops->count; j++) { + memcpy(POINTER_OFFSET(target_data, (size_t)target_loops->indices[j] * data_size), + elem, + data_size); + } } } } + + MEM_SAFE_FREE(source_lmap); + MEM_SAFE_FREE(source_lmap_mem); + MEM_SAFE_FREE(target_lmap); + MEM_SAFE_FREE(target_lmap_mem); free_bvhtree_from_mesh(&bvhtree); + + /* Transfer active/render color attributes */ + + CustomDataLayer *active_layer = BKE_id_attributes_active_color_get(&source->id); + CustomDataLayer *render_layer = BKE_id_attributes_render_color_get(&source->id); + + if (active_layer) { + BKE_id_attributes_active_color_set( + &target->id, BKE_id_attributes_color_find(&target->id, active_layer->name)); + } + + if (render_layer) { + BKE_id_attributes_render_color_set( + &target->id, BKE_id_attributes_color_find(&target->id, render_layer->name)); + } } struct Mesh *BKE_mesh_remesh_voxel_fix_poles(const Mesh *mesh) diff --git a/source/blender/blenkernel/intern/mesh_runtime.c b/source/blender/blenkernel/intern/mesh_runtime.cc index 7bd52abeb0d..b06e867cf37 100644 --- a/source/blender/blenkernel/intern/mesh_runtime.c +++ b/source/blender/blenkernel/intern/mesh_runtime.cc @@ -14,8 +14,7 @@ #include "DNA_object_types.h" #include "BLI_math_geom.h" -#include "BLI_task.h" -#include "BLI_threads.h" +#include "BLI_task.hh" #include "BKE_bvhutils.h" #include "BKE_lib_id.h" @@ -35,12 +34,12 @@ */ static void mesh_runtime_init_mutexes(Mesh *mesh) { - mesh->runtime.eval_mutex = MEM_mallocN(sizeof(ThreadMutex), "mesh runtime eval_mutex"); - BLI_mutex_init(mesh->runtime.eval_mutex); - mesh->runtime.normals_mutex = MEM_mallocN(sizeof(ThreadMutex), "mesh runtime normals_mutex"); - BLI_mutex_init(mesh->runtime.normals_mutex); - mesh->runtime.render_mutex = MEM_mallocN(sizeof(ThreadMutex), "mesh runtime render_mutex"); - BLI_mutex_init(mesh->runtime.render_mutex); + mesh->runtime.eval_mutex = MEM_new<ThreadMutex>("mesh runtime eval_mutex"); + BLI_mutex_init(static_cast<ThreadMutex *>(mesh->runtime.eval_mutex)); + mesh->runtime.normals_mutex = MEM_new<ThreadMutex>("mesh runtime normals_mutex"); + BLI_mutex_init(static_cast<ThreadMutex *>(mesh->runtime.normals_mutex)); + mesh->runtime.render_mutex = MEM_new<ThreadMutex>("mesh runtime render_mutex"); + BLI_mutex_init(static_cast<ThreadMutex *>(mesh->runtime.render_mutex)); } /** @@ -48,20 +47,20 @@ static void mesh_runtime_init_mutexes(Mesh *mesh) */ static void mesh_runtime_free_mutexes(Mesh *mesh) { - if (mesh->runtime.eval_mutex != NULL) { - BLI_mutex_end(mesh->runtime.eval_mutex); + if (mesh->runtime.eval_mutex != nullptr) { + BLI_mutex_end(static_cast<ThreadMutex *>(mesh->runtime.eval_mutex)); MEM_freeN(mesh->runtime.eval_mutex); - mesh->runtime.eval_mutex = NULL; + mesh->runtime.eval_mutex = nullptr; } - if (mesh->runtime.normals_mutex != NULL) { - BLI_mutex_end(mesh->runtime.normals_mutex); + if (mesh->runtime.normals_mutex != nullptr) { + BLI_mutex_end(static_cast<ThreadMutex *>(mesh->runtime.normals_mutex)); MEM_freeN(mesh->runtime.normals_mutex); - mesh->runtime.normals_mutex = NULL; + mesh->runtime.normals_mutex = nullptr; } - if (mesh->runtime.render_mutex != NULL) { - BLI_mutex_end(mesh->runtime.render_mutex); + if (mesh->runtime.render_mutex != nullptr) { + BLI_mutex_end(static_cast<ThreadMutex *>(mesh->runtime.render_mutex)); MEM_freeN(mesh->runtime.render_mutex); - mesh->runtime.render_mutex = NULL; + mesh->runtime.render_mutex = nullptr; } } @@ -80,28 +79,28 @@ void BKE_mesh_runtime_reset_on_copy(Mesh *mesh, const int UNUSED(flag)) { Mesh_Runtime *runtime = &mesh->runtime; - runtime->mesh_eval = NULL; - runtime->edit_data = NULL; - runtime->batch_cache = NULL; - runtime->subdiv_ccg = NULL; - memset(&runtime->looptris, 0, sizeof(runtime->looptris)); - runtime->bvh_cache = NULL; - runtime->shrinkwrap_data = NULL; + runtime->mesh_eval = nullptr; + runtime->edit_data = nullptr; + runtime->batch_cache = nullptr; + runtime->subdiv_ccg = nullptr; + runtime->looptris = blender::dna::shallow_zero_initialize(); + runtime->bvh_cache = nullptr; + runtime->shrinkwrap_data = nullptr; runtime->vert_normals_dirty = true; runtime->poly_normals_dirty = true; - runtime->vert_normals = NULL; - runtime->poly_normals = NULL; + runtime->vert_normals = nullptr; + runtime->poly_normals = nullptr; mesh_runtime_init_mutexes(mesh); } void BKE_mesh_runtime_clear_cache(Mesh *mesh) { - if (mesh->runtime.mesh_eval != NULL) { - mesh->runtime.mesh_eval->edit_mesh = NULL; - BKE_id_free(NULL, mesh->runtime.mesh_eval); - mesh->runtime.mesh_eval = NULL; + if (mesh->runtime.mesh_eval != nullptr) { + mesh->runtime.mesh_eval->edit_mesh = nullptr; + BKE_id_free(nullptr, mesh->runtime.mesh_eval); + mesh->runtime.mesh_eval = nullptr; } BKE_mesh_runtime_clear_geometry(mesh); BKE_mesh_batch_cache_free(mesh); @@ -121,7 +120,7 @@ static void mesh_ensure_looptri_data(Mesh *mesh) const uint totpoly = mesh->totpoly; const int looptris_len = poly_to_tri_count(totpoly, mesh->totloop); - BLI_assert(mesh->runtime.looptris.array_wip == NULL); + BLI_assert(mesh->runtime.looptris.array_wip == nullptr); SWAP(MLoopTri *, mesh->runtime.looptris.array, mesh->runtime.looptris.array_wip); @@ -133,9 +132,9 @@ static void mesh_ensure_looptri_data(Mesh *mesh) } if (totpoly) { - if (mesh->runtime.looptris.array_wip == NULL) { - mesh->runtime.looptris.array_wip = MEM_malloc_arrayN( - looptris_len, sizeof(*mesh->runtime.looptris.array_wip), __func__); + if (mesh->runtime.looptris.array_wip == nullptr) { + mesh->runtime.looptris.array_wip = static_cast<MLoopTri *>( + MEM_malloc_arrayN(looptris_len, sizeof(*mesh->runtime.looptris.array_wip), __func__)); mesh->runtime.looptris.len_alloc = looptris_len; } @@ -146,7 +145,7 @@ static void mesh_ensure_looptri_data(Mesh *mesh) void BKE_mesh_runtime_looptri_recalc(Mesh *mesh) { mesh_ensure_looptri_data(mesh); - BLI_assert(mesh->totpoly == 0 || mesh->runtime.looptris.array_wip != NULL); + BLI_assert(mesh->totpoly == 0 || mesh->runtime.looptris.array_wip != nullptr); BKE_mesh_recalc_looptri(mesh->mloop, mesh->mpoly, @@ -155,11 +154,11 @@ void BKE_mesh_runtime_looptri_recalc(Mesh *mesh) mesh->totpoly, mesh->runtime.looptris.array_wip); - BLI_assert(mesh->runtime.looptris.array == NULL); + BLI_assert(mesh->runtime.looptris.array == nullptr); atomic_cas_ptr((void **)&mesh->runtime.looptris.array, mesh->runtime.looptris.array, mesh->runtime.looptris.array_wip); - mesh->runtime.looptris.array_wip = NULL; + mesh->runtime.looptris.array_wip = nullptr; } int BKE_mesh_runtime_looptri_len(const Mesh *mesh) @@ -170,12 +169,6 @@ int BKE_mesh_runtime_looptri_len(const Mesh *mesh) return looptri_len; } -static void mesh_runtime_looptri_recalc_isolated(void *userdata) -{ - Mesh *mesh = userdata; - BKE_mesh_runtime_looptri_recalc(mesh); -} - const MLoopTri *BKE_mesh_runtime_looptri_ensure(const Mesh *mesh) { ThreadMutex *mesh_eval_mutex = (ThreadMutex *)mesh->runtime.eval_mutex; @@ -183,12 +176,13 @@ const MLoopTri *BKE_mesh_runtime_looptri_ensure(const Mesh *mesh) MLoopTri *looptri = mesh->runtime.looptris.array; - if (looptri != NULL) { + if (looptri != nullptr) { BLI_assert(BKE_mesh_runtime_looptri_len(mesh) == mesh->runtime.looptris.len); } else { /* Must isolate multithreaded tasks while holding a mutex lock. */ - BLI_task_isolate(mesh_runtime_looptri_recalc_isolated, (void *)mesh); + blender::threading::isolate_task( + [&]() { BKE_mesh_runtime_looptri_recalc(const_cast<Mesh *>(mesh)); }); looptri = mesh->runtime.looptris.array; } @@ -211,18 +205,18 @@ void BKE_mesh_runtime_verttri_from_looptri(MVertTri *r_verttri, bool BKE_mesh_runtime_ensure_edit_data(struct Mesh *mesh) { - if (mesh->runtime.edit_data != NULL) { + if (mesh->runtime.edit_data != nullptr) { return false; } - mesh->runtime.edit_data = MEM_callocN(sizeof(EditMeshData), "EditMeshData"); + mesh->runtime.edit_data = MEM_cnew<EditMeshData>(__func__); return true; } bool BKE_mesh_runtime_reset_edit_data(Mesh *mesh) { EditMeshData *edit_data = mesh->runtime.edit_data; - if (edit_data == NULL) { + if (edit_data == nullptr) { return false; } @@ -236,13 +230,13 @@ bool BKE_mesh_runtime_reset_edit_data(Mesh *mesh) bool BKE_mesh_runtime_clear_edit_data(Mesh *mesh) { - if (mesh->runtime.edit_data == NULL) { + if (mesh->runtime.edit_data == nullptr) { return false; } BKE_mesh_runtime_reset_edit_data(mesh); MEM_freeN(mesh->runtime.edit_data); - mesh->runtime.edit_data = NULL; + mesh->runtime.edit_data = nullptr; return true; } @@ -251,13 +245,13 @@ void BKE_mesh_runtime_clear_geometry(Mesh *mesh) { if (mesh->runtime.bvh_cache) { bvhcache_free(mesh->runtime.bvh_cache); - mesh->runtime.bvh_cache = NULL; + mesh->runtime.bvh_cache = nullptr; } MEM_SAFE_FREE(mesh->runtime.looptris.array); /* TODO(sergey): Does this really belong here? */ - if (mesh->runtime.subdiv_ccg != NULL) { + if (mesh->runtime.subdiv_ccg != nullptr) { BKE_subdiv_ccg_destroy(mesh->runtime.subdiv_ccg); - mesh->runtime.subdiv_ccg = NULL; + mesh->runtime.subdiv_ccg = nullptr; } BKE_shrinkwrap_discard_boundary_data(mesh); } @@ -269,8 +263,8 @@ void BKE_mesh_runtime_clear_geometry(Mesh *mesh) * \{ */ /* Draw Engine */ -void (*BKE_mesh_batch_cache_dirty_tag_cb)(Mesh *me, eMeshBatchDirtyMode mode) = NULL; -void (*BKE_mesh_batch_cache_free_cb)(Mesh *me) = NULL; +void (*BKE_mesh_batch_cache_dirty_tag_cb)(Mesh *me, eMeshBatchDirtyMode mode) = nullptr; +void (*BKE_mesh_batch_cache_free_cb)(Mesh *me) = nullptr; void BKE_mesh_batch_cache_dirty_tag(Mesh *me, eMeshBatchDirtyMode mode) { diff --git a/source/blender/blenkernel/intern/mesh_wrapper.c b/source/blender/blenkernel/intern/mesh_wrapper.cc index f9fcaa0dceb..8291765c2ef 100644 --- a/source/blender/blenkernel/intern/mesh_wrapper.c +++ b/source/blender/blenkernel/intern/mesh_wrapper.cc @@ -27,7 +27,7 @@ #include "BLI_ghash.h" #include "BLI_math.h" -#include "BLI_task.h" +#include "BLI_task.hh" #include "BLI_threads.h" #include "BLI_utildefines.h" @@ -51,7 +51,7 @@ Mesh *BKE_mesh_wrapper_from_editmesh_with_coords(BMEditMesh *em, const float (*vert_coords)[3], const Mesh *me_settings) { - Mesh *me = BKE_id_new_nomain(ID_ME, NULL); + Mesh *me = static_cast<Mesh *>(BKE_id_new_nomain(ID_ME, nullptr)); BKE_mesh_copy_parameters_for_eval(me, me_settings); BKE_mesh_runtime_ensure_edit_data(me); @@ -63,10 +63,10 @@ Mesh *BKE_mesh_wrapper_from_editmesh_with_coords(BMEditMesh *em, /* Use edit-mesh directly where possible. */ me->runtime.is_original = true; - me->edit_mesh = MEM_dupallocN(em); + me->edit_mesh = static_cast<BMEditMesh *>(MEM_dupallocN(em)); me->edit_mesh->is_shallow_copy = true; -/* Make sure, we crash if these are ever used. */ + /* Make sure we crash if these are ever used. */ #ifdef DEBUG me->totvert = INT_MAX; me->totedge = INT_MAX; @@ -88,55 +88,7 @@ Mesh *BKE_mesh_wrapper_from_editmesh(BMEditMesh *em, const CustomData_MeshMasks *cd_mask_extra, const Mesh *me_settings) { - return BKE_mesh_wrapper_from_editmesh_with_coords(em, cd_mask_extra, NULL, me_settings); -} - -static void mesh_wrapper_ensure_mdata_isolated(void *userdata) -{ - Mesh *me = userdata; - - const eMeshWrapperType geom_type_orig = me->runtime.wrapper_type; - me->runtime.wrapper_type = ME_WRAPPER_TYPE_MDATA; - - switch (geom_type_orig) { - case ME_WRAPPER_TYPE_MDATA: - case ME_WRAPPER_TYPE_SUBD: { - break; /* Quiet warning. */ - } - case ME_WRAPPER_TYPE_BMESH: { - me->totvert = 0; - me->totedge = 0; - me->totpoly = 0; - me->totloop = 0; - - BLI_assert(me->edit_mesh != NULL); - BLI_assert(me->runtime.edit_data != NULL); - - BMEditMesh *em = me->edit_mesh; - BM_mesh_bm_to_me_for_eval(em->bm, me, &me->runtime.cd_mask_extra); - - /* Adding original index layers assumes that all BMesh mesh wrappers are created from - * original edit mode meshes (the only case where adding original indices makes sense). - * If that assumption is broken, the layers might be incorrect in that they might not - * actually be "original". - * - * There is also a performance aspect, where this also assumes that original indices are - * always needed when converting an edit mesh to a mesh. That might be wrong, but it's not - * harmful. */ - BKE_mesh_ensure_default_orig_index_customdata(me); - - EditMeshData *edit_data = me->runtime.edit_data; - if (edit_data->vertexCos) { - BKE_mesh_vert_coords_apply(me, edit_data->vertexCos); - me->runtime.is_original = false; - } - break; - } - } - - if (me->runtime.wrapper_type_finalize) { - BKE_mesh_wrapper_deferred_finalize(me, &me->runtime.cd_mask_extra); - } + return BKE_mesh_wrapper_from_editmesh_with_coords(em, cd_mask_extra, nullptr, me_settings); } void BKE_mesh_wrapper_ensure_mdata(Mesh *me) @@ -150,7 +102,51 @@ void BKE_mesh_wrapper_ensure_mdata(Mesh *me) } /* Must isolate multithreaded tasks while holding a mutex lock. */ - BLI_task_isolate(mesh_wrapper_ensure_mdata_isolated, me); + blender::threading::isolate_task([&]() { + const eMeshWrapperType geom_type_orig = static_cast<eMeshWrapperType>( + me->runtime.wrapper_type); + me->runtime.wrapper_type = ME_WRAPPER_TYPE_MDATA; + + switch (geom_type_orig) { + case ME_WRAPPER_TYPE_MDATA: + case ME_WRAPPER_TYPE_SUBD: { + break; /* Quiet warning. */ + } + case ME_WRAPPER_TYPE_BMESH: { + me->totvert = 0; + me->totedge = 0; + me->totpoly = 0; + me->totloop = 0; + + BLI_assert(me->edit_mesh != nullptr); + BLI_assert(me->runtime.edit_data != nullptr); + + BMEditMesh *em = me->edit_mesh; + BM_mesh_bm_to_me_for_eval(em->bm, me, &me->runtime.cd_mask_extra); + + /* Adding original index layers assumes that all BMesh mesh wrappers are created from + * original edit mode meshes (the only case where adding original indices makes sense). + * If that assumption is broken, the layers might be incorrect in that they might not + * actually be "original". + * + * There is also a performance aspect, where this also assumes that original indices are + * always needed when converting an edit mesh to a mesh. That might be wrong, but it's not + * harmful. */ + BKE_mesh_ensure_default_orig_index_customdata(me); + + EditMeshData *edit_data = me->runtime.edit_data; + if (edit_data->vertexCos) { + BKE_mesh_vert_coords_apply(me, edit_data->vertexCos); + me->runtime.is_original = false; + } + break; + } + } + + if (me->runtime.wrapper_type_finalize) { + BKE_mesh_wrapper_deferred_finalize(me, &me->runtime.cd_mask_extra); + } + }); BLI_mutex_unlock(mesh_eval_mutex); } @@ -181,7 +177,7 @@ void BKE_mesh_wrapper_vert_coords_copy(const Mesh *me, BMesh *bm = me->edit_mesh->bm; BLI_assert(vert_coords_len <= bm->totvert); EditMeshData *edit_data = me->runtime.edit_data; - if (edit_data->vertexCos != NULL) { + if (edit_data->vertexCos != nullptr) { for (int i = 0; i < vert_coords_len; i++) { copy_v3_v3(vert_coords[i], edit_data->vertexCos[i]); } @@ -219,7 +215,7 @@ void BKE_mesh_wrapper_vert_coords_copy_with_mat4(const Mesh *me, BMesh *bm = me->edit_mesh->bm; BLI_assert(vert_coords_len == bm->totvert); EditMeshData *edit_data = me->runtime.edit_data; - if (edit_data->vertexCos != NULL) { + if (edit_data->vertexCos != nullptr) { for (int i = 0; i < vert_coords_len; i++) { mul_v3_m4v3(vert_coords[i], mat, edit_data->vertexCos[i]); } @@ -340,7 +336,7 @@ static Mesh *mesh_wrapper_ensure_subdivision(const Object *ob, Mesh *me) SubsurfRuntimeData *runtime_data = BKE_subsurf_modifier_ensure_runtime(smd); Subdiv *subdiv = BKE_subsurf_modifier_subdiv_descriptor_ensure(smd, &subdiv_settings, me, false); - if (subdiv == NULL) { + if (subdiv == nullptr) { /* Happens on bad topology, but also on empty input mesh. */ return me; } @@ -352,8 +348,8 @@ static Mesh *mesh_wrapper_ensure_subdivision(const Object *ob, Mesh *me) } if (subdiv_mesh != me) { - if (me->runtime.mesh_eval != NULL) { - BKE_id_free(NULL, me->runtime.mesh_eval); + if (me->runtime.mesh_eval != nullptr) { + BKE_id_free(nullptr, me->runtime.mesh_eval); } me->runtime.mesh_eval = subdiv_mesh; me->runtime.wrapper_type = ME_WRAPPER_TYPE_SUBD; @@ -362,20 +358,6 @@ static Mesh *mesh_wrapper_ensure_subdivision(const Object *ob, Mesh *me) return me->runtime.mesh_eval; } -typedef struct SubdivisionWrapperIsolatedTaskData { - const Object *ob; - Mesh *me; - Mesh *result; -} SubdivisionWrapperIsolatedTaskData; - -static void mesh_wrapper_ensure_subdivision_isolated(void *userdata) -{ - SubdivisionWrapperIsolatedTaskData *task_data = (SubdivisionWrapperIsolatedTaskData *)userdata; - const Object *ob = task_data->ob; - Mesh *me = task_data->me; - task_data->result = mesh_wrapper_ensure_subdivision(ob, me); -} - Mesh *BKE_mesh_wrapper_ensure_subdivision(const Object *ob, Mesh *me) { ThreadMutex *mesh_eval_mutex = (ThreadMutex *)me->runtime.eval_mutex; @@ -386,15 +368,13 @@ Mesh *BKE_mesh_wrapper_ensure_subdivision(const Object *ob, Mesh *me) return me->runtime.mesh_eval; } - SubdivisionWrapperIsolatedTaskData task_data; - task_data.ob = ob; - task_data.me = me; + Mesh *result; /* Must isolate multithreaded tasks while holding a mutex lock. */ - BLI_task_isolate(mesh_wrapper_ensure_subdivision_isolated, &task_data); + blender::threading::isolate_task([&]() { result = mesh_wrapper_ensure_subdivision(ob, me); }); BLI_mutex_unlock(mesh_eval_mutex); - return task_data.result; + return result; } /** \} */ diff --git a/source/blender/blenkernel/intern/nla.c b/source/blender/blenkernel/intern/nla.c index a94562a32ec..9b6d768b2d3 100644 --- a/source/blender/blenkernel/intern/nla.c +++ b/source/blender/blenkernel/intern/nla.c @@ -1977,8 +1977,11 @@ bool BKE_nla_tweakmode_enter(AnimData *adt) /* go over all the tracks after AND INCLUDING the active one, tagging them as being disabled * - the active track needs to also be tagged, otherwise, it'll overlap with the tweaks going on */ - for (nlt = activeTrack; nlt; nlt = nlt->next) { - nlt->flag |= NLATRACK_DISABLED; + activeTrack->flag |= NLATRACK_DISABLED; + if ((adt->flag & ADT_NLA_EVAL_UPPER_TRACKS) == 0) { + for (nlt = activeTrack->next; nlt; nlt = nlt->next) { + nlt->flag |= NLATRACK_DISABLED; + } } /* handle AnimData level changes: diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 7efdd855a04..4acccca322a 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -719,7 +719,7 @@ void ntreeBlendReadData(BlendDataReader *reader, bNodeTree *ntree) } case SH_NODE_TEX_POINTDENSITY: { NodeShaderTexPointDensity *npd = (NodeShaderTexPointDensity *)node->storage; - memset(&npd->pd, 0, sizeof(npd->pd)); + npd->pd = blender::dna::shallow_zero_initialize(); break; } case SH_NODE_TEX_IMAGE: { diff --git a/source/blender/blenkernel/intern/object.cc b/source/blender/blenkernel/intern/object.cc index a54e2910b79..5ff1f6b950f 100644 --- a/source/blender/blenkernel/intern/object.cc +++ b/source/blender/blenkernel/intern/object.cc @@ -3087,12 +3087,12 @@ void BKE_object_matrix_local_get(struct Object *ob, float r_mat[4][4]) static bool ob_parcurve(Object *ob, Object *par, float r_mat[4][4]) { Curve *cu = (Curve *)par->data; - float vec[4], dir[3], quat[4], radius, ctime; + float vec[4], quat[4], radius, ctime; /* NOTE: Curve cache is supposed to be evaluated here already, however there * are cases where we can not guarantee that. This includes, for example, * dependency cycles. We can't correct anything from here, since that would - * cause a threading conflicts. + * cause threading conflicts. * * TODO(sergey): Some of the legit looking cases like T56619 need to be * looked into, and maybe curve cache (and other dependencies) are to be @@ -3125,7 +3125,7 @@ static bool ob_parcurve(Object *ob, Object *par, float r_mat[4][4]) /* vec: 4 items! */ if (BKE_where_on_path( - par, ctime, vec, dir, (cu->flag & CU_FOLLOW) ? quat : nullptr, &radius, nullptr)) { + par, ctime, vec, nullptr, (cu->flag & CU_FOLLOW) ? quat : nullptr, &radius, nullptr)) { if (cu->flag & CU_FOLLOW) { quat_apply_track(quat, ob->trackflag, ob->upflag); normalize_qt(quat); @@ -3558,6 +3558,55 @@ void BKE_object_apply_mat4(Object *ob, BKE_object_apply_mat4_ex(ob, mat, use_parent ? ob->parent : nullptr, ob->parentinv, use_compat); } +void BKE_object_apply_parent_inverse(struct Object *ob) +{ + /* + * Use parent's world transform as the child's origin. + * + * Let: + * local = identity + * world = orthonormalized(parent) + * + * Then: + * world = parent @ parentinv @ local + * inv(parent) @ world = parentinv + * parentinv = inv(parent) @ world + * + * NOTE: If ob->obmat has shear, then this `parentinv` is insufficient because + * parent @ parentinv => shearless result + * + * Thus, local will have shear which cannot be decomposed into TRS: + * local = inv(parent @ parentinv) @ world + * + * This is currently not supported for consistency in the handling of shear during the other + * parenting ops: Parent (Keep Transform), Clear [Parent] and Keep Transform. + */ + float par_locrot[4][4], par_imat[4][4]; + BKE_object_get_parent_matrix(ob, ob->parent, par_locrot); + invert_m4_m4(par_imat, par_locrot); + + orthogonalize_m4_stable(par_locrot, 0, true); + + mul_m4_m4m4(ob->parentinv, par_imat, par_locrot); + + /* Now, preserve `world` given the new `parentinv`. + * + * world = parent @ parentinv @ local + * inv(parent) @ world = parentinv @ local + * inv(parentinv) @ inv(parent) @ world = local + * + * local = inv(parentinv) @ inv(parent) @ world + */ + float ob_local[4][4]; + copy_m4_m4(ob_local, ob->parentinv); + invert_m4(ob_local); + mul_m4_m4_post(ob_local, par_imat); + mul_m4_m4_post(ob_local, ob->obmat); + + /* Send use_compat=False so the rotation is predictable. */ + BKE_object_apply_mat4(ob, ob_local, false, false); +} + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/blenkernel/intern/object_dupli.cc b/source/blender/blenkernel/intern/object_dupli.cc index 0ae8f144583..407a2c8955c 100644 --- a/source/blender/blenkernel/intern/object_dupli.cc +++ b/source/blender/blenkernel/intern/object_dupli.cc @@ -787,7 +787,7 @@ static void make_duplis_geometry_set_impl(const DupliContext *ctx, dupli->ob_data = (ID *)volume; } } - if (!ELEM(ctx->object->type, OB_CURVES_LEGACY, OB_FONT) || geometry_set_is_instance) { + if (!ELEM(ctx->object->type, OB_CURVES_LEGACY, OB_FONT, OB_CURVES) || geometry_set_is_instance) { if (const CurveComponent *component = geometry_set.get_component_for_read<CurveComponent>()) { if (const Curve *curve = component->get_curve_for_render()) { DupliObject *dupli = make_dupli(ctx, ctx->object, parent_transform, component_index++); diff --git a/source/blender/blenkernel/intern/paint.c b/source/blender/blenkernel/intern/paint.c index eb3f47760fc..5fd7984ea90 100644 --- a/source/blender/blenkernel/intern/paint.c +++ b/source/blender/blenkernel/intern/paint.c @@ -1511,6 +1511,8 @@ void BKE_sculptsession_free(Object *ob) BKE_sculptsession_free_vwpaint_data(ob->sculpt); + MEM_SAFE_FREE(ss->last_paint_canvas_key); + MEM_freeN(ss); ob->sculpt = NULL; @@ -1771,6 +1773,24 @@ static void sculpt_update_object(Depsgraph *depsgraph, } } + /* + * We should rebuild the PBVH_pixels when painting canvas changes. + * + * The relevant changes are stored/encoded in the paint canvas key. + * These include the active uv map, and resolutions. + */ + if (U.experimental.use_sculpt_texture_paint && ss->pbvh) { + char *paint_canvas_key = BKE_paint_canvas_key_get(&scene->toolsettings->paint_mode, ob); + if (ss->last_paint_canvas_key == NULL || !STREQ(paint_canvas_key, ss->last_paint_canvas_key)) { + MEM_SAFE_FREE(ss->last_paint_canvas_key); + ss->last_paint_canvas_key = paint_canvas_key; + BKE_pbvh_mark_rebuild_pixels(ss->pbvh); + } + else { + MEM_freeN(paint_canvas_key); + } + } + /* We could be more precise when we have access to the active tool. */ const bool use_paint_slots = (ob->mode & OB_MODE_SCULPT) != 0; if (use_paint_slots) { @@ -1849,6 +1869,10 @@ void BKE_sculpt_color_layer_create_if_needed(struct Object *object) BKE_id_attributes_active_color_set(&orig_me->id, layer); DEG_id_tag_update(&orig_me->id, ID_RECALC_GEOMETRY_ALL_MODES); + + if (object->sculpt && object->sculpt->pbvh) { + BKE_pbvh_update_active_vcol(object->sculpt->pbvh, orig_me); + } } void BKE_sculpt_update_object_for_edit( diff --git a/source/blender/blenkernel/intern/paint_canvas.cc b/source/blender/blenkernel/intern/paint_canvas.cc new file mode 100644 index 00000000000..b72418d88c0 --- /dev/null +++ b/source/blender/blenkernel/intern/paint_canvas.cc @@ -0,0 +1,131 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#include "BLI_compiler_compat.h" + +#include "DNA_material_types.h" +#include "DNA_mesh_types.h" +#include "DNA_scene_types.h" + +#include "BKE_customdata.h" +#include "BKE_image.h" +#include "BKE_material.h" +#include "BKE_paint.h" + +#include "IMB_imbuf_types.h" + +namespace blender::bke::paint::canvas { +static TexPaintSlot *get_active_slot(Object *ob) +{ + Material *mat = BKE_object_material_get(ob, ob->actcol); + if (mat == nullptr) { + return nullptr; + } + if (mat->texpaintslot == nullptr) { + return nullptr; + } + if (mat->paint_active_slot >= mat->tot_slots) { + return nullptr; + } + + TexPaintSlot *slot = &mat->texpaintslot[mat->paint_active_slot]; + return slot; +} + +} // namespace blender::bke::paint::canvas + +extern "C" { + +using namespace blender::bke::paint::canvas; + +bool BKE_paint_canvas_image_get(PaintModeSettings *settings, + Object *ob, + Image **r_image, + ImageUser **r_image_user) +{ + *r_image = nullptr; + *r_image_user = nullptr; + + switch (settings->canvas_source) { + case PAINT_CANVAS_SOURCE_COLOR_ATTRIBUTE: + break; + + case PAINT_CANVAS_SOURCE_IMAGE: + *r_image = settings->canvas_image; + *r_image_user = &settings->image_user; + break; + + case PAINT_CANVAS_SOURCE_MATERIAL: { + TexPaintSlot *slot = get_active_slot(ob); + if (slot == nullptr) { + break; + } + + *r_image = slot->ima; + *r_image_user = slot->image_user; + break; + } + } + return *r_image != nullptr; +} + +int BKE_paint_canvas_uvmap_layer_index_get(const struct PaintModeSettings *settings, + struct Object *ob) +{ + switch (settings->canvas_source) { + case PAINT_CANVAS_SOURCE_COLOR_ATTRIBUTE: + return -1; + case PAINT_CANVAS_SOURCE_IMAGE: { + /* Use active uv map of the object. */ + if (ob->type != OB_MESH) { + return -1; + } + + const Mesh *mesh = static_cast<Mesh *>(ob->data); + return CustomData_get_active_layer_index(&mesh->ldata, CD_MLOOPUV); + } + case PAINT_CANVAS_SOURCE_MATERIAL: { + /* Use uv map of the canvas. */ + TexPaintSlot *slot = get_active_slot(ob); + if (slot == nullptr) { + break; + } + + if (ob->type != OB_MESH) { + return -1; + } + + if (slot->uvname == nullptr) { + return -1; + } + + const Mesh *mesh = static_cast<Mesh *>(ob->data); + return CustomData_get_named_layer_index(&mesh->ldata, CD_MLOOPUV, slot->uvname); + } + } + return -1; +} + +char *BKE_paint_canvas_key_get(struct PaintModeSettings *settings, struct Object *ob) +{ + std::stringstream ss; + int active_uv_map_layer_index = BKE_paint_canvas_uvmap_layer_index_get(settings, ob); + ss << "UV_MAP:" << active_uv_map_layer_index; + + Image *image; + ImageUser *image_user; + if (BKE_paint_canvas_image_get(settings, ob, &image, &image_user)) { + ImageUser tile_user = *image_user; + LISTBASE_FOREACH (ImageTile *, image_tile, &image->tiles) { + tile_user.tile = image_tile->tile_number; + ImBuf *image_buffer = BKE_image_acquire_ibuf(image, &tile_user, nullptr); + if (!image_buffer) { + continue; + } + ss << ",TILE_" << image_tile->tile_number; + ss << "(" << image_buffer->x << "," << image_buffer->y << ")"; + BKE_image_release_ibuf(image, image_buffer, nullptr); + } + } + + return BLI_strdup(ss.str().c_str()); +} +} diff --git a/source/blender/blenkernel/intern/particle.c b/source/blender/blenkernel/intern/particle.c index 9ea1336a95a..cc4738b1faa 100644 --- a/source/blender/blenkernel/intern/particle.c +++ b/source/blender/blenkernel/intern/particle.c @@ -1249,7 +1249,9 @@ typedef struct ParticleInterpolationData { PTCacheEditPoint *epoint; PTCacheEditKey *ekey[2]; - float birthtime, dietime; + float birthtime; + /** Die on this frame, see #ParticleData.dietime for details. */ + float dietime; int bspline; } ParticleInterpolationData; /** @@ -1311,15 +1313,15 @@ static void get_pointcache_keys_for_time(Object *UNUSED(ob), } static int get_pointcache_times_for_particle(PointCache *cache, int index, - float *start, - float *end) + float *r_start, + float *r_dietime) { PTCacheMem *pm; int ret = 0; for (pm = cache->mem_cache.first; pm; pm = pm->next) { if (BKE_ptcache_mem_index_find(pm, index) >= 0) { - *start = pm->frame; + *r_start = pm->frame; ret++; break; } @@ -1327,7 +1329,8 @@ static int get_pointcache_times_for_particle(PointCache *cache, for (pm = cache->mem_cache.last; pm; pm = pm->prev) { if (BKE_ptcache_mem_index_find(pm, index) >= 0) { - *end = pm->frame; + /* Die *after* the last available frame. */ + *r_dietime = pm->frame + 1; ret++; break; } @@ -1343,7 +1346,9 @@ float psys_get_dietime_from_cache(PointCache *cache, int index) for (pm = cache->mem_cache.last; pm; pm = pm->prev) { if (BKE_ptcache_mem_index_find(pm, index) >= 0) { - return (float)pm->frame; + /* Die *after* the last available frame. */ + dietime = pm->frame + 1; + break; } } @@ -1374,14 +1379,14 @@ static void init_particle_interpolation(Object *ob, pind->dietime = (key + pa->totkey - 1)->time; } else if (pind->cache) { - float start = 0.0f, end = 0.0f; + float start = 0.0f, dietime = 0.0f; get_pointcache_keys_for_time(ob, pind->cache, &pind->pm, -1, 0.0f, NULL, NULL); pind->birthtime = pa ? pa->time : pind->cache->startframe; - pind->dietime = pa ? pa->dietime : pind->cache->endframe; + pind->dietime = pa ? pa->dietime : (pind->cache->endframe + 1); - if (get_pointcache_times_for_particle(pind->cache, pa - psys->particles, &start, &end)) { + if (get_pointcache_times_for_particle(pind->cache, pa - psys->particles, &start, &dietime)) { pind->birthtime = MAX2(pind->birthtime, start); - pind->dietime = MIN2(pind->dietime, end); + pind->dietime = MIN2(pind->dietime, dietime); } } else { diff --git a/source/blender/blenkernel/intern/particle_child.c b/source/blender/blenkernel/intern/particle_child.c index 5dba4d3f003..524ee31229b 100644 --- a/source/blender/blenkernel/intern/particle_child.c +++ b/source/blender/blenkernel/intern/particle_child.c @@ -85,7 +85,7 @@ static void do_kink_spiral_deform(ParticleKey *state, * and goes up to the Golden Spiral for 1.0 * https://en.wikipedia.org/wiki/Golden_spiral */ - const float b = shape * (1.0f + sqrtf(5.0f)) / (float)M_PI_4; + const float b = shape * (1.0f + sqrtf(5.0f)) / (float)M_PI * 0.25f; /* angle of the spiral against the curve (rotated opposite to make a smooth transition) */ const float start_angle = ((b != 0.0f) ? atanf(1.0f / b) : (float)-M_PI_2) + (b > 0.0f ? -(float)M_PI_2 : (float)M_PI_2); diff --git a/source/blender/blenkernel/intern/pbvh.c b/source/blender/blenkernel/intern/pbvh.c index 5d307697208..e91f360ef22 100644 --- a/source/blender/blenkernel/intern/pbvh.c +++ b/source/blender/blenkernel/intern/pbvh.c @@ -686,6 +686,8 @@ void BKE_pbvh_free(PBVH *pbvh) if (node->bm_other_verts) { BLI_gset_free(node->bm_other_verts, NULL); } + + pbvh_pixels_free(node); } } @@ -1769,7 +1771,7 @@ BMesh *BKE_pbvh_get_bmesh(PBVH *pbvh) void BKE_pbvh_node_mark_update(PBVHNode *node) { node->flag |= PBVH_UpdateNormals | PBVH_UpdateBB | PBVH_UpdateOriginalBB | - PBVH_UpdateDrawBuffers | PBVH_UpdateRedraw; + PBVH_UpdateDrawBuffers | PBVH_UpdateRedraw | PBVH_RebuildPixels; } void BKE_pbvh_node_mark_update_mask(PBVHNode *node) @@ -1782,6 +1784,16 @@ void BKE_pbvh_node_mark_update_color(PBVHNode *node) node->flag |= PBVH_UpdateColor | PBVH_UpdateDrawBuffers | PBVH_UpdateRedraw; } +void BKE_pbvh_mark_rebuild_pixels(PBVH *pbvh) +{ + for (int n = 0; n < pbvh->totnode; n++) { + PBVHNode *node = &pbvh->nodes[n]; + if (node->flag & PBVH_Leaf) { + node->flag |= PBVH_RebuildPixels; + } + } +} + void BKE_pbvh_node_mark_update_visibility(PBVHNode *node) { node->flag |= PBVH_UpdateVisibility | PBVH_RebuildDrawBuffers | PBVH_UpdateDrawBuffers | diff --git a/source/blender/blenkernel/intern/pbvh_intern.h b/source/blender/blenkernel/intern/pbvh_intern.h index 37f8dfd9b6b..77bd00da50a 100644 --- a/source/blender/blenkernel/intern/pbvh_intern.h +++ b/source/blender/blenkernel/intern/pbvh_intern.h @@ -6,6 +6,10 @@ * \ingroup bke */ +#ifdef __cplusplus +extern "C" { +#endif + /* Axis-aligned bounding box */ typedef struct { float bmin[3], bmax[3]; @@ -32,13 +36,18 @@ struct PBVHNode { * 'nodes' array. */ int children_offset; - /* Pointer into the PBVH prim_indices array and the number of - * primitives used by this leaf node. + /* List of primitives for this node. Semantics depends on + * PBVH type: + * + * - PBVH_FACES: Indices into the PBVH.looptri array. + * - PBVH_GRIDS: Multires grid indices. + * - PBVH_BMESH: Unused. See PBVHNode.bm_faces. * - * Used for leaf nodes in both mesh- and multires-based PBVHs. + * NOTE: This is a pointer inside of PBVH.prim_indices; it + * is not allocated separately per node. */ int *prim_indices; - unsigned int totprim; + unsigned int totprim; /* Number of primitives inside prim_indices. */ /* Array of indices into the mesh's MVert array. Contains the * indices of all vertices used by faces that are within this @@ -63,9 +72,8 @@ struct PBVHNode { unsigned int uniq_verts, face_verts; /* Array of indices into the Mesh's MLoop array. - * PBVH_FACES only. The first part of the array - * are loops unique to this node, see comment for - * vert_indices for more details.*/ + * PBVH_FACES only. + */ int *loop_indices; unsigned int loop_indices_num; @@ -93,6 +101,11 @@ struct PBVHNode { PBVHProxyNode *proxies; /* Dyntopo */ + + /* GSet of pointers to the BMFaces used by this node. + * NOTE: PBVH_BMESH only. Faces are always triangles + * (dynamic topology forcibly triangulates the mesh). + */ GSet *bm_faces; GSet *bm_unique_verts; GSet *bm_other_verts; @@ -102,6 +115,7 @@ struct PBVHNode { /* Used to store the brush color during a stroke and composite it over the original color */ PBVHColorBufferNode color_buffer; + PBVHPixelsNode pixels; }; typedef enum { @@ -117,6 +131,7 @@ struct PBVH { PBVHNode *nodes; int node_mem_count, totnode; + /* Memory backing for PBVHNode.prim_indices. */ int *prim_indices; int totprim; int totvert; @@ -250,3 +265,11 @@ bool pbvh_bmesh_node_nearest_to_ray(PBVHNode *node, bool use_original); void pbvh_bmesh_normals_update(PBVHNode **nodes, int totnode); + +/* pbvh_pixels.hh */ +void pbvh_pixels_free(PBVHNode *node); +void pbvh_pixels_free_brush_test(PBVHNode *node); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/blenkernel/intern/pbvh_pixels.cc b/source/blender/blenkernel/intern/pbvh_pixels.cc new file mode 100644 index 00000000000..d8dd2f4b382 --- /dev/null +++ b/source/blender/blenkernel/intern/pbvh_pixels.cc @@ -0,0 +1,393 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2022 Blender Foundation. All rights reserved. */ + +#include "BKE_customdata.h" +#include "BKE_mesh_mapping.h" +#include "BKE_pbvh.h" +#include "BKE_pbvh_pixels.hh" + +#include "DNA_image_types.h" +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_object_types.h" + +#include "BLI_math.h" +#include "BLI_task.h" + +#include "BKE_image_wrappers.hh" + +#include "bmesh.h" + +#include "pbvh_intern.h" + +namespace blender::bke::pbvh::pixels { + +/** Durind debugging this check could be enabled. It will write to each image pixel that is covered + * by the pbvh. */ +constexpr bool USE_WATERTIGHT_CHECK = false; + +/** + * Calculate the delta of two neighbour uv coordinates in the given image buffer. + */ +static float2 calc_barycentric_delta(const float2 uvs[3], + const float2 start_uv, + const float2 end_uv) +{ + + float3 start_barycentric; + barycentric_weights_v2(uvs[0], uvs[1], uvs[2], start_uv, start_barycentric); + float3 end_barycentric; + barycentric_weights_v2(uvs[0], uvs[1], uvs[2], end_uv, end_barycentric); + float3 barycentric = end_barycentric - start_barycentric; + return float2(barycentric.x, barycentric.y); +} + +static float2 calc_barycentric_delta_x(const ImBuf *image_buffer, + const float2 uvs[3], + const int x, + const int y) +{ + const float2 start_uv(float(x) / image_buffer->x, float(y) / image_buffer->y); + const float2 end_uv(float(x + 1) / image_buffer->x, float(y) / image_buffer->y); + return calc_barycentric_delta(uvs, start_uv, end_uv); +} + +static void extract_barycentric_pixels(UDIMTilePixels &tile_data, + const ImBuf *image_buffer, + const int triangle_index, + const float2 uvs[3], + const int minx, + const int miny, + const int maxx, + const int maxy) +{ + for (int y = miny; y < maxy; y++) { + bool start_detected = false; + PackedPixelRow pixel_row; + pixel_row.triangle_index = triangle_index; + pixel_row.num_pixels = 0; + int x; + + for (x = minx; x < maxx; x++) { + float2 uv(float(x) / image_buffer->x, float(y) / image_buffer->y); + float3 barycentric_weights; + barycentric_weights_v2(uvs[0], uvs[1], uvs[2], uv, barycentric_weights); + + const bool is_inside = barycentric_inside_triangle_v2(barycentric_weights); + if (!start_detected && is_inside) { + start_detected = true; + pixel_row.start_image_coordinate = ushort2(x, y); + pixel_row.start_barycentric_coord = float2(barycentric_weights.x, barycentric_weights.y); + } + else if (start_detected && !is_inside) { + break; + } + } + + if (!start_detected) { + continue; + } + pixel_row.num_pixels = x - pixel_row.start_image_coordinate.x; + tile_data.pixel_rows.append(pixel_row); + } +} + +static void init_triangles(PBVH *pbvh, PBVHNode *node, NodeData *node_data, const MLoop *mloop) +{ + for (int i = 0; i < node->totprim; i++) { + const MLoopTri *lt = &pbvh->looptri[node->prim_indices[i]]; + node_data->triangles.append( + int3(mloop[lt->tri[0]].v, mloop[lt->tri[1]].v, mloop[lt->tri[2]].v)); + } +} + +struct EncodePixelsUserData { + Image *image; + ImageUser *image_user; + PBVH *pbvh; + Vector<PBVHNode *> *nodes; + MLoopUV *ldata_uv; +}; + +static void do_encode_pixels(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict UNUSED(tls)) +{ + EncodePixelsUserData *data = static_cast<EncodePixelsUserData *>(userdata); + Image *image = data->image; + ImageUser image_user = *data->image_user; + PBVH *pbvh = data->pbvh; + PBVHNode *node = (*data->nodes)[n]; + NodeData *node_data = static_cast<NodeData *>(node->pixels.node_data); + LISTBASE_FOREACH (ImageTile *, tile, &data->image->tiles) { + image::ImageTileWrapper image_tile(tile); + image_user.tile = image_tile.get_tile_number(); + ImBuf *image_buffer = BKE_image_acquire_ibuf(image, &image_user, nullptr); + if (image_buffer == nullptr) { + continue; + } + + float2 tile_offset = float2(image_tile.get_tile_offset()); + UDIMTilePixels tile_data; + + Triangles &triangles = node_data->triangles; + for (int triangle_index = 0; triangle_index < triangles.size(); triangle_index++) { + const MLoopTri *lt = &pbvh->looptri[node->prim_indices[triangle_index]]; + float2 uvs[3] = { + float2(data->ldata_uv[lt->tri[0]].uv) - tile_offset, + float2(data->ldata_uv[lt->tri[1]].uv) - tile_offset, + float2(data->ldata_uv[lt->tri[2]].uv) - tile_offset, + }; + + const float minv = clamp_f(min_fff(uvs[0].y, uvs[1].y, uvs[2].y), 0.0f, 1.0f); + const int miny = floor(minv * image_buffer->y); + const float maxv = clamp_f(max_fff(uvs[0].y, uvs[1].y, uvs[2].y), 0.0f, 1.0f); + const int maxy = min_ii(ceil(maxv * image_buffer->y), image_buffer->y); + const float minu = clamp_f(min_fff(uvs[0].x, uvs[1].x, uvs[2].x), 0.0f, 1.0f); + const int minx = floor(minu * image_buffer->x); + const float maxu = clamp_f(max_fff(uvs[0].x, uvs[1].x, uvs[2].x), 0.0f, 1.0f); + const int maxx = min_ii(ceil(maxu * image_buffer->x), image_buffer->x); + + TrianglePaintInput &triangle = triangles.get_paint_input(triangle_index); + triangle.delta_barycentric_coord_u = calc_barycentric_delta_x(image_buffer, uvs, minx, miny); + extract_barycentric_pixels( + tile_data, image_buffer, triangle_index, uvs, minx, miny, maxx, maxy); + } + + BKE_image_release_ibuf(image, image_buffer, nullptr); + + if (tile_data.pixel_rows.is_empty()) { + continue; + } + + tile_data.tile_number = image_tile.get_tile_number(); + node_data->tiles.append(tile_data); + } +} + +static bool should_pixels_be_updated(PBVHNode *node) +{ + if ((node->flag & PBVH_Leaf) == 0) { + return false; + } + if ((node->flag & PBVH_RebuildPixels) != 0) { + return true; + } + NodeData *node_data = static_cast<NodeData *>(node->pixels.node_data); + if (node_data != nullptr) { + return false; + } + return true; +} + +static int64_t count_nodes_to_update(PBVH *pbvh) +{ + int64_t result = 0; + for (int n = 0; n < pbvh->totnode; n++) { + PBVHNode *node = &pbvh->nodes[n]; + if (should_pixels_be_updated(node)) { + result++; + } + } + return result; +} + +/** + * Find the nodes that needs to be updated. + * + * The nodes that require updated are added to the r_nodes_to_update parameter. + * Will fill in r_visited_polygons with polygons that are owned by nodes that do not require + * updates. + * + * returns if there were any nodes found (true). + */ +static bool find_nodes_to_update(PBVH *pbvh, Vector<PBVHNode *> &r_nodes_to_update) +{ + int64_t nodes_to_update_len = count_nodes_to_update(pbvh); + if (nodes_to_update_len == 0) { + return false; + } + + r_nodes_to_update.reserve(nodes_to_update_len); + + for (int n = 0; n < pbvh->totnode; n++) { + PBVHNode *node = &pbvh->nodes[n]; + if (!should_pixels_be_updated(node)) { + continue; + } + r_nodes_to_update.append(node); + node->flag = static_cast<PBVHNodeFlags>(node->flag | PBVH_RebuildPixels); + + if (node->pixels.node_data == nullptr) { + NodeData *node_data = MEM_new<NodeData>(__func__); + node->pixels.node_data = node_data; + } + else { + NodeData *node_data = static_cast<NodeData *>(node->pixels.node_data); + node_data->clear_data(); + } + } + + return true; +} + +static void apply_watertight_check(PBVH *pbvh, Image *image, ImageUser *image_user) +{ + ImageUser watertight = *image_user; + LISTBASE_FOREACH (ImageTile *, tile_data, &image->tiles) { + image::ImageTileWrapper image_tile(tile_data); + watertight.tile = image_tile.get_tile_number(); + ImBuf *image_buffer = BKE_image_acquire_ibuf(image, &watertight, nullptr); + if (image_buffer == nullptr) { + continue; + } + for (int n = 0; n < pbvh->totnode; n++) { + PBVHNode *node = &pbvh->nodes[n]; + if ((node->flag & PBVH_Leaf) == 0) { + continue; + } + NodeData *node_data = static_cast<NodeData *>(node->pixels.node_data); + UDIMTilePixels *tile_node_data = node_data->find_tile_data(image_tile); + if (tile_node_data == nullptr) { + continue; + } + + for (PackedPixelRow &pixel_row : tile_node_data->pixel_rows) { + int pixel_offset = pixel_row.start_image_coordinate.y * image_buffer->x + + pixel_row.start_image_coordinate.x; + for (int x = 0; x < pixel_row.num_pixels; x++) { + if (image_buffer->rect_float) { + copy_v4_fl(&image_buffer->rect_float[pixel_offset * 4], 1.0); + } + if (image_buffer->rect) { + uint8_t *dest = static_cast<uint8_t *>( + static_cast<void *>(&image_buffer->rect[pixel_offset])); + copy_v4_uchar(dest, 255); + } + pixel_offset += 1; + } + } + } + BKE_image_release_ibuf(image, image_buffer, nullptr); + } + BKE_image_partial_update_mark_full_update(image); +} + +static void update_pixels(PBVH *pbvh, + const struct MLoop *mloop, + struct CustomData *ldata, + struct Image *image, + struct ImageUser *image_user) +{ + Vector<PBVHNode *> nodes_to_update; + + if (!find_nodes_to_update(pbvh, nodes_to_update)) { + return; + } + + MLoopUV *ldata_uv = static_cast<MLoopUV *>(CustomData_get_layer(ldata, CD_MLOOPUV)); + if (ldata_uv == nullptr) { + return; + } + + for (PBVHNode *node : nodes_to_update) { + NodeData *node_data = static_cast<NodeData *>(node->pixels.node_data); + init_triangles(pbvh, node, node_data, mloop); + } + + EncodePixelsUserData user_data; + user_data.pbvh = pbvh; + user_data.image = image; + user_data.image_user = image_user; + user_data.ldata_uv = ldata_uv; + user_data.nodes = &nodes_to_update; + + TaskParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, true, nodes_to_update.size()); + BLI_task_parallel_range(0, nodes_to_update.size(), &user_data, do_encode_pixels, &settings); + if (USE_WATERTIGHT_CHECK) { + apply_watertight_check(pbvh, image, image_user); + } + + /* Clear the UpdatePixels flag. */ + for (PBVHNode *node : nodes_to_update) { + node->flag = static_cast<PBVHNodeFlags>(node->flag & ~PBVH_RebuildPixels); + } + +//#define DO_PRINT_STATISTICS +#ifdef DO_PRINT_STATISTICS + /* Print some statistics about compression ratio. */ + { + int64_t compressed_data_len = 0; + int64_t num_pixels = 0; + for (int n = 0; n < pbvh->totnode; n++) { + PBVHNode *node = &pbvh->nodes[n]; + if ((node->flag & PBVH_Leaf) == 0) { + continue; + } + NodeData *node_data = static_cast<NodeData *>(node->pixels.node_data); + compressed_data_len += node_data->triangles.mem_size(); + for (const UDIMTilePixels &tile_data : node_data->tiles) { + compressed_data_len += tile_data.encoded_pixels.size() * sizeof(PackedPixelRow); + for (const PackedPixelRow &encoded_pixels : tile_data.encoded_pixels) { + num_pixels += encoded_pixels.num_pixels; + } + } + } + printf("Encoded %lld pixels in %lld bytes (%f bytes per pixel)\n", + num_pixels, + compressed_data_len, + float(compressed_data_len) / num_pixels); + } +#endif +} + +NodeData &BKE_pbvh_pixels_node_data_get(PBVHNode &node) +{ + BLI_assert(node.pixels.node_data != nullptr); + NodeData *node_data = static_cast<NodeData *>(node.pixels.node_data); + return *node_data; +} + +void BKE_pbvh_pixels_mark_image_dirty(PBVHNode &node, Image &image, ImageUser &image_user) +{ + BLI_assert(node.pixels.node_data != nullptr); + NodeData *node_data = static_cast<NodeData *>(node.pixels.node_data); + if (node_data->flags.dirty) { + ImageUser local_image_user = image_user; + LISTBASE_FOREACH (ImageTile *, tile, &image.tiles) { + image::ImageTileWrapper image_tile(tile); + local_image_user.tile = image_tile.get_tile_number(); + ImBuf *image_buffer = BKE_image_acquire_ibuf(&image, &local_image_user, nullptr); + if (image_buffer == nullptr) { + continue; + } + + node_data->mark_region(image, image_tile, *image_buffer); + BKE_image_release_ibuf(&image, image_buffer, nullptr); + } + node_data->flags.dirty = false; + } +} + +} // namespace blender::bke::pbvh::pixels + +extern "C" { +using namespace blender::bke::pbvh::pixels; + +void BKE_pbvh_build_pixels(PBVH *pbvh, + const struct MLoop *mloop, + struct CustomData *ldata, + struct Image *image, + struct ImageUser *image_user) +{ + update_pixels(pbvh, mloop, ldata, image, image_user); +} + +void pbvh_pixels_free(PBVHNode *node) +{ + NodeData *node_data = static_cast<NodeData *>(node->pixels.node_data); + MEM_delete(node_data); + node->pixels.node_data = nullptr; +} +} diff --git a/source/blender/blenkernel/intern/pointcache.c b/source/blender/blenkernel/intern/pointcache.c index 19abff19b77..d5d304343df 100644 --- a/source/blender/blenkernel/intern/pointcache.c +++ b/source/blender/blenkernel/intern/pointcache.c @@ -285,8 +285,10 @@ static int ptcache_particle_write(int index, void *psys_v, void **data, int cfra } } else { - /* Particles are only stored in their lifetime. */ - if (cfra < pa->time - step || cfra > pa->dietime + step) { + /* Inclusive ranges for particle lifetime (`dietime - 1` for an inclusive end-frame). */ + const int pa_sfra = (int)pa->time - step; + const int pa_efra = ((int)pa->dietime - 1) + step; + if (!(cfra >= pa_sfra && cfra <= pa_efra)) { return 0; } } @@ -399,9 +401,12 @@ static void ptcache_particle_interpolate(int index, pa = psys->particles + index; - /* particle wasn't read from first cache so can't interpolate */ - if ((int)cfra1 < pa->time - psys->pointcache->step || - (int)cfra1 > pa->dietime + psys->pointcache->step) { + /* Inclusive ranges for particle lifetime (`dietime - 1` for an inclusive end-frame). */ + const int pa_sfra = (int)pa->time - psys->pointcache->step; + const int pa_efra = ((int)pa->dietime - 1) + psys->pointcache->step; + + /* Particle wasn't read from first cache so can't interpolate. */ + if (!(cfra1 >= pa_sfra && cfra1 <= pa_efra)) { return; } @@ -482,12 +487,16 @@ static int ptcache_particle_totwrite(void *psys_v, int cfra) if (psys->part->flag & PART_DIED) { /* Also store dead particles when they are displayed. */ for (p = 0; p < psys->totpart; p++, pa++) { - totwrite += (cfra >= pa->time - step); + const int pa_sfra = (int)pa->time - step; + totwrite += (cfra >= pa_sfra); } } else { for (p = 0; p < psys->totpart; p++, pa++) { - totwrite += (cfra >= pa->time - step && cfra <= pa->dietime + step); + /* Inclusive ranges for particle lifetime (`dietime - 1` for an inclusive end-frame). */ + const int pa_sfra = (int)pa->time - step; + const int pa_efra = ((int)pa->dietime - 1) + step; + totwrite += (cfra >= pa_sfra) && (cfra <= pa_efra); } } diff --git a/source/blender/blenkernel/intern/subsurf_ccg.c b/source/blender/blenkernel/intern/subsurf_ccg.c index 91deeda8381..5e7a0fc116b 100644 --- a/source/blender/blenkernel/intern/subsurf_ccg.c +++ b/source/blender/blenkernel/intern/subsurf_ccg.c @@ -1963,8 +1963,6 @@ static CCGDerivedMesh *getCCGDerivedMesh(CCGSubSurf *ss, ccgSubSurf_getNumFinalFaces(ss) * 4, ccgSubSurf_getNumFinalFaces(ss)); - CustomData_free_layer_active(&ccgdm->dm.polyData, CD_NORMAL, ccgdm->dm.numPolyData); - ccgdm->reverseFaceMap = MEM_callocN(sizeof(int) * ccgSubSurf_getNumFinalFaces(ss), "reverseFaceMap"); diff --git a/source/blender/blenkernel/intern/vfont.c b/source/blender/blenkernel/intern/vfont.c index 5f751da1ee1..9a6f861eae8 100644 --- a/source/blender/blenkernel/intern/vfont.c +++ b/source/blender/blenkernel/intern/vfont.c @@ -1372,7 +1372,7 @@ static bool vfont_to_curve(Object *ob, ct = chartransdata; for (i = 0; i <= slen; i++, ct++) { - float ctime, dtime, vec[4], tvec[4], rotvec[3]; + float ctime, dtime, vec[4], rotvec[3]; float si, co; /* Rotate around center character. */ @@ -1392,9 +1392,9 @@ static bool vfont_to_curve(Object *ob, CLAMP(ctime, 0.0f, 1.0f); /* Calculate the right loc AND the right rot separately. */ - /* `vec`, `tvec` need 4 items. */ - BKE_where_on_path(cu->textoncurve, ctime, vec, tvec, NULL, NULL, NULL); - BKE_where_on_path(cu->textoncurve, ctime + dtime, tvec, rotvec, NULL, NULL, NULL); + /* `vec` needs 4 items. */ + BKE_where_on_path(cu->textoncurve, ctime, vec, NULL, NULL, NULL, NULL); + BKE_where_on_path(cu->textoncurve, ctime + dtime, NULL, rotvec, NULL, NULL, NULL); mul_v3_fl(vec, sizefac); diff --git a/source/blender/blenkernel/nla_private.h b/source/blender/blenkernel/nla_private.h index 47cef2a53ec..41d1eef733c 100644 --- a/source/blender/blenkernel/nla_private.h +++ b/source/blender/blenkernel/nla_private.h @@ -142,7 +142,11 @@ typedef struct NlaKeyframingContext { /* Data of the currently edited strip (copy, or fake strip for the main action). */ NlaStrip strip; NlaEvalStrip *eval_strip; + /* Storage for the action track as a strip. */ + NlaStrip action_track_strip; + /* Strips above tweaked strip. */ + ListBase upper_estrips; /* Evaluated NLA stack below the tweak strip. */ NlaEvalData lower_eval_data; } NlaKeyframingContext; @@ -173,7 +177,22 @@ NlaEvalStrip *nlastrips_ctime_get_strip(ListBase *list, /** * Evaluates the given evaluation strip. */ -void nlastrip_evaluate(PointerRNA *ptr, + +enum eNlaStripEvaluate_Mode { + /* Blend upper strip with lower stack. */ + STRIP_EVAL_BLEND, + /* Given upper strip and blended snapshot, solve for lower stack. */ + STRIP_EVAL_BLEND_GET_INVERTED_LOWER_SNAPSHOT, + /* Store strip fcurve values in snapshot, properly marking blend_domain values. + * + * Currently only used for transitions to distinguish fcurve sampled values from default or lower + * stack values. + */ + STRIP_EVAL_NOBLEND, +}; + +void nlastrip_evaluate(const int evaluation_mode, + PointerRNA *ptr, NlaEvalData *channels, ListBase *modifiers, NlaEvalStrip *nes, @@ -222,6 +241,36 @@ void nlasnapshot_blend_get_inverted_upper_snapshot(NlaEvalData *eval_data, float upper_influence, NlaEvalSnapshot *r_upper_snapshot); +void nlasnapshot_blend_get_inverted_lower_snapshot(NlaEvalData *eval_data, + NlaEvalSnapshot *blended_snapshot, + NlaEvalSnapshot *upper_snapshot, + const short upper_blendmode, + const float upper_influence, + NlaEvalSnapshot *r_lower_snapshot); + +void nlasnapshot_blend_strip(PointerRNA *ptr, + NlaEvalData *channels, + ListBase *modifiers, + NlaEvalStrip *nes, + NlaEvalSnapshot *snapshot, + const struct AnimationEvalContext *anim_eval_context, + const bool flush_to_original); + +void nlasnapshot_blend_strip_get_inverted_lower_snapshot( + PointerRNA *ptr, + NlaEvalData *channels, + ListBase *modifiers, + NlaEvalStrip *nes, + NlaEvalSnapshot *snapshot, + const struct AnimationEvalContext *anim_eval_context); + +void nlasnapshot_blend_strip_no_blend(PointerRNA *ptr, + NlaEvalData *channels, + ListBase *modifiers, + NlaEvalStrip *nes, + NlaEvalSnapshot *snapshot, + const struct AnimationEvalContext *anim_eval_context); + #ifdef __cplusplus } #endif diff --git a/source/blender/blenlib/BLI_math_rotation.hh b/source/blender/blenlib/BLI_math_rotation.hh new file mode 100644 index 00000000000..e8b746b34df --- /dev/null +++ b/source/blender/blenlib/BLI_math_rotation.hh @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +/** \file + * \ingroup bli + */ + +#include "BLI_math_vec_types.hh" + +namespace blender::math { + +/** + * Rotate the unit-length \a direction around the unit-length \a axis by the \a angle. + */ +float3 rotate_direction_around_axis(const float3 &direction, const float3 &axis, float angle); + +} // namespace blender::math diff --git a/source/blender/blenlib/BLI_vector.hh b/source/blender/blenlib/BLI_vector.hh index da9ab9c313e..acf47f67168 100644 --- a/source/blender/blenlib/BLI_vector.hh +++ b/source/blender/blenlib/BLI_vector.hh @@ -387,6 +387,16 @@ class Vector { } /** + * Reset the size of the vector so that it contains new_size elements. + * All existing elements are destructed, and not copied if the data must be reallocated. + */ + void reinitialize(const int64_t new_size) + { + this->clear(); + this->resize(new_size); + } + + /** * Afterwards the vector has 0 elements, but will still have * memory to be refilled again. */ diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt index 99e07264276..e8a3851e082 100644 --- a/source/blender/blenlib/CMakeLists.txt +++ b/source/blender/blenlib/CMakeLists.txt @@ -94,6 +94,7 @@ set(SRC intern/math_interp.c intern/math_matrix.c intern/math_rotation.c + intern/math_rotation.cc intern/math_solvers.c intern/math_statistics.c intern/math_time.c @@ -251,6 +252,7 @@ set(SRC BLI_math_matrix.h BLI_math_mpq.hh BLI_math_rotation.h + BLI_math_rotation.hh BLI_math_solvers.h BLI_math_statistics.h BLI_math_time.h diff --git a/source/blender/blenlib/intern/math_base_inline.c b/source/blender/blenlib/intern/math_base_inline.c index bb6bc0db00d..a983821f15e 100644 --- a/source/blender/blenlib/intern/math_base_inline.c +++ b/source/blender/blenlib/intern/math_base_inline.c @@ -524,6 +524,15 @@ MINLINE uint max_uu(uint a, uint b) return (b < a) ? a : b; } +MINLINE unsigned long long min_ulul(unsigned long long a, unsigned long long b) +{ + return (a < b) ? a : b; +} +MINLINE unsigned long long max_ulul(unsigned long long a, unsigned long long b) +{ + return (b < a) ? a : b; +} + MINLINE float min_fff(float a, float b, float c) { return min_ff(min_ff(a, b), c); diff --git a/source/blender/blenlib/intern/math_rotation.cc b/source/blender/blenlib/intern/math_rotation.cc new file mode 100644 index 00000000000..74300d55954 --- /dev/null +++ b/source/blender/blenlib/intern/math_rotation.cc @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup bli + */ + +#include "BLI_math_base.h" +#include "BLI_math_rotation.hh" +#include "BLI_math_vector.h" +#include "BLI_math_vector.hh" + +namespace blender::math { + +float3 rotate_direction_around_axis(const float3 &direction, const float3 &axis, const float angle) +{ + BLI_ASSERT_UNIT_V3(direction); + BLI_ASSERT_UNIT_V3(axis); + + const float3 axis_scaled = axis * math::dot(direction, axis); + const float3 diff = direction - axis_scaled; + const float3 cross = math::cross(axis, diff); + + return axis_scaled + diff * std::cos(angle) + cross * std::sin(angle); +} + +} // namespace blender::math diff --git a/source/blender/blenlib/tests/BLI_math_rotation_test.cc b/source/blender/blenlib/tests/BLI_math_rotation_test.cc index a10e441cfbe..a283118bea2 100644 --- a/source/blender/blenlib/tests/BLI_math_rotation_test.cc +++ b/source/blender/blenlib/tests/BLI_math_rotation_test.cc @@ -4,6 +4,8 @@ #include "BLI_math_base.h" #include "BLI_math_rotation.h" +#include "BLI_math_rotation.hh" +#include "BLI_math_vector.hh" #include <cmath> @@ -147,3 +149,23 @@ TEST(math_rotation, quat_split_swing_and_twist_negative) EXPECT_V4_NEAR(swing, expected_swing, FLT_EPSILON); EXPECT_V4_NEAR(twist, expected_twist, FLT_EPSILON); } + +namespace blender::math::tests { + +TEST(math_rotation, RotateDirectionAroundAxis) +{ + const float3 a = rotate_direction_around_axis({1, 0, 0}, {0, 0, 1}, M_PI_2); + EXPECT_NEAR(a.x, 0.0f, FLT_EPSILON); + EXPECT_NEAR(a.y, 1.0f, FLT_EPSILON); + EXPECT_NEAR(a.z, 0.0f, FLT_EPSILON); + const float3 b = rotate_direction_around_axis({1, 0, 0}, {0, 0, 1}, M_PI); + EXPECT_NEAR(b.x, -1.0f, FLT_EPSILON); + EXPECT_NEAR(b.y, 0.0f, FLT_EPSILON); + EXPECT_NEAR(b.z, 0.0f, FLT_EPSILON); + const float3 c = rotate_direction_around_axis({0, 0, 1}, {0, 0, 1}, 0.0f); + EXPECT_NEAR(c.x, 0.0f, FLT_EPSILON); + EXPECT_NEAR(c.y, 0.0f, FLT_EPSILON); + EXPECT_NEAR(c.z, 1.0f, FLT_EPSILON); +} + +} // namespace blender::math::tests diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index aba0bfe84d2..27890a908ab 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -2619,7 +2619,7 @@ static void lib_link_workspace_layout_restore(struct IDNameLib_Map *id_map, scpt->script = restore_pointer_by_name(id_map, (ID *)scpt->script, USER_REAL); - /*screen->script = NULL; - 2.45 set to null, better re-run the script */ + // screen->script = NULL; /* 2.45 set to null, better re-run the script. */ if (scpt->script) { SCRIPT_SET_NULL(scpt->script); } diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index 95851c0e9ff..ec60183218c 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -2677,5 +2677,17 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) */ { /* Keep this block, even when empty. */ + + /* Enable named attributes overlay in node editor. */ + 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_NAMED_ATTRIBUTES; + } + } + } + } } } diff --git a/source/blender/blenloader/intern/versioning_defaults.c b/source/blender/blenloader/intern/versioning_defaults.c index 000f132adcb..f65976ee55f 100644 --- a/source/blender/blenloader/intern/versioning_defaults.c +++ b/source/blender/blenloader/intern/versioning_defaults.c @@ -532,11 +532,13 @@ void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template) for (Mesh *mesh = bmain->meshes.first; mesh; mesh = mesh->id.next) { /* Match default for new meshes. */ mesh->smoothresh = DEG2RADF(30); + /* Match voxel remesher options for all existing meshes in templates. */ + mesh->flag |= ME_REMESH_REPROJECT_VOLUME | ME_REMESH_REPROJECT_PAINT_MASK | + ME_REMESH_REPROJECT_SCULPT_FACE_SETS | ME_REMESH_REPROJECT_VERTEX_COLORS; /* For Sculpting template. */ if (app_template && STREQ(app_template, "Sculpting")) { mesh->remesh_voxel_size = 0.035f; - mesh->flag |= ME_REMESH_FIX_POLES | ME_REMESH_REPROJECT_VOLUME; BKE_mesh_smooth_flag_set(mesh, false); } else { diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c index 490328106ca..2ae660ab1b6 100644 --- a/source/blender/blenloader/intern/writefile.c +++ b/source/blender/blenloader/intern/writefile.c @@ -611,11 +611,14 @@ static void mywrite_id_begin(WriteData *wd, ID *id) if (wd->use_memfile) { wd->mem.current_id_session_uuid = id->session_uuid; - /* If current next memchunk does not match the ID we are about to write, try to find the - * correct memchunk in the mapping using ID's session_uuid. */ + /* If current next memchunk does not match the ID we are about to write, or is not the _first_ + * one for said ID, try to find the correct memchunk in the mapping using ID's session_uuid. */ + MemFileChunk *curr_memchunk = wd->mem.reference_current_chunk; + MemFileChunk *prev_memchunk = curr_memchunk != NULL ? curr_memchunk->prev : NULL; if (wd->mem.id_session_uuid_mapping != NULL && - (wd->mem.reference_current_chunk == NULL || - wd->mem.reference_current_chunk->id_session_uuid != id->session_uuid)) { + (curr_memchunk == NULL || curr_memchunk->id_session_uuid != id->session_uuid || + (prev_memchunk != NULL && + (prev_memchunk->id_session_uuid == curr_memchunk->id_session_uuid)))) { void *ref = BLI_ghash_lookup(wd->mem.id_session_uuid_mapping, POINTER_FROM_UINT(id->session_uuid)); if (ref != NULL) { diff --git a/source/blender/bmesh/operators/bmo_smooth_laplacian.c b/source/blender/bmesh/operators/bmo_smooth_laplacian.c index 67ab57fa403..08efe5383a8 100644 --- a/source/blender/bmesh/operators/bmo_smooth_laplacian.c +++ b/source/blender/bmesh/operators/bmo_smooth_laplacian.c @@ -37,7 +37,7 @@ struct BLaplacianSystem { BMOperator *op; LinearSolver *context; - /*Data*/ + /* Data. */ float min_area; }; typedef struct BLaplacianSystem LaplacianSystem; diff --git a/source/blender/compositor/intern/COM_NodeOperationBuilder.cc b/source/blender/compositor/intern/COM_NodeOperationBuilder.cc index 788686f3036..1fdec43eb3b 100644 --- a/source/blender/compositor/intern/COM_NodeOperationBuilder.cc +++ b/source/blender/compositor/intern/COM_NodeOperationBuilder.cc @@ -104,7 +104,7 @@ void NodeOperationBuilder::convert_to_operations(ExecutionSystem *system) prune_operations(); /* ensure topological (link-based) order of nodes */ - /*sort_operations();*/ /* not needed yet */ + // sort_operations(); /* not needed yet. */ if (context_->get_execution_model() == eExecutionModel::Tiled) { /* create execution groups */ diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc index b5945c06ad3..23147b63e27 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc @@ -699,7 +699,7 @@ void DepsgraphRelationBuilder::build_object(Object *object) OperationKey ob_eval_key(&object->id, NodeType::TRANSFORM, OperationCode::TRANSFORM_EVAL); add_relation(init_transform_key, local_transform_key, "Transform Init"); /* Various flags, flushing from bases/collections. */ - build_object_from_layer_relations(object); + build_object_layer_component_relations(object); /* Parenting. */ if (object->parent != nullptr) { /* Make sure parent object's relations are built. */ @@ -787,7 +787,37 @@ void DepsgraphRelationBuilder::build_object(Object *object) build_parameters(&object->id); } -void DepsgraphRelationBuilder::build_object_from_layer_relations(Object *object) +/* NOTE: Implies that the object has base in the current view layer. */ +void DepsgraphRelationBuilder::build_object_from_view_layer_base(Object *object) +{ + /* It is possible to have situation when an object is pulled into the dependency graph in a + * few different ways: + * + * - Indirect driver dependency, which doesn't have a Base (or, Base is unknown). + * - Via a base from a view layer (view layer of the graph, or view layer of a set scene). + * - Possibly other ways, which are not important for decision making here. + * + * There needs to be a relation from view layer which has a base with the object so that the + * order of flags evaluation is correct (object-level base flags evaluation requires view layer + * to be evaluated first). + * + * This build call handles situation when object comes from a view layer, hence has a base, and + * needs a relation from the view layer. Do the relation prior to check of whether the object + * relations are built so that the relation is created from every view layer which has a base + * with this object. */ + + OperationKey view_layer_done_key( + &scene_->id, NodeType::LAYER_COLLECTIONS, OperationCode::VIEW_LAYER_EVAL); + OperationKey object_from_layer_entry_key( + &object->id, NodeType::OBJECT_FROM_LAYER, OperationCode::OBJECT_FROM_LAYER_ENTRY); + + add_relation(view_layer_done_key, object_from_layer_entry_key, "View Layer flags to Object"); + + /* Regular object building. */ + build_object(object); +} + +void DepsgraphRelationBuilder::build_object_layer_component_relations(Object *object) { OperationKey object_from_layer_entry_key( &object->id, NodeType::OBJECT_FROM_LAYER, OperationCode::OBJECT_FROM_LAYER_ENTRY); @@ -810,10 +840,6 @@ void DepsgraphRelationBuilder::build_object_from_layer_relations(Object *object) OperationKey synchronize_key( &object->id, NodeType::SYNCHRONIZATION, OperationCode::SYNCHRONIZE_TO_ORIGINAL); add_relation(object_from_layer_exit_key, synchronize_key, "Synchronize to Original"); - - OperationKey view_layer_done_key( - &scene_->id, NodeType::LAYER_COLLECTIONS, OperationCode::VIEW_LAYER_EVAL); - add_relation(view_layer_done_key, object_from_layer_entry_key, "View Layer flags to Object"); } void DepsgraphRelationBuilder::build_object_data(Object *object) diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.h b/source/blender/depsgraph/intern/builder/deg_builder_relations.h index e1312c68cea..1ccecc9a3f2 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.h @@ -200,7 +200,8 @@ class DepsgraphRelationBuilder : public DepsgraphBuilder { Object *object, Collection *collection); virtual void build_object(Object *object); - virtual void build_object_from_layer_relations(Object *object); + virtual void build_object_from_view_layer_base(Object *object); + virtual void build_object_layer_component_relations(Object *object); virtual void build_object_data(Object *object); virtual void build_object_data_camera(Object *object); virtual void build_object_data_geometry(Object *object); diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations_view_layer.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations_view_layer.cc index f9306620a9d..d723e5beb75 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations_view_layer.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations_view_layer.cc @@ -81,7 +81,7 @@ void DepsgraphRelationBuilder::build_view_layer(Scene *scene, * do nullptr-pointer check of the base, so it's fine to pass original one. */ LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) { if (need_pull_base_into_graph(base)) { - build_object(base->object); + build_object_from_view_layer_base(base->object); } } 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 5597d496890..058f57e5a61 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 @@ -163,7 +163,7 @@ const ID *nested_id_hack_get_discarded_pointers(NestedIDHackTempStorage *storage switch (GS(id->name)) { # define SPECIAL_CASE(id_type, dna_type, field, variable) \ case id_type: { \ - storage->variable = *(dna_type *)id; \ + storage->variable = dna::shallow_copy(*(dna_type *)id); \ storage->variable.field = nullptr; \ return &storage->variable.id; \ } diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index aed527639c5..6d3c203b076 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -16,6 +16,7 @@ set(INC ../editors/space_view3d ../functions ../gpu + ../gpu/intern ../imbuf ../makesdna ../makesrna @@ -123,6 +124,7 @@ set(SRC engines/eevee/eevee_sampling.c engines/eevee/eevee_screen_raytrace.c engines/eevee/eevee_shaders.c + engines/eevee/eevee_shaders_extra.cc engines/eevee/eevee_shadows.c engines/eevee/eevee_shadows_cascade.c engines/eevee/eevee_shadows_cube.c @@ -266,9 +268,13 @@ set(GLSL_SRC engines/eevee/shaders/closure_eval_lib.glsl engines/eevee/shaders/closure_eval_diffuse_lib.glsl engines/eevee/shaders/closure_eval_glossy_lib.glsl + engines/eevee/shaders/closure_eval_surface_lib.glsl engines/eevee/shaders/closure_eval_refraction_lib.glsl + engines/eevee/shaders/closure_eval_volume_lib.glsl engines/eevee/shaders/closure_eval_translucent_lib.glsl engines/eevee/shaders/closure_type_lib.glsl + engines/eevee/shaders/eevee_empty.glsl + engines/eevee/shaders/eevee_empty_volume.glsl engines/eevee/shaders/effect_bloom_frag.glsl engines/eevee/shaders/effect_dof_bokeh_frag.glsl engines/eevee/shaders/effect_dof_dilate_tiles_frag.glsl @@ -302,7 +308,6 @@ set(GLSL_SRC engines/eevee/shaders/object_motion_frag.glsl engines/eevee/shaders/object_motion_vert.glsl engines/eevee/shaders/prepass_frag.glsl - engines/eevee/shaders/prepass_vert.glsl engines/eevee/shaders/shadow_accum_frag.glsl engines/eevee/shaders/shadow_frag.glsl engines/eevee/shaders/shadow_vert.glsl @@ -333,6 +338,7 @@ set(GLSL_SRC engines/eevee/shaders/volumetric_resolve_frag.glsl engines/eevee/shaders/volumetric_scatter_frag.glsl engines/eevee/shaders/volumetric_integration_frag.glsl + engines/eevee/shaders/world_vert.glsl engines/workbench/shaders/workbench_cavity_lib.glsl engines/workbench/shaders/workbench_common_lib.glsl @@ -364,6 +370,7 @@ set(GLSL_SRC engines/workbench/workbench_shader_shared.h + intern/shaders/common_attribute_lib.glsl intern/shaders/common_colormanagement_lib.glsl intern/shaders/common_globals_lib.glsl intern/shaders/common_gpencil_lib.glsl diff --git a/source/blender/draw/engines/eevee/eevee_data.c b/source/blender/draw/engines/eevee/eevee_data.c index ee70cebcfd2..0c56b67ca99 100644 --- a/source/blender/draw/engines/eevee/eevee_data.c +++ b/source/blender/draw/engines/eevee/eevee_data.c @@ -160,7 +160,7 @@ EEVEE_HairMotionData *EEVEE_motion_blur_hair_data_get(EEVEE_ObjectMotionData *mb { if (mb_data->hair_data == NULL) { /* Ugly, we allocate for each modifiers and just fill based on modifier index in the list. */ - int psys_len = (ob->type != OB_CURVES) ? BLI_listbase_count(&ob->modifiers) : 1; + int psys_len = BLI_listbase_count(&ob->modifiers); EEVEE_HairMotionData *hair_step = MEM_callocN( sizeof(EEVEE_HairMotionData) + sizeof(hair_step->psys[0]) * psys_len, __func__); hair_step->psys_len = psys_len; @@ -170,6 +170,18 @@ EEVEE_HairMotionData *EEVEE_motion_blur_hair_data_get(EEVEE_ObjectMotionData *mb return mb_data->hair_data; } +EEVEE_HairMotionData *EEVEE_motion_blur_curves_data_get(EEVEE_ObjectMotionData *mb_data) +{ + if (mb_data->hair_data == NULL) { + EEVEE_HairMotionData *hair_step = MEM_callocN( + sizeof(EEVEE_HairMotionData) + sizeof(hair_step->psys[0]), __func__); + hair_step->psys_len = 1; + hair_step->type = EEVEE_MOTION_DATA_HAIR; + mb_data->hair_data = hair_step; + } + return mb_data->hair_data; +} + /* View Layer data. */ void EEVEE_view_layer_data_free(void *storage) diff --git a/source/blender/draw/engines/eevee/eevee_engine.c b/source/blender/draw/engines/eevee/eevee_engine.c index 6cd1a31085f..81edee17c76 100644 --- a/source/blender/draw/engines/eevee/eevee_engine.c +++ b/source/blender/draw/engines/eevee/eevee_engine.c @@ -52,6 +52,8 @@ static void eevee_engine_init(void *ved) stl->g_data->valid_taa_history = (txl->taa_history != NULL); stl->g_data->queued_shaders_count = 0; stl->g_data->render_timesteps = 1; + stl->g_data->disable_ligthprobes = v3d && + (v3d->object_type_exclude_viewport & (1 << OB_LIGHTPROBE)); /* Main Buffer */ DRW_texture_ensure_fullscreen_2d(&txl->color, GPU_RGBA16F, DRW_TEX_FILTER); @@ -111,7 +113,7 @@ void EEVEE_cache_populate(void *vedata, Object *ob) EEVEE_materials_cache_populate(vedata, sldata, ob, &cast_shadow); } else if (ob->type == OB_CURVES) { - EEVEE_object_hair_cache_populate(vedata, sldata, ob, &cast_shadow); + EEVEE_object_curves_cache_populate(vedata, sldata, ob, &cast_shadow); } else if (ob->type == OB_VOLUME) { EEVEE_volumes_cache_object_add(sldata, vedata, draw_ctx->scene, ob); @@ -253,6 +255,10 @@ static void eevee_draw_scene(void *vedata) /* Set ray type. */ sldata->common_data.ray_type = EEVEE_RAY_CAMERA; sldata->common_data.ray_depth = 0.0f; + if (stl->g_data->disable_ligthprobes) { + sldata->common_data.prb_num_render_cube = 1; + sldata->common_data.prb_num_render_grid = 1; + } GPU_uniformbuf_update(sldata->common_ubo, &sldata->common_data); GPU_framebuffer_bind(fbl->main_fb); diff --git a/source/blender/draw/engines/eevee/eevee_lightprobes.c b/source/blender/draw/engines/eevee/eevee_lightprobes.c index 219c44de9dc..94915180483 100644 --- a/source/blender/draw/engines/eevee/eevee_lightprobes.c +++ b/source/blender/draw/engines/eevee/eevee_lightprobes.c @@ -1193,6 +1193,11 @@ void EEVEE_lightprobes_refresh_planar(EEVEE_ViewLayerData *sldata, EEVEE_Data *v common_data->ssrefract_toggle = false; common_data->sss_toggle = false; + if (vedata->stl->g_data->disable_ligthprobes) { + sldata->common_data.prb_num_render_cube = 1; + sldata->common_data.prb_num_render_grid = 1; + } + 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. */ diff --git a/source/blender/draw/engines/eevee/eevee_materials.c b/source/blender/draw/engines/eevee/eevee_materials.c index 4b9ad693971..c46e5dd75d6 100644 --- a/source/blender/draw/engines/eevee/eevee_materials.c +++ b/source/blender/draw/engines/eevee/eevee_materials.c @@ -73,7 +73,12 @@ void EEVEE_material_bind_resources(DRWShadingGroup *shgrp, bool use_diffuse = GPU_material_flag_get(gpumat, GPU_MATFLAG_DIFFUSE); bool use_glossy = GPU_material_flag_get(gpumat, GPU_MATFLAG_GLOSSY); bool use_refract = GPU_material_flag_get(gpumat, GPU_MATFLAG_REFRACT); + bool use_ao = GPU_material_flag_get(gpumat, GPU_MATFLAG_AO); +#ifdef __APPLE__ + /* NOTE: Some implementation do not optimize out the unused samplers. */ + use_diffuse = use_glossy = use_refract = use_ao = true; +#endif LightCache *lcache = vedata->stl->g_data->light_cache; EEVEE_EffectsInfo *effects = vedata->stl->effects; EEVEE_PrivateData *pd = vedata->stl->g_data; @@ -91,6 +96,8 @@ void EEVEE_material_bind_resources(DRWShadingGroup *shgrp, if (use_diffuse || use_glossy || use_refract) { DRW_shgroup_uniform_texture_ref(shgrp, "shadowCubeTexture", &sldata->shadow_cube_pool); DRW_shgroup_uniform_texture_ref(shgrp, "shadowCascadeTexture", &sldata->shadow_cascade_pool); + } + if (use_diffuse || use_glossy || use_refract || use_ao) { DRW_shgroup_uniform_texture_ref(shgrp, "maxzBuffer", &vedata->txl->maxzbuffer); } if ((use_diffuse || use_glossy) && !use_ssrefraction) { @@ -374,6 +381,13 @@ void EEVEE_materials_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) DRW_shgroup_uniform_block(grp, "light_block", sldata->light_ubo); DRW_shgroup_uniform_block(grp, "shadow_block", sldata->shadow_ubo); DRW_shgroup_uniform_block_ref(grp, "renderpass_block", &stl->g_data->renderpass_ubo); + DRW_shgroup_uniform_texture(grp, "utilTex", e_data.util_tex); + DRW_shgroup_uniform_texture_ref(grp, "shadowCubeTexture", &sldata->shadow_cube_pool); + DRW_shgroup_uniform_texture_ref(grp, "shadowCascadeTexture", &sldata->shadow_cascade_pool); + DRW_shgroup_uniform_texture_ref(grp, "probePlanars", &vedata->txl->planar_pool); + DRW_shgroup_uniform_texture_ref(grp, "probeCubes", &stl->g_data->light_cache->cube_tx.tex); + DRW_shgroup_uniform_texture_ref(grp, "irradianceGrid", &stl->g_data->light_cache->grid_tx.tex); + DRW_shgroup_uniform_texture_ref(grp, "maxzBuffer", &vedata->txl->maxzbuffer); DRW_shgroup_call(grp, DRW_cache_fullscreen_quad_get(), NULL); } @@ -578,7 +592,7 @@ static EeveeMaterialCache material_opaque(EEVEE_Data *vedata, SET_FLAG_FROM_TEST(mat_options, use_ssrefract, VAR_MAT_REFRACT); SET_FLAG_FROM_TEST(mat_options, is_hair, VAR_MAT_HAIR); GPUMaterial *gpumat = EEVEE_material_get(vedata, scene, ma, NULL, mat_options); - const bool use_sss = GPU_material_flag_get(gpumat, GPU_MATFLAG_SSS); + const bool use_sss = GPU_material_flag_get(gpumat, GPU_MATFLAG_SUBSURFACE); int ssr_id = (((effects->enabled_effects & EFFECT_SSR) != 0) && !use_ssrefract) ? 1 : 0; int option = (use_ssrefract ? 0 : (use_sss ? 1 : 2)) * 2 + do_cull; @@ -740,34 +754,6 @@ BLI_INLINE EeveeMaterialCache eevee_material_cache_get( return matcache; } -static void eevee_hair_cache_populate(EEVEE_Data *vedata, - EEVEE_ViewLayerData *sldata, - Object *ob, - ParticleSystem *psys, - ModifierData *md, - int matnr, - bool *cast_shadow) -{ - 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, 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_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, NULL); - DRW_shgroup_add_material_resources(*matcache.shadow_grp_p, matcache.shading_gpumat); - *cast_shadow = true; - } - - EEVEE_motion_blur_hair_cache_populate(sldata, vedata, ob, psys, md); -} - #define ADD_SHGROUP_CALL(shgrp, ob, geom, oedata) \ do { \ if (oedata) { \ @@ -899,18 +885,56 @@ void EEVEE_particle_hair_cache_populate(EEVEE_Data *vedata, if (draw_as != PART_DRAW_PATH) { continue; } - eevee_hair_cache_populate(vedata, sldata, ob, psys, md, part->omat, cast_shadow); + EeveeMaterialCache matcache = eevee_material_cache_get( + vedata, sldata, ob, part->omat - 1, true); + + if (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_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, NULL); + DRW_shgroup_add_material_resources(*matcache.shadow_grp_p, matcache.shading_gpumat); + *cast_shadow = true; + } + + EEVEE_motion_blur_hair_cache_populate(sldata, vedata, ob, psys, md); } } } } -void EEVEE_object_hair_cache_populate(EEVEE_Data *vedata, - EEVEE_ViewLayerData *sldata, - Object *ob, - bool *cast_shadow) +void EEVEE_object_curves_cache_populate(EEVEE_Data *vedata, + EEVEE_ViewLayerData *sldata, + Object *ob, + bool *cast_shadow) { - eevee_hair_cache_populate(vedata, sldata, ob, NULL, NULL, CURVES_MATERIAL_NR, cast_shadow); + EeveeMaterialCache matcache = eevee_material_cache_get( + vedata, sldata, ob, CURVES_MATERIAL_NR - 1, true); + + if (matcache.depth_grp) { + *matcache.depth_grp_p = DRW_shgroup_curves_create_sub(ob, 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_curves_create_sub( + ob, 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_curves_create_sub(ob, matcache.shadow_grp, NULL); + DRW_shgroup_add_material_resources(*matcache.shadow_grp_p, matcache.shading_gpumat); + *cast_shadow = true; + } + + EEVEE_motion_blur_curves_cache_populate(sldata, vedata, ob); } void EEVEE_materials_cache_finish(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata) diff --git a/source/blender/draw/engines/eevee/eevee_motion_blur.c b/source/blender/draw/engines/eevee/eevee_motion_blur.c index fbc19a01a8b..e3342508a14 100644 --- a/source/blender/draw/engines/eevee/eevee_motion_blur.c +++ b/source/blender/draw/engines/eevee/eevee_motion_blur.c @@ -270,6 +270,57 @@ void EEVEE_motion_blur_hair_cache_populate(EEVEE_ViewLayerData *UNUSED(sldata), } } +void EEVEE_motion_blur_curves_cache_populate(EEVEE_ViewLayerData *UNUSED(sldata), + EEVEE_Data *vedata, + Object *ob) +{ + EEVEE_PassList *psl = vedata->psl; + EEVEE_StorageList *stl = vedata->stl; + EEVEE_EffectsInfo *effects = stl->effects; + + if (!DRW_state_is_scene_render() || psl->velocity_hair == NULL) { + return; + } + + /* For now we assume curves objects are always moving. */ + EEVEE_ObjectMotionData *mb_data = EEVEE_motion_blur_object_data_get(&effects->motion_blur, ob); + if (mb_data == NULL) { + return; + } + + int mb_step = effects->motion_blur_step; + /* Store transform. */ + copy_m4_m4(mb_data->obmat[mb_step], ob->obmat); + + EEVEE_HairMotionData *mb_curves = EEVEE_motion_blur_curves_data_get(mb_data); + + if (mb_step == MB_CURR) { + /* Fill missing matrices if the object was hidden in previous or next frame. */ + if (is_zero_m4(mb_data->obmat[MB_PREV])) { + copy_m4_m4(mb_data->obmat[MB_PREV], mb_data->obmat[MB_CURR]); + } + if (is_zero_m4(mb_data->obmat[MB_NEXT])) { + copy_m4_m4(mb_data->obmat[MB_NEXT], mb_data->obmat[MB_CURR]); + } + + GPUTexture *tex_prev = mb_curves->psys[0].step_data[MB_PREV].hair_pos_tx; + GPUTexture *tex_next = mb_curves->psys[0].step_data[MB_NEXT].hair_pos_tx; + + DRWShadingGroup *grp = DRW_shgroup_curves_create_sub(ob, 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]); + DRW_shgroup_uniform_texture(grp, "prvBuffer", tex_prev); + DRW_shgroup_uniform_texture(grp, "nxtBuffer", tex_next); + DRW_shgroup_uniform_bool(grp, "useDeform", &mb_curves->use_deform, 1); + } + else { + /* Store vertex position buffer. */ + mb_curves->psys[0].step_data[mb_step].hair_pos = DRW_curves_pos_buffer_get(ob); + mb_curves->use_deform = true; + } +} + void EEVEE_motion_blur_cache_populate(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata, Object *ob) diff --git a/source/blender/draw/engines/eevee/eevee_private.h b/source/blender/draw/engines/eevee/eevee_private.h index 5220bac4e3f..9f97dacf9fe 100644 --- a/source/blender/draw/engines/eevee/eevee_private.h +++ b/source/blender/draw/engines/eevee/eevee_private.h @@ -462,7 +462,7 @@ typedef struct EEVEE_RenderPassData { int renderPassSSSColor; int renderPassEnvironment; int renderPassAOV; - int renderPassAOVActive; + uint renderPassAOVActive; int _pad[3]; } EEVEE_RenderPassData; @@ -666,7 +666,7 @@ typedef struct EEVEE_HairMotionData { /** Needs to be first to ensure casting. */ eEEVEEMotionData type; int use_deform; - /** Allocator will alloc enough slot for all particle systems. Or 1 if it's a hair object. */ + /** Allocator will alloc enough slot for all particle systems. Or 1 if it's a curves object. */ int psys_len; struct { /* The vbos and textures are not owned. */ @@ -1012,6 +1012,7 @@ typedef struct EEVEE_PrivateData { struct DRWCallBuffer *planar_display_shgrp; struct GHash *material_hash; float background_alpha; /* TODO: find a better place for this. */ + bool disable_ligthprobes; /* Chosen lightcache: can come from Lookdev or the viewlayer. */ struct LightCache *light_cache; /* For planar probes */ @@ -1050,7 +1051,7 @@ typedef struct EEVEE_PrivateData { /* Renderpasses */ /* Bitmask containing the active render_passes */ eViewLayerEEVEEPassType render_passes; - int aov_hash; + uint aov_hash; int num_aovs_used; struct CryptomatteSession *cryptomatte_session; bool cryptomatte_accurate_mode; @@ -1095,6 +1096,7 @@ EEVEE_ObjectEngineData *EEVEE_object_data_ensure(Object *ob); EEVEE_ObjectMotionData *EEVEE_motion_blur_object_data_get(EEVEE_MotionBlurData *mb, Object *ob); EEVEE_GeometryMotionData *EEVEE_motion_blur_geometry_data_get(EEVEE_ObjectMotionData *mb_data); EEVEE_HairMotionData *EEVEE_motion_blur_hair_data_get(EEVEE_ObjectMotionData *mb_data, Object *ob); +EEVEE_HairMotionData *EEVEE_motion_blur_curves_data_get(EEVEE_ObjectMotionData *mb_data); EEVEE_LightProbeEngineData *EEVEE_lightprobe_data_get(Object *ob); EEVEE_LightProbeEngineData *EEVEE_lightprobe_data_ensure(Object *ob); EEVEE_LightEngineData *EEVEE_light_data_get(Object *ob); @@ -1120,10 +1122,10 @@ void EEVEE_particle_hair_cache_populate(EEVEE_Data *vedata, EEVEE_ViewLayerData *sldata, Object *ob, bool *cast_shadow); -void EEVEE_object_hair_cache_populate(EEVEE_Data *vedata, - EEVEE_ViewLayerData *sldata, - Object *ob, - bool *cast_shadow); +void EEVEE_object_curves_cache_populate(EEVEE_Data *vedata, + EEVEE_ViewLayerData *sldata, + Object *ob, + bool *cast_shadow); void EEVEE_materials_cache_finish(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata); void EEVEE_materials_free(void); void EEVEE_update_noise(EEVEE_PassList *psl, EEVEE_FramebufferList *fbl, const double offsets[3]); @@ -1290,6 +1292,20 @@ struct GPUMaterial *EEVEE_material_get( EEVEE_Data *vedata, struct Scene *scene, Material *ma, World *wo, int options); void EEVEE_shaders_free(void); +void eevee_shader_extra_init(void); +void eevee_shader_extra_exit(void); +void eevee_shader_material_create_info_amend(GPUMaterial *gpumat, + GPUCodegenOutput *codegen, + char *frag, + char *vert, + char *geom, + char *defines); +GPUShader *eevee_shaders_sh_create_helper(const char *name, + const char *vert_name, + const char *frag_name, + const char *defines, + bool use_layered_rendering); + /* eevee_lightprobes.c */ bool EEVEE_lightprobes_obj_visibility_cb(bool vis_in, void *user_data); @@ -1463,6 +1479,9 @@ void EEVEE_motion_blur_hair_cache_populate(EEVEE_ViewLayerData *sldata, Object *ob, struct ParticleSystem *psys, struct ModifierData *md); +void EEVEE_motion_blur_curves_cache_populate(EEVEE_ViewLayerData *sldata, + EEVEE_Data *vedata, + Object *ob); void EEVEE_motion_blur_swap_data(EEVEE_Data *vedata); void EEVEE_motion_blur_cache_finish(EEVEE_Data *vedata); void EEVEE_motion_blur_draw(EEVEE_Data *vedata); @@ -1504,7 +1523,7 @@ bool EEVEE_renderpasses_only_first_sample_pass_active(EEVEE_Data *vedata); * Calculate the hash for an AOV. The least significant bit is used to store the AOV * type the rest of the bits are used for the name hash. */ -int EEVEE_renderpasses_aov_hash(const ViewLayerAOV *aov); +uint EEVEE_renderpasses_aov_hash(const ViewLayerAOV *aov); /* eevee_temporal_sampling.c */ diff --git a/source/blender/draw/engines/eevee/eevee_render.c b/source/blender/draw/engines/eevee/eevee_render.c index e971edbaa44..47e2b95f367 100644 --- a/source/blender/draw/engines/eevee/eevee_render.c +++ b/source/blender/draw/engines/eevee/eevee_render.c @@ -230,7 +230,7 @@ void EEVEE_render_cache(void *vedata, } } else if (ob->type == OB_CURVES) { - EEVEE_object_hair_cache_populate(vedata, sldata, ob, &cast_shadow); + EEVEE_object_curves_cache_populate(vedata, sldata, ob, &cast_shadow); if (do_cryptomatte) { EEVEE_cryptomatte_object_curves_cache_populate(data, sldata, ob); } diff --git a/source/blender/draw/engines/eevee/eevee_renderpasses.c b/source/blender/draw/engines/eevee/eevee_renderpasses.c index 3814cf44fd6..237c830d547 100644 --- a/source/blender/draw/engines/eevee/eevee_renderpasses.c +++ b/source/blender/draw/engines/eevee/eevee_renderpasses.c @@ -60,9 +60,9 @@ bool EEVEE_renderpasses_only_first_sample_pass_active(EEVEE_Data *vedata) return (g_data->render_passes & ~EEVEE_RENDERPASSES_POST_PROCESS_ON_FIRST_SAMPLE) == 0; } -int EEVEE_renderpasses_aov_hash(const ViewLayerAOV *aov) +uint EEVEE_renderpasses_aov_hash(const ViewLayerAOV *aov) { - int hash = BLI_hash_string(aov->name) << 1; + uint hash = BLI_hash_string(aov->name) << 1u; SET_FLAG_FROM_TEST(hash, aov->type == AOV_TYPE_COLOR, EEVEE_AOV_HASH_COLOR_TYPE_MASK); return hash; } diff --git a/source/blender/draw/engines/eevee/eevee_shaders.c b/source/blender/draw/engines/eevee/eevee_shaders.c index ae5e6e94f74..4e4a2a9eb8e 100644 --- a/source/blender/draw/engines/eevee/eevee_shaders.c +++ b/source/blender/draw/engines/eevee/eevee_shaders.c @@ -170,6 +170,7 @@ extern char datatoc_common_math_lib_glsl[]; extern char datatoc_common_math_geom_lib_glsl[]; extern char datatoc_common_view_lib_glsl[]; extern char datatoc_gpu_shader_common_obinfos_lib_glsl[]; +extern char datatoc_gpu_shader_codegen_lib_glsl[]; extern char datatoc_ambient_occlusion_lib_glsl[]; extern char datatoc_background_vert_glsl[]; @@ -178,6 +179,7 @@ extern char datatoc_bsdf_lut_frag_glsl[]; extern char datatoc_bsdf_sampling_lib_glsl[]; extern char datatoc_btdf_lut_frag_glsl[]; extern char datatoc_closure_type_lib_glsl[]; +extern char datatoc_closure_eval_volume_lib_glsl[]; extern char datatoc_common_uniforms_lib_glsl[]; extern char datatoc_common_utiltex_lib_glsl[]; extern char datatoc_cryptomatte_frag_glsl[]; @@ -230,6 +232,7 @@ extern char datatoc_lightprobe_planar_downsample_vert_glsl[]; extern char datatoc_lightprobe_vert_glsl[]; extern char datatoc_lights_lib_glsl[]; extern char datatoc_closure_eval_lib_glsl[]; +extern char datatoc_closure_eval_surface_lib_glsl[]; extern char datatoc_closure_eval_diffuse_lib_glsl[]; extern char datatoc_closure_eval_glossy_lib_glsl[]; extern char datatoc_closure_eval_refraction_lib_glsl[]; @@ -239,7 +242,6 @@ extern char datatoc_object_motion_frag_glsl[]; extern char datatoc_object_motion_vert_glsl[]; extern char datatoc_octahedron_lib_glsl[]; extern char datatoc_prepass_frag_glsl[]; -extern char datatoc_prepass_vert_glsl[]; extern char datatoc_random_lib_glsl[]; extern char datatoc_raytrace_lib_glsl[]; extern char datatoc_renderpass_lib_glsl[]; @@ -261,6 +263,7 @@ extern char datatoc_volumetric_lib_glsl[]; extern char datatoc_volumetric_resolve_frag_glsl[]; extern char datatoc_volumetric_scatter_frag_glsl[]; extern char datatoc_volumetric_vert_glsl[]; +extern char datatoc_world_vert_glsl[]; /* *********** FUNCTIONS *********** */ @@ -275,6 +278,7 @@ static void eevee_shader_library_ensure(void) DRW_SHADER_LIB_ADD(e_data.lib, common_view_lib); DRW_SHADER_LIB_ADD(e_data.lib, common_uniforms_lib); DRW_SHADER_LIB_ADD(e_data.lib, gpu_shader_common_obinfos_lib); + DRW_SHADER_LIB_ADD(e_data.lib, gpu_shader_codegen_lib); DRW_SHADER_LIB_ADD(e_data.lib, random_lib); DRW_SHADER_LIB_ADD(e_data.lib, renderpass_lib); DRW_SHADER_LIB_ADD(e_data.lib, bsdf_common_lib); @@ -299,6 +303,8 @@ static void eevee_shader_library_ensure(void) DRW_SHADER_LIB_ADD(e_data.lib, closure_eval_glossy_lib); DRW_SHADER_LIB_ADD(e_data.lib, closure_eval_translucent_lib); DRW_SHADER_LIB_ADD(e_data.lib, closure_eval_refraction_lib); + DRW_SHADER_LIB_ADD(e_data.lib, closure_eval_surface_lib); + DRW_SHADER_LIB_ADD(e_data.lib, closure_eval_volume_lib); e_data.surface_lit_frag = DRW_shader_library_create_shader_string(e_data.lib, datatoc_surface_frag_glsl); @@ -313,6 +319,7 @@ static void eevee_shader_library_ensure(void) void EEVEE_shaders_material_shaders_init(void) { + eevee_shader_extra_init(); eevee_shader_library_ensure(); } @@ -828,6 +835,7 @@ struct GPUShader *EEVEE_shaders_volumes_clear_sh_get() datatoc_volumetric_frag_glsl, e_data.lib, SHADER_DEFINES + "#define STANDALONE\n" "#define VOLUMETRICS\n" "#define CLEAR\n"); } @@ -842,6 +850,7 @@ struct GPUShader *EEVEE_shaders_volumes_scatter_sh_get() datatoc_volumetric_scatter_frag_glsl, e_data.lib, SHADER_DEFINES + "#define STANDALONE\n" "#define VOLUMETRICS\n" "#define VOLUME_SHADOW\n"); } @@ -857,6 +866,7 @@ struct GPUShader *EEVEE_shaders_volumes_scatter_with_lights_sh_get() datatoc_volumetric_scatter_frag_glsl, e_data.lib, SHADER_DEFINES + "#define STANDALONE\n" "#define VOLUMETRICS\n" "#define VOLUME_LIGHTING\n" "#define VOLUME_SHADOW\n"); @@ -872,7 +882,9 @@ struct GPUShader *EEVEE_shaders_volumes_integration_sh_get() datatoc_volumetric_geom_glsl, datatoc_volumetric_integration_frag_glsl, e_data.lib, - USE_VOLUME_OPTI ? "#define USE_VOLUME_OPTI\n" SHADER_DEFINES : SHADER_DEFINES); + USE_VOLUME_OPTI ? "#define USE_VOLUME_OPTI\n" + "#define STANDALONE\n" SHADER_DEFINES : + "#define STANDALONE\n" SHADER_DEFINES); } return e_data.volumetric_integration_sh; } @@ -1232,7 +1244,7 @@ Material *EEVEE_material_default_glossy_get(void) Material *EEVEE_material_default_error_get(void) { if (!e_data.error_mat) { - Material *ma = BKE_id_new_nomain(ID_MA, "EEVEEE default metal"); + Material *ma = BKE_id_new_nomain(ID_MA, "EEVEEE default error"); bNodeTree *ntree = ntreeAddTree(NULL, "Shader Nodetree", ntreeType_Shader->idname); ma->nodetree = ntree; @@ -1375,7 +1387,7 @@ static char *eevee_get_vert(int options) str = DRW_shader_library_create_shader_string(e_data.lib, datatoc_volumetric_vert_glsl); } else if ((options & (VAR_WORLD_PROBE | VAR_WORLD_BACKGROUND)) != 0) { - str = DRW_shader_library_create_shader_string(e_data.lib, datatoc_background_vert_glsl); + str = DRW_shader_library_create_shader_string(e_data.lib, datatoc_world_vert_glsl); } else { str = DRW_shader_library_create_shader_string(e_data.lib, datatoc_surface_vert_glsl); @@ -1412,68 +1424,43 @@ static char *eevee_get_frag(int options) return str; } -static void eevee_material_post_eval(GPUMaterial *mat, - int options, - const char **UNUSED(vert_code), - const char **geom_code, - const char **UNUSED(frag_lib), - const char **UNUSED(defines)) +static void eevee_material_post_eval(void *UNUSED(thunk), + GPUMaterial *mat, + GPUCodegenOutput *codegen) { - const bool is_hair = (options & VAR_MAT_HAIR) != 0; - const bool is_mesh = (options & VAR_MAT_MESH) != 0; + uint64_t options = GPU_material_uuid_get(mat); - /* Force geometry usage if GPU_BARYCENTRIC_DIST or GPU_BARYCENTRIC_TEXCO are used. - * NOTE: GPU_BARYCENTRIC_TEXCO only requires it if the shader is not drawing hairs. */ - if (!is_hair && is_mesh && GPU_material_flag_get(mat, GPU_MATFLAG_BARYCENTRIC) && - *geom_code == NULL) { - *geom_code = e_data.surface_geom_barycentric; - } + char *vert = eevee_get_vert(options); + char *geom = eevee_get_geom(options); + char *frag = eevee_get_frag(options); + char *defines = eevee_get_defines(options); + + eevee_shader_material_create_info_amend(mat, codegen, frag, vert, geom, defines); + + MEM_SAFE_FREE(defines); + MEM_SAFE_FREE(vert); + MEM_SAFE_FREE(geom); + MEM_SAFE_FREE(frag); } static struct GPUMaterial *eevee_material_get_ex( - struct Scene *scene, Material *ma, World *wo, int options, bool deferred) + struct Scene *UNUSED(scene), Material *ma, World *wo, int options, bool deferred) { BLI_assert(ma || wo); const bool is_volume = (options & VAR_MAT_VOLUME) != 0; const bool is_default = (options & VAR_DEFAULT) != 0; - const void *engine = &DRW_engine_viewport_eevee_type; GPUMaterial *mat = NULL; + GPUCodegenCallbackFn cbfn = &eevee_material_post_eval; if (ma) { - mat = DRW_shader_find_from_material(ma, engine, options, deferred); - } - else { - mat = DRW_shader_find_from_world(wo, engine, options, deferred); - } - - if (mat) { - return mat; - } - - char *defines = eevee_get_defines(options); - char *vert = eevee_get_vert(options); - char *geom = eevee_get_geom(options); - char *frag = eevee_get_frag(options); - - if (ma) { - GPUMaterialEvalCallbackFn cbfn = &eevee_material_post_eval; - bNodeTree *ntree = !is_default ? ma->nodetree : EEVEE_shader_default_surface_nodetree(ma); - mat = DRW_shader_create_from_material( - scene, ma, ntree, engine, options, is_volume, vert, geom, frag, defines, deferred, cbfn); + mat = DRW_shader_from_material(ma, ntree, options, is_volume, deferred, cbfn, NULL); } else { bNodeTree *ntree = !is_default ? wo->nodetree : EEVEE_shader_default_world_nodetree(wo); - mat = DRW_shader_create_from_world( - scene, wo, ntree, engine, options, is_volume, vert, geom, frag, defines, deferred, NULL); + mat = DRW_shader_from_world(wo, ntree, options, is_volume, deferred, cbfn, NULL); } - - MEM_SAFE_FREE(defines); - MEM_SAFE_FREE(vert); - MEM_SAFE_FREE(geom); - MEM_SAFE_FREE(frag); - return mat; } @@ -1520,6 +1507,7 @@ struct GPUMaterial *EEVEE_material_get( void EEVEE_shaders_free(void) { + eevee_shader_extra_exit(); MEM_SAFE_FREE(e_data.surface_prepass_frag); MEM_SAFE_FREE(e_data.surface_lit_frag); MEM_SAFE_FREE(e_data.surface_geom_barycentric); diff --git a/source/blender/draw/engines/eevee/eevee_shaders_extra.cc b/source/blender/draw/engines/eevee/eevee_shaders_extra.cc new file mode 100644 index 00000000000..bb1a0b0abe4 --- /dev/null +++ b/source/blender/draw/engines/eevee/eevee_shaders_extra.cc @@ -0,0 +1,173 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2022 Blender Foundation. */ + +/** \file + * \ingroup EEVEE + * + * This file is only there to handle ShaderCreateInfos. + */ + +#include "GPU_shader.h" + +#include "BLI_string_ref.hh" + +#include "gpu_shader_create_info.hh" + +#include "eevee_private.h" + +using blender::gpu::shader::StageInterfaceInfo; + +static StageInterfaceInfo *stage_interface = nullptr; + +void eevee_shader_extra_init() +{ + if (stage_interface != nullptr) { + return; + } + + using namespace blender::gpu::shader; + stage_interface = new StageInterfaceInfo("ShaderStageInterface", ""); + stage_interface->smooth(Type::VEC3, "worldPosition"); + stage_interface->smooth(Type::VEC3, "viewPosition"); + stage_interface->smooth(Type::VEC3, "worldNormal"); + stage_interface->smooth(Type::VEC3, "viewNormal"); + stage_interface->flat(Type::INT, "resourceIDFrag"); +} + +void eevee_shader_extra_exit() +{ + delete stage_interface; +} + +void eevee_shader_material_create_info_amend(GPUMaterial *gpumat, + GPUCodegenOutput *codegen_, + char *frag, + char *vert, + char *geom, + char *defines) +{ + using namespace blender::gpu::shader; + + uint64_t options = GPU_material_uuid_get(gpumat); + const bool is_background = (options & (VAR_WORLD_PROBE | VAR_WORLD_BACKGROUND)) != 0; + const bool is_volume = (options & (VAR_MAT_VOLUME)) != 0; + const bool is_hair = (options & (VAR_MAT_HAIR)) != 0; + const bool is_mesh = (options & (VAR_MAT_MESH)) != 0; + const bool is_point_cloud = (options & (VAR_MAT_POINTCLOUD)) != 0; + + GPUCodegenOutput &codegen = *codegen_; + ShaderCreateInfo &info = *reinterpret_cast<ShaderCreateInfo *>(codegen.create_info); + + info.legacy_resource_location(true); + info.auto_resource_location(true); + + if (GPU_material_flag_get(gpumat, GPU_MATFLAG_SUBSURFACE)) { + info.define("USE_SSS"); + } + if (GPU_material_flag_get(gpumat, GPU_MATFLAG_SHADER_TO_RGBA)) { + info.define("USE_SHADER_TO_RGBA"); + } + if (GPU_material_flag_get(gpumat, GPU_MATFLAG_BARYCENTRIC) && !is_volume && !is_hair && + !is_point_cloud && !is_background) { + info.define("USE_BARYCENTRICS"); + info.builtins(BuiltinBits::BARYCENTRIC_COORD); + } + if (GPU_material_flag_get(gpumat, GPU_MATFLAG_BARYCENTRIC) && is_hair) { + info.define("USE_BARYCENTRICS"); + } + + std::stringstream attr_load; + + const bool do_fragment_attrib_load = is_background || is_volume; + + if (is_hair && !info.vertex_out_interfaces_.is_empty()) { + /** Hair attributes comme from sampler buffer. Transfer attributes to sampler. */ + for (auto &input : info.vertex_inputs_) { + info.sampler(0, ImageType::FLOAT_BUFFER, input.name, Frequency::BATCH); + } + info.vertex_inputs_.clear(); + } + else if (do_fragment_attrib_load && !info.vertex_out_interfaces_.is_empty()) { + /* Codegen outputs only one interface. */ + const StageInterfaceInfo &iface = *info.vertex_out_interfaces_.first(); + /* Globals the attrib_load() can write to when it is in the fragment shader. */ + attr_load << "struct " << iface.name << " {\n"; + for (const auto &inout : iface.inouts) { + attr_load << " " << inout.type << " " << inout.name << ";\n"; + } + attr_load << "};\n"; + attr_load << iface.name << " " << iface.instance_name << ";\n"; + /* Global vars just to make code valid. Only Orco is supported. */ + for (const ShaderCreateInfo::VertIn &in : info.vertex_inputs_) { + attr_load << in.type << " " << in.name << ";\n"; + } + info.vertex_out_interfaces_.clear(); + } + + if (!is_volume) { + info.define("EEVEE_GENERATED_INTERFACE"); + info.vertex_out(*stage_interface); + } + + attr_load << "void attrib_load()\n"; + attr_load << "{\n"; + attr_load << ((codegen.attr_load) ? codegen.attr_load : ""); + attr_load << "}\n\n"; + + std::stringstream vert_gen, frag_gen, geom_gen; + + if (do_fragment_attrib_load) { + frag_gen << attr_load.str(); + } + else { + vert_gen << attr_load.str(); + } + + { + vert_gen << vert; + info.vertex_source_generated = vert_gen.str(); + /* Everything is in generated source. */ + info.vertex_source(is_volume ? "eevee_empty_volume.glsl" : "eevee_empty.glsl"); + } + + { + frag_gen << frag; + if (codegen.material_functions) { + frag_gen << codegen.material_functions; + } + frag_gen << "Closure nodetree_exec()\n"; + frag_gen << "{\n"; + if (GPU_material_is_volume_shader(gpumat)) { + frag_gen << ((codegen.volume) ? codegen.volume : "return CLOSURE_DEFAULT;\n"); + } + else { + frag_gen << ((codegen.surface) ? codegen.surface : "return CLOSURE_DEFAULT;\n"); + } + frag_gen << "}\n\n"; + + if (codegen.displacement && (is_hair || is_mesh)) { + info.define("EEVEE_DISPLACEMENT_BUMP"); + + frag_gen << "vec3 displacement_exec()\n"; + frag_gen << "{\n"; + frag_gen << codegen.displacement; + frag_gen << "}\n\n"; + } + + info.fragment_source_generated = frag_gen.str(); + /* Everything is in generated source. */ + info.fragment_source(is_volume ? "eevee_empty_volume.glsl" : "eevee_empty.glsl"); + } + + if (geom) { + geom_gen << geom; + info.geometry_source_generated = geom_gen.str(); + info.geometry_layout(PrimitiveIn::TRIANGLES, PrimitiveOut::TRIANGLE_STRIP, 3); + /* Everything is in generated source. */ + info.geometry_source("eevee_empty.glsl"); + } + + if (defines) { + info.typedef_source_generated += blender::StringRefNull(defines); + } +} diff --git a/source/blender/draw/engines/eevee/eevee_volumes.c b/source/blender/draw/engines/eevee/eevee_volumes.c index 7d210f15d8b..6a0c9fb36df 100644 --- a/source/blender/draw/engines/eevee/eevee_volumes.c +++ b/source/blender/draw/engines/eevee/eevee_volumes.c @@ -604,6 +604,7 @@ void EEVEE_volumes_cache_finish(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo); DRW_shgroup_uniform_block(grp, "probe_block", sldata->probe_ubo); DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined); + DRW_shgroup_uniform_block(grp, "shadow_block", sldata->shadow_ubo); DRW_shgroup_call_procedural_triangles(grp, NULL, 1); } 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 1c7ef775ac2..d8adf302e37 100644 --- a/source/blender/draw/engines/eevee/shaders/ambient_occlusion_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/ambient_occlusion_lib.glsl @@ -26,6 +26,10 @@ # endif #endif +#ifndef GPU_FRAGMENT_SHADER +# define gl_FragCoord vec4(0.0) +#endif + uniform sampler2D horizonBuffer; /* aoSettings flags */ @@ -40,6 +44,15 @@ struct OcclusionData { vec4 horizons; /* Custom large scale occlusion. */ float custom_occlusion; + +#ifdef GPU_METAL + /* Constructors required for OcclusionData(..) syntax. */ + inline OcclusionData() = default; + inline OcclusionData(vec4 in_horizons, float in_custom_occlusion) + : horizons(in_horizons), custom_occlusion(in_custom_occlusion) + { + } +#endif }; vec4 pack_occlusion_data(OcclusionData data) @@ -415,3 +428,34 @@ OcclusionData occlusion_load(vec3 vP, float custom_occlusion) return data; } + +#ifndef GPU_FRAGMENT_SHADER +# undef gl_FragCoord +#endif + +float ambient_occlusion_eval(vec3 normal, + float max_distance, + const float inverted, + const float sample_count) +{ + /* Avoid multiline define causing compiler issues. */ + /* clang-format off */ +#if defined(GPU_FRAGMENT_SHADER) && (defined(MESH_SHADER) || defined(HAIR_SHADER)) && !defined(DEPTH_SHADER) && !defined(VOLUMETRICS) + /* clang-format on */ + vec3 bent_normal; + vec4 rand = texelfetch_noise_tex(gl_FragCoord.xy); + OcclusionData data = occlusion_search( + viewPosition, maxzBuffer, max_distance, inverted, sample_count); + + vec3 V = cameraVec(worldPosition); + vec3 N = normalize(normal); + vec3 Ng = safe_normalize(cross(dFdx(worldPosition), dFdy(worldPosition))); + + float unused_error, visibility; + vec3 unused; + occlusion_eval(data, V, N, Ng, inverted, visibility, unused_error, unused); + return visibility; +#else + return 1.0; +#endif +} diff --git a/source/blender/draw/engines/eevee/shaders/closure_eval_diffuse_lib.glsl b/source/blender/draw/engines/eevee/shaders/closure_eval_diffuse_lib.glsl index 5bf20fe6979..574b24b3650 100644 --- a/source/blender/draw/engines/eevee/shaders/closure_eval_diffuse_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/closure_eval_diffuse_lib.glsl @@ -1,13 +1,23 @@ #pragma BLENDER_REQUIRE(lights_lib.glsl) #pragma BLENDER_REQUIRE(lightprobe_lib.glsl) #pragma BLENDER_REQUIRE(ambient_occlusion_lib.glsl) +#pragma BLENDER_REQUIRE(closure_eval_lib.glsl) +#pragma BLENDER_REQUIRE(renderpass_lib.glsl) struct ClosureInputDiffuse { vec3 N; /** Shading normal. */ vec3 albedo; /** Used for multibounce GTAO approximation. Not applied to final radiance. */ }; -#define CLOSURE_INPUT_Diffuse_DEFAULT ClosureInputDiffuse(vec3(0.0), vec3(0.0)) +#ifdef GPU_METAL +/* C++ struct initialization. */ +# define CLOSURE_INPUT_Diffuse_DEFAULT \ + { \ + vec3(0.0), vec3(0.0) \ + } +#else +# define CLOSURE_INPUT_Diffuse_DEFAULT ClosureInputDiffuse(vec3(0.0), vec3(0.0)) +#endif struct ClosureEvalDiffuse { vec3 probe_sampling_dir; /** Direction to sample probes from. */ @@ -80,6 +90,7 @@ void closure_Diffuse_eval_end(ClosureInputDiffuse cl_in, ClosureEvalCommon cl_common, inout ClosureOutputDiffuse cl_out) { + cl_out.radiance = render_pass_diffuse_mask(cl_out.radiance); #if defined(DEPTH_SHADER) || defined(WORLD_BACKGROUND) /* This makes shader resources become unused and avoid issues with samplers. (see T59747) */ cl_out.radiance = vec3(0.0); diff --git a/source/blender/draw/engines/eevee/shaders/closure_eval_glossy_lib.glsl b/source/blender/draw/engines/eevee/shaders/closure_eval_glossy_lib.glsl index ddc6a0b9661..0deaf4054d2 100644 --- a/source/blender/draw/engines/eevee/shaders/closure_eval_glossy_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/closure_eval_glossy_lib.glsl @@ -4,13 +4,22 @@ #pragma BLENDER_REQUIRE(lightprobe_lib.glsl) #pragma BLENDER_REQUIRE(ambient_occlusion_lib.glsl) #pragma BLENDER_REQUIRE(bsdf_common_lib.glsl) +#pragma BLENDER_REQUIRE(closure_eval_lib.glsl) +#pragma BLENDER_REQUIRE(renderpass_lib.glsl) struct ClosureInputGlossy { vec3 N; /** Shading normal. */ float roughness; /** Input roughness, not squared. */ }; -#define CLOSURE_INPUT_Glossy_DEFAULT ClosureInputGlossy(vec3(0.0), 0.0) +#ifdef GPU_METAL +# define CLOSURE_INPUT_Glossy_DEFAULT \ + { \ + vec3(0.0), 0.0 \ + } +#else +# define CLOSURE_INPUT_Glossy_DEFAULT ClosureInputGlossy(vec3(0.0), 0.0) +#endif struct ClosureEvalGlossy { vec4 ltc_mat; /** LTC matrix values. */ @@ -136,6 +145,7 @@ void closure_Glossy_eval_end(ClosureInputGlossy cl_in, ClosureEvalCommon cl_common, inout ClosureOutputGlossy cl_out) { + cl_out.radiance = render_pass_glossy_mask(cl_out.radiance); #if defined(DEPTH_SHADER) || defined(WORLD_BACKGROUND) /* This makes shader resources become unused and avoid issues with samplers. (see T59747) */ cl_out.radiance = vec3(0.0); diff --git a/source/blender/draw/engines/eevee/shaders/closure_eval_lib.glsl b/source/blender/draw/engines/eevee/shaders/closure_eval_lib.glsl index 311887cf2f5..3f07f80571a 100644 --- a/source/blender/draw/engines/eevee/shaders/closure_eval_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/closure_eval_lib.glsl @@ -1,8 +1,14 @@ #pragma BLENDER_REQUIRE(common_utiltex_lib.glsl) +// #pragma (gpu_shader_codegen_lib.glsl) #pragma BLENDER_REQUIRE(lights_lib.glsl) #pragma BLENDER_REQUIRE(lightprobe_lib.glsl) +#ifndef GPU_FRAGMENT_SHADER +# define gl_FragCoord vec4(0.0) +# define gl_FrontFacing true +#endif + /** * Extensive use of Macros to be able to change the maximum amount of evaluated closure easily. * NOTE: GLSL does not support variadic macros. @@ -157,7 +163,15 @@ #define ClosureInputDummy ClosureOutput #define ClosureOutputDummy ClosureOutput #define ClosureEvalDummy ClosureOutput -#define CLOSURE_EVAL_DUMMY ClosureOutput(vec3(0)) +#ifdef GPU_METAL +/* C++ struct initialization. */ +# define CLOSURE_EVAL_DUMMY \ + { \ + vec3(0) \ + } +#else +# define CLOSURE_EVAL_DUMMY ClosureOutput(vec3(0)) +#endif #define CLOSURE_INPUT_Dummy_DEFAULT CLOSURE_EVAL_DUMMY #define closure_Dummy_eval_init(cl_in, cl_common, cl_out) CLOSURE_EVAL_DUMMY #define closure_Dummy_planar_eval(cl_in, cl_eval, cl_common, data, cl_out) @@ -180,8 +194,15 @@ struct ClosureInputCommon { /** Custom occlusion value set by the user. */ float occlusion; }; - -#define CLOSURE_INPUT_COMMON_DEFAULT ClosureInputCommon(1.0) +#ifdef GPU_METAL +/* C++ struct initialization. */ +# define CLOSURE_INPUT_COMMON_DEFAULT \ + { \ + 1.0 \ + } +#else +# define CLOSURE_INPUT_COMMON_DEFAULT ClosureInputCommon(1.0) +#endif struct ClosureEvalCommon { /** Result of SSAO. */ @@ -225,7 +246,11 @@ ClosureEvalCommon closure_Common_eval_init(ClosureInputCommon cl_in) cl_eval.N = safe_normalize(gl_FrontFacing ? worldNormal : -worldNormal); cl_eval.vN = safe_normalize(gl_FrontFacing ? viewNormal : -viewNormal); cl_eval.vP = viewPosition; +#ifdef GPU_FRAGMENT_SHADER cl_eval.Ng = safe_normalize(cross(dFdx(cl_eval.P), dFdy(cl_eval.P))); +#else + cl_eval.Ng = cl_eval.N; +#endif cl_eval.vNg = transform_direction(ViewMatrix, cl_eval.Ng); cl_eval.occlusion_data = occlusion_load(cl_eval.vP, cl_in.occlusion); @@ -322,3 +347,8 @@ ClosureGridData closure_grid_eval_init(int id, inout ClosureEvalCommon cl_common } /** \} */ + +#ifndef GPU_FRAGMENT_SHADER +# undef gl_FragCoord +# undef gl_FrontFacing +#endif diff --git a/source/blender/draw/engines/eevee/shaders/closure_eval_refraction_lib.glsl b/source/blender/draw/engines/eevee/shaders/closure_eval_refraction_lib.glsl index 9011eea07c4..5c6769b185a 100644 --- a/source/blender/draw/engines/eevee/shaders/closure_eval_refraction_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/closure_eval_refraction_lib.glsl @@ -4,14 +4,23 @@ #pragma BLENDER_REQUIRE(lightprobe_lib.glsl) #pragma BLENDER_REQUIRE(ambient_occlusion_lib.glsl) #pragma BLENDER_REQUIRE(ssr_lib.glsl) +#pragma BLENDER_REQUIRE(closure_eval_lib.glsl) +#pragma BLENDER_REQUIRE(renderpass_lib.glsl) struct ClosureInputRefraction { vec3 N; /** Shading normal. */ float roughness; /** Input roughness, not squared. */ float ior; /** Index of refraction ratio. */ }; - -#define CLOSURE_INPUT_Refraction_DEFAULT ClosureInputRefraction(vec3(0.0), 0.0, 0.0) +#ifdef GPU_METAL +/* C++ struct initialization. */ +# define CLOSURE_INPUT_Refraction_DEFAULT \ + { \ + vec3(0.0), 0.0, 0.0 \ + } +#else +# define CLOSURE_INPUT_Refraction_DEFAULT ClosureInputRefraction(vec3(0.0), 0.0, 0.0) +#endif struct ClosureEvalRefraction { vec3 P; /** LTC matrix values. */ @@ -116,6 +125,7 @@ void closure_Refraction_eval_end(ClosureInputRefraction cl_in, ClosureEvalCommon cl_common, inout ClosureOutputRefraction cl_out) { + cl_out.radiance = render_pass_glossy_mask(cl_out.radiance); #if defined(DEPTH_SHADER) || defined(WORLD_BACKGROUND) /* This makes shader resources become unused and avoid issues with samplers. (see T59747) */ cl_out.radiance = vec3(0.0); diff --git a/source/blender/draw/engines/eevee/shaders/closure_eval_surface_lib.glsl b/source/blender/draw/engines/eevee/shaders/closure_eval_surface_lib.glsl new file mode 100644 index 00000000000..fa94b5ed272 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/closure_eval_surface_lib.glsl @@ -0,0 +1,325 @@ + +#pragma BLENDER_REQUIRE(closure_eval_diffuse_lib.glsl) +#pragma BLENDER_REQUIRE(closure_eval_glossy_lib.glsl) +#pragma BLENDER_REQUIRE(closure_eval_refraction_lib.glsl) +#pragma BLENDER_REQUIRE(closure_eval_translucent_lib.glsl) +#pragma BLENDER_REQUIRE(renderpass_lib.glsl) + +#ifdef USE_SHADER_TO_RGBA +bool do_sss = false; +bool do_ssr = false; +#else +bool do_sss = true; +bool do_ssr = true; +#endif + +vec3 out_sss_radiance; +vec3 out_sss_color; +float out_sss_radius; + +float out_ssr_roughness; +vec3 out_ssr_color; +vec3 out_ssr_N; + +bool aov_is_valid = false; +vec3 out_aov; + +bool output_sss(ClosureDiffuse diffuse, ClosureOutputDiffuse diffuse_out) +{ + if (diffuse.sss_id == 0u || !do_sss || !sssToggle || outputSssId == 0) { + return false; + } + if (renderPassSSSColor) { + return false; + } + out_sss_radiance = diffuse_out.radiance; + out_sss_color = diffuse.color * diffuse.weight; + out_sss_radius = avg(diffuse.sss_radius); + do_sss = false; + return true; +} + +bool output_ssr(ClosureReflection reflection) +{ + if (!do_ssr || !ssrToggle || outputSsrId == 0) { + return false; + } + out_ssr_roughness = reflection.roughness; + out_ssr_color = reflection.color * reflection.weight; + out_ssr_N = reflection.N; + do_ssr = false; + return true; +} + +void output_aov(vec4 color, float value, uint hash) +{ + /* Keep in sync with `render_pass_aov_hash` and `EEVEE_renderpasses_aov_hash`. */ + hash <<= 1u; + + if (renderPassAOV && !aov_is_valid && hash == render_pass_aov_hash()) { + aov_is_valid = true; + if (render_pass_aov_is_color()) { + out_aov = color.rgb; + } + else { + out_aov = vec3(value); + } + } +} + +/* Single BSDFs. */ +CLOSURE_EVAL_FUNCTION_DECLARE_1(DiffuseBSDF, Diffuse) +Closure closure_eval(ClosureDiffuse diffuse) +{ + /* Glue with the old system. */ + CLOSURE_VARS_DECLARE_1(Diffuse); + + in_Diffuse_0.N = diffuse.N; + in_Diffuse_0.albedo = diffuse.color; + + CLOSURE_EVAL_FUNCTION_1(DiffuseBSDF, Diffuse); + + Closure closure = CLOSURE_DEFAULT; + if (!output_sss(diffuse, out_Diffuse_0)) { + closure.radiance += out_Diffuse_0.radiance * diffuse.color * diffuse.weight; + } + return closure; +} + +CLOSURE_EVAL_FUNCTION_DECLARE_1(TranslucentBSDF, Translucent) +Closure closure_eval(ClosureTranslucent translucent) +{ + /* Glue with the old system. */ + CLOSURE_VARS_DECLARE_1(Translucent); + + in_Translucent_0.N = translucent.N; + + CLOSURE_EVAL_FUNCTION_1(TranslucentBSDF, Translucent); + + Closure closure = CLOSURE_DEFAULT; + closure.radiance += out_Translucent_0.radiance * translucent.color * translucent.weight; + return closure; +} + +CLOSURE_EVAL_FUNCTION_DECLARE_1(GlossyBSDF, Glossy) +Closure closure_eval(ClosureReflection reflection) +{ + /* Glue with the old system. */ + CLOSURE_VARS_DECLARE_1(Glossy); + + in_Glossy_0.N = reflection.N; + in_Glossy_0.roughness = reflection.roughness; + + CLOSURE_EVAL_FUNCTION_1(GlossyBSDF, Glossy); + + Closure closure = CLOSURE_DEFAULT; + if (!output_ssr(reflection)) { + closure.radiance += out_Glossy_0.radiance * reflection.color * reflection.weight; + } + return closure; +} + +CLOSURE_EVAL_FUNCTION_DECLARE_1(RefractionBSDF, Refraction) +Closure closure_eval(ClosureRefraction refraction) +{ + /* Glue with the old system. */ + CLOSURE_VARS_DECLARE_1(Refraction); + + in_Refraction_0.N = refraction.N; + in_Refraction_0.roughness = refraction.roughness; + in_Refraction_0.ior = refraction.ior; + + CLOSURE_EVAL_FUNCTION_1(RefractionBSDF, Refraction); + + Closure closure = CLOSURE_DEFAULT; + closure.radiance += out_Refraction_0.radiance * refraction.color * refraction.weight; + return closure; +} + +Closure closure_eval(ClosureEmission emission) +{ + Closure closure = CLOSURE_DEFAULT; + closure.radiance += render_pass_emission_mask(emission.emission) * emission.weight; + return closure; +} + +Closure closure_eval(ClosureTransparency transparency) +{ + Closure closure = CLOSURE_DEFAULT; + closure.transmittance += transparency.transmittance * transparency.weight; + closure.holdout += transparency.holdout * transparency.weight; + return closure; +} + +/* Glass BSDF. */ +CLOSURE_EVAL_FUNCTION_DECLARE_2(GlassBSDF, Glossy, Refraction) +Closure closure_eval(ClosureReflection reflection, ClosureRefraction refraction) +{ + /* Glue with the old system. */ + CLOSURE_VARS_DECLARE_2(Glossy, Refraction); + + in_Glossy_0.N = reflection.N; + in_Glossy_0.roughness = reflection.roughness; + in_Refraction_1.N = refraction.N; + in_Refraction_1.roughness = refraction.roughness; + in_Refraction_1.ior = refraction.ior; + + CLOSURE_EVAL_FUNCTION_2(GlassBSDF, Glossy, Refraction); + + Closure closure = CLOSURE_DEFAULT; + closure.radiance += out_Refraction_1.radiance * refraction.color * refraction.weight; + if (!output_ssr(reflection)) { + closure.radiance += out_Glossy_0.radiance * reflection.color * reflection.weight; + } + return closure; +} + +/* Dielectric BSDF */ +CLOSURE_EVAL_FUNCTION_DECLARE_2(DielectricBSDF, Diffuse, Glossy) +Closure closure_eval(ClosureDiffuse diffuse, ClosureReflection reflection) +{ + /* Glue with the old system. */ + CLOSURE_VARS_DECLARE_2(Diffuse, Glossy); + + in_Diffuse_0.N = diffuse.N; + in_Diffuse_0.albedo = diffuse.color; + in_Glossy_1.N = reflection.N; + in_Glossy_1.roughness = reflection.roughness; + + CLOSURE_EVAL_FUNCTION_2(DielectricBSDF, Diffuse, Glossy); + + Closure closure = CLOSURE_DEFAULT; + if (!output_sss(diffuse, out_Diffuse_0)) { + closure.radiance += out_Diffuse_0.radiance * diffuse.color * diffuse.weight; + } + if (!output_ssr(reflection)) { + closure.radiance += out_Glossy_1.radiance * reflection.color * reflection.weight; + } + return closure; +} + +/* Specular BSDF */ +CLOSURE_EVAL_FUNCTION_DECLARE_3(SpecularBSDF, Diffuse, Glossy, Glossy) +Closure closure_eval(ClosureDiffuse diffuse, + ClosureReflection reflection, + ClosureReflection clearcoat) +{ + /* Glue with the old system. */ + CLOSURE_VARS_DECLARE_3(Diffuse, Glossy, Glossy); + + in_Diffuse_0.N = diffuse.N; + in_Diffuse_0.albedo = diffuse.color; + in_Glossy_1.N = reflection.N; + in_Glossy_1.roughness = reflection.roughness; + in_Glossy_2.N = clearcoat.N; + in_Glossy_2.roughness = clearcoat.roughness; + + CLOSURE_EVAL_FUNCTION_3(SpecularBSDF, Diffuse, Glossy, Glossy); + + Closure closure = CLOSURE_DEFAULT; + if (!output_sss(diffuse, out_Diffuse_0)) { + closure.radiance += out_Diffuse_0.radiance * diffuse.color * diffuse.weight; + } + closure.radiance += out_Glossy_2.radiance * clearcoat.color * clearcoat.weight; + if (!output_ssr(reflection)) { + closure.radiance += out_Glossy_1.radiance * reflection.color * reflection.weight; + } + return closure; +} + +/* Principled BSDF */ +CLOSURE_EVAL_FUNCTION_DECLARE_4(PrincipledBSDF, Diffuse, Glossy, Glossy, Refraction) +Closure closure_eval(ClosureDiffuse diffuse, + ClosureReflection reflection, + ClosureReflection clearcoat, + ClosureRefraction refraction) +{ + /* Glue with the old system. */ + CLOSURE_VARS_DECLARE_4(Diffuse, Glossy, Glossy, Refraction); + + in_Diffuse_0.N = diffuse.N; + in_Diffuse_0.albedo = diffuse.color; + in_Glossy_1.N = reflection.N; + in_Glossy_1.roughness = reflection.roughness; + in_Glossy_2.N = clearcoat.N; + in_Glossy_2.roughness = clearcoat.roughness; + in_Refraction_3.N = refraction.N; + in_Refraction_3.roughness = refraction.roughness; + in_Refraction_3.ior = refraction.ior; + + CLOSURE_EVAL_FUNCTION_4(PrincipledBSDF, Diffuse, Glossy, Glossy, Refraction); + + Closure closure = CLOSURE_DEFAULT; + closure.radiance += out_Glossy_2.radiance * clearcoat.color * clearcoat.weight; + closure.radiance += out_Refraction_3.radiance * refraction.color * refraction.weight; + if (!output_sss(diffuse, out_Diffuse_0)) { + closure.radiance += out_Diffuse_0.radiance * diffuse.color * diffuse.weight; + } + if (!output_ssr(reflection)) { + closure.radiance += out_Glossy_1.radiance * reflection.color * reflection.weight; + } + return closure; +} + +CLOSURE_EVAL_FUNCTION_DECLARE_2(PrincipledBSDFMetalClearCoat, Glossy, Glossy) +Closure closure_eval(ClosureReflection reflection, ClosureReflection clearcoat) +{ + /* Glue with the old system. */ + CLOSURE_VARS_DECLARE_2(Glossy, Glossy); + + in_Glossy_0.N = reflection.N; + in_Glossy_0.roughness = reflection.roughness; + in_Glossy_1.N = clearcoat.N; + in_Glossy_1.roughness = clearcoat.roughness; + + CLOSURE_EVAL_FUNCTION_2(PrincipledBSDFMetalClearCoat, Glossy, Glossy); + + Closure closure = CLOSURE_DEFAULT; + closure.radiance += out_Glossy_1.radiance * clearcoat.color * clearcoat.weight; + if (!output_ssr(reflection)) { + closure.radiance += out_Glossy_0.radiance * reflection.color * reflection.weight; + } + return closure; +} + +/* Not supported for surface shaders. */ +Closure closure_eval(ClosureVolumeScatter volume_scatter) +{ + return CLOSURE_DEFAULT; +} +Closure closure_eval(ClosureVolumeAbsorption volume_absorption) +{ + return CLOSURE_DEFAULT; +} +Closure closure_eval(ClosureVolumeScatter volume_scatter, + ClosureVolumeAbsorption volume_absorption, + ClosureEmission emission) +{ + return CLOSURE_DEFAULT; +} + +/* Not implemented yet. */ +Closure closure_eval(ClosureHair hair) +{ + return CLOSURE_DEFAULT; +} + +vec4 closure_to_rgba(Closure closure) +{ + return vec4(closure.radiance, 1.0 - saturate(avg(closure.transmittance))); +} + +Closure closure_add(Closure cl1, Closure cl2) +{ + Closure cl; + cl.radiance = cl1.radiance + cl2.radiance; + cl.transmittance = cl1.transmittance + cl2.transmittance; + cl.holdout = cl1.holdout + cl2.holdout; + return cl; +} + +Closure closure_mix(Closure cl1, Closure cl2, float fac) +{ + /* Weights have already been applied. */ + return closure_add(cl1, cl2); +} diff --git a/source/blender/draw/engines/eevee/shaders/closure_eval_translucent_lib.glsl b/source/blender/draw/engines/eevee/shaders/closure_eval_translucent_lib.glsl index 183219c9088..89a6f10e634 100644 --- a/source/blender/draw/engines/eevee/shaders/closure_eval_translucent_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/closure_eval_translucent_lib.glsl @@ -3,13 +3,21 @@ #pragma BLENDER_REQUIRE(lights_lib.glsl) #pragma BLENDER_REQUIRE(lightprobe_lib.glsl) #pragma BLENDER_REQUIRE(ambient_occlusion_lib.glsl) +#pragma BLENDER_REQUIRE(closure_eval_lib.glsl) +#pragma BLENDER_REQUIRE(renderpass_lib.glsl) struct ClosureInputTranslucent { vec3 N; /** Shading normal. */ }; - -#define CLOSURE_INPUT_Translucent_DEFAULT ClosureInputTranslucent(vec3(0.0)) - +#ifdef GPU_METAL +/* C++ struct initialization. */ +# define CLOSURE_INPUT_Translucent_DEFAULT \ + { \ + vec3(0.0) \ + } +#else +# define CLOSURE_INPUT_Translucent_DEFAULT ClosureInputTranslucent(vec3(0.0)) +#endif /* Stubs. */ #define ClosureEvalTranslucent ClosureEvalDummy #define ClosureOutputTranslucent ClosureOutput @@ -63,6 +71,7 @@ void closure_Translucent_eval_end(ClosureInputTranslucent cl_in, ClosureEvalCommon cl_common, inout ClosureOutputTranslucent cl_out) { + cl_out.radiance = render_pass_diffuse_mask(cl_out.radiance); #if defined(DEPTH_SHADER) || defined(WORLD_BACKGROUND) /* This makes shader resources become unused and avoid issues with samplers. (see T59747) */ cl_out.radiance = vec3(0.0); diff --git a/source/blender/draw/engines/eevee/shaders/closure_eval_volume_lib.glsl b/source/blender/draw/engines/eevee/shaders/closure_eval_volume_lib.glsl new file mode 100644 index 00000000000..e450b8ad3c8 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/closure_eval_volume_lib.glsl @@ -0,0 +1,113 @@ + +void output_aov(vec4 color, float value, uint hash) +{ + /* Unsupported. */ +} + +/* Surface BSDFs. */ +Closure closure_eval(ClosureDiffuse diffuse) +{ + return CLOSURE_DEFAULT; +} +Closure closure_eval(ClosureTranslucent translucent) +{ + return CLOSURE_DEFAULT; +} +Closure closure_eval(ClosureReflection reflection) +{ + return CLOSURE_DEFAULT; +} +Closure closure_eval(ClosureRefraction refraction) +{ + return CLOSURE_DEFAULT; +} +Closure closure_eval(ClosureEmission emission) +{ + Closure closure = CLOSURE_DEFAULT; + closure.emission = emission.emission; + return closure; +} +Closure closure_eval(ClosureTransparency transparency) +{ + return CLOSURE_DEFAULT; +} +Closure closure_eval(ClosureReflection reflection, ClosureRefraction refraction) +{ + return CLOSURE_DEFAULT; +} +Closure closure_eval(ClosureDiffuse diffuse, ClosureReflection reflection) +{ + return CLOSURE_DEFAULT; +} +Closure closure_eval(ClosureDiffuse diffuse, + ClosureReflection reflection, + ClosureReflection clearcoat) +{ + return CLOSURE_DEFAULT; +} +Closure closure_eval(ClosureDiffuse diffuse, + ClosureReflection reflection, + ClosureReflection clearcoat, + ClosureRefraction refraction) +{ + return CLOSURE_DEFAULT; +} +Closure closure_eval(ClosureReflection reflection, ClosureReflection clearcoat) +{ + return CLOSURE_DEFAULT; +} +Closure closure_eval(ClosureHair hair) +{ + return CLOSURE_DEFAULT; +} + +Closure closure_eval(ClosureVolumeScatter volume_scatter) +{ + Closure closure = CLOSURE_DEFAULT; + closure.scatter = volume_scatter.scattering; + closure.anisotropy = volume_scatter.anisotropy; + return closure; +} +Closure closure_eval(ClosureVolumeAbsorption volume_absorption) +{ + Closure closure = CLOSURE_DEFAULT; + closure.absorption = volume_absorption.absorption; + return closure; +} +Closure closure_eval(ClosureVolumeScatter volume_scatter, + ClosureVolumeAbsorption volume_absorption, + ClosureEmission emission) +{ + Closure closure = CLOSURE_DEFAULT; + closure.absorption = volume_absorption.absorption; + closure.scatter = volume_scatter.scattering; + closure.anisotropy = volume_scatter.anisotropy; + closure.emission = emission.emission; + return closure; +} + +vec4 closure_to_rgba(Closure closure) +{ + /* Not supported */ + return vec4(0.0); +} + +Closure closure_mix(Closure cl1, Closure cl2, float fac) +{ + Closure cl; + cl.absorption = mix(cl1.absorption, cl2.absorption, fac); + cl.scatter = mix(cl1.scatter, cl2.scatter, fac); + cl.emission = mix(cl1.emission, cl2.emission, fac); + cl.anisotropy = mix(cl1.anisotropy, cl2.anisotropy, fac); + return cl; +} + +Closure closure_add(Closure cl1, Closure cl2) +{ + Closure cl; + cl.absorption = cl1.absorption + cl2.absorption; + cl.scatter = cl1.scatter + cl2.scatter; + cl.emission = cl1.emission + cl2.emission; + cl.anisotropy = (cl1.anisotropy + cl2.anisotropy) / 2.0; /* Average phase (no multi lobe) */ + return cl; +} diff --git a/source/blender/draw/engines/eevee/shaders/closure_type_lib.glsl b/source/blender/draw/engines/eevee/shaders/closure_type_lib.glsl index fefc8743691..0096cd1747f 100644 --- a/source/blender/draw/engines/eevee/shaders/closure_type_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/closure_type_lib.glsl @@ -1,6 +1,8 @@ -#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl) -#pragma BLENDER_REQUIRE(renderpass_lib.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_codegen_lib.glsl) +/* #pragma (common_math_geom_lib.glsl) */ +/* #pragma (common_uniforms_lib.glsl) */ +/* #pragma (renderpass_lib.glsl) */ #ifndef VOLUMETRICS @@ -20,172 +22,92 @@ struct Closure { vec3 radiance; vec3 transmittance; float holdout; - vec4 ssr_data; - vec2 ssr_normal; - int flag; -# ifdef USE_SSS - vec3 sss_irradiance; - vec3 sss_albedo; - float sss_radius; -# endif - #endif + +/* Metal Default Constructor - Requred for C++ constructor syntax. */ +#ifdef GPU_METAL + inline Closure() = default; +# ifdef VOLUMETRICS + /* Explicit Closure constructors -- To support GLSL syntax */ + inline Closure(vec3 in_absorption, vec3 in_scatter, vec3 in_emission, float in_anisotropy) + : absorption(in_absorption), + scatter(in_scatter), + emission(in_emission), + anisotropy(in_anisotropy) + { + } +# else + /* Explicit Closure constructors -- To support GLSL syntax */ + inline Closure(vec3 in_radiance, vec3 in_transmittance, float in_holdout) + : radiance(in_radiance), transmittance(in_transmittance), holdout(in_holdout) + { + } +# endif /* VOLUMETRICS */ +#endif /* GPU_METAL */ }; #ifndef GPU_METAL /* Prototype */ -Closure nodetree_exec(void); +Closure nodetree_exec(); +vec4 closure_to_rgba(Closure); +void output_aov(vec4 color, float value, uint hash); +vec3 coordinate_camera(vec3 P); +vec3 coordinate_screen(vec3 P); +vec3 coordinate_reflect(vec3 P, vec3 N); +vec3 coordinate_incoming(vec3 P); + +/* Single BSDFs. */ +Closure closure_eval(ClosureDiffuse diffuse); +Closure closure_eval(ClosureTranslucent translucent); +Closure closure_eval(ClosureReflection reflection); +Closure closure_eval(ClosureRefraction refraction); +Closure closure_eval(ClosureEmission emission); +Closure closure_eval(ClosureTransparency transparency); +Closure closure_eval(ClosureVolumeScatter volume_scatter); +Closure closure_eval(ClosureVolumeAbsorption volume_absorption); +Closure closure_eval(ClosureHair hair); + +/* Glass BSDF. */ +Closure closure_eval(ClosureReflection reflection, ClosureRefraction refraction); +/* Dielectric BSDF. */ +Closure closure_eval(ClosureDiffuse diffuse, ClosureReflection reflection); +/* ClearCoat BSDF. */ +Closure closure_eval(ClosureReflection reflection, ClosureReflection clearcoat); +/* Volume BSDF. */ +Closure closure_eval(ClosureVolumeScatter volume_scatter, + ClosureVolumeAbsorption volume_absorption, + ClosureEmission emission); +/* Specular BSDF. */ +Closure closure_eval(ClosureDiffuse diffuse, + ClosureReflection reflection, + ClosureReflection clearcoat); +/* Principled BSDF. */ +Closure closure_eval(ClosureDiffuse diffuse, + ClosureReflection reflection, + ClosureReflection clearcoat, + ClosureRefraction refraction); + +Closure closure_add(Closure cl1, Closure cl2); +Closure closure_mix(Closure cl1, Closure cl2, float fac); + +float ambient_occlusion_eval(vec3 normal, + float distance, + const float inverted, + const float sample_count); + +/* WORKAROUND: Included later with libs. This is because we are mixing include systems. */ +vec3 safe_normalize(vec3 N); +float fast_sqrt(float a); +vec3 cameraVec(vec3 P); +vec2 btdf_lut(float a, float b, float c); +vec2 brdf_lut(float a, float b); +vec3 F_brdf_multi_scatter(vec3 a, vec3 b, vec2 c); +vec3 F_brdf_single_scatter(vec3 a, vec3 b, vec2 c); +float F_eta(float a, float b); #endif -/* clang-format off */ -/* Avoid multi-line defines. */ #ifdef VOLUMETRICS # define CLOSURE_DEFAULT Closure(vec3(0), vec3(0), vec3(0), 0.0) -#elif !defined(USE_SSS) -# define CLOSURE_DEFAULT Closure(vec3(0), vec3(0), 0.0, vec4(0), vec2(0), 0) #else -# define CLOSURE_DEFAULT Closure(vec3(0), vec3(0), 0.0, vec4(0), vec2(0), 0, vec3(0), vec3(0), 0.0) -#endif -/* clang-format on */ - -#define FLAG_TEST(flag, val) (((flag) & (val)) != 0) - -#define CLOSURE_SSR_FLAG 1 -#define CLOSURE_SSS_FLAG 2 -#define CLOSURE_HOLDOUT_FLAG 4 - -#ifdef VOLUMETRICS -Closure closure_mix(Closure cl1, Closure cl2, float fac) -{ - Closure cl; - cl.absorption = mix(cl1.absorption, cl2.absorption, fac); - cl.scatter = mix(cl1.scatter, cl2.scatter, fac); - cl.emission = mix(cl1.emission, cl2.emission, fac); - cl.anisotropy = mix(cl1.anisotropy, cl2.anisotropy, fac); - return cl; -} - -Closure closure_add(Closure cl1, Closure cl2) -{ - Closure cl; - cl.absorption = cl1.absorption + cl2.absorption; - cl.scatter = cl1.scatter + cl2.scatter; - cl.emission = cl1.emission + cl2.emission; - cl.anisotropy = (cl1.anisotropy + cl2.anisotropy) / 2.0; /* Average phase (no multi lobe) */ - return cl; -} - -Closure closure_emission(vec3 rgb) -{ - Closure cl = CLOSURE_DEFAULT; - cl.emission = rgb; - return cl; -} - -#else /* SURFACE */ - -Closure closure_mix(Closure cl1, Closure cl2, float fac) -{ - Closure cl; - cl.holdout = mix(cl1.holdout, cl2.holdout, fac); - - if (FLAG_TEST(cl1.flag, CLOSURE_HOLDOUT_FLAG)) { - fac = 1.0; - } - else if (FLAG_TEST(cl2.flag, CLOSURE_HOLDOUT_FLAG)) { - fac = 0.0; - } - - cl.transmittance = mix(cl1.transmittance, cl2.transmittance, fac); - cl.radiance = mix(cl1.radiance, cl2.radiance, fac); - cl.flag = cl1.flag | cl2.flag; - cl.ssr_data = mix(cl1.ssr_data, cl2.ssr_data, fac); - bool use_cl1_ssr = FLAG_TEST(cl1.flag, CLOSURE_SSR_FLAG); - /* When mixing SSR don't blend roughness and normals but only specular (ssr_data.xyz). */ - cl.ssr_data.w = (use_cl1_ssr) ? cl1.ssr_data.w : cl2.ssr_data.w; - cl.ssr_normal = (use_cl1_ssr) ? cl1.ssr_normal : cl2.ssr_normal; - -# ifdef USE_SSS - cl.sss_albedo = mix(cl1.sss_albedo, cl2.sss_albedo, fac); - bool use_cl1_sss = FLAG_TEST(cl1.flag, CLOSURE_SSS_FLAG); - /* It also does not make sense to mix SSS radius or irradiance. */ - cl.sss_radius = (use_cl1_sss) ? cl1.sss_radius : cl2.sss_radius; - cl.sss_irradiance = (use_cl1_sss) ? cl1.sss_irradiance : cl2.sss_irradiance; -# endif - return cl; -} - -Closure closure_add(Closure cl1, Closure cl2) -{ - Closure cl; - cl.transmittance = cl1.transmittance + cl2.transmittance; - cl.radiance = cl1.radiance + cl2.radiance; - cl.holdout = cl1.holdout + cl2.holdout; - cl.flag = cl1.flag | cl2.flag; - cl.ssr_data = cl1.ssr_data + cl2.ssr_data; - bool use_cl1_ssr = FLAG_TEST(cl1.flag, CLOSURE_SSR_FLAG); - /* When mixing SSR don't blend roughness and normals. */ - cl.ssr_data.w = (use_cl1_ssr) ? cl1.ssr_data.w : cl2.ssr_data.w; - cl.ssr_normal = (use_cl1_ssr) ? cl1.ssr_normal : cl2.ssr_normal; - -# ifdef USE_SSS - cl.sss_albedo = cl1.sss_albedo + cl2.sss_albedo; - bool use_cl1_sss = FLAG_TEST(cl1.flag, CLOSURE_SSS_FLAG); - /* It also does not make sense to mix SSS radius or irradiance. */ - cl.sss_radius = (use_cl1_sss) ? cl1.sss_radius : cl2.sss_radius; - cl.sss_irradiance = (use_cl1_sss) ? cl1.sss_irradiance : cl2.sss_irradiance; -# endif - return cl; -} - -Closure closure_emission(vec3 rgb) -{ - Closure cl = CLOSURE_DEFAULT; - cl.radiance = rgb; - return cl; -} - -#endif - -#ifndef VOLUMETRICS - -/* Let radiance passthrough or replace it to get the BRDF and color - * to applied to the SSR result. */ -vec3 closure_mask_ssr_radiance(vec3 radiance, float ssr_id) -{ - return (ssrToggle && int(ssr_id) == outputSsrId) ? vec3(1.0) : radiance; -} - -void closure_load_ssr_data( - vec3 ssr_radiance, float roughness, vec3 N, float ssr_id, inout Closure cl) -{ - /* Still encode to avoid artifacts in the SSR pass. */ - vec3 vN = normalize(mat3(ViewMatrix) * N); - cl.ssr_normal = normal_encode(vN, viewCameraVec(viewPosition)); - - if (ssrToggle && int(ssr_id) == outputSsrId) { - cl.ssr_data = vec4(ssr_radiance, roughness); - cl.flag |= CLOSURE_SSR_FLAG; - } - else { - cl.radiance += ssr_radiance; - } -} - -void closure_load_sss_data( - float radius, vec3 sss_irradiance, vec3 sss_albedo, int sss_id, inout Closure cl) -{ -# ifdef USE_SSS - if (sss_id == outputSssId) { - cl.sss_irradiance = sss_irradiance; - cl.sss_radius = radius; - cl.sss_albedo = sss_albedo; - cl.flag |= CLOSURE_SSS_FLAG; - /* Irradiance will be convolved by SSSS pass. Do not add to radiance. */ - sss_irradiance = vec3(0); - } -# endif - cl.radiance += render_pass_diffuse_mask(vec3(1), sss_irradiance) * sss_albedo; -} - +# define CLOSURE_DEFAULT Closure(vec3(0), vec3(0), 0.0) #endif diff --git a/source/blender/draw/engines/eevee/shaders/eevee_empty.glsl b/source/blender/draw/engines/eevee/shaders/eevee_empty.glsl new file mode 100644 index 00000000000..e00bcc4e557 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_empty.glsl @@ -0,0 +1,7 @@ + +/* Empty GLSL source to satisfy the GPUShaderCreateInfo requirements. */ +/* Needed includes for shader nodes. */ +#pragma BLENDER_REQUIRE(closure_type_lib.glsl) +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(common_math_lib.glsl) +#pragma BLENDER_REQUIRE(common_attribute_lib.glsl) diff --git a/source/blender/draw/engines/eevee/shaders/eevee_empty_volume.glsl b/source/blender/draw/engines/eevee/shaders/eevee_empty_volume.glsl new file mode 100644 index 00000000000..a748c0092b6 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/eevee_empty_volume.glsl @@ -0,0 +1,8 @@ + +/* Empty GLSL source to satisfy the GPUShaderCreateInfo requirements. */ +/* Needed includes for shader nodes. */ +#pragma BLENDER_REQUIRE(closure_type_lib.glsl) +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(common_math_lib.glsl) +#pragma BLENDER_REQUIRE(common_attribute_lib.glsl) +#pragma BLENDER_REQUIRE(closure_eval_volume_lib.glsl) diff --git a/source/blender/draw/engines/eevee/shaders/effect_dof_lib.glsl b/source/blender/draw/engines/eevee/shaders/effect_dof_lib.glsl index e288e1a55ea..9ed6ffa90c7 100644 --- a/source/blender/draw/engines/eevee/shaders/effect_dof_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/effect_dof_lib.glsl @@ -334,7 +334,15 @@ struct DofGatherData { float layer_opacity; }; -#define GATHER_DATA_INIT DofGatherData(vec4(0.0), 0.0, 0.0, 0.0, 0.0, 0.0, 0.0) +#ifdef GPU_METAL +/* C++ struct initialization. */ +# define GATHER_DATA_INIT \ + { \ + vec4(0.0), 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 \ + } +#else +# define GATHER_DATA_INIT DofGatherData(vec4(0.0), 0.0, 0.0, 0.0, 0.0, 0.0, 0.0) +#endif void dof_gather_ammend_weight(inout DofGatherData sample_data, float weight) { diff --git a/source/blender/draw/engines/eevee/shaders/effect_subsurface_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_subsurface_frag.glsl index 1aff93e01f8..e86c5e06c99 100644 --- a/source/blender/draw/engines/eevee/shaders/effect_subsurface_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/effect_subsurface_frag.glsl @@ -11,6 +11,7 @@ layout(std140) uniform sssProfile { vec4 sss_kernel[MAX_SSS_SAMPLES]; vec4 radii_max_radius; + float avg_inv_radius; int sss_samples; }; @@ -26,7 +27,7 @@ void main(void) vec2 pixel_size = 1.0 / vec2(textureSize(depthBuffer, 0).xy); /* TODO: precompute. */ vec2 uvs = gl_FragCoord.xy * pixel_size; vec3 sss_irradiance = texture(sssIrradiance, uvs).rgb; - float sss_radius = texture(sssRadius, uvs).r * radii_max_radius.w; + float sss_radius = texture(sssRadius, uvs).r * radii_max_radius.w * avg_inv_radius; float depth = texture(depthBuffer, uvs).r; float depth_view = get_view_z_from_depth(depth); diff --git a/source/blender/draw/engines/eevee/shaders/effect_translucency_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_translucency_frag.glsl index 3cf21dc32d1..5e1725ace97 100644 --- a/source/blender/draw/engines/eevee/shaders/effect_translucency_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/effect_translucency_frag.glsl @@ -23,12 +23,13 @@ layout(std140) uniform sssProfile { vec4 sss_kernel[MAX_SSS_SAMPLES]; vec4 radii_max_radius; + float avg_inv_radius; int sss_samples; }; vec3 sss_profile(float s) { - s /= radii_max_radius.w; + s /= radii_max_radius.w * avg_inv_radius; return texture(sssTexProfile, saturate(s) * SSS_LUT_SCALE + SSS_LUT_BIAS).rgb; } diff --git a/source/blender/draw/engines/eevee/shaders/lightprobe_filter_visibility_frag.glsl b/source/blender/draw/engines/eevee/shaders/lightprobe_filter_visibility_frag.glsl index d25ef23a706..681e69ae384 100644 --- a/source/blender/draw/engines/eevee/shaders/lightprobe_filter_visibility_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/lightprobe_filter_visibility_frag.glsl @@ -15,7 +15,6 @@ uniform float visibilityRange; uniform float visibilityBlur; uniform float sampleCount; -uniform float; out vec4 FragColor; diff --git a/source/blender/draw/engines/eevee/shaders/prepass_frag.glsl b/source/blender/draw/engines/eevee/shaders/prepass_frag.glsl index fd08dfda060..c8eea8d7860 100644 --- a/source/blender/draw/engines/eevee/shaders/prepass_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/prepass_frag.glsl @@ -2,15 +2,12 @@ /* Required by some nodes. */ #pragma BLENDER_REQUIRE(common_hair_lib.glsl) #pragma BLENDER_REQUIRE(common_utiltex_lib.glsl) +#pragma BLENDER_REQUIRE(common_math_lib.glsl) +#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl) #pragma BLENDER_REQUIRE(common_view_lib.glsl) #pragma BLENDER_REQUIRE(common_uniforms_lib.glsl) -#pragma BLENDER_REQUIRE(closure_type_lib.glsl) -#pragma BLENDER_REQUIRE(closure_eval_lib.glsl) -#pragma BLENDER_REQUIRE(closure_eval_diffuse_lib.glsl) -#pragma BLENDER_REQUIRE(closure_eval_glossy_lib.glsl) -#pragma BLENDER_REQUIRE(closure_eval_translucent_lib.glsl) -#pragma BLENDER_REQUIRE(closure_eval_refraction_lib.glsl) +#pragma BLENDER_REQUIRE(closure_eval_surface_lib.glsl) #pragma BLENDER_REQUIRE(surface_lib.glsl) #ifdef USE_ALPHA_HASH @@ -73,6 +70,7 @@ float hashed_alpha_threshold(vec3 co) void main() { #if defined(USE_ALPHA_HASH) + g_data = init_globals(); Closure cl = nodetree_exec(); diff --git a/source/blender/draw/engines/eevee/shaders/prepass_vert.glsl b/source/blender/draw/engines/eevee/shaders/prepass_vert.glsl deleted file mode 100644 index f650e2eeb8c..00000000000 --- a/source/blender/draw/engines/eevee/shaders/prepass_vert.glsl +++ /dev/null @@ -1,35 +0,0 @@ - -#pragma BLENDER_REQUIRE(common_hair_lib.glsl) -#pragma BLENDER_REQUIRE(common_view_lib.glsl) - -#ifndef HAIR_SHADER -in vec3 pos; -#endif - -void main() -{ - GPU_INTEL_VERTEX_SHADER_WORKAROUND - -#ifdef HAIR_SHADER - float time, thick_time, thickness; - vec3 worldPosition, tan, binor; - hair_get_pos_tan_binor_time((ProjectionMatrix[3][3] == 0.0), - ModelMatrixInverse, - ViewMatrixInverse[3].xyz, - ViewMatrixInverse[2].xyz, - worldPosition, - tan, - binor, - time, - thickness, - thick_time); -#else - vec3 worldPosition = point_object_to_world(pos); -#endif - - gl_Position = point_world_to_ndc(worldPosition); - -#ifdef CLIP_PLANES - gl_ClipDistance[0] = dot(vec4(worldPosition.xyz, 1.0), clipPlanes[0]); -#endif -} diff --git a/source/blender/draw/engines/eevee/shaders/renderpass_lib.glsl b/source/blender/draw/engines/eevee/shaders/renderpass_lib.glsl index 3e0a5e76d00..f276e4f26ca 100644 --- a/source/blender/draw/engines/eevee/shaders/renderpass_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/renderpass_lib.glsl @@ -1,4 +1,4 @@ -#define EEVEE_AOV_HASH_COLOR_TYPE_MASK 1 +#define EEVEE_AOV_HASH_COLOR_TYPE_MASK 1u /* ---------------------------------------------------------------------- */ /** \name Resources @@ -14,7 +14,7 @@ layout(std140) uniform renderpass_block bool renderPassSSSColor; bool renderPassEnvironment; bool renderPassAOV; - int renderPassAOVActive; + uint renderPassAOVActive; }; /** \} */ @@ -23,19 +23,14 @@ layout(std140) uniform renderpass_block /** \name Functions * \{ */ -vec3 render_pass_diffuse_mask(vec3 diffuse_color, vec3 diffuse_light) +vec3 render_pass_diffuse_mask(vec3 diffuse_light) { - return renderPassDiffuse ? (renderPassDiffuseLight ? diffuse_light : diffuse_color) : vec3(0.0); + return renderPassDiffuse ? (renderPassDiffuseLight ? diffuse_light : vec3(1.0)) : vec3(0.0); } -vec3 render_pass_sss_mask(vec3 sss_color) +vec3 render_pass_glossy_mask(vec3 specular_light) { - return renderPassSSSColor ? sss_color : vec3(0.0); -} - -vec3 render_pass_glossy_mask(vec3 specular_color, vec3 specular_light) -{ - return renderPassGlossy ? (renderPassGlossyLight ? specular_light : specular_color) : vec3(0.0); + return renderPassGlossy ? (renderPassGlossyLight ? specular_light : vec3(1.0)) : vec3(0.0); } vec3 render_pass_emission_mask(vec3 emission_light) @@ -45,10 +40,10 @@ vec3 render_pass_emission_mask(vec3 emission_light) bool render_pass_aov_is_color() { - return (renderPassAOVActive & EEVEE_AOV_HASH_COLOR_TYPE_MASK) != 0; + return (renderPassAOVActive & EEVEE_AOV_HASH_COLOR_TYPE_MASK) != 0u; } -int render_pass_aov_hash() +uint render_pass_aov_hash() { return renderPassAOVActive & ~EEVEE_AOV_HASH_COLOR_TYPE_MASK; } diff --git a/source/blender/draw/engines/eevee/shaders/shadow_vert.glsl b/source/blender/draw/engines/eevee/shaders/shadow_vert.glsl index cbfa9737a84..0e8e8dd9d01 100644 --- a/source/blender/draw/engines/eevee/shaders/shadow_vert.glsl +++ b/source/blender/draw/engines/eevee/shaders/shadow_vert.glsl @@ -1,5 +1,6 @@ #pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(common_math_lib.glsl) #pragma BLENDER_REQUIRE(common_hair_lib.glsl) #pragma BLENDER_REQUIRE(surface_lib.glsl) @@ -12,6 +13,7 @@ void main() #ifdef HAIR_SHADER hairStrandID = hair_get_strand_id(); + hairBary = hair_get_barycentric(); vec3 pos, binor; hair_get_pos_tan_binor_time((ProjectionMatrix[3][3] == 0.0), ModelMatrixInverse, @@ -52,3 +54,93 @@ void main() # endif #endif } + +#ifdef HAIR_SHADER +# ifdef OBINFO_LIB +vec3 attr_load_orco(samplerBuffer cd_buf) +{ + vec3 P = hair_get_strand_pos(); + vec3 lP = transform_point(ModelMatrixInverse, P); + return OrcoTexCoFactors[0].xyz + lP * OrcoTexCoFactors[1].xyz; +} +# endif + +vec4 attr_load_tangent(samplerBuffer cd_buf) +{ + /* Not supported. */ + return vec4(0.0, 0.0, 0.0, 1.0); +} + +vec3 attr_load_uv(samplerBuffer cd_buf) +{ + return texelFetch(cd_buf, hairStrandID).rgb; +} + +vec4 attr_load_color(samplerBuffer cd_buf) +{ + return texelFetch(cd_buf, hairStrandID).rgba; +} + +vec4 attr_load_vec4(samplerBuffer cd_buf) +{ + return texelFetch(cd_buf, hairStrandID).rgba; +} + +vec3 attr_load_vec3(samplerBuffer cd_buf) +{ + return texelFetch(cd_buf, hairStrandID).rgb; +} + +vec2 attr_load_vec2(samplerBuffer cd_buf) +{ + return texelFetch(cd_buf, hairStrandID).rg; +} + +float attr_load_float(samplerBuffer cd_buf) +{ + return texelFetch(cd_buf, hairStrandID).r; +} + +#else + +# ifdef OBINFO_LIB +vec3 attr_load_orco(samplerBuffer cd_buf) +{ + vec3 P = hair_get_strand_pos(); + vec3 lP = transform_point(ModelMatrixInverse, P); + return OrcoTexCoFactors[0].xyz + lP * OrcoTexCoFactors[1].xyz; +} +# endif + +vec4 attr_load_tangent(vec4 tangent) +{ + tangent.xyz = safe_normalize(normal_object_to_world(tangent.xyz)); + return tangent; +} + +/* Simple passthrough. */ +vec4 attr_load_vec4(vec4 attr) +{ + return attr; +} +vec3 attr_load_vec3(vec3 attr) +{ + return attr; +} +vec2 attr_load_vec2(vec2 attr) +{ + return attr; +} +vec2 attr_load_float(vec2 attr) +{ + return attr; +} +vec4 attr_load_color(vec4 attr) +{ + return attr; +} +vec3 attr_load_uv(vec3 attr) +{ + return attr; +} +#endif diff --git a/source/blender/draw/engines/eevee/shaders/surface_frag.glsl b/source/blender/draw/engines/eevee/shaders/surface_frag.glsl index 889bf439d5f..9ad7a4fdbc1 100644 --- a/source/blender/draw/engines/eevee/shaders/surface_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/surface_frag.glsl @@ -2,16 +2,14 @@ /* Required by some nodes. */ #pragma BLENDER_REQUIRE(common_hair_lib.glsl) #pragma BLENDER_REQUIRE(common_utiltex_lib.glsl) +#pragma BLENDER_REQUIRE(common_math_lib.glsl) +#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl) -#pragma BLENDER_REQUIRE(closure_type_lib.glsl) -#pragma BLENDER_REQUIRE(closure_eval_lib.glsl) -#pragma BLENDER_REQUIRE(closure_eval_diffuse_lib.glsl) -#pragma BLENDER_REQUIRE(closure_eval_glossy_lib.glsl) -#pragma BLENDER_REQUIRE(closure_eval_translucent_lib.glsl) -#pragma BLENDER_REQUIRE(closure_eval_refraction_lib.glsl) +#pragma BLENDER_REQUIRE(closure_eval_surface_lib.glsl) #pragma BLENDER_REQUIRE(surface_lib.glsl) #pragma BLENDER_REQUIRE(volumetric_lib.glsl) +#pragma BLENDER_REQUIRE(renderpass_lib.glsl) #ifdef USE_ALPHA_BLEND /* Use dual source blending to be able to make a whole range of effects. */ @@ -22,18 +20,74 @@ layout(location = 0, index = 1) out vec4 outTransmittance; layout(location = 0) out vec4 outRadiance; layout(location = 1) out vec2 ssrNormals; layout(location = 2) out vec4 ssrData; -# ifdef USE_SSS layout(location = 3) out vec3 sssIrradiance; layout(location = 4) out float sssRadius; layout(location = 5) out vec3 sssAlbedo; + +#endif + +uniform float backgroundAlpha; + +#ifdef EEVEE_DISPLACEMENT_BUMP + +# ifndef GPU_METAL +/* Prototype. */ +vec3 displacement_exec(); # endif +/* Return new shading normal. */ +vec3 displacement_bump() +{ + vec2 dHd; + dF_branch(dot(displacement_exec(), g_data.N + dF_impl(g_data.N)), dHd); + + vec3 dPdx = dFdx(g_data.P); + vec3 dPdy = dFdy(g_data.P); + + /* Get surface tangents from normal. */ + vec3 Rx = cross(dPdy, g_data.N); + vec3 Ry = cross(g_data.N, dPdx); + + /* Compute surface gradient and determinant. */ + float det = dot(dPdx, Rx); + + vec3 surfgrad = dHd.x * Rx + dHd.y * Ry; + + float facing = FrontFacing ? 1.0 : -1.0; + return normalize(abs(det) * g_data.N - facing * sign(det) * surfgrad); +} + #endif void main() { + g_data = init_globals(); + +#ifdef EEVEE_DISPLACEMENT_BUMP + g_data.N = displacement_bump(); +#endif + +#if defined(WORLD_BACKGROUND) || defined(PROBE_CAPTURE) + attrib_load(); +#endif + + out_ssr_color = vec3(0.0); + out_ssr_roughness = 0.0; + out_ssr_N = g_data.N; + + out_sss_radiance = vec3(0.0); + out_sss_radius = 0.0; + out_sss_color = vec3(0.0); + Closure cl = nodetree_exec(); +#ifdef WORLD_BACKGROUND + if (!renderPassEnvironment) { + cl.holdout += 1.0 - backgroundAlpha; + cl.radiance *= backgroundAlpha; + } +#endif + float holdout = saturate(1.0 - cl.holdout); float transmit = saturate(avg(cl.transmittance)); float alpha = 1.0 - transmit; @@ -53,38 +107,40 @@ void main() outTransmittance = vec4(cl.transmittance, transmit) * holdout; #else outRadiance = vec4(cl.radiance, holdout); - ssrNormals = cl.ssr_normal; - ssrData = cl.ssr_data; -# ifdef USE_SSS - sssIrradiance = cl.sss_irradiance; - sssRadius = cl.sss_radius; - sssAlbedo = cl.sss_albedo; -# endif + ssrNormals = normal_encode(normalize(mat3(ViewMatrix) * out_ssr_N), vec3(0.0)); + ssrData = vec4(out_ssr_color, out_ssr_roughness); + sssIrradiance = out_sss_radiance; + sssRadius = out_sss_radius; + sssAlbedo = out_sss_color; #endif - /* For Probe capture */ -#ifdef USE_SSS - float fac = float(!sssToggle); - - /* TODO(fclem): we shouldn't need this. - * Just disable USE_SSS when USE_REFRACTION is enabled. */ -# ifdef USE_REFRACTION +#ifdef USE_REFRACTION /* SSRefraction pass is done after the SSS pass. * In order to not lose the diffuse light totally we * need to merge the SSS radiance to the main radiance. */ - fac = 1.0; -# endif - - outRadiance.rgb += cl.sss_irradiance.rgb * cl.sss_albedo.rgb * fac; + const bool use_refraction = true; +#else + const bool use_refraction = false; #endif + /* For Probe capture */ + if (!sssToggle || use_refraction) { + outRadiance.rgb += out_sss_radiance * out_sss_color; + } #ifndef USE_ALPHA_BLEND float alpha_div = safe_rcp(alpha); outRadiance.rgb *= alpha_div; ssrData.rgb *= alpha_div; -# ifdef USE_SSS sssAlbedo.rgb *= alpha_div; -# endif + + if (renderPassAOV) { + if (aov_is_valid) { + outRadiance = vec4(out_aov, 1.0); + } + else { + outRadiance = vec4(0.0); + } + } #endif #ifdef LOOKDEV @@ -92,3 +148,34 @@ void main() gl_FragDepth = 0.0; #endif } + +/* Only supported attrib for world/background shaders. */ +vec3 attr_load_orco(vec4 orco) +{ + return g_data.P; +} +/* Unsupported. */ +vec4 attr_load_tangent(vec4 tangent) +{ + return vec4(0); +} +vec4 attr_load_vec4(vec4 attr) +{ + return vec4(0); +} +vec3 attr_load_vec3(vec3 attr) +{ + return vec3(0); +} +vec2 attr_load_vec2(vec2 attr) +{ + return vec2(0); +} +vec4 attr_load_color(vec4 attr) +{ + return vec4(0); +} +vec3 attr_load_uv(vec3 attr) +{ + return vec3(0); +} diff --git a/source/blender/draw/engines/eevee/shaders/surface_lib.glsl b/source/blender/draw/engines/eevee/shaders/surface_lib.glsl index d7fc5e0b52a..1f2f7cb65cc 100644 --- a/source/blender/draw/engines/eevee/shaders/surface_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/surface_lib.glsl @@ -1,12 +1,23 @@ /** This describe the entire interface of the shader. */ +#pragma BLENDER_REQUIRE(common_math_lib.glsl) + #define SURFACE_INTERFACE \ vec3 worldPosition; \ vec3 viewPosition; \ vec3 worldNormal; \ vec3 viewNormal; -#if defined(STEP_RESOLVE) || defined(STEP_RAYTRACE) +#ifndef IN_OUT +# if defined(GPU_VERTEX_SHADER) +# define IN_OUT out +# elif defined(GPU_FRAGMENT_SHADER) +# define IN_OUT in +# endif +#endif + +#ifndef EEVEE_GENERATED_INTERFACE +# if defined(STEP_RESOLVE) || defined(STEP_RAYTRACE) /* SSR will set these global variables itself. * Also make false positive compiler warnings disappear by setting values. */ vec3 worldPosition = vec3(0); @@ -14,22 +25,23 @@ vec3 viewPosition = vec3(0); vec3 worldNormal = vec3(0); vec3 viewNormal = vec3(0); -#elif defined(GPU_GEOMETRY_SHADER) +# elif defined(GPU_GEOMETRY_SHADER) in ShaderStageInterface{SURFACE_INTERFACE} dataIn[]; out ShaderStageInterface{SURFACE_INTERFACE} dataOut; -# define PASS_SURFACE_INTERFACE(vert) \ - dataOut.worldPosition = dataIn[vert].worldPosition; \ - dataOut.viewPosition = dataIn[vert].viewPosition; \ - dataOut.worldNormal = dataIn[vert].worldNormal; \ - dataOut.viewNormal = dataIn[vert].viewNormal; +# define PASS_SURFACE_INTERFACE(vert) \ + dataOut.worldPosition = dataIn[vert].worldPosition; \ + dataOut.viewPosition = dataIn[vert].viewPosition; \ + dataOut.worldNormal = dataIn[vert].worldNormal; \ + dataOut.viewNormal = dataIn[vert].viewNormal; -#else /* GPU_VERTEX_SHADER || GPU_FRAGMENT_SHADER*/ +# else /* GPU_VERTEX_SHADER || GPU_FRAGMENT_SHADER*/ IN_OUT ShaderStageInterface{SURFACE_INTERFACE}; -#endif +# endif +#endif /* EEVEE_GENERATED_INTERFACE */ #ifdef HAIR_SHADER IN_OUT ShaderHairInterface @@ -40,6 +52,7 @@ IN_OUT ShaderHairInterface float hairThickness; float hairTime; flat int hairStrandID; + vec2 hairBary; }; #endif @@ -52,3 +65,138 @@ IN_OUT ShaderPointCloudInterface flat int pointID; }; #endif + +#if defined(GPU_FRAGMENT_SHADER) && defined(CODEGEN_LIB) + +# if defined(USE_BARYCENTRICS) && !defined(HAIR_SHADER) +vec3 barycentric_distances_get() +{ + /* NOTE: No need to undo perspective divide since it is not applied yet. */ + vec3 pos0 = (ProjectionMatrixInverse * gpu_position_at_vertex(0)).xyz; + vec3 pos1 = (ProjectionMatrixInverse * gpu_position_at_vertex(1)).xyz; + vec3 pos2 = (ProjectionMatrixInverse * gpu_position_at_vertex(2)).xyz; + vec3 edge21 = pos2 - pos1; + vec3 edge10 = pos1 - pos0; + vec3 edge02 = pos0 - pos2; + vec3 d21 = safe_normalize(edge21); + vec3 d10 = safe_normalize(edge10); + vec3 d02 = safe_normalize(edge02); + vec3 dists; + float d = dot(d21, edge02); + dists.x = sqrt(dot(edge02, edge02) - d * d); + d = dot(d02, edge10); + dists.y = sqrt(dot(edge10, edge10) - d * d); + d = dot(d10, edge21); + dists.z = sqrt(dot(edge21, edge21) - d * d); + return dists.xyz; +} +# endif + +GlobalData init_globals(void) +{ + GlobalData surf; + +# if defined(WORLD_BACKGROUND) || defined(PROBE_CAPTURE) + surf.P = -cameraVec(worldPosition); + surf.N = surf.Ng = -surf.P; + surf.ray_length = 0.0; +# else + surf.P = worldPosition; + surf.N = safe_normalize(worldNormal); + surf.Ng = safe_normalize(cross(dFdx(surf.P), dFdy(surf.P))); + surf.ray_length = distance(surf.P, cameraPos); +# endif + surf.barycentric_coords = vec2(0.0); + surf.barycentric_dists = vec3(0.0); + if (!FrontFacing) { + surf.N = -surf.N; + } +# ifdef HAIR_SHADER + /* Shade as a cylinder. */ + vec3 B = normalize(cross(worldNormal, hairTangent)); + float cos_theta; + if (hairThicknessRes == 1) { + /* Random cosine normal distribution on the hair surface. */ + cos_theta = texelfetch_noise_tex(gl_FragCoord.xy).x * 2.0 - 1.0; + } + else { + /* Shade as a cylinder. */ + cos_theta = hairThickTime / hairThickness; + } + float sin_theta = sqrt(max(0.0, 1.0 - cos_theta * cos_theta)); + surf.N = safe_normalize(worldNormal * sin_theta + B * cos_theta); + surf.T = hairTangent; + surf.is_strand = true; + surf.hair_time = hairTime; + surf.hair_thickness = hairThickness; + surf.hair_strand_id = hairStrandID; +# ifdef USE_BARYCENTRICS + surf.barycentric_coords = hair_resolve_barycentric(hairBary); +# endif +# else + surf.T = vec3(0.0); + surf.is_strand = false; + surf.hair_time = 0.0; + surf.hair_thickness = 0.0; + surf.hair_strand_id = 0; +# ifdef USE_BARYCENTRICS + surf.barycentric_coords = gpu_BaryCoord.xy; + surf.barycentric_dists = barycentric_distances_get(); +# endif +# endif + surf.ray_type = rayType; + surf.ray_depth = 0.0; + return surf; +} +#endif + +vec3 coordinate_camera(vec3 P) +{ + vec3 vP; +#if defined(PROBE_CAPTURE) + /* Unsupported. It would make the probe camera-dependent. */ + vP = P; +#elif defined(WORLD_BACKGROUND) + vP = transform_direction(ViewMatrix, P); +#else + vP = transform_point(ViewMatrix, P); +#endif + vP.z = -vP.z; + return vP; +} + +vec3 coordinate_screen(vec3 P) +{ + vec3 window = vec3(0.0); +#if defined(PROBE_CAPTURE) + /* Unsupported. It would make the probe camera-dependent. */ + window.xy = vec2(0.5); + +#elif defined(WORLD_BACKGROUND) + window.xy = project_point(ProjectionMatrix, viewPosition).xy * 0.5 + 0.5; + window.xy = window.xy * CameraTexCoFactors.xy + CameraTexCoFactors.zw; + +#else /* MESH */ + window.xy = project_point(ViewProjectionMatrix, P).xy * 0.5 + 0.5; + window.xy = window.xy * CameraTexCoFactors.xy + CameraTexCoFactors.zw; +#endif + return window; +} + +vec3 coordinate_reflect(vec3 P, vec3 N) +{ +#if defined(WORLD_BACKGROUND) || defined(PROBE_CAPTURE) + return N; +#else + return -reflect(cameraVec(P), N); +#endif +} + +vec3 coordinate_incoming(vec3 P) +{ +#if defined(WORLD_BACKGROUND) || defined(PROBE_CAPTURE) + return -P; +#else + return cameraVec(P); +#endif +}
\ No newline at end of file diff --git a/source/blender/draw/engines/eevee/shaders/surface_vert.glsl b/source/blender/draw/engines/eevee/shaders/surface_vert.glsl index 51e9eda6cc2..6c6b810422b 100644 --- a/source/blender/draw/engines/eevee/shaders/surface_vert.glsl +++ b/source/blender/draw/engines/eevee/shaders/surface_vert.glsl @@ -1,6 +1,9 @@ #pragma BLENDER_REQUIRE(common_hair_lib.glsl) #pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(common_math_lib.glsl) +#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl) +#pragma BLENDER_REQUIRE(closure_eval_surface_lib.glsl) #pragma BLENDER_REQUIRE(surface_lib.glsl) #ifndef HAIR_SHADER @@ -18,6 +21,7 @@ void main() #ifdef HAIR_SHADER hairStrandID = hair_get_strand_id(); + hairBary = hair_get_barycentric(); vec3 pos, binor; hair_get_pos_tan_binor_time((ProjectionMatrix[3][3] == 0.0), ModelMatrixInverse, @@ -53,11 +57,103 @@ void main() /* No need to normalize since this is just a rotation. */ viewNormal = normal_world_to_view(worldNormal); -# ifdef USE_ATTR -# ifdef HAIR_SHADER - pos = hair_get_strand_pos(); -# endif - pass_attr(pos, NormalMatrix, ModelMatrixInverse); -# endif + + attrib_load(); #endif } + +#ifdef HAIR_SHADER +# ifdef OBINFO_LIB +vec3 attr_load_orco(samplerBuffer cd_buf) +{ + vec3 P = hair_get_strand_pos(); + vec3 lP = transform_point(ModelMatrixInverse, P); + return OrcoTexCoFactors[0].xyz + lP * OrcoTexCoFactors[1].xyz; +} +# endif + +vec4 attr_load_tangent(samplerBuffer cd_buf) +{ + return vec4(hairTangent, 1.0); +} + +vec3 attr_load_uv(samplerBuffer cd_buf) +{ + return texelFetch(cd_buf, hairStrandID).rgb; +} + +vec4 attr_load_color(samplerBuffer cd_buf) +{ + return texelFetch(cd_buf, hairStrandID).rgba; +} + +vec4 attr_load_vec4(samplerBuffer cd_buf) +{ + return texelFetch(cd_buf, hairStrandID).rgba; +} + +vec3 attr_load_vec3(samplerBuffer cd_buf) +{ + return texelFetch(cd_buf, hairStrandID).rgb; +} + +vec2 attr_load_vec2(samplerBuffer cd_buf) +{ + return texelFetch(cd_buf, hairStrandID).rg; +} + +float attr_load_float(samplerBuffer cd_buf) +{ + return texelFetch(cd_buf, hairStrandID).r; +} + +#else + +# ifdef OBINFO_LIB +vec3 attr_load_orco(vec4 orco) +{ + /* We know when there is no orco layer when orco.w is 1.0 because it uses the generic vertex + * attrib (which is [0,0,0,1]). */ + if (orco.w == 0.0) { + return orco.xyz * 0.5 + 0.5; + } + else { + /* If the object does not have any deformation, the orco layer calculation is done on the fly + * using the orco_madd factors. */ + return OrcoTexCoFactors[0].xyz + pos * OrcoTexCoFactors[1].xyz; + } +} +# endif + +vec4 attr_load_tangent(vec4 tangent) +{ + tangent.xyz = normal_object_to_world(tangent.xyz); + return tangent; +} + +/* Simple passthrough. */ +vec4 attr_load_vec4(vec4 attr) +{ + return attr; +} +vec3 attr_load_vec3(vec3 attr) +{ + return attr; +} +vec2 attr_load_vec2(vec2 attr) +{ + return attr; +} +float attr_load_float(float attr) +{ + return attr; +} +vec4 attr_load_color(vec4 attr) +{ + return attr; +} +vec3 attr_load_uv(vec3 attr) +{ + return attr; +} +#endif diff --git a/source/blender/draw/engines/eevee/shaders/volumetric_frag.glsl b/source/blender/draw/engines/eevee/shaders/volumetric_frag.glsl index 9f1afc4767c..e0a79872928 100644 --- a/source/blender/draw/engines/eevee/shaders/volumetric_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/volumetric_frag.glsl @@ -1,6 +1,5 @@ #pragma BLENDER_REQUIRE(volumetric_lib.glsl) -#pragma BLENDER_REQUIRE(closure_type_lib.glsl) /* Based on Frosbite Unified Volumetric. * https://www.ea.com/frostbite/news/physically-based-unified-volumetric-rendering-in-frostbite */ @@ -18,9 +17,7 @@ flat in int slice; vec3 worldPosition = vec3(0.0); vec3 viewPosition = vec3(0.0); vec3 viewNormal = vec3(0.0); -#ifdef MESH_SHADER -vec3 volumeObjectLocalCoord = vec3(0.0); -#endif +vec3 volumeOrco = vec3(0.0); layout(location = 0) out vec4 volumeScattering; layout(location = 1) out vec4 volumeExtinction; @@ -29,6 +26,52 @@ layout(location = 3) out vec4 volumePhase; /* Store volumetric properties into the froxel textures. */ +#ifdef MESH_SHADER +GlobalData init_globals(void) +{ + GlobalData surf; + surf.P = worldPosition; + surf.N = vec3(0.0); + surf.Ng = vec3(0.0); + surf.is_strand = false; + surf.hair_time = 0.0; + surf.hair_thickness = 0.0; + surf.hair_strand_id = 0; + surf.barycentric_coords = vec2(0.0); + surf.barycentric_dists = vec3(0.0); + surf.ray_type = RAY_TYPE_CAMERA; + surf.ray_depth = 0.0; + surf.ray_length = distance(surf.P, cameraPos); + return surf; +} + +vec3 coordinate_camera(vec3 P) +{ + vec3 vP; + vP = transform_point(ViewMatrix, P); + vP.z = -vP.z; + return vP; +} + +vec3 coordinate_screen(vec3 P) +{ + vec3 window = vec3(0.0); + window.xy = project_point(ViewProjectionMatrix, P).xy * 0.5 + 0.5; + window.xy = window.xy * CameraTexCoFactors.xy + CameraTexCoFactors.zw; + return window; +} + +vec3 coordinate_reflect(vec3 P, vec3 N) +{ + return vec3(0.0); +} + +vec3 coordinate_incoming(vec3 P) +{ + return cameraVec(P); +} +#endif + void main() { ivec3 volume_cell = ivec3(ivec2(gl_FragCoord.xy), slice); @@ -37,33 +80,40 @@ void main() viewPosition = get_view_space_from_depth(ndc_cell.xy, ndc_cell.z); worldPosition = point_view_to_world(viewPosition); #ifdef MESH_SHADER - volumeObjectLocalCoord = point_world_to_object(worldPosition); + volumeOrco = point_world_to_object(worldPosition); /* TODO: redundant transform */ - volumeObjectLocalCoord = (volumeObjectLocalCoord - volumeOrcoLoc + volumeOrcoSize) / - (volumeOrcoSize * 2.0); - volumeObjectLocalCoord = (volumeObjectToTexture * vec4(volumeObjectLocalCoord, 1.0)).xyz; + volumeOrco = (volumeOrco - volumeOrcoLoc + volumeOrcoSize) / (volumeOrcoSize * 2.0); + volumeOrco = (volumeObjectToTexture * vec4(volumeOrco, 1.0)).xyz; - if (any(lessThan(volumeObjectLocalCoord, vec3(0.0))) || - any(greaterThan(volumeObjectLocalCoord, vec3(1.0)))) + if (any(lessThan(volumeOrco, vec3(0.0))) || any(greaterThan(volumeOrco, vec3(1.0)))) { + /* Note: Discard is not an explicit return in Metal prior to versions 2.3. + * adding return after discard ensures consistent behaviour and avoids GPU + * side-effects where control flow continues with undefined values. */ discard; + return; + } #endif #ifdef CLEAR - Closure cl = CLOSURE_DEFAULT; + volumeScattering = vec4(0.0, 0.0, 0.0, 1.0); + volumeExtinction = vec4(0.0, 0.0, 0.0, 1.0); + volumeEmissive = vec4(0.0, 0.0, 0.0, 1.0); + volumePhase = vec4(0.0, 0.0, 0.0, 0.0); #else +# ifdef MESH_SHADER + g_data = init_globals(); + attrib_load(); +# endif Closure cl = nodetree_exec(); -#endif - -#ifdef MESH_SHADER +# ifdef MESH_SHADER cl.scatter *= volumeDensityScale; cl.absorption *= volumeDensityScale; cl.emission *= volumeDensityScale; -#endif +# endif volumeScattering = vec4(cl.scatter, 1.0); volumeExtinction = vec4(cl.absorption + cl.scatter, 1.0); volumeEmissive = vec4(cl.emission, 1.0); - /* Do not add phase weight if no scattering. */ if (all(equal(cl.scatter, vec3(0.0)))) { volumePhase = vec4(0.0); @@ -71,4 +121,38 @@ void main() else { volumePhase = vec4(cl.anisotropy, vec3(1.0)); } +#endif +} + +vec3 attr_load_orco(vec4 orco) +{ + return volumeOrco; +} +vec4 attr_load_tangent(vec4 tangent) +{ + return vec4(0); +} +vec4 attr_load_vec4(vec4 attr) +{ + return vec4(0); +} +vec3 attr_load_vec3(vec3 attr) +{ + return vec3(0); +} +vec2 attr_load_vec2(vec2 attr) +{ + return vec2(0); +} +float attr_load_float(float attr) +{ + return 0.0; +} +vec4 attr_load_color(vec4 attr) +{ + return vec4(0); +} +vec3 attr_load_uv(vec3 attr) +{ + return vec3(0); } diff --git a/source/blender/draw/engines/eevee/shaders/volumetric_geom.glsl b/source/blender/draw/engines/eevee/shaders/volumetric_geom.glsl index 5226da57a06..1269761ffa4 100644 --- a/source/blender/draw/engines/eevee/shaders/volumetric_geom.glsl +++ b/source/blender/draw/engines/eevee/shaders/volumetric_geom.glsl @@ -1,11 +1,7 @@ #pragma BLENDER_REQUIRE(common_view_lib.glsl) -#ifdef MESH_SHADER -/* TODO: tight slices. */ -layout(triangles) in; -layout(triangle_strip, max_vertices = 3) out; -#else /* World */ +#ifdef STANDALONE layout(triangles) in; layout(triangle_strip, max_vertices = 3) out; #endif diff --git a/source/blender/draw/engines/eevee/shaders/volumetric_integration_frag.glsl b/source/blender/draw/engines/eevee/shaders/volumetric_integration_frag.glsl index 11f57c0a82e..26b60c992e1 100644 --- a/source/blender/draw/engines/eevee/shaders/volumetric_integration_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/volumetric_integration_frag.glsl @@ -11,8 +11,10 @@ uniform sampler3D volumeScattering; /* Result of the scatter step */ uniform sampler3D volumeExtinction; #ifdef USE_VOLUME_OPTI -uniform layout(r11f_g11f_b10f) writeonly restrict image3D finalScattering_img; -uniform layout(r11f_g11f_b10f) writeonly restrict image3D finalTransmittance_img; +uniform layout(r11f_g11f_b10f) +writeonly restrict image3D finalScattering_img; +uniform layout(r11f_g11f_b10f) +writeonly restrict image3D finalTransmittance_img; vec3 finalScattering; vec3 finalTransmittance; @@ -70,7 +72,11 @@ void main() vec3 Tr = exp(-s_extinction * s_len); /* integrate along the current step segment */ - Lscat = (Lscat - Lscat * Tr) / max(vec3(1e-8), s_extinction); + /* Note: Original calculation carries precision issues when compiling for AMD GPUs + * and running Metal. This version of the equation retains precision well for all + * macOS HW configurations. */ + Lscat = (Lscat * (1.0f - Tr)) / max(vec3(1e-8), s_extinction); + /* accumulate and also take into account the transmittance from previous steps */ finalScattering += finalTransmittance * Lscat; diff --git a/source/blender/draw/engines/eevee/shaders/volumetric_vert.glsl b/source/blender/draw/engines/eevee/shaders/volumetric_vert.glsl index b70747ecec3..b574e8cdb4c 100644 --- a/source/blender/draw/engines/eevee/shaders/volumetric_vert.glsl +++ b/source/blender/draw/engines/eevee/shaders/volumetric_vert.glsl @@ -30,8 +30,30 @@ void main() vPos.w = 1.0; PASS_RESOURCE_ID +} + +/* Stubs */ +vec2 btdf_lut(float a, float b, float c) +{ + return vec2(0.0); +} + +vec2 brdf_lut(float a, float b) +{ + return vec2(0.0); +} -#ifdef USE_ATTR - pass_attr(vec3(0.0), mat3(1), mat4(1)); -#endif +vec3 F_brdf_multi_scatter(vec3 a, vec3 b, vec2 c) +{ + return vec3(0.0); +} + +vec3 F_brdf_single_scatter(vec3 a, vec3 b, vec2 c) +{ + return vec3(0.0); +} + +float F_eta(float a, float b) +{ + return 0.0; } diff --git a/source/blender/draw/engines/eevee/shaders/world_vert.glsl b/source/blender/draw/engines/eevee/shaders/world_vert.glsl new file mode 100644 index 00000000000..29892a7ffb4 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/world_vert.glsl @@ -0,0 +1,24 @@ + +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(common_math_lib.glsl) +#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl) + +#pragma BLENDER_REQUIRE(closure_eval_surface_lib.glsl) + +in vec2 pos; + +RESOURCE_ID_VARYING + +void main() +{ + GPU_INTEL_VERTEX_SHADER_WORKAROUND + + PASS_RESOURCE_ID + + gl_Position = vec4(pos, 1.0, 1.0); + viewPosition = project_point(ProjectionMatrixInverse, vec3(pos, 0.0)); + worldPosition = project_point(ViewProjectionMatrixInverse, vec3(pos, 0.0)); + /* Not usable. */ + viewNormal = vec3(0.0); + worldNormal = vec3(0.0); +} diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_common_lib.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_common_lib.glsl index cf38c1fc12c..75bd3d30d68 100644 --- a/source/blender/draw/engines/gpencil/shaders/gpencil_common_lib.glsl +++ b/source/blender/draw/engines/gpencil/shaders/gpencil_common_lib.glsl @@ -116,7 +116,7 @@ void blend_mode_output( color.a *= opacity; frag_revealage = frag_color = clamp(1.0 / max(vec4(1e-6), 1.0 - color * color.a), 0.0, 1e18); break; - case MODE_HARDLIGHT: + case MODE_HARDLIGHT: { /* Reminder: Blending func is multiply blend (dst.rgba * src.rgba). */ /** * We need to separate the overlay equation into 2 term (one mul and one add). @@ -134,6 +134,7 @@ void blend_mode_output( frag_revealage = frag_color = 2.0 * s + 2.0 * color * (1.0 - s * 2.0); frag_revealage = max(vec4(0.0), frag_revealage); break; + } case MODE_HARDLIGHT_SECOND_PASS: /* Reminder: Blending func is additive blend (dst.rgba + src.rgba). */ color = mix(vec4(0.5), color, color.a * opacity); diff --git a/source/blender/draw/engines/overlay/overlay_extra.c b/source/blender/draw/engines/overlay/overlay_extra.c index 73450db8eea..abcca5525c7 100644 --- a/source/blender/draw/engines/overlay/overlay_extra.c +++ b/source/blender/draw/engines/overlay/overlay_extra.c @@ -528,13 +528,13 @@ static void OVERLAY_forcefield(OVERLAY_ExtraCallBuffers *cb, Object *ob, ViewLay case PFIELD_GUIDE: if (cu && (cu->flag & CU_PATH) && ob->runtime.curve_cache->anim_path_accum_length) { instdata.size_x = instdata.size_y = instdata.size_z = pd->f_strength; - float pos[4], tmp[3]; - BKE_where_on_path(ob, 0.0f, pos, tmp, NULL, NULL, NULL); + float pos[4]; + BKE_where_on_path(ob, 0.0f, pos, NULL, NULL, NULL, NULL); copy_v3_v3(instdata.pos, ob->obmat[3]); translate_m4(instdata.mat, pos[0], pos[1], pos[2]); DRW_buffer_add_entry(cb->field_curve, color, &instdata); - BKE_where_on_path(ob, 1.0f, pos, tmp, NULL, NULL, NULL); + BKE_where_on_path(ob, 1.0f, pos, NULL, NULL, NULL, NULL); copy_v3_v3(instdata.pos, ob->obmat[3]); translate_m4(instdata.mat, pos[0], pos[1], pos[2]); DRW_buffer_add_entry(cb->field_sphere_limit, color, &instdata); diff --git a/source/blender/draw/engines/overlay/shaders/background_frag.glsl b/source/blender/draw/engines/overlay/shaders/background_frag.glsl index 19313c0415b..6b45b341ca4 100644 --- a/source/blender/draw/engines/overlay/shaders/background_frag.glsl +++ b/source/blender/draw/engines/overlay/shaders/background_frag.glsl @@ -57,13 +57,13 @@ void main() /* XXX do interpolation in a non-linear space to have a better visual result. */ col_high = pow(colorBackground.rgb, vec3(1.0 / 2.2)); col_low = pow(colorBackgroundGradient.rgb, vec3(1.0 / 2.2)); - bg_col = mix(col_low, col_high, uvcoordsvar.t); + bg_col = mix(col_low, col_high, uvcoordsvar.y); /* Convert back to linear. */ bg_col = pow(bg_col, vec3(2.2)); /* Dither to hide low precision buffer. (Could be improved) */ bg_col += dither(); break; - case BG_RADIAL: + case BG_RADIAL: { /* Do interpolation in a non-linear space to have a better visual result. */ col_high = pow(colorBackground.rgb, vec3(1.0 / 2.2)); col_low = pow(colorBackgroundGradient.rgb, vec3(1.0 / 2.2)); @@ -76,12 +76,14 @@ void main() /* Dither to hide low precision buffer. (Could be improved) */ bg_col += dither(); break; - case BG_CHECKER: + } + case BG_CHECKER: { float size = sizeChecker * sizePixel; ivec2 p = ivec2(floor(gl_FragCoord.xy / size)); bool check = mod(p.x, 2) == mod(p.y, 2); bg_col = (check) ? colorCheckerPrimary.rgb : colorCheckerSecondary.rgb; break; + } case BG_MASK: fragColor = vec4(vec3(1.0 - alpha), 0.0); return; diff --git a/source/blender/draw/engines/overlay/shaders/outline_detect_frag.glsl b/source/blender/draw/engines/overlay/shaders/outline_detect_frag.glsl index 19d54a57479..ba0a4c0da81 100644 --- a/source/blender/draw/engines/overlay/shaders/outline_detect_frag.glsl +++ b/source/blender/draw/engines/overlay/shaders/outline_detect_frag.glsl @@ -358,6 +358,12 @@ void main() line_end = vec2(0.0, 0.5); break; default: + /* Ensure values are assigned to, avoids undefined behaviour for + * divergent control-flow. This can occur if discard is called + * as discard is not treated as a return in Metal 2.2. So + * side-effects can still cause problems. */ + line_start = vec2(0.0); + line_end = vec2(0.0); break; } diff --git a/source/blender/draw/engines/workbench/shaders/workbench_prepass_hair_vert.glsl b/source/blender/draw/engines/workbench/shaders/workbench_prepass_hair_vert.glsl index 71cf08b7e8c..cfc94ef7c9a 100644 --- a/source/blender/draw/engines/workbench/shaders/workbench_prepass_hair_vert.glsl +++ b/source/blender/draw/engines/workbench/shaders/workbench_prepass_hair_vert.glsl @@ -8,8 +8,10 @@ /* From http://libnoise.sourceforge.net/noisegen/index.html */ float integer_noise(int n) { - n = (n >> 13) ^ n; - int nn = (n * (n * n * 60493 + 19990303) + 1376312589) & 0x7fffffff; + /* Integer bit-shifts cause precision issues due to overflow + * in a number of workbench tests. Use uint instead. */ + uint nn = (uint(n) >> 13u) ^ uint(n); + nn = (nn * (nn * nn * 60493u + 19990303u) + 1376312589u) & 0x7fffffffu; return (float(nn) / 1073741824.0); } diff --git a/source/blender/draw/engines/workbench/shaders/workbench_volume_frag.glsl b/source/blender/draw/engines/workbench/shaders/workbench_volume_frag.glsl index 4ff281ccd29..36059b6076f 100644 --- a/source/blender/draw/engines/workbench/shaders/workbench_volume_frag.glsl +++ b/source/blender/draw/engines/workbench/shaders/workbench_volume_frag.glsl @@ -218,7 +218,15 @@ void main() /* Manual depth test. TODO: remove. */ float depth = texelFetch(depthBuffer, ivec2(gl_FragCoord.xy), 0).r; if (gl_FragCoord.z >= depth) { + /* Note: In the Metal API, prior to Metal 2.3, Discard is not an explicit return and can + * produce undefined behaviour. This is especially prominent with derivatives if control-flow + * divergence is present. + * + * Adding a return call eliminates undefined behaviour and a later out-of-bounds read causing + * a crash on AMD platforms. + * This behaviour can also affect OpenGL on certain devices. */ discard; + return; } vec3 Lscat; @@ -268,6 +276,7 @@ void main() /* Start is further away than the end. * That means no volume is intersected. */ discard; + return; } fragColor = volume_integration(ls_ray_ori, diff --git a/source/blender/draw/engines/workbench/shaders/workbench_world_light_lib.glsl b/source/blender/draw/engines/workbench/shaders/workbench_world_light_lib.glsl index 531ed461057..20053b8917c 100644 --- a/source/blender/draw/engines/workbench/shaders/workbench_world_light_lib.glsl +++ b/source/blender/draw/engines/workbench/shaders/workbench_world_light_lib.glsl @@ -64,22 +64,35 @@ vec3 get_world_lighting(vec3 base_color, float roughness, float metallic, vec3 N if (world_data.use_specular) { /* Prepare Specular computation. Eval 4 lights at once. */ vec3 R = -reflect(I, N); + +#ifdef GPU_METAL + /* Split vectors into arrays of floats. Partial vector references are unsupported by MSL. */ + float spec_angle[4], spec_NL[4], wrap_NL[4]; +# define AS_VEC4(a) vec4(a[0], a[1], a[2], a[3]) +#else vec4 spec_angle, spec_NL, wrap_NL; - prep_specular(world_data.lights[0].direction.xyz, I, N, R, spec_NL.x, wrap_NL.x, spec_angle.x); - prep_specular(world_data.lights[1].direction.xyz, I, N, R, spec_NL.y, wrap_NL.y, spec_angle.y); - prep_specular(world_data.lights[2].direction.xyz, I, N, R, spec_NL.z, wrap_NL.z, spec_angle.z); - prep_specular(world_data.lights[3].direction.xyz, I, N, R, spec_NL.w, wrap_NL.w, spec_angle.w); +# define AS_VEC4(a) a +#endif + prep_specular( + world_data.lights[0].direction.xyz, I, N, R, spec_NL[0], wrap_NL[0], spec_angle[0]); + prep_specular( + world_data.lights[1].direction.xyz, I, N, R, spec_NL[1], wrap_NL[1], spec_angle[1]); + prep_specular( + world_data.lights[2].direction.xyz, I, N, R, spec_NL[2], wrap_NL[2], spec_angle[2]); + prep_specular( + world_data.lights[3].direction.xyz, I, N, R, spec_NL[3], wrap_NL[3], spec_angle[3]); vec4 gloss = vec4(1.0 - roughness); /* Reduce gloss for smooth light. (simulate bigger light) */ gloss *= 1.0 - wrap; vec4 shininess = exp2(10.0 * gloss + 1.0); - vec4 spec_light = blinn_specular(shininess, spec_angle, spec_NL); + vec4 spec_light = blinn_specular(shininess, AS_VEC4(spec_angle), AS_VEC4(spec_NL)); /* Simulate Env. light. */ vec4 w = mix(wrap, vec4(1.0), roughness); - vec4 spec_env = wrapped_lighting(wrap_NL, w); + vec4 spec_env = wrapped_lighting(AS_VEC4(wrap_NL), w); +#undef AS_VEC4 spec_light = mix(spec_light, spec_env, wrap * wrap); diff --git a/source/blender/draw/engines/workbench/workbench_engine.c b/source/blender/draw/engines/workbench/workbench_engine.c index 85c8f9c420a..566fd30096d 100644 --- a/source/blender/draw/engines/workbench/workbench_engine.c +++ b/source/blender/draw/engines/workbench/workbench_engine.c @@ -425,7 +425,8 @@ void workbench_cache_populate(void *ved, Object *ob) } else if (ob->type == OB_CURVES) { int color_type = workbench_color_type_get(wpd, ob, NULL, NULL, NULL); - workbench_cache_hair_populate(wpd, ob, NULL, NULL, color_type, false, CURVES_MATERIAL_NR); + DRWShadingGroup *grp = workbench_material_hair_setup(wpd, ob, CURVES_MATERIAL_NR, color_type); + DRW_shgroup_curves_create_sub(ob, grp, NULL); } else if (ob->type == OB_VOLUME) { if (wpd->shading.type != OB_WIRE) { diff --git a/source/blender/draw/intern/DRW_render.h b/source/blender/draw/intern/DRW_render.h index 1bf67a4f315..712118e8282 100644 --- a/source/blender/draw/intern/DRW_render.h +++ b/source/blender/draw/intern/DRW_render.h @@ -28,6 +28,7 @@ #include "DNA_world_types.h" #include "GPU_framebuffer.h" +#include "GPU_material.h" #include "GPU_primitive.h" #include "GPU_shader.h" #include "GPU_storage_buffer.h" @@ -197,13 +198,6 @@ void DRW_texture_free(struct GPUTexture *tex); /* Shaders */ -typedef void (*GPUMaterialEvalCallbackFn)(struct GPUMaterial *mat, - int options, - const char **vert_code, - const char **geom_code, - const char **frag_lib, - const char **defines); - struct GPUShader *DRW_shader_create_ex( const char *vert, const char *geom, const char *frag, const char *defines, const char *name); struct GPUShader *DRW_shader_create_with_lib_ex(const char *vert, @@ -242,38 +236,20 @@ struct GPUShader *DRW_shader_create_fullscreen_with_shaderlib_ex(const char *fra #define DRW_shader_create_fullscreen_with_shaderlib(frag, lib, defines) \ DRW_shader_create_fullscreen_with_shaderlib_ex(frag, lib, defines, __func__) -struct GPUMaterial *DRW_shader_find_from_world(struct World *wo, - const void *engine_type, - int options, - bool deferred); -struct GPUMaterial *DRW_shader_find_from_material(struct Material *ma, - const void *engine_type, - int options, - bool deferred); -struct GPUMaterial *DRW_shader_create_from_world(struct Scene *scene, - struct World *wo, - struct bNodeTree *ntree, - const void *engine_type, - int options, - bool is_volume_shader, - const char *vert, - const char *geom, - const char *frag_lib, - const char *defines, - bool deferred, - GPUMaterialEvalCallbackFn callback); -struct GPUMaterial *DRW_shader_create_from_material(struct Scene *scene, - struct Material *ma, - struct bNodeTree *ntree, - const void *engine_type, - int options, - bool is_volume_shader, - const char *vert, - const char *geom, - const char *frag_lib, - const char *defines, - bool deferred, - GPUMaterialEvalCallbackFn callback); +struct GPUMaterial *DRW_shader_from_world(struct World *wo, + struct bNodeTree *ntree, + const uint64_t shader_id, + const bool is_volume_shader, + bool deferred, + GPUCodegenCallbackFn callback, + void *thunk); +struct GPUMaterial *DRW_shader_from_material(struct Material *ma, + struct bNodeTree *ntree, + const uint64_t shader_id, + const bool is_volume_shader, + bool deferred, + GPUCodegenCallbackFn callback, + void *thunk); void DRW_shader_free(struct GPUShader *shader); #define DRW_SHADER_FREE_SAFE(shader) \ do { \ diff --git a/source/blender/draw/intern/draw_cache_extract_mesh.cc b/source/blender/draw/intern/draw_cache_extract_mesh.cc index 717ea00fc0c..8a7b4fc9703 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh.cc +++ b/source/blender/draw/intern/draw_cache_extract_mesh.cc @@ -844,6 +844,7 @@ static void mesh_buffer_cache_create_requested_subdiv(MeshBatchCache *cache, EXTRACT_ADD_REQUESTED(vbo, edituv_stretch_angle); EXTRACT_ADD_REQUESTED(ibo, lines_paint_mask); EXTRACT_ADD_REQUESTED(ibo, lines_adjacency); + EXTRACT_ADD_REQUESTED(vbo, orco); EXTRACT_ADD_REQUESTED(vbo, vcol); EXTRACT_ADD_REQUESTED(vbo, weights); EXTRACT_ADD_REQUESTED(vbo, sculpt_data); diff --git a/source/blender/draw/intern/draw_cache_impl_mesh.c b/source/blender/draw/intern/draw_cache_impl_mesh.c index e6f34d3dd0d..ec33d2b5361 100644 --- a/source/blender/draw/intern/draw_cache_impl_mesh.c +++ b/source/blender/draw/intern/draw_cache_impl_mesh.c @@ -2121,7 +2121,6 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, MDEPS_ASSERT_MAP_INDEX(TRIS_PER_MAT_INDEX); - /* Meh loose Scene const correctness here. */ const bool use_subsurf_fdots = scene ? BKE_modifiers_uses_subsurf_facedots(scene, ob) : false; if (do_uvcage) { diff --git a/source/blender/draw/intern/draw_cache_impl_subdivision.cc b/source/blender/draw/intern/draw_cache_impl_subdivision.cc index 2653035a39f..c859a72b371 100644 --- a/source/blender/draw/intern/draw_cache_impl_subdivision.cc +++ b/source/blender/draw/intern/draw_cache_impl_subdivision.cc @@ -1162,8 +1162,8 @@ static void draw_subdiv_ubo_update_and_bind(const DRWSubdivCache *cache, GPU_uniformbuf_update(cache->ubo, &storage); - const int location = GPU_shader_get_uniform_block(shader, "shader_data"); - GPU_uniformbuf_bind(cache->ubo, location); + const int binding = GPU_shader_get_uniform_block_binding(shader, "shader_data"); + GPU_uniformbuf_bind(cache->ubo, binding); } /** \} */ @@ -1942,7 +1942,9 @@ static bool draw_subdiv_create_requested_buffers(const Scene *scene, return false; } - const bool optimal_display = (smd->flags & eSubsurfModifierFlag_ControlEdges); + /* Edges which do not come from coarse edges should not be drawn in edit mode, only in object + * mode when optimal display in turned off. */ + const bool optimal_display = (smd->flags & eSubsurfModifierFlag_ControlEdges) || is_editmode; draw_cache->bm = bm; draw_cache->mesh = mesh_eval; diff --git a/source/blender/draw/intern/draw_common.h b/source/blender/draw/intern/draw_common.h index 34b930ae9c8..d302140d9ac 100644 --- a/source/blender/draw/intern/draw_common.h +++ b/source/blender/draw/intern/draw_common.h @@ -53,12 +53,17 @@ struct DRWShadingGroup *DRW_shgroup_hair_create_sub(struct Object *object, struct ModifierData *md, struct DRWShadingGroup *shgrp, struct GPUMaterial *gpu_material); + +struct DRWShadingGroup *DRW_shgroup_curves_create_sub(struct Object *object, + struct DRWShadingGroup *shgrp, + struct GPUMaterial *gpu_material); /** * \note Only valid after #DRW_hair_update(). */ struct GPUVertBuf *DRW_hair_pos_buffer_get(struct Object *object, struct ParticleSystem *psys, struct ModifierData *md); +struct GPUVertBuf *DRW_curves_pos_buffer_get(struct Object *object); void DRW_hair_duplimat_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 e06df334d23..aac6f7e58c5 100644 --- a/source/blender/draw/intern/draw_hair.c +++ b/source/blender/draw/intern/draw_hair.c @@ -164,17 +164,28 @@ static ParticleHairCache *drw_hair_particle_cache_get(Object *object, int subdiv, int thickness_res) { - bool update; ParticleHairCache *cache; - if (psys) { - /* Old particle hair. */ - update = particles_ensure_procedural_data( - object, psys, md, &cache, gpu_material, subdiv, thickness_res); - } - else { - /* New curves object. */ - update = curves_ensure_procedural_data(object, &cache, gpu_material, subdiv, thickness_res); + bool update = particles_ensure_procedural_data( + object, psys, md, &cache, gpu_material, subdiv, thickness_res); + + if (update) { + if (drw_hair_shader_type_get() == PART_REFINE_SHADER_COMPUTE) { + drw_hair_particle_cache_update_compute(cache, subdiv); + } + else { + drw_hair_particle_cache_update_transform_feedback(cache, subdiv); + } } + return cache; +} + +static ParticleHairCache *drw_curves_cache_get(Object *object, + GPUMaterial *gpu_material, + int subdiv, + int thickness_res) +{ + ParticleHairCache *cache; + bool update = curves_ensure_procedural_data(object, &cache, gpu_material, subdiv, thickness_res); if (update) { if (drw_hair_shader_type_get() == PART_REFINE_SHADER_COMPUTE) { @@ -201,37 +212,44 @@ GPUVertBuf *DRW_hair_pos_buffer_get(Object *object, ParticleSystem *psys, Modifi return cache->final[subdiv].proc_buf; } +GPUVertBuf *DRW_curves_pos_buffer_get(Object *object) +{ + const DRWContextState *draw_ctx = DRW_context_state_get(); + Scene *scene = draw_ctx->scene; + + int subdiv = scene->r.hair_subdiv; + int thickness_res = (scene->r.hair_type == SCE_HAIR_SHAPE_STRAND) ? 1 : 2; + + ParticleHairCache *cache = drw_curves_cache_get(object, NULL, subdiv, thickness_res); + + return cache->final[subdiv].proc_buf; +} + void DRW_hair_duplimat_get(Object *object, - ParticleSystem *psys, + ParticleSystem *UNUSED(psys), ModifierData *UNUSED(md), float (*dupli_mat)[4]) { Object *dupli_parent = DRW_object_get_dupli_parent(object); DupliObject *dupli_object = DRW_object_get_dupli(object); - if (psys) { - if ((dupli_parent != NULL) && (dupli_object != NULL)) { - if (dupli_object->type & OB_DUPLICOLLECTION) { - unit_m4(dupli_mat); - Collection *collection = dupli_parent->instance_collection; - if (collection != NULL) { - sub_v3_v3(dupli_mat[3], collection->instance_offset); - } - mul_m4_m4m4(dupli_mat, dupli_parent->obmat, dupli_mat); - } - else { - copy_m4_m4(dupli_mat, dupli_object->ob->obmat); - invert_m4(dupli_mat); - mul_m4_m4m4(dupli_mat, object->obmat, dupli_mat); + if ((dupli_parent != NULL) && (dupli_object != NULL)) { + if (dupli_object->type & OB_DUPLICOLLECTION) { + unit_m4(dupli_mat); + Collection *collection = dupli_parent->instance_collection; + if (collection != NULL) { + sub_v3_v3(dupli_mat[3], collection->instance_offset); } + mul_m4_m4m4(dupli_mat, dupli_parent->obmat, dupli_mat); } else { - unit_m4(dupli_mat); + copy_m4_m4(dupli_mat, dupli_object->ob->obmat); + invert_m4(dupli_mat); + mul_m4_m4m4(dupli_mat, object->obmat, dupli_mat); } } else { - /* New curves object. */ - copy_m4_m4(dupli_mat, object->obmat); + unit_m4(dupli_mat); } } @@ -280,27 +298,15 @@ DRWShadingGroup *DRW_shgroup_hair_create_sub(Object *object, DRW_hair_duplimat_get(object, psys, md, dupli_mat); /* Get hair shape parameters. */ - float hair_rad_shape, hair_rad_root, hair_rad_tip; - bool hair_close_tip; - if (psys) { - /* Old particle hair. */ - ParticleSettings *part = psys->part; - hair_rad_shape = part->shape; - hair_rad_root = part->rad_root * part->rad_scale * 0.5f; - hair_rad_tip = part->rad_tip * part->rad_scale * 0.5f; - hair_close_tip = (part->shape_flag & PART_SHAPE_CLOSE_TIP) != 0; - } - else { - /* TODO: implement for new curves object. */ - hair_rad_shape = 1.0f; - hair_rad_root = 0.005f; - hair_rad_tip = 0.0f; - hair_close_tip = true; - } + ParticleSettings *part = psys->part; + float hair_rad_shape = part->shape; + float hair_rad_root = part->rad_root * part->rad_scale * 0.5f; + float hair_rad_tip = part->rad_tip * part->rad_scale * 0.5f; + bool hair_close_tip = (part->shape_flag & PART_SHAPE_CLOSE_TIP) != 0; 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_texture(shgrp, "l", 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); @@ -317,6 +323,71 @@ DRWShadingGroup *DRW_shgroup_hair_create_sub(Object *object, return shgrp; } +DRWShadingGroup *DRW_shgroup_curves_create_sub(Object *object, + DRWShadingGroup *shgrp_parent, + GPUMaterial *gpu_material) +{ + const DRWContextState *draw_ctx = DRW_context_state_get(); + Scene *scene = draw_ctx->scene; + + int subdiv = scene->r.hair_subdiv; + int thickness_res = (scene->r.hair_type == SCE_HAIR_SHAPE_STRAND) ? 1 : 2; + + ParticleHairCache *curves_cache = drw_curves_cache_get( + object, gpu_material, subdiv, thickness_res); + + DRWShadingGroup *shgrp = DRW_shgroup_create_sub(shgrp_parent); + + /* TODO: optimize this. Only bind the ones GPUMaterial needs. */ + for (int i = 0; i < curves_cache->num_uv_layers; i++) { + for (int n = 0; n < MAX_LAYER_NAME_CT && curves_cache->uv_layer_names[i][n][0] != '\0'; n++) { + DRW_shgroup_uniform_texture( + shgrp, curves_cache->uv_layer_names[i][n], curves_cache->uv_tex[i]); + } + } + for (int i = 0; i < curves_cache->num_col_layers; i++) { + for (int n = 0; n < MAX_LAYER_NAME_CT && curves_cache->col_layer_names[i][n][0] != '\0'; n++) { + DRW_shgroup_uniform_texture( + shgrp, curves_cache->col_layer_names[i][n], curves_cache->col_tex[i]); + } + } + + /* Fix issue with certain driver not drawing anything if there is no texture bound to + * "ac", "au", "u" or "c". */ + if (curves_cache->num_uv_layers == 0) { + DRW_shgroup_uniform_texture(shgrp, "u", g_dummy_texture); + DRW_shgroup_uniform_texture(shgrp, "au", g_dummy_texture); + } + if (curves_cache->num_col_layers == 0) { + DRW_shgroup_uniform_texture(shgrp, "c", g_dummy_texture); + DRW_shgroup_uniform_texture(shgrp, "ac", g_dummy_texture); + } + + /* TODO: Generalize radius implementation for curves data type. */ + float hair_rad_shape = 1.0f; + float hair_rad_root = 0.005f; + float hair_rad_tip = 0.0f; + bool hair_close_tip = true; + + DRW_shgroup_uniform_texture(shgrp, "hairPointBuffer", curves_cache->final[subdiv].proc_tex); + if (curves_cache->length_tex) { + DRW_shgroup_uniform_texture(shgrp, "hairLen", curves_cache->length_tex); + } + DRW_shgroup_uniform_int(shgrp, "hairStrandsRes", &curves_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); + DRW_shgroup_uniform_mat4_copy(shgrp, "hairDupliMatrix", object->obmat); + DRW_shgroup_uniform_float_copy(shgrp, "hairRadRoot", hair_rad_root); + DRW_shgroup_uniform_float_copy(shgrp, "hairRadTip", hair_rad_tip); + DRW_shgroup_uniform_bool_copy(shgrp, "hairCloseTip", hair_close_tip); + /* TODO(fclem): Until we have a better way to cull the curves and render with orco, bypass + * culling test. */ + GPUBatch *geom = curves_cache->final[subdiv].proc_hairs[thickness_res - 1]; + DRW_shgroup_call_no_cull(shgrp, geom, object); + + return shgrp; +} + void DRW_hair_update(void) { #ifndef USE_TRANSFORM_FEEDBACK diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c index ec3a5b3a7b1..88d05fcd928 100644 --- a/source/blender/draw/intern/draw_manager.c +++ b/source/blender/draw/intern/draw_manager.c @@ -1286,6 +1286,10 @@ void DRW_notify_view_update(const DRWUpdateContext *update_ctx) const bool gpencil_engine_needed = drw_gpencil_engine_needed(depsgraph, v3d); + if (G.is_rendering) { + return; + } + /* XXX Really nasty locking. But else this could * be executed by the material previews thread * while rendering a viewport. */ diff --git a/source/blender/draw/intern/draw_manager_shader.c b/source/blender/draw/intern/draw_manager_shader.c index 1936aa599ff..9f8a68f81f6 100644 --- a/source/blender/draw/intern/draw_manager_shader.c +++ b/source/blender/draw/intern/draw_manager_shader.c @@ -417,119 +417,81 @@ GPUShader *DRW_shader_create_fullscreen_with_shaderlib_ex(const char *frag, return sh; } -GPUMaterial *DRW_shader_find_from_world(World *wo, - const void *engine_type, - const int options, - bool deferred) +GPUMaterial *DRW_shader_from_world(World *wo, + struct bNodeTree *ntree, + const uint64_t shader_id, + const bool is_volume_shader, + bool deferred, + GPUCodegenCallbackFn callback, + void *thunk) { - GPUMaterial *mat = GPU_material_from_nodetree_find(&wo->gpumaterial, engine_type, options); - if (DRW_state_is_image_render() || !deferred) { - if (mat != NULL && GPU_material_status(mat) == GPU_MAT_QUEUED) { - /* XXX Hack : we return NULL so that the engine will call DRW_shader_create_from_XXX - * with the shader code and we will resume the compilation from there. */ - return NULL; - } - } - return mat; -} - -GPUMaterial *DRW_shader_find_from_material(Material *ma, - const void *engine_type, - const int options, - bool deferred) -{ - GPUMaterial *mat = GPU_material_from_nodetree_find(&ma->gpumaterial, engine_type, options); - if (DRW_state_is_image_render() || !deferred) { - if (mat != NULL && GPU_material_status(mat) == GPU_MAT_QUEUED) { - /* XXX Hack : we return NULL so that the engine will call DRW_shader_create_from_XXX - * with the shader code and we will resume the compilation from there. */ - return NULL; - } - } - return mat; -} - -GPUMaterial *DRW_shader_create_from_world(struct Scene *scene, - World *wo, - struct bNodeTree *ntree, - const void *engine_type, - const int options, - const bool is_volume_shader, - const char *vert, - const char *geom, - const char *frag_lib, - const char *defines, - bool deferred, - GPUMaterialEvalCallbackFn callback) -{ - GPUMaterial *mat = NULL; - if (DRW_state_is_image_render() || !deferred) { - mat = GPU_material_from_nodetree_find(&wo->gpumaterial, engine_type, options); - } - - if (mat == NULL) { - scene = (Scene *)DEG_get_original_id(&DST.draw_ctx.scene->id); - mat = GPU_material_from_nodetree(scene, - NULL, - ntree, - &wo->gpumaterial, - engine_type, - options, - is_volume_shader, - vert, - geom, - frag_lib, - defines, - wo->id.name, - callback); + Scene *scene = (Scene *)DEG_get_original_id(&DST.draw_ctx.scene->id); + GPUMaterial *mat = GPU_material_from_nodetree(scene, + NULL, + ntree, + &wo->gpumaterial, + wo->id.name, + shader_id, + is_volume_shader, + false, + callback, + thunk); + if (!DRW_state_is_image_render() && deferred && GPU_material_status(mat) == GPU_MAT_QUEUED) { + /* Shader has been already queued. */ + return mat; } - if (GPU_material_status(mat) == GPU_MAT_QUEUED) { + if (GPU_material_status(mat) == GPU_MAT_CREATED) { + GPU_material_status_set(mat, GPU_MAT_QUEUED); drw_deferred_shader_add(mat, deferred); } + if (!deferred && GPU_material_status(mat) == GPU_MAT_QUEUED) { + /* Force compilation for shaders already queued. */ + drw_deferred_shader_add(mat, false); + } return mat; } -GPUMaterial *DRW_shader_create_from_material(struct Scene *scene, - Material *ma, - struct bNodeTree *ntree, - const void *engine_type, - const int options, - const bool is_volume_shader, - const char *vert, - const char *geom, - const char *frag_lib, - const char *defines, - bool deferred, - GPUMaterialEvalCallbackFn callback) +GPUMaterial *DRW_shader_from_material(Material *ma, + struct bNodeTree *ntree, + const uint64_t shader_id, + const bool is_volume_shader, + bool deferred, + GPUCodegenCallbackFn callback, + void *thunk) { - GPUMaterial *mat = NULL; - if (DRW_state_is_image_render() || !deferred) { - mat = GPU_material_from_nodetree_find(&ma->gpumaterial, engine_type, options); + Scene *scene = (Scene *)DEG_get_original_id(&DST.draw_ctx.scene->id); + GPUMaterial *mat = GPU_material_from_nodetree(scene, + ma, + ntree, + &ma->gpumaterial, + ma->id.name, + shader_id, + is_volume_shader, + false, + callback, + thunk); + + if (DRW_state_is_image_render()) { + /* Do not deferred if doing render. */ + deferred = false; } - if (mat == NULL) { - scene = (Scene *)DEG_get_original_id(&DST.draw_ctx.scene->id); - mat = GPU_material_from_nodetree(scene, - ma, - ntree, - &ma->gpumaterial, - engine_type, - options, - is_volume_shader, - vert, - geom, - frag_lib, - defines, - ma->id.name, - callback); + if (deferred && GPU_material_status(mat) == GPU_MAT_QUEUED) { + /* Shader has been already queued. */ + return mat; } - if (GPU_material_status(mat) == GPU_MAT_QUEUED) { + if (GPU_material_status(mat) == GPU_MAT_CREATED) { + GPU_material_status_set(mat, GPU_MAT_QUEUED); drw_deferred_shader_add(mat, deferred); } + if (!deferred && GPU_material_status(mat) == GPU_MAT_QUEUED) { + /* Force compilation for shaders already queued. */ + drw_deferred_shader_add(mat, false); + } return mat; } @@ -552,15 +514,15 @@ void DRW_shader_free(GPUShader *shader) * contains the needed libraries for this shader. * \{ */ -/* 32 because we use a 32bit bitmap. */ -#define MAX_LIB 32 +/* 64 because we use a 64bit bitmap. */ +#define MAX_LIB 64 #define MAX_LIB_NAME 64 #define MAX_LIB_DEPS 8 struct DRWShaderLibrary { const char *libs[MAX_LIB]; char libs_name[MAX_LIB][MAX_LIB_NAME]; - uint32_t libs_deps[MAX_LIB]; + uint64_t libs_deps[MAX_LIB]; }; DRWShaderLibrary *DRW_shader_library_create(void) @@ -589,23 +551,27 @@ static int drw_shader_library_search(const DRWShaderLibrary *lib, const char *na } /* Return bitmap of dependencies. */ -static uint32_t drw_shader_dependencies_get(const DRWShaderLibrary *lib, const char *lib_code) +static uint64_t drw_shader_dependencies_get(const DRWShaderLibrary *lib, + const char *pragma_str, + const char *lib_code, + const char *UNUSED(lib_name)) { /* Search dependencies. */ - uint32_t deps = 0; + uint pragma_len = strlen(pragma_str); + uint64_t deps = 0; const char *haystack = lib_code; - while ((haystack = strstr(haystack, "BLENDER_REQUIRE("))) { - haystack += 16; + while ((haystack = strstr(haystack, pragma_str))) { + haystack += pragma_len; int dep = drw_shader_library_search(lib, haystack); if (dep == -1) { - char dbg_name[33]; + char dbg_name[MAX_NAME]; int i = 0; while ((*haystack != ')') && (i < (sizeof(dbg_name) - 2))) { dbg_name[i] = *haystack; haystack++; i++; } - dbg_name[i + 1] = '\0'; + dbg_name[i] = '\0'; CLOG_INFO(&LOG, 0, @@ -614,7 +580,7 @@ static uint32_t drw_shader_dependencies_get(const DRWShaderLibrary *lib, const c dbg_name); } else { - deps |= 1u << (uint32_t)dep; + deps |= 1llu << ((uint64_t)dep); } } return deps; @@ -633,7 +599,8 @@ void DRW_shader_library_add_file(DRWShaderLibrary *lib, const char *lib_code, co if (index > -1) { lib->libs[index] = lib_code; BLI_strncpy(lib->libs_name[index], lib_name, MAX_LIB_NAME); - lib->libs_deps[index] = drw_shader_dependencies_get(lib, lib_code); + lib->libs_deps[index] = drw_shader_dependencies_get( + lib, "BLENDER_REQUIRE(", lib_code, lib_name); } else { printf("Error: Too many libraries. Cannot add %s.\n", lib_name); @@ -643,21 +610,20 @@ void DRW_shader_library_add_file(DRWShaderLibrary *lib, const char *lib_code, co char *DRW_shader_library_create_shader_string(const DRWShaderLibrary *lib, const char *shader_code) { - uint32_t deps = drw_shader_dependencies_get(lib, shader_code); + uint64_t deps = drw_shader_dependencies_get(lib, "BLENDER_REQUIRE(", shader_code, "shader code"); DynStr *ds = BLI_dynstr_new(); /* Add all dependencies recursively. */ for (int i = MAX_LIB - 1; i > -1; i--) { - if (lib->libs[i] && (deps & (1u << (uint32_t)i))) { + if (lib->libs[i] && (deps & (1llu << (uint64_t)i))) { deps |= lib->libs_deps[i]; } } /* Concatenate all needed libs into one string. */ - for (int i = 0; i < MAX_LIB; i++) { - if (deps & 1u) { + for (int i = 0; i < MAX_LIB && deps != 0llu; i++, deps >>= 1llu) { + if (deps & 1llu) { BLI_dynstr_append(ds, lib->libs[i]); } - deps = deps >> 1; } BLI_dynstr_append(ds, shader_code); diff --git a/source/blender/draw/intern/draw_shader_shared.h b/source/blender/draw/intern/draw_shader_shared.h index 5fc76bc25e6..58875c0496a 100644 --- a/source/blender/draw/intern/draw_shader_shared.h +++ b/source/blender/draw/intern/draw_shader_shared.h @@ -36,16 +36,19 @@ struct ViewInfos { }; BLI_STATIC_ASSERT_ALIGN(ViewInfos, 16) +/* Do not override old definitions if the shader uses this header but not shader info. */ +#ifdef USE_GPU_SHADER_CREATE_INFO /* TODO(@fclem): Mass rename. */ -#define ViewProjectionMatrix drw_view.persmat -#define ViewProjectionMatrixInverse drw_view.persinv -#define ViewMatrix drw_view.viewmat -#define ViewMatrixInverse drw_view.viewinv -#define ProjectionMatrix drw_view.winmat -#define ProjectionMatrixInverse drw_view.wininv -#define clipPlanes drw_view.clip_planes -#define ViewVecs drw_view.viewvecs -#define CameraTexCoFactors drw_view.viewcamtexcofac +# define ViewProjectionMatrix drw_view.persmat +# define ViewProjectionMatrixInverse drw_view.persinv +# define ViewMatrix drw_view.viewmat +# define ViewMatrixInverse drw_view.viewinv +# define ProjectionMatrix drw_view.winmat +# define ProjectionMatrixInverse drw_view.wininv +# define clipPlanes drw_view.clip_planes +# define ViewVecs drw_view.viewvecs +# define CameraTexCoFactors drw_view.viewcamtexcofac +#endif struct ObjectMatrices { float4x4 drw_modelMatrix; diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_orco.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_orco.cc index d4c801ab066..ed1a0ccd178 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_orco.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_orco.cc @@ -7,6 +7,8 @@ #include "extract_mesh.h" +#include "draw_subdivision.h" + namespace blender::draw { /* ---------------------------------------------------------------------- */ @@ -77,12 +79,77 @@ static void extract_orco_iter_poly_mesh(const MeshRenderData *mr, } } +static void extract_orco_init_subdiv(const DRWSubdivCache *subdiv_cache, + const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buffer, + void *UNUSED(data)) +{ + static GPUVertFormat format = {0}; + if (format.attr_len == 0) { + /* FIXME(fclem): We use the last component as a way to differentiate from generic vertex + * attributes. This is a substantial waste of video-ram and should be done another way. + * Unfortunately, at the time of writing, I did not found any other "non disruptive" + * alternative. */ + GPU_vertformat_attr_add(&format, "orco", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); + } + + GPUVertBuf *dst_buffer = static_cast<GPUVertBuf *>(buffer); + GPU_vertbuf_init_build_on_device(dst_buffer, &format, subdiv_cache->num_subdiv_loops); + + GPUVertBuf *coarse_vbo = GPU_vertbuf_calloc(); + /* Dynamic as we upload and interpolate layers one at a time. */ + GPU_vertbuf_init_with_format_ex(coarse_vbo, &format, GPU_USAGE_DYNAMIC); + GPU_vertbuf_data_alloc(coarse_vbo, mr->loop_len); + + float(*coarse_vbo_data)[4] = static_cast<float(*)[4]>(GPU_vertbuf_get_data(coarse_vbo)); + + CustomData *cd_vdata = &mr->me->vdata; + float(*orco)[3] = static_cast<float(*)[3]>(CustomData_get_layer(cd_vdata, CD_ORCO)); + + if (mr->extract_type == MR_EXTRACT_MESH) { + const MLoop *mloop = mr->mloop; + const MPoly *mp = mr->mpoly; + + int ml_index = 0; + for (int i = 0; i < mr->poly_len; i++, mp++) { + const MLoop *ml = &mloop[mp->loopstart]; + + for (int j = 0; j < mp->totloop; j++, ml++, ml_index++) { + float *loop_orco = coarse_vbo_data[ml_index]; + copy_v3_v3(loop_orco, orco[ml->v]); + loop_orco[3] = 0.0; /* Tag as not a generic attribute. */ + } + } + } + else { + BMIter iter; + BMFace *f; + BM_ITER_MESH (f, &iter, mr->bm, BM_FACES_OF_MESH) { + BMLoop *l_iter; + BMLoop *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); + float *loop_orco = coarse_vbo_data[l_index]; + copy_v3_v3(loop_orco, orco[BM_elem_index_get(l_iter->v)]); + loop_orco[3] = 0.0; /* Tag as not a generic attribute. */ + } while ((l_iter = l_iter->next) != l_first); + } + } + + draw_subdiv_interp_custom_data(subdiv_cache, coarse_vbo, dst_buffer, 4, 0, false); + + GPU_vertbuf_discard(coarse_vbo); +} + constexpr MeshExtract create_extractor_orco() { MeshExtract extractor = {nullptr}; extractor.init = extract_orco_init; extractor.iter_poly_bm = extract_orco_iter_poly_bm; extractor.iter_poly_mesh = extract_orco_iter_poly_mesh; + extractor.init_subdiv = extract_orco_init_subdiv; extractor.data_type = MR_DATA_NONE; extractor.data_size = sizeof(MeshExtract_Orco_Data); extractor.use_threading = true; diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_tan.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_tan.cc index 225d1676151..25f78d68914 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_tan.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_tan.cc @@ -317,7 +317,7 @@ static void extract_tan_init_subdiv(const DRWSubdivCache *subdiv_cache, GPU_vertbuf_tag_dirty(coarse_vbo); /* Include stride in offset. */ const int dst_offset = (int)subdiv_cache->num_subdiv_loops * 4 * pack_layer_index++; - draw_subdiv_interp_custom_data(subdiv_cache, coarse_vbo, dst_buffer, 4, dst_offset, true); + draw_subdiv_interp_custom_data(subdiv_cache, coarse_vbo, dst_buffer, 4, dst_offset, false); } CustomData_free(&loop_data, mr->loop_len); 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 97e5386b14d..d52226a4c90 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 @@ -143,7 +143,7 @@ static void extract_vcol_init(const MeshRenderData *mr, 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; - Mesh me_query = {0}; + Mesh me_query = blender::dna::shallow_zero_initialize(); BKE_id_attribute_copy_domains_temp( ID_ME, cd_vdata, nullptr, cd_ldata, nullptr, nullptr, &me_query.id); @@ -256,7 +256,7 @@ static void extract_vcol_init_subdiv(const DRWSubdivCache *subdiv_cache, const CustomData *cd_ldata = extract_bmesh ? &coarse_mesh->edit_mesh->bm->ldata : &coarse_mesh->ldata; - Mesh me_query = *coarse_mesh; + Mesh me_query = blender::dna::shallow_copy(*coarse_mesh); BKE_id_attribute_copy_domains_temp( ID_ME, cd_vdata, nullptr, cd_ldata, nullptr, nullptr, &me_query.id); diff --git a/source/blender/draw/intern/shaders/common_attribute_lib.glsl b/source/blender/draw/intern/shaders/common_attribute_lib.glsl new file mode 100644 index 00000000000..99db2929a13 --- /dev/null +++ b/source/blender/draw/intern/shaders/common_attribute_lib.glsl @@ -0,0 +1,21 @@ + +/* Prototype of functions to implement to load attributes data. + * Implementation changes based on object data type. */ + +vec3 attr_load_orco(vec4 orco); +vec4 attr_load_tangent(vec4 tangent); +vec3 attr_load_uv(vec3 uv); +vec4 attr_load_color(vec4 color); +vec4 attr_load_vec4(vec4 attr); +vec3 attr_load_vec3(vec3 attr); +vec2 attr_load_vec2(vec2 attr); +float attr_load_float(float attr); + +vec3 attr_load_orco(samplerBuffer orco); +vec4 attr_load_tangent(samplerBuffer tangent); +vec3 attr_load_uv(samplerBuffer uv); +vec4 attr_load_color(samplerBuffer color); +vec4 attr_load_vec4(samplerBuffer attr); +vec3 attr_load_vec3(samplerBuffer attr); +vec2 attr_load_vec2(samplerBuffer attr); +float attr_load_float(samplerBuffer attr); diff --git a/source/blender/draw/intern/shaders/common_hair_lib.glsl b/source/blender/draw/intern/shaders/common_hair_lib.glsl index 6a8f1132e1b..ff52b483d77 100644 --- a/source/blender/draw/intern/shaders/common_hair_lib.glsl +++ b/source/blender/draw/intern/shaders/common_hair_lib.glsl @@ -211,6 +211,11 @@ void hair_get_pos_tan_binor_time(bool is_persp, wpos += wbinor * thick_time * scale; } + else { + /* Note: Ensures 'hairThickTime' is initialised - + * avoids undefined behaviour on certain macOS configurations. */ + thick_time = 0.0; + } } float hair_get_customdata_float(const samplerBuffer cd_buf) diff --git a/source/blender/draw/intern/shaders/common_hair_refine_vert.glsl b/source/blender/draw/intern/shaders/common_hair_refine_vert.glsl index 371d43827b9..2eccae5bceb 100644 --- a/source/blender/draw/intern/shaders/common_hair_refine_vert.glsl +++ b/source/blender/draw/intern/shaders/common_hair_refine_vert.glsl @@ -18,7 +18,7 @@ void main(void) vec4 weights = hair_get_weights_cardinal(interp_time); finalColor = hair_interp_data(data0, data1, data2, data3, weights); -#ifdef TF_WORKAROUND +#if defined(TF_WORKAROUND) int id = gl_VertexID - idOffset; gl_Position.x = ((float(id % targetWidth) + 0.5) / float(targetWidth)) * 2.0 - 1.0; gl_Position.y = ((float(id / targetWidth) + 0.5) / float(targetHeight)) * 2.0 - 1.0; @@ -26,5 +26,10 @@ void main(void) gl_Position.w = 1.0; gl_PointSize = 1.0; +#else +# ifdef GPU_METAL + /* Metal still expects an output position for TF shaders. */ + gl_Position = vec4(0.0, 0.0, 0.0, 1.0); +# endif #endif } diff --git a/source/blender/draw/intern/shaders/common_math_lib.glsl b/source/blender/draw/intern/shaders/common_math_lib.glsl index 4d0ffaeb40f..1ac26c91b93 100644 --- a/source/blender/draw/intern/shaders/common_math_lib.glsl +++ b/source/blender/draw/intern/shaders/common_math_lib.glsl @@ -1,4 +1,8 @@ +/* WORKAROUND: to guard against double include in EEVEE. */ +#ifndef COMMON_MATH_LIB_GLSL +#define COMMON_MATH_LIB_GLSL + /* ---------------------------------------------------------------------- */ /** \name Common Math Utilities * \{ */ @@ -276,3 +280,5 @@ vec3 hue_gradient(float t) vec3 p = abs(fract(t + vec3(1.0, 2.0 / 3.0, 1.0 / 3.0)) * 6.0 - 3.0); return (clamp(p - 1.0, 0.0, 1.0)); } + +#endif /* COMMON_MATH_LIB_GLSL */ diff --git a/source/blender/draw/intern/shaders/common_smaa_lib.glsl b/source/blender/draw/intern/shaders/common_smaa_lib.glsl index 73f65fb0799..dbc4c998b34 100644 --- a/source/blender/draw/intern/shaders/common_smaa_lib.glsl +++ b/source/blender/draw/intern/shaders/common_smaa_lib.glsl @@ -569,7 +569,7 @@ SamplerState PointSampler # define SMAAGather(tex, coord) tex.Gather(LinearSampler, coord, 0) # endif #endif -#if defined(SMAA_GLSL_3) || defined(SMAA_GLSL_4) +#if defined(SMAA_GLSL_3) || defined(SMAA_GLSL_4) || defined(GPU_METAL) # define SMAATexture2D(tex) sampler2D tex # define SMAATexturePass2D(tex) tex # define SMAASampleLevelZero(tex, coord) textureLod(tex, coord, 0.0) @@ -641,14 +641,14 @@ float2 SMAACalculatePredicatedThreshold(float2 texcoord, */ void SMAAMovc(bool2 cond, inout float2 variable, float2 value) { - SMAA_FLATTEN if (cond.x) variable.x = value.x; - SMAA_FLATTEN if (cond.y) variable.y = value.y; + /* Use select function (select(genType A, genType B, genBType cond)). */ + variable = select(variable, value, cond); } void SMAAMovc(bool4 cond, inout float4 variable, float4 value) { - SMAAMovc(cond.xy, variable.xy, value.xy); - SMAAMovc(cond.zw, variable.zw, value.zw); + /* Use select function (select(genType A, genType B, genBType cond)). */ + variable = select(variable, value, cond); } #if SMAA_INCLUDE_VS @@ -1281,7 +1281,15 @@ float4 SMAABlendingWeightCalculationPS(float2 texcoord, // Fix corners: coords.y = texcoord.y; - SMAADetectHorizontalCornerPattern(SMAATexturePass2D(edgesTex), weights.rg, coords.xyzy, d); + +# ifdef GPU_METAL + /* Partial vector references are unsupported in MSL. */ + vec2 _weights = weights.rg; + SMAADetectHorizontalCornerPattern(SMAATexturePass2D(edgesTex), _weights, coords.xyzy, d); + weights.rg = _weights; +# else + SMAADetectHorizontalCornerPattern(SMAATexturePass2D(edgesTex), weights.rg, coords.xyzy, d); +# endif # if !defined(SMAA_DISABLE_DIAG_DETECTION) } @@ -1324,7 +1332,15 @@ float4 SMAABlendingWeightCalculationPS(float2 texcoord, // Fix corners: coords.x = texcoord.x; + +# ifdef GPU_METAL + /* Partial vector references are unsupported in MSL. */ + vec2 _weights = weights.ba; + SMAADetectVerticalCornerPattern(SMAATexturePass2D(edgesTex), _weights, coords.xyxz, d); + weights.ba = _weights; +# else SMAADetectVerticalCornerPattern(SMAATexturePass2D(edgesTex), weights.ba, coords.xyxz, d); +# endif } return weights; diff --git a/source/blender/draw/intern/shaders/common_view_lib.glsl b/source/blender/draw/intern/shaders/common_view_lib.glsl index a2b8cb4bbd6..4086162a530 100644 --- a/source/blender/draw/intern/shaders/common_view_lib.glsl +++ b/source/blender/draw/intern/shaders/common_view_lib.glsl @@ -1,5 +1,10 @@ + +/* WORKAROUND: to guard against double include in EEVEE. */ +#ifndef COMMON_VIEW_LIB_GLSL +#define COMMON_VIEW_LIB_GLSL + /* Temporary until we fully make the switch. */ -#ifndef USE_GPU_SHADER_CREATE_INFO +#if !defined(USE_GPU_SHADER_CREATE_INFO) # define DRW_RESOURCE_CHUNK_LEN 512 @@ -37,7 +42,10 @@ layout(std140) uniform viewBlock #define cameraForward ViewMatrixInverse[2].xyz #define cameraPos ViewMatrixInverse[3].xyz -#define cameraVec(P) ((ProjectionMatrix[3][3] == 0.0) ? normalize(cameraPos - P) : cameraForward) +vec3 cameraVec(vec3 P) +{ + return ((ProjectionMatrix[3][3] == 0.0) ? normalize(cameraPos - P) : cameraForward); +} #define viewCameraVec(vP) ((ProjectionMatrix[3][3] == 0.0) ? normalize(-vP) : vec3(0.0, 0.0, 1.0)) #ifdef world_clip_planes_calc_clip_distance @@ -92,14 +100,14 @@ vec4 pack_line_data(vec2 frag_co, vec2 edge_start, vec2 edge_pos) } /* Temporary until we fully make the switch. */ -#ifndef DRW_SHADER_SHARED_H +#ifndef USE_GPU_SHADER_CREATE_INFO uniform int drw_resourceChunk; -#endif /* DRW_SHADER_SHARED_H */ +#endif /* USE_GPU_SHADER_CREATE_INFO */ #ifdef GPU_VERTEX_SHADER /* Temporary until we fully make the switch. */ -# ifndef DRW_SHADER_SHARED_H +# ifndef USE_GPU_SHADER_CREATE_INFO /* clang-format off */ # if defined(IN_PLACE_INSTANCES) || defined(INSTANCED_ATTR) || defined(DRW_LEGACY_MODEL_MATRIX) || defined(GPU_DEPRECATED_AMD_DRIVER) @@ -121,7 +129,10 @@ uniform int drw_ResourceID; /* Use this to declare and pass the value if * the fragment shader uses the resource_id. */ -# ifdef USE_GEOMETRY_SHADER +# if defined(EEVEE_GENERATED_INTERFACE) +# define RESOURCE_ID_VARYING +# define PASS_RESOURCE_ID resourceIDFrag = resource_id; +# elif defined(USE_GEOMETRY_SHADER) # define RESOURCE_ID_VARYING flat out int resourceIDGeom; # define PASS_RESOURCE_ID resourceIDGeom = resource_id; # else @@ -129,12 +140,12 @@ uniform int drw_ResourceID; # define PASS_RESOURCE_ID resourceIDFrag = resource_id; # endif -# endif /* DRW_SHADER_SHARED_H */ +# endif /* USE_GPU_SHADER_CREATE_INFO */ #endif /* GPU_VERTEX_SHADER */ /* Temporary until we fully make the switch. */ -#ifdef DRW_SHADER_SHARED_H +#ifdef USE_GPU_SHADER_CREATE_INFO /* TODO(fclem): Rename PASS_RESOURCE_ID to DRW_RESOURCE_ID_VARYING_SET */ # if defined(UNIFORM_RESOURCE_ID) # define resource_id drw_ResourceID @@ -159,16 +170,23 @@ uniform int drw_ResourceID; /* If used in a fragment / geometry shader, we pass * resource_id as varying. */ # ifdef GPU_GEOMETRY_SHADER -# define RESOURCE_ID_VARYING \ - flat out int resourceIDFrag; \ - flat in int resourceIDGeom[]; +/* TODO(fclem): Remove. This is getting ridiculous. */ +# if !defined(EEVEE_GENERATED_INTERFACE) +# define RESOURCE_ID_VARYING \ + flat out int resourceIDFrag; \ + flat in int resourceIDGeom[]; +# else +# define RESOURCE_ID_VARYING +# endif # define resource_id resourceIDGeom # define PASS_RESOURCE_ID resourceIDFrag = resource_id[0]; # endif -# ifdef GPU_FRAGMENT_SHADER +# if defined(GPU_FRAGMENT_SHADER) +# if !defined(EEVEE_GENERATED_INTERFACE) flat in int resourceIDFrag; +# endif # define resource_id resourceIDFrag # endif #endif @@ -185,7 +203,9 @@ struct ObjectMatrices { mat4 drw_modelMatrix; mat4 drw_modelMatrixInverse; }; +# endif /* DRW_SHADER_SHARED_H */ +# ifndef USE_GPU_SHADER_CREATE_INFO layout(std140) uniform modelBlock { ObjectMatrices drw_matrices[DRW_RESOURCE_CHUNK_LEN]; @@ -193,24 +213,24 @@ layout(std140) uniform modelBlock # define ModelMatrix (drw_matrices[resource_id].drw_modelMatrix) # define ModelMatrixInverse (drw_matrices[resource_id].drw_modelMatrixInverse) -# endif /* DRW_SHADER_SHARED_H */ +# endif /* USE_GPU_SHADER_CREATE_INFO */ #else /* GPU_INTEL */ /* Temporary until we fully make the switch. */ -# ifndef DRW_SHADER_SHARED_H +# ifndef USE_GPU_SHADER_CREATE_INFO /* Intel GPU seems to suffer performance impact when the model matrix is in UBO storage. * So for now we just force using the legacy path. */ /* Note that this is also a workaround of a problem on osx (amd or nvidia) * and older amd driver on windows. */ uniform mat4 ModelMatrix; uniform mat4 ModelMatrixInverse; -# endif /* DRW_SHADER_SHARED_H */ +# endif /* USE_GPU_SHADER_CREATE_INFO */ #endif /* Temporary until we fully make the switch. */ -#ifndef DRW_SHADER_SHARED_H +#ifndef USE_GPU_SHADER_CREATE_INFO # define resource_handle (drw_resourceChunk * DRW_RESOURCE_CHUNK_LEN + resource_id) #endif @@ -337,3 +357,5 @@ vec3 get_view_vector_from_screen_uv(vec2 uv) return vec3(0.0, 0.0, 1.0); } } + +#endif /* COMMON_VIEW_LIB_GLSL */ diff --git a/source/blender/editors/animation/anim_channels_defines.c b/source/blender/editors/animation/anim_channels_defines.c index ead189c0389..eb7f8e8ad83 100644 --- a/source/blender/editors/animation/anim_channels_defines.c +++ b/source/blender/editors/animation/anim_channels_defines.c @@ -2796,7 +2796,7 @@ static bAnimChannelType ACF_DSSPK = { acf_dsspk_setting_ptr, /* pointer for setting */ }; -/* Hair Expander ------------------------------------------- */ +/* Curves Expander ------------------------------------------- */ /* TODO: just get this from RNA? */ static int acf_dscurves_icon(bAnimListElem *UNUSED(ale)) @@ -2859,7 +2859,7 @@ static void *acf_dscurves_setting_ptr(bAnimListElem *ale, } /** Curves expander type define. */ -static bAnimChannelType ACF_DSHAIR = { +static bAnimChannelType ACF_DSCURVES = { "Curves Expander", /* type name */ ACHANNEL_ROLE_EXPANDER, /* role */ @@ -4129,7 +4129,7 @@ static void ANIM_init_channel_typeinfo_data(void) animchannelTypeInfo[type++] = &ACF_DSSPK; /* Speaker Channel */ animchannelTypeInfo[type++] = &ACF_DSGPENCIL; /* GreasePencil Channel */ animchannelTypeInfo[type++] = &ACF_DSMCLIP; /* MovieClip Channel */ - animchannelTypeInfo[type++] = &ACF_DSHAIR; /* Hair Channel */ + animchannelTypeInfo[type++] = &ACF_DSCURVES; /* Curves Channel */ animchannelTypeInfo[type++] = &ACF_DSPOINTCLOUD; /* PointCloud Channel */ animchannelTypeInfo[type++] = &ACF_DSVOLUME; /* Volume Channel */ animchannelTypeInfo[type++] = &ACF_DSSIMULATION; /* Simulation Channel */ diff --git a/source/blender/editors/animation/anim_markers.c b/source/blender/editors/animation/anim_markers.c index d7bbc0eab2b..1a3ab100768 100644 --- a/source/blender/editors/animation/anim_markers.c +++ b/source/blender/editors/animation/anim_markers.c @@ -1465,6 +1465,83 @@ static void MARKER_OT_select_all(wmOperatorType *ot) /** \} */ /* -------------------------------------------------------------------- */ +/** \name Select Left/Right of Frame + * \{ */ + +typedef enum eMarkers_LeftRightSelect_Mode { + MARKERS_LRSEL_LEFT = 0, + MARKERS_LRSEL_RIGHT, +} eMarkers_LeftRightSelect_Mode; + +static const EnumPropertyItem prop_markers_select_leftright_modes[] = { + {MARKERS_LRSEL_LEFT, "LEFT", 0, "Before Current Frame", ""}, + {MARKERS_LRSEL_RIGHT, "RIGHT", 0, "After Current Frame", ""}, + {0, NULL, 0, NULL, NULL}, +}; + +static void ED_markers_select_leftright(bAnimContext *ac, + const eMarkers_LeftRightSelect_Mode mode, + const bool extend) +{ + ListBase *markers = ac->markers; + Scene *scene = ac->scene; + + if (markers == NULL) { + return; + } + + if (!extend) { + deselect_markers(markers); + } + + LISTBASE_FOREACH (TimeMarker *, marker, markers) { + if ((mode == MARKERS_LRSEL_LEFT && marker->frame <= CFRA) || + (mode == MARKERS_LRSEL_RIGHT && marker->frame >= CFRA)) { + marker->flag |= SELECT; + } + } +} + +static int ed_marker_select_leftright_exec(bContext *C, wmOperator *op) +{ + const eMarkers_LeftRightSelect_Mode mode = RNA_enum_get(op->ptr, "mode"); + const bool extend = RNA_boolean_get(op->ptr, "extend"); + + bAnimContext ac; + if (ANIM_animdata_get_context(C, &ac) == 0) { + return OPERATOR_CANCELLED; + } + + ED_markers_select_leftright(&ac, mode, extend); + + WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_SELECTED, NULL); + + return OPERATOR_FINISHED; +} + +static void MARKER_OT_select_leftright(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Select Markers Before/After Current Frame"; + ot->description = "Select markers on and left/right of the current frame"; + ot->idname = "MARKER_OT_select_leftright"; + + /* api callbacks */ + ot->exec = ed_marker_select_leftright_exec; + ot->poll = ed_markers_poll_markers_exist; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* rna storage */ + RNA_def_enum( + ot->srna, "mode", prop_markers_select_leftright_modes, MARKERS_LRSEL_LEFT, "mode", "Mode"); + RNA_def_boolean(ot->srna, "extend", false, "extend", "Extend"); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name Remove Marker * * Remove selected time-markers. @@ -1735,6 +1812,7 @@ void ED_operatortypes_marker(void) WM_operatortype_append(MARKER_OT_select); WM_operatortype_append(MARKER_OT_select_box); WM_operatortype_append(MARKER_OT_select_all); + WM_operatortype_append(MARKER_OT_select_leftright); WM_operatortype_append(MARKER_OT_delete); WM_operatortype_append(MARKER_OT_rename); WM_operatortype_append(MARKER_OT_make_links_scene); diff --git a/source/blender/editors/animation/keyframing.c b/source/blender/editors/animation/keyframing.c index 8d079641e9f..5b742ddf272 100644 --- a/source/blender/editors/animation/keyframing.c +++ b/source/blender/editors/animation/keyframing.c @@ -14,6 +14,7 @@ #include "MEM_guardedalloc.h" #include "BLI_blenlib.h" +#include "BLI_dynstr.h" #include "BLI_math.h" #include "BLI_utildefines.h" @@ -1109,9 +1110,62 @@ static float *visualkey_get_values( /* ------------------------- Insert Key API ------------------------- */ +/* Check indices that were intended to be remapped and report any failed remaps. */ +static void get_keyframe_values_create_reports(ReportList *reports, + PointerRNA ptr, + PropertyRNA *prop, + const int index, + const int count, + const bool force_all, + const BLI_bitmap *successful_remaps) +{ + + DynStr *ds_failed_indices = BLI_dynstr_new(); + + int total_failed = 0; + for (int i = 0; i < count; i++) { + const bool cur_index_evaluated = ELEM(index, i, -1) || force_all; + if (!cur_index_evaluated) { + /* values[i] was never intended to be remapped. */ + continue; + } + + if (BLI_BITMAP_TEST_BOOL(successful_remaps, i)) { + /* values[i] succesfully remapped. */ + continue; + } + + total_failed++; + /* Report that values[i] were intended to be remapped but failed remapping process. */ + BLI_dynstr_appendf(ds_failed_indices, "%d, ", i); + } + + if (total_failed == 0) { + BLI_dynstr_free(ds_failed_indices); + return; + } + + char *str_failed_indices = BLI_dynstr_get_cstring(ds_failed_indices); + BLI_dynstr_free(ds_failed_indices); + + BKE_reportf(reports, + RPT_WARNING, + "Could not insert %i keyframe(s) due to zero NLA influence, base value, or value " + "remapping failed: %s.%s for indices [%s]", + total_failed, + ptr.owner_id->name, + RNA_property_ui_name(prop), + str_failed_indices); + + MEM_freeN(str_failed_indices); +} + /** * Retrieve current property values to keyframe, * possibly applying NLA correction when necessary. + * + * \param r_successful_remaps: Enables bits for indices which are both intended to be remapped and + * were successfully remapped. Bitmap allocated so it must be freed afterward. */ static float *get_keyframe_values(ReportList *reports, PointerRNA ptr, @@ -1121,8 +1175,10 @@ static float *get_keyframe_values(ReportList *reports, eInsertKeyFlags flag, float *buffer, int buffer_size, + const struct AnimationEvalContext *anim_eval_context, int *r_count, - bool *r_force_all) + bool *r_force_all, + BLI_bitmap **r_successful_remaps) { float *values; @@ -1138,17 +1194,25 @@ static float *get_keyframe_values(ReportList *reports, values = setting_get_rna_values(&ptr, prop, buffer, buffer_size, r_count); } - /* adjust the value for NLA factors */ - if (!BKE_animsys_nla_remap_keyframe_values( - nla_context, &ptr, prop, values, *r_count, index, r_force_all)) { - BKE_report( - reports, RPT_ERROR, "Could not insert keyframe due to zero NLA influence or base value"); + *r_successful_remaps = BLI_BITMAP_NEW(*r_count, __func__); - if (values != buffer) { - MEM_freeN(values); - } - return NULL; - } + /* adjust the value for NLA factors */ + BKE_animsys_nla_remap_keyframe_values(nla_context, + &ptr, + prop, + values, + *r_count, + index, + anim_eval_context, + r_force_all, + *r_successful_remaps); + get_keyframe_values_create_reports(reports, + ptr, + prop, + index, + *r_count, + r_force_all ? *r_force_all : false, + *r_successful_remaps); return values; } @@ -1284,6 +1348,7 @@ bool insert_keyframe_direct(ReportList *reports, int value_count; int index = fcu->array_index; + BLI_bitmap *successful_remaps = NULL; float *values = get_keyframe_values(reports, ptr, prop, @@ -1292,13 +1357,10 @@ bool insert_keyframe_direct(ReportList *reports, flag, value_buffer, RNA_MAX_ARRAY_LENGTH, + anim_eval_context, &value_count, - NULL); - - if (values == NULL) { - /* This happens if NLA rejects this insertion. */ - return false; - } + NULL, + &successful_remaps); if (index >= 0 && index < value_count) { curval = values[index]; @@ -1308,6 +1370,14 @@ bool insert_keyframe_direct(ReportList *reports, MEM_freeN(values); } + const bool curval_valid = BLI_BITMAP_TEST_BOOL(successful_remaps, index); + MEM_freeN(successful_remaps); + + /* This happens if NLA rejects this insertion. */ + if (!curval_valid) { + return false; + } + return insert_keyframe_value(reports, &ptr, prop, fcu, anim_eval_context, curval, keytype, flag); } @@ -1461,6 +1531,7 @@ int insert_keyframe(Main *bmain, int value_count; bool force_all; + BLI_bitmap *successful_remaps = NULL; float *values = get_keyframe_values(reports, ptr, prop, @@ -1469,77 +1540,72 @@ int insert_keyframe(Main *bmain, flag, value_buffer, RNA_MAX_ARRAY_LENGTH, + anim_eval_context, &value_count, - &force_all); + &force_all, + &successful_remaps); - if (values != NULL) { - /* Key the entire array. */ - if (array_index == -1 || force_all) { - /* In force mode, if any of the curves succeeds, drop the replace mode and restart. */ - if (force_all && (flag & (INSERTKEY_REPLACE | INSERTKEY_AVAILABLE)) != 0) { - int exclude = -1; + /* Key the entire array. */ + if (array_index == -1 || force_all) { + /* In force mode, if any of the curves succeeds, drop the replace mode and restart. */ + if (force_all && (flag & (INSERTKEY_REPLACE | INSERTKEY_AVAILABLE)) != 0) { + int exclude = -1; - for (array_index = 0; array_index < value_count; array_index++) { - if (insert_keyframe_fcurve_value(bmain, - reports, - &ptr, - prop, - act, - group, - rna_path, - array_index, - &remapped_context, - values[array_index], - keytype, - flag)) { - ret++; - exclude = array_index; - break; - } + for (array_index = 0; array_index < value_count; array_index++) { + if (!BLI_BITMAP_TEST_BOOL(successful_remaps, array_index)) { + continue; } - if (exclude != -1) { - flag &= ~(INSERTKEY_REPLACE | INSERTKEY_AVAILABLE); - - for (array_index = 0; array_index < value_count; array_index++) { - if (array_index != exclude) { - ret += insert_keyframe_fcurve_value(bmain, - reports, - &ptr, - prop, - act, - group, - rna_path, - array_index, - &remapped_context, - values[array_index], - keytype, - flag); - } - } + if (insert_keyframe_fcurve_value(bmain, + reports, + &ptr, + prop, + act, + group, + rna_path, + array_index, + &remapped_context, + values[array_index], + keytype, + flag)) { + ret++; + exclude = array_index; + break; } } - /* Simply insert all channels. */ - else { + + if (exclude != -1) { + flag &= ~(INSERTKEY_REPLACE | INSERTKEY_AVAILABLE); + for (array_index = 0; array_index < value_count; array_index++) { - ret += insert_keyframe_fcurve_value(bmain, - reports, - &ptr, - prop, - act, - group, - rna_path, - array_index, - &remapped_context, - values[array_index], - keytype, - flag); + if (!BLI_BITMAP_TEST_BOOL(successful_remaps, array_index)) { + continue; + } + + if (array_index != exclude) { + ret += insert_keyframe_fcurve_value(bmain, + reports, + &ptr, + prop, + act, + group, + rna_path, + array_index, + &remapped_context, + values[array_index], + keytype, + flag); + } } } } - /* Key a single index. */ + /* Simply insert all channels. */ else { - if (array_index >= 0 && array_index < value_count) { + for (array_index = 0; array_index < value_count; array_index++) { + if (!BLI_BITMAP_TEST_BOOL(successful_remaps, array_index)) { + continue; + } + ret += insert_keyframe_fcurve_value(bmain, reports, &ptr, @@ -1554,12 +1620,31 @@ int insert_keyframe(Main *bmain, flag); } } - - if (values != value_buffer) { - MEM_freeN(values); + } + /* Key a single index. */ + else { + if (array_index >= 0 && array_index < value_count && + BLI_BITMAP_TEST_BOOL(successful_remaps, array_index)) { + ret += insert_keyframe_fcurve_value(bmain, + reports, + &ptr, + prop, + act, + group, + rna_path, + array_index, + &remapped_context, + values[array_index], + keytype, + flag); } } + if (values != value_buffer) { + MEM_freeN(values); + } + + MEM_freeN(successful_remaps); BKE_animsys_free_nla_keyframing_context_cache(&tmp_nla_cache); if (ret) { @@ -1979,23 +2064,57 @@ static int insert_key_menu_invoke(bContext *C, wmOperator *op, const wmEvent *UN { Scene *scene = CTX_data_scene(C); - /* if prompting or no active Keying Set, show the menu */ - if ((scene->active_keyingset == 0) || RNA_boolean_get(op->ptr, "always_prompt")) { - uiPopupMenu *pup; - uiLayout *layout; - - /* call the menu, which will call this operator again, hence the canceled */ - pup = UI_popup_menu_begin(C, WM_operatortype_name(op->type, op->ptr), ICON_NONE); - layout = UI_popup_menu_layout(pup); - uiItemsEnumO(layout, "ANIM_OT_keyframe_insert_menu", "type"); - UI_popup_menu_end(C, pup); + /* When there is an active keying set and no request to prompt, keyframe immediately. */ + if ((scene->active_keyingset != 0) && !RNA_boolean_get(op->ptr, "always_prompt")) { + /* Just call the exec() on the active keying-set. */ + RNA_enum_set(op->ptr, "type", 0); + return op->type->exec(C, op); + } + + /* Show a menu listing all keying-sets, the enum is expanded here to make use of the + * operator that accesses the keying-set by name. This is important for the ability + * to assign shortcuts to arbitrarily named keying sets. See T89560. + * These menu items perform the key-frame insertion (not this operator) + * hence the #OPERATOR_INTERFACE return. */ + uiPopupMenu *pup = UI_popup_menu_begin(C, WM_operatortype_name(op->type, op->ptr), ICON_NONE); + uiLayout *layout = UI_popup_menu_layout(pup); + + /* Even though `ANIM_OT_keyframe_insert_menu` can show a menu in one line, + * prefer `ANIM_OT_keyframe_insert_by_name` so users can bind keys to specific + * keying sets by name in the key-map instead of the index which isn't stable. */ + PropertyRNA *prop = RNA_struct_find_property(op->ptr, "type"); + const EnumPropertyItem *item_array = NULL; + int totitem; + bool free; + + RNA_property_enum_items_gettexted(C, op->ptr, prop, &item_array, &totitem, &free); + + for (int i = 0; i < totitem; i++) { + const EnumPropertyItem *item = &item_array[i]; + if (item->identifier[0] != '\0') { + uiItemStringO(layout, + item->name, + item->icon, + "ANIM_OT_keyframe_insert_by_name", + "type", + item->identifier); + } + else { + /* This enum shouldn't contain headings, assert there are none. + * NOTE: If in the future the enum includes them, additional layout code can be + * added to show them - although that doesn't seem likely. */ + BLI_assert(item->name == NULL); + uiItemS(layout); + } + } - return OPERATOR_INTERFACE; + if (free) { + MEM_freeN((void *)item_array); } - /* just call the exec() on the active keyingset */ - RNA_enum_set(op->ptr, "type", 0); - return op->type->exec(C, op); + UI_popup_menu_end(C, pup); + + return OPERATOR_INTERFACE; } void ANIM_OT_keyframe_insert_menu(wmOperatorType *ot) diff --git a/source/blender/editors/curves/intern/curves_ops.cc b/source/blender/editors/curves/intern/curves_ops.cc index 3a4dcd4f5f6..7d07c211542 100644 --- a/source/blender/editors/curves/intern/curves_ops.cc +++ b/source/blender/editors/curves/intern/curves_ops.cc @@ -121,147 +121,171 @@ static float4 compute_mface_weights_for_position(const Mesh &mesh, return mface_weights; } -static int curves_convert_to_particle_system_exec(bContext *C, wmOperator *UNUSED(op)) +static void try_convert_single_object(Object &curves_ob, + Main &bmain, + Scene &scene, + bool *r_could_not_convert_some_curves) { - Main *bmain = CTX_data_main(C); - Scene *scene = CTX_data_scene(C); - - CTX_DATA_BEGIN (C, Object *, curves_ob, selected_objects) { - if (curves_ob->type != OB_CURVES) { - continue; - } - Curves &curves_id = *static_cast<Curves *>(curves_ob->data); - CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry); - if (curves_id.surface == nullptr) { - continue; - } - Object &surface_ob = *curves_id.surface; - if (surface_ob.type != OB_MESH) { - continue; + if (curves_ob.type != OB_CURVES) { + return; + } + Curves &curves_id = *static_cast<Curves *>(curves_ob.data); + CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry); + if (curves_id.surface == nullptr) { + return; + } + Object &surface_ob = *curves_id.surface; + if (surface_ob.type != OB_MESH) { + return; + } + Mesh &surface_me = *static_cast<Mesh *>(surface_ob.data); + + const Span<float3> positions_cu = curves.positions(); + const VArray<int> looptri_indices = curves.surface_triangle_indices(); + const Span<MLoopTri> looptris{BKE_mesh_runtime_looptri_ensure(&surface_me), + BKE_mesh_runtime_looptri_len(&surface_me)}; + + /* Find indices of curves that can be transferred to the old hair system. */ + Vector<int> curves_indices_to_transfer; + for (const int curve_i : curves.curves_range()) { + const int looptri_i = looptri_indices[curve_i]; + if (looptri_i >= 0 && looptri_i < looptris.size()) { + curves_indices_to_transfer.append(curve_i); } - Mesh &surface_me = *static_cast<Mesh *>(surface_ob.data); - - const Span<float3> positions_cu = curves.positions(); - const VArray<int> looptri_indices = curves.surface_triangle_indices(); - const Span<MLoopTri> looptris{BKE_mesh_runtime_looptri_ensure(&surface_me), - BKE_mesh_runtime_looptri_len(&surface_me)}; - - /* Find indices of curves that can be transferred to the old hair system. */ - Vector<int> curves_indices_to_transfer; - for (const int curve_i : curves.curves_range()) { - const int looptri_i = looptri_indices[curve_i]; - if (looptri_i >= 0 && looptri_i < looptris.size()) { - curves_indices_to_transfer.append(curve_i); - } + else { + *r_could_not_convert_some_curves = true; } + } - const int hairs_num = curves_indices_to_transfer.size(); - if (hairs_num == 0) { - continue; - } + const int hairs_num = curves_indices_to_transfer.size(); + if (hairs_num == 0) { + return; + } - ParticleSystem *particle_system = nullptr; - LISTBASE_FOREACH (ParticleSystem *, psys, &surface_ob.particlesystem) { - if (STREQ(psys->name, curves_ob->id.name + 2)) { - particle_system = psys; - break; - } - } - if (particle_system == nullptr) { - ParticleSystemModifierData &psmd = *reinterpret_cast<ParticleSystemModifierData *>( - object_add_particle_system(bmain, scene, &surface_ob, curves_ob->id.name + 2)); - particle_system = psmd.psys; + ParticleSystem *particle_system = nullptr; + LISTBASE_FOREACH (ParticleSystem *, psys, &surface_ob.particlesystem) { + if (STREQ(psys->name, curves_ob.id.name + 2)) { + particle_system = psys; + break; } + } + if (particle_system == nullptr) { + ParticleSystemModifierData &psmd = *reinterpret_cast<ParticleSystemModifierData *>( + object_add_particle_system(&bmain, &scene, &surface_ob, curves_ob.id.name + 2)); + particle_system = psmd.psys; + particle_system->part->draw_step = 3; + } + + ParticleSettings &settings = *particle_system->part; - ParticleSettings &settings = *particle_system->part; + psys_free_particles(particle_system); + settings.type = PART_HAIR; + settings.totpart = 0; + psys_changed_type(&surface_ob, particle_system); - psys_free_particles(particle_system); - settings.type = PART_HAIR; - settings.totpart = 0; - psys_changed_type(&surface_ob, particle_system); + MutableSpan<ParticleData> particles{ + static_cast<ParticleData *>(MEM_calloc_arrayN(hairs_num, sizeof(ParticleData), __func__)), + hairs_num}; - MutableSpan<ParticleData> particles{ - static_cast<ParticleData *>(MEM_calloc_arrayN(hairs_num, sizeof(ParticleData), __func__)), - hairs_num}; + /* The old hair system still uses #MFace, so make sure those are available on the mesh. */ + BKE_mesh_tessface_calc(&surface_me); - /* The old hair system still uses #MFace, so make sure those are available on the mesh. */ - BKE_mesh_tessface_calc(&surface_me); + /* Prepare utility data structure to map hair roots to mfaces. */ + const Span<int> mface_to_poly_map{ + static_cast<int *>(CustomData_get_layer(&surface_me.fdata, CD_ORIGINDEX)), + surface_me.totface}; + Array<Vector<int>> poly_to_mface_map(surface_me.totpoly); + for (const int mface_i : mface_to_poly_map.index_range()) { + const int poly_i = mface_to_poly_map[mface_i]; + poly_to_mface_map[poly_i].append(mface_i); + } - /* Prepare utility data structure to map hair roots to mfaces. */ - const Span<int> mface_to_poly_map{ - static_cast<int *>(CustomData_get_layer(&surface_me.fdata, CD_ORIGINDEX)), - surface_me.totface}; - Array<Vector<int>> poly_to_mface_map(surface_me.totpoly); - for (const int mface_i : mface_to_poly_map.index_range()) { - const int poly_i = mface_to_poly_map[mface_i]; - poly_to_mface_map[poly_i].append(mface_i); + /* Prepare transformation matrices. */ + const float4x4 curves_to_world_mat = curves_ob.obmat; + const float4x4 surface_to_world_mat = surface_ob.obmat; + const float4x4 world_to_surface_mat = surface_to_world_mat.inverted(); + const float4x4 curves_to_surface_mat = world_to_surface_mat * curves_to_world_mat; + + for (const int new_hair_i : curves_indices_to_transfer.index_range()) { + const int curve_i = curves_indices_to_transfer[new_hair_i]; + const IndexRange points = curves.points_for_curve(curve_i); + + const int looptri_i = looptri_indices[curve_i]; + const MLoopTri &looptri = looptris[looptri_i]; + const int poly_i = looptri.poly; + + const float3 &root_pos_cu = positions_cu[points.first()]; + const float3 root_pos_su = curves_to_surface_mat * root_pos_cu; + + const int mface_i = find_mface_for_root_position( + surface_me, poly_to_mface_map[poly_i], root_pos_su); + const MFace &mface = surface_me.mface[mface_i]; + + const float4 mface_weights = compute_mface_weights_for_position( + surface_me, mface, root_pos_su); + + ParticleData &particle = particles[new_hair_i]; + const int num_keys = points.size(); + MutableSpan<HairKey> hair_keys{ + static_cast<HairKey *>(MEM_calloc_arrayN(num_keys, sizeof(HairKey), __func__)), num_keys}; + + particle.hair = hair_keys.data(); + particle.totkey = hair_keys.size(); + copy_v4_v4(particle.fuv, mface_weights); + particle.num = mface_i; + /* Not sure if there is a better way to initialize this. */ + particle.num_dmcache = DMCACHE_NOTFOUND; + + float4x4 hair_to_surface_mat; + psys_mat_hair_to_object( + &surface_ob, &surface_me, PART_FROM_FACE, &particle, hair_to_surface_mat.values); + /* In theory, #psys_mat_hair_to_object should handle this, but it doesn't right now. */ + copy_v3_v3(hair_to_surface_mat.values[3], root_pos_su); + const float4x4 surface_to_hair_mat = hair_to_surface_mat.inverted(); + + for (const int key_i : hair_keys.index_range()) { + const float3 &key_pos_cu = positions_cu[points[key_i]]; + const float3 key_pos_su = curves_to_surface_mat * key_pos_cu; + const float3 key_pos_ha = surface_to_hair_mat * key_pos_su; + + HairKey &key = hair_keys[key_i]; + copy_v3_v3(key.co, key_pos_ha); + key.time = 100.0f * key_i / (float)(hair_keys.size() - 1); } + } - /* Prepare transformation matrices. */ - const float4x4 curves_to_world_mat = curves_ob->obmat; - const float4x4 surface_to_world_mat = surface_ob.obmat; - const float4x4 world_to_surface_mat = surface_to_world_mat.inverted(); - const float4x4 curves_to_surface_mat = world_to_surface_mat * curves_to_world_mat; + particle_system->particles = particles.data(); + particle_system->totpart = particles.size(); + particle_system->flag |= PSYS_EDITED; + particle_system->recalc |= ID_RECALC_PSYS_RESET; - for (const int new_hair_i : curves_indices_to_transfer.index_range()) { - const int curve_i = curves_indices_to_transfer[new_hair_i]; - const IndexRange points = curves.points_for_curve(curve_i); - - const int looptri_i = looptri_indices[curve_i]; - const MLoopTri &looptri = looptris[looptri_i]; - const int poly_i = looptri.poly; - - const float3 &root_pos_cu = positions_cu[points.first()]; - const float3 root_pos_su = curves_to_surface_mat * root_pos_cu; - - const int mface_i = find_mface_for_root_position( - surface_me, poly_to_mface_map[poly_i], root_pos_su); - const MFace &mface = surface_me.mface[mface_i]; - - const float4 mface_weights = compute_mface_weights_for_position( - surface_me, mface, root_pos_su); - - ParticleData &particle = particles[new_hair_i]; - const int num_keys = points.size(); - MutableSpan<HairKey> hair_keys{ - static_cast<HairKey *>(MEM_calloc_arrayN(num_keys, sizeof(HairKey), __func__)), - num_keys}; - - particle.hair = hair_keys.data(); - particle.totkey = hair_keys.size(); - copy_v4_v4(particle.fuv, mface_weights); - particle.num = mface_i; - /* Not sure if there is a better way to initialize this. */ - particle.num_dmcache = DMCACHE_NOTFOUND; - - float4x4 hair_to_surface_mat; - psys_mat_hair_to_object( - &surface_ob, &surface_me, PART_FROM_FACE, &particle, hair_to_surface_mat.values); - /* In theory, #psys_mat_hair_to_object should handle this, but it doesn't right now. */ - copy_v3_v3(hair_to_surface_mat.values[3], root_pos_su); - const float4x4 surface_to_hair_mat = hair_to_surface_mat.inverted(); - - for (const int key_i : hair_keys.index_range()) { - const float3 &key_pos_cu = positions_cu[points[key_i]]; - const float3 key_pos_su = curves_to_surface_mat * key_pos_cu; - const float3 key_pos_ha = surface_to_hair_mat * key_pos_su; - - HairKey &key = hair_keys[key_i]; - copy_v3_v3(key.co, key_pos_ha); - key.time = 100.0f * key_i / (float)(hair_keys.size() - 1); - } - } + DEG_id_tag_update(&surface_ob.id, ID_RECALC_GEOMETRY); + DEG_id_tag_update(&settings.id, ID_RECALC_COPY_ON_WRITE); +} + +static int curves_convert_to_particle_system_exec(bContext *C, wmOperator *op) +{ + Main &bmain = *CTX_data_main(C); + Scene &scene = *CTX_data_scene(C); - particle_system->particles = particles.data(); - particle_system->totpart = particles.size(); - particle_system->flag |= PSYS_EDITED; - particle_system->recalc |= ID_RECALC_PSYS_RESET; + bool could_not_convert_some_curves = false; - DEG_id_tag_update(&surface_ob.id, ID_RECALC_GEOMETRY); - DEG_id_tag_update(&settings.id, ID_RECALC_COPY_ON_WRITE); + Object &active_object = *CTX_data_active_object(C); + try_convert_single_object(active_object, bmain, scene, &could_not_convert_some_curves); + + CTX_DATA_BEGIN (C, Object *, curves_ob, selected_objects) { + if (curves_ob != &active_object) { + try_convert_single_object(*curves_ob, bmain, scene, &could_not_convert_some_curves); + } } CTX_DATA_END; + if (could_not_convert_some_curves) { + BKE_report(op->reports, + RPT_INFO, + "Some curves could not be converted because they were not attached to the surface"); + } + WM_main_add_notifier(NC_OBJECT | ND_PARTICLE | NA_EDITED, nullptr); return OPERATOR_FINISHED; @@ -473,7 +497,7 @@ static void CURVES_OT_snap_curves_to_surface(wmOperatorType *ot) "Deform", "Re-attach curves to a deformed surface using the existing attachment information. This " "only works when the topology of the surface mesh has not changed"}, - {0, NULL, 0, NULL, NULL}, + {0, nullptr, 0, nullptr, nullptr}, }; RNA_def_enum(ot->srna, diff --git a/source/blender/editors/datafiles/CMakeLists.txt b/source/blender/editors/datafiles/CMakeLists.txt index 8fca0c46c82..9e7e00d5656 100644 --- a/source/blender/editors/datafiles/CMakeLists.txt +++ b/source/blender/editors/datafiles/CMakeLists.txt @@ -767,9 +767,9 @@ set_property(GLOBAL PROPERTY ICON_GEOM_NAMES ops.armature.extrude_cursor ops.armature.extrude_move ops.curve.draw - ops.curve.pen ops.curve.extrude_cursor ops.curve.extrude_move + ops.curve.pen ops.curve.radius ops.curve.vertex_random ops.curves.sculpt_add @@ -777,6 +777,7 @@ set_property(GLOBAL PROPERTY ICON_GEOM_NAMES ops.curves.sculpt_cut ops.curves.sculpt_delete ops.curves.sculpt_grow_shrink + ops.curves.sculpt_snake_hook ops.generic.cursor ops.generic.select ops.generic.select_box @@ -847,8 +848,8 @@ set_property(GLOBAL PROPERTY ICON_GEOM_NAMES ops.sculpt.border_hide ops.sculpt.border_mask ops.sculpt.box_trim - ops.sculpt.color_filter ops.sculpt.cloth_filter + ops.sculpt.color_filter ops.sculpt.face_set_edit ops.sculpt.lasso_face_set ops.sculpt.lasso_mask diff --git a/source/blender/editors/geometry/geometry_attributes.cc b/source/blender/editors/geometry/geometry_attributes.cc index 75094b46c8b..467b8efa622 100644 --- a/source/blender/editors/geometry/geometry_attributes.cc +++ b/source/blender/editors/geometry/geometry_attributes.cc @@ -380,7 +380,7 @@ void GEOMETRY_OT_color_attribute_add(wmOperatorType *ot) prop = RNA_def_string(ot->srna, "name", "Color", MAX_NAME, "Name", "Name of color attribute"); RNA_def_property_flag(prop, PROP_SKIP_SAVE); - static EnumPropertyItem domains[3] = {{ATTR_DOMAIN_POINT, "POINT", 0, "Point", ""}, + static EnumPropertyItem domains[3] = {{ATTR_DOMAIN_POINT, "POINT", 0, "Vertex", ""}, {ATTR_DOMAIN_CORNER, "CORNER", 0, "Face Corner", ""}, {0, nullptr, 0, nullptr, nullptr}}; diff --git a/source/blender/editors/gizmo_library/gizmo_draw_utils.c b/source/blender/editors/gizmo_library/gizmo_draw_utils.c index 8d2aec18451..c6303c197e7 100644 --- a/source/blender/editors/gizmo_library/gizmo_draw_utils.c +++ b/source/blender/editors/gizmo_library/gizmo_draw_utils.c @@ -82,9 +82,35 @@ void wm_gizmo_vec_draw( const float color[4], const float (*verts)[3], uint vert_count, uint pos, uint primitive_type) { immUniformColor4fv(color); - immBegin(primitive_type, vert_count); - for (int i = 0; i < vert_count; i++) { - immVertex3fv(pos, verts[i]); + + if (primitive_type == GPU_PRIM_LINE_LOOP) { + /* Line loop alternative for Metal/Vulkan. */ + immBegin(GPU_PRIM_LINES, vert_count * 2); + immVertex3fv(pos, verts[0]); + for (int i = 1; i < vert_count; i++) { + immVertex3fv(pos, verts[i]); + immVertex3fv(pos, verts[i]); + } + immVertex3fv(pos, verts[0]); + immEnd(); + } + else if (primitive_type == GPU_PRIM_TRI_FAN) { + /* Note(Metal): Tri-fan alternative for Metal. Triangle List is more efficient for small + * primitive counts. */ + int tri_count = vert_count - 2; + immBegin(GPU_PRIM_TRIS, tri_count * 3); + for (int i = 0; i < tri_count; i++) { + immVertex3fv(pos, verts[0]); + immVertex3fv(pos, verts[i + 1]); + immVertex3fv(pos, verts[i + 2]); + } + immEnd(); + } + else { + immBegin(primitive_type, vert_count); + for (int i = 0; i < vert_count; i++) { + immVertex3fv(pos, verts[i]); + } + immEnd(); } - immEnd(); } diff --git a/source/blender/editors/gizmo_library/gizmo_types/button2d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/button2d_gizmo.c index 5e20cc73f1a..b326d6d1859 100644 --- a/source/blender/editors/gizmo_library/gizmo_types/button2d_gizmo.c +++ b/source/blender/editors/gizmo_library/gizmo_types/button2d_gizmo.c @@ -74,20 +74,21 @@ static void button2d_geom_draw_backdrop(const wmGizmo *gz, GPU_viewport_size_get_f(viewport); GPUVertFormat *format = immVertexFormat(); - uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + /* Note(Metal): Prefer 3D coordinate for 2D rendering when using 3D shader. */ + uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); /* TODO: other draw styles. */ if (color[3] == 1.0 && fill_alpha == 1.0 && select == false) { immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); immUniformColor4fv(color); - imm_draw_circle_fill_2d(pos, 0, 0, 1.0f, CIRCLE_RESOLUTION); + imm_draw_circle_fill_3d(pos, 0.0f, 0.0f, 1.0f, CIRCLE_RESOLUTION); immUnbindProgram(); immBindBuiltinProgram(GPU_SHADER_3D_POLYLINE_UNIFORM_COLOR); immUniform2fv("viewportSize", &viewport[2]); immUniform1f("lineWidth", gz->line_width * U.pixelsize); immUniformColor4fv(color); - imm_draw_circle_wire_2d(pos, 0, 0, 1.0f, CIRCLE_RESOLUTION); + imm_draw_circle_wire_3d(pos, 0.0f, 0.0f, 1.0f, CIRCLE_RESOLUTION); immUnbindProgram(); } else { @@ -96,7 +97,7 @@ static void button2d_geom_draw_backdrop(const wmGizmo *gz, const float fill_color[4] = {UNPACK3(color), fill_alpha * color[3]}; immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); immUniformColor4fv(fill_color); - imm_draw_circle_fill_2d(pos, 0, 0, 1.0f, CIRCLE_RESOLUTION); + imm_draw_circle_fill_3d(pos, 0.0f, 0.0f, 1.0f, CIRCLE_RESOLUTION); immUnbindProgram(); } @@ -106,7 +107,7 @@ static void button2d_geom_draw_backdrop(const wmGizmo *gz, immUniform2fv("viewportSize", &viewport[2]); immUniform1f("lineWidth", gz->line_width * U.pixelsize); immUniformColor4fv(color); - imm_draw_circle_wire_2d(pos, 0, 0, 1.0f, CIRCLE_RESOLUTION); + imm_draw_circle_wire_3d(pos, 0.0f, 0.0f, 1.0f, CIRCLE_RESOLUTION); immUnbindProgram(); } } 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 4c54aa10c33..e4cb6d149f5 100644 --- a/source/blender/editors/gizmo_library/gizmo_types/cage2d_gizmo.c +++ b/source/blender/editors/gizmo_library/gizmo_types/cage2d_gizmo.c @@ -99,7 +99,8 @@ static void cage2d_draw_box_corners(const rctf *r, const float color[3], const float line_width) { - uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + /* Note(Metal): Prefer using 3D coordinates with 3D shader, even if rendering 2D gizmo's. */ + uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); immBindBuiltinProgram(GPU_SHADER_3D_POLYLINE_UNIFORM_COLOR); immUniformColor3fv(color); @@ -112,25 +113,25 @@ static void cage2d_draw_box_corners(const rctf *r, immBegin(GPU_PRIM_LINES, 16); - immVertex2f(pos, r->xmin, r->ymin + margin[1]); - immVertex2f(pos, r->xmin, r->ymin); - immVertex2f(pos, r->xmin, r->ymin); - immVertex2f(pos, r->xmin + margin[0], r->ymin); + immVertex3f(pos, r->xmin, r->ymin + margin[1], 0.0f); + immVertex3f(pos, r->xmin, r->ymin, 0.0f); + immVertex3f(pos, r->xmin, r->ymin, 0.0f); + immVertex3f(pos, r->xmin + margin[0], r->ymin, 0.0f); - immVertex2f(pos, r->xmax, r->ymin + margin[1]); - immVertex2f(pos, r->xmax, r->ymin); - immVertex2f(pos, r->xmax, r->ymin); - immVertex2f(pos, r->xmax - margin[0], r->ymin); + immVertex3f(pos, r->xmax, r->ymin + margin[1], 0.0f); + immVertex3f(pos, r->xmax, r->ymin, 0.0f); + immVertex3f(pos, r->xmax, r->ymin, 0.0f); + immVertex3f(pos, r->xmax - margin[0], r->ymin, 0.0f); - immVertex2f(pos, r->xmax, r->ymax - margin[1]); - immVertex2f(pos, r->xmax, r->ymax); - immVertex2f(pos, r->xmax, r->ymax); - immVertex2f(pos, r->xmax - margin[0], r->ymax); + immVertex3f(pos, r->xmax, r->ymax - margin[1], 0.0f); + immVertex3f(pos, r->xmax, r->ymax, 0.0f); + immVertex3f(pos, r->xmax, r->ymax, 0.0f); + immVertex3f(pos, r->xmax - margin[0], r->ymax, 0.0f); - immVertex2f(pos, r->xmin, r->ymax - margin[1]); - immVertex2f(pos, r->xmin, r->ymax); - immVertex2f(pos, r->xmin, r->ymax); - immVertex2f(pos, r->xmin + margin[0], r->ymax); + immVertex3f(pos, r->xmin, r->ymax - margin[1], 0.0f); + immVertex3f(pos, r->xmin, r->ymax, 0.0f); + immVertex3f(pos, r->xmin, r->ymax, 0.0f); + immVertex3f(pos, r->xmin + margin[0], r->ymax, 0.0f); immEnd(); @@ -440,12 +441,35 @@ static void cage2d_draw_box_interaction(const float color[4], static void imm_draw_point_aspect_2d( uint pos, float x, float y, float rad_x, float rad_y, bool solid) { - immBegin(solid ? GPU_PRIM_TRI_FAN : GPU_PRIM_LINE_LOOP, 4); - immVertex2f(pos, x - rad_x, y - rad_y); - immVertex2f(pos, x - rad_x, y + rad_y); - immVertex2f(pos, x + rad_x, y + rad_y); - immVertex2f(pos, x + rad_x, y - rad_y); - immEnd(); + if (solid) { + /* Note(Metal/AMD): Small Triangle-list primitives more optimal for GPU HW than Trianglestrip. + */ + immBegin(GPU_PRIM_TRIS, 6); + immVertex2f(pos, x - rad_x, y - rad_y); + immVertex2f(pos, x - rad_x, y + rad_y); + immVertex2f(pos, x + rad_x, y + rad_y); + + immVertex2f(pos, x - rad_x, y - rad_y); + immVertex2f(pos, x + rad_x, y + rad_y); + immVertex2f(pos, x + rad_x, y - rad_y); + immEnd(); + } + else { + /* Note(Metal/AMD): Small Line-list primitives more optimal for GPU HW than Linestrip. */ + immBegin(GPU_PRIM_LINES, 8); + immVertex2f(pos, x - rad_x, y - rad_y); + immVertex2f(pos, x - rad_x, y + rad_y); + + immVertex2f(pos, x - rad_x, y + rad_y); + immVertex2f(pos, x + rad_x, y + rad_y); + + immVertex2f(pos, x + rad_x, y + rad_y); + immVertex2f(pos, x + rad_x, y - rad_y); + + immVertex2f(pos, x + rad_x, y - rad_y); + immVertex2f(pos, x - rad_x, y - rad_y); + immEnd(); + } } static void cage2d_draw_circle_wire(const rctf *r, @@ -455,7 +479,9 @@ static void cage2d_draw_circle_wire(const rctf *r, const int draw_options, const float line_width) { - uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + /* Note(Metal): Prefer using 3D coordinates with 3D shader input, even if rendering 2D gizmo's. + */ + uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); immBindBuiltinProgram(GPU_SHADER_3D_POLYLINE_UNIFORM_COLOR); immUniformColor3fv(color); @@ -465,17 +491,28 @@ static void cage2d_draw_circle_wire(const rctf *r, immUniform2fv("viewportSize", &viewport[2]); immUniform1f("lineWidth", line_width * U.pixelsize); - immBegin(GPU_PRIM_LINE_LOOP, 4); - immVertex2f(pos, r->xmin, r->ymin); - immVertex2f(pos, r->xmax, r->ymin); - immVertex2f(pos, r->xmax, r->ymax); - immVertex2f(pos, r->xmin, r->ymax); + /* Small 'lines' primitives more efficient for hardware processing than linestrip. */ + immBegin(GPU_PRIM_LINES, 8); + immVertex3f(pos, r->xmin, r->ymin, 0.0f); + immVertex3f(pos, r->xmax, r->ymin, 0.0f); + + immVertex3f(pos, r->xmax, r->ymin, 0.0f); + immVertex3f(pos, r->xmax, r->ymax, 0.0f); + + immVertex3f(pos, r->xmax, r->ymax, 0.0f); + immVertex3f(pos, r->xmin, r->ymax, 0.0f); + + immVertex3f(pos, r->xmin, r->ymax, 0.0f); + immVertex3f(pos, r->xmin, r->ymin, 0.0f); immEnd(); if (transform_flag & ED_GIZMO_CAGE2D_XFORM_FLAG_ROTATE) { - immBegin(GPU_PRIM_LINE_LOOP, 2); - immVertex2f(pos, BLI_rctf_cent_x(r), r->ymax); - immVertex2f(pos, BLI_rctf_cent_x(r), r->ymax + margin[1]); + immBegin(GPU_PRIM_LINES, 4); + immVertex3f(pos, BLI_rctf_cent_x(r), r->ymax, 0.0f); + immVertex3f(pos, BLI_rctf_cent_x(r), r->ymax + margin[1], 0.0f); + + immVertex3f(pos, BLI_rctf_cent_x(r), r->ymax + margin[1], 0.0f); + immVertex3f(pos, BLI_rctf_cent_x(r), r->ymax, 0.0f); immEnd(); } @@ -485,10 +522,10 @@ static void cage2d_draw_circle_wire(const rctf *r, const float center[2] = {BLI_rctf_cent_x(r), BLI_rctf_cent_y(r)}; immBegin(GPU_PRIM_LINES, 4); - immVertex2f(pos, center[0] - rad[0], center[1] - rad[1]); - immVertex2f(pos, center[0] + rad[0], center[1] + rad[1]); - immVertex2f(pos, center[0] + rad[0], center[1] - rad[1]); - immVertex2f(pos, center[0] - rad[0], center[1] + rad[1]); + immVertex3f(pos, center[0] - rad[0], center[1] - rad[1], 0.0f); + immVertex3f(pos, center[0] + rad[0], center[1] + rad[1], 0.0f); + immVertex3f(pos, center[0] + rad[0], center[1] - rad[1], 0.0f); + immVertex3f(pos, center[0] - rad[0], center[1] + rad[1], 0.0f); immEnd(); } } 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 e1f584bf9e4..a76242404ba 100644 --- a/source/blender/editors/gizmo_library/gizmo_types/dial3d_gizmo.c +++ b/source/blender/editors/gizmo_library/gizmo_types/dial3d_gizmo.c @@ -96,7 +96,8 @@ static void dial_geom_draw(const float color[4], ED_GIZMO_DIAL_DRAW_FLAG_FILL))); GPUVertFormat *format = immVertexFormat(); - uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + /* Note(Metal): Prefer using 3D coordinates with 3D shader, even if rendering 2D gizmo's. */ + uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); if (clip_plane) { immBindBuiltinProgram(filled ? GPU_SHADER_3D_CLIPPED_UNIFORM_COLOR : @@ -114,18 +115,19 @@ static void dial_geom_draw(const float color[4], if (filled) { if (arc_partial_angle == 0.0f) { if (arc_inner_factor == 0.0f) { - imm_draw_circle_fill_2d(pos, 0, 0, 1.0, DIAL_RESOLUTION); + imm_draw_circle_fill_3d(pos, 0.0f, 0.0f, 1.0f, DIAL_RESOLUTION); } else { - imm_draw_disk_partial_fill_2d( - pos, 0, 0, arc_inner_factor, 1.0f, DIAL_RESOLUTION, 0, RAD2DEGF(M_PI * 2)); + imm_draw_disk_partial_fill_3d( + pos, 0.0f, 0.0f, 0.0f, arc_inner_factor, 1.0f, DIAL_RESOLUTION, 0, RAD2DEGF(M_PI * 2)); } } else { float arc_partial_deg = RAD2DEGF((M_PI * 2) - arc_partial_angle); - imm_draw_disk_partial_fill_2d(pos, - 0, - 0, + imm_draw_disk_partial_fill_3d(pos, + 0.0f, + 0.0f, + 0.0f, arc_inner_factor, 1.0f, DIAL_RESOLUTION, @@ -140,15 +142,15 @@ static void dial_geom_draw(const float color[4], immUniform1f("lineWidth", line_width * U.pixelsize); if (arc_partial_angle == 0.0f) { - imm_draw_circle_wire_2d(pos, 0, 0, 1.0, DIAL_RESOLUTION); + imm_draw_circle_wire_3d(pos, 0.0f, 0.0f, 1.0f, DIAL_RESOLUTION); if (arc_inner_factor != 0.0f) { - imm_draw_circle_wire_2d(pos, 0, 0, arc_inner_factor, DIAL_RESOLUTION); + imm_draw_circle_wire_3d(pos, 0.0f, 0.0f, arc_inner_factor, DIAL_RESOLUTION); } } else { float arc_partial_deg = RAD2DEGF((M_PI * 2) - arc_partial_angle); - imm_draw_circle_partial_wire_2d( - pos, 0, 0, 1.0, DIAL_RESOLUTION, -arc_partial_deg / 2, arc_partial_deg); + imm_draw_circle_partial_wire_3d( + pos, 0.0f, 0.0f, 0.0f, 1.0f, DIAL_RESOLUTION, -arc_partial_deg / 2, arc_partial_deg); # if 0 if (arc_inner_factor != 0.0f) { BLI_assert(0); @@ -186,7 +188,7 @@ static void dial_ghostarc_draw_helpline(const float angle, immUniformColor4fv(color); immBegin(GPU_PRIM_LINE_STRIP, 2); - immVertex3f(pos, 0.0f, 0, 0.0f); + immVertex3f(pos, 0.0f, 0.0f, 0.0f); immVertex3fv(pos, co_outer); immEnd(); 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 447fe1005a1..5fb1173521a 100644 --- a/source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c +++ b/source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c @@ -98,7 +98,8 @@ static void move_geom_draw(const wmGizmo *gz, ED_GIZMO_MOVE_DRAW_FLAG_FILL))); GPUVertFormat *format = immVertexFormat(); - uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + /* Note(Metal): Prefer using 3D coordinates with 3D shader, even if rendering 2D gizmo's. */ + uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); immBindBuiltinProgram(filled ? GPU_SHADER_3D_UNIFORM_COLOR : GPU_SHADER_3D_POLYLINE_UNIFORM_COLOR); @@ -115,20 +116,20 @@ static void move_geom_draw(const wmGizmo *gz, if (draw_style == ED_GIZMO_MOVE_STYLE_RING_2D) { if (filled) { - imm_draw_circle_fill_2d(pos, 0, 0, radius, DIAL_RESOLUTION); + imm_draw_circle_fill_3d(pos, 0.0f, 0.0f, radius, DIAL_RESOLUTION); } else { - imm_draw_circle_wire_2d(pos, 0, 0, radius, DIAL_RESOLUTION); + imm_draw_circle_wire_3d(pos, 0.0f, 0.0f, radius, DIAL_RESOLUTION); } } else if (draw_style == ED_GIZMO_MOVE_STYLE_CROSS_2D) { const float radius_diag = M_SQRT1_2 * radius; immBegin(GPU_PRIM_LINES, 4); - immVertex2f(pos, radius_diag, radius_diag); - immVertex2f(pos, -radius_diag, -radius_diag); + immVertex3f(pos, radius_diag, radius_diag, 0.0f); + immVertex3f(pos, -radius_diag, -radius_diag, 0.0f); - immVertex2f(pos, -radius_diag, radius_diag); - immVertex2f(pos, radius_diag, -radius_diag); + immVertex3f(pos, -radius_diag, radius_diag, 0.0f); + immVertex3f(pos, radius_diag, -radius_diag, 0.0f); immEnd(); } else { diff --git a/source/blender/editors/gpencil/gpencil_select.c b/source/blender/editors/gpencil/gpencil_select.c index d962dcdfa10..c4fd34212c3 100644 --- a/source/blender/editors/gpencil/gpencil_select.c +++ b/source/blender/editors/gpencil/gpencil_select.c @@ -826,7 +826,7 @@ static bool gpencil_select_same_material(bContext *C) bGPdata *gpd = ED_gpencil_data_get_active(C); const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd); /* First, build set containing all the colors of selected strokes */ - GSet *selected_colors = BLI_gset_str_new("GP Selected Colors"); + GSet *selected_colors = BLI_gset_int_new("GP Selected Colors"); bool changed = false; @@ -835,7 +835,7 @@ static bool gpencil_select_same_material(bContext *C) /* add instead of insert here, otherwise the uniqueness check gets skipped, * and we get many duplicate entries... */ - BLI_gset_add(selected_colors, &gps->mat_nr); + BLI_gset_add(selected_colors, POINTER_FROM_INT(gps->mat_nr)); } } CTX_DATA_END; @@ -843,7 +843,8 @@ static bool gpencil_select_same_material(bContext *C) /* Second, select any visible stroke that uses these colors */ if (is_curve_edit) { CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) { - if (gps->editcurve != NULL && BLI_gset_haskey(selected_colors, &gps->mat_nr)) { + if (gps->editcurve != NULL && + BLI_gset_haskey(selected_colors, POINTER_FROM_INT(gps->mat_nr))) { bGPDcurve *gpc = gps->editcurve; for (int i = 0; i < gpc->tot_curve_points; i++) { bGPDcurve_point *gpc_pt = &gpc->curve_points[i]; @@ -861,7 +862,7 @@ static bool gpencil_select_same_material(bContext *C) } else { CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) { - if (BLI_gset_haskey(selected_colors, &gps->mat_nr)) { + if (BLI_gset_haskey(selected_colors, POINTER_FROM_INT(gps->mat_nr))) { /* select this stroke */ bGPDspoint *pt; int i; diff --git a/source/blender/editors/include/ED_armature.h b/source/blender/editors/include/ED_armature.h index 01885911ac4..d969277fef5 100644 --- a/source/blender/editors/include/ED_armature.h +++ b/source/blender/editors/include/ED_armature.h @@ -363,7 +363,7 @@ void ED_mesh_deform_bind_callback(struct Object *object, struct MeshDeformModifierData *mmd, struct Mesh *cagemesh, float *vertexcos, - int totvert, + int verts_num, float cagemat[4][4]); /* Pose backups, pose_backup.c */ diff --git a/source/blender/editors/include/ED_paint.h b/source/blender/editors/include/ED_paint.h index cd28fbe9687..ba5834fd508 100644 --- a/source/blender/editors/include/ED_paint.h +++ b/source/blender/editors/include/ED_paint.h @@ -114,11 +114,6 @@ void ED_paintcurve_undo_push_end(struct bContext *C); void ED_paintcurve_undosys_type(struct UndoType *ut); /* paint_canvas.cc */ -struct Image *ED_paint_canvas_image_get(const struct PaintModeSettings *settings, - struct Object *ob); -int ED_paint_canvas_uvmap_layer_index_get(const struct PaintModeSettings *settings, - struct Object *ob); - /** Color type of an object can be overridden in sculpt/paint mode. */ eV3DShadingColorType ED_paint_shading_color_override(struct bContext *C, const struct PaintModeSettings *settings, diff --git a/source/blender/editors/include/ED_sculpt.h b/source/blender/editors/include/ED_sculpt.h index 3100c135db4..54d67c50d5c 100644 --- a/source/blender/editors/include/ED_sculpt.h +++ b/source/blender/editors/include/ED_sculpt.h @@ -17,6 +17,9 @@ struct UndoType; struct ViewContext; struct bContext; struct rcti; +struct wmMsgSubscribeKey; +struct wmMsgSubscribeValue; +struct wmRegionMessageSubscribeParams; /* sculpt.c */ diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index c14cce28052..3f58aab3e53 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -595,6 +595,7 @@ typedef void (*uiMenuHandleFunc)(struct bContext *C, void *arg, int event); */ typedef bool (*uiMenuStepFunc)(struct bContext *C, int direction, void *arg1); +typedef void *(*uiCopyArgFunc)(const void *arg); typedef void (*uiFreeArgFunc)(void *arg); /* interface_query.c */ @@ -2065,6 +2066,24 @@ void uiLayoutSetFunc(uiLayout *layout, uiMenuHandleFunc handlefunc, void *argv); void uiLayoutSetContextPointer(uiLayout *layout, const char *name, struct PointerRNA *ptr); struct bContextStore *uiLayoutGetContextStore(uiLayout *layout); void uiLayoutContextCopy(uiLayout *layout, struct bContextStore *context); + +/** + * Set tooltip function for all buttons in the layout. + * func, arg and free_arg are passed on to UI_but_func_tooltip_set, so their meaning is the same. + * + * \param func: The callback function that gets called to get tooltip content + * \param arg: An optional opaque pointer that gets passed to func + * \param free_arg: An optional callback for freeing arg (can be set to e.g. MEM_freeN) + * \param copy_arg: An optional callback for duplicating arg in case UI_but_func_tooltip_set + * is being called on multiple buttons (can be set to e.g. MEM_dupallocN). If set to NULL, arg will + * be passed as-is to all buttons. + */ +void uiLayoutSetTooltipFunc(uiLayout *layout, + uiButToolTipFunc func, + void *arg, + uiCopyArgFunc copy_arg, + uiFreeArgFunc free_arg); + /** * This is a bit of a hack but best keep it in one place at least. */ diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index 8935df7b581..2c408619fe7 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -3018,12 +3018,12 @@ static bool ui_textedit_set_cursor_pos_foreach_glyph(const char *UNUSED(str), const size_t str_step_ofs, const rcti *glyph_step_bounds, const int UNUSED(glyph_advance_x), - const rctf *glyph_bounds, + const rcti *glyph_bounds, const int UNUSED(glyph_bearing[2]), void *user_data) { int *cursor_data = user_data; - const float center = glyph_step_bounds->xmin + (BLI_rctf_size_x(glyph_bounds) / 2.0f); + const int center = glyph_step_bounds->xmin + (BLI_rcti_size_x(glyph_bounds) / 2.0f); if (cursor_data[0] < center) { cursor_data[1] = str_step_ofs; return false; diff --git a/source/blender/editors/interface/interface_icons.c b/source/blender/editors/interface/interface_icons.c index e3bf1a48907..332b9b44b0a 100644 --- a/source/blender/editors/interface/interface_icons.c +++ b/source/blender/editors/interface/interface_icons.c @@ -1584,10 +1584,10 @@ static void icon_draw_cache_texture_flush_ex(GPUTexture *texture, GPUShader *shader = GPU_shader_get_builtin_shader(GPU_SHADER_2D_IMAGE_MULTI_RECT_COLOR); GPU_shader_bind(shader); - const int data_loc = GPU_shader_get_uniform_block(shader, "multi_rect_data"); + const int data_binding = GPU_shader_get_uniform_block_binding(shader, "multi_rect_data"); GPUUniformBuf *ubo = GPU_uniformbuf_create_ex( sizeof(struct MultiRectCallData), texture_draw_calls->drawcall_cache, __func__); - GPU_uniformbuf_bind(ubo, data_loc); + GPU_uniformbuf_bind(ubo, data_binding); const int img_binding = GPU_shader_get_texture_binding(shader, "image"); GPU_texture_bind_ex(texture, GPU_SAMPLER_ICON, img_binding, false); diff --git a/source/blender/editors/interface/interface_layout.c b/source/blender/editors/interface/interface_layout.c index ac807f06891..c1bb2ed6d18 100644 --- a/source/blender/editors/interface/interface_layout.c +++ b/source/blender/editors/interface/interface_layout.c @@ -5681,6 +5681,40 @@ void uiLayoutContextCopy(uiLayout *layout, bContextStore *context) layout->context = CTX_store_add_all(&block->contexts, context); } +void uiLayoutSetTooltipFunc(uiLayout *layout, + uiButToolTipFunc func, + void *arg, + uiCopyArgFunc copy_arg, + uiFreeArgFunc free_arg) +{ + bool arg_used = false; + + LISTBASE_FOREACH (uiItem *, item, &layout->items) { + /* Each button will call free_arg for "its" argument, so we need to + * duplicate the allocation for each button after the first. */ + if (copy_arg != NULL && arg_used) { + arg = copy_arg(arg); + } + arg_used = true; + + if (item->type == ITEM_BUTTON) { + uiButtonItem *bitem = (uiButtonItem *)item; + if (bitem->but->type == UI_BTYPE_DECORATOR) { + continue; + } + UI_but_func_tooltip_set(bitem->but, func, arg, free_arg); + } + else { + uiLayoutSetTooltipFunc((uiLayout *)item, func, arg, copy_arg, free_arg); + } + } + + if (!arg_used) { + /* Free the original copy of arg in case the layout is empty. */ + free_arg(arg); + } +} + void uiLayoutSetContextFromBut(uiLayout *layout, uiBut *but) { if (but->opptr) { diff --git a/source/blender/editors/interface/interface_query.cc b/source/blender/editors/interface/interface_query.cc index 2767a184619..337b2852d57 100644 --- a/source/blender/editors/interface/interface_query.cc +++ b/source/blender/editors/interface/interface_query.cc @@ -61,7 +61,7 @@ bool ui_but_is_toggle(const uiBut *but) bool ui_but_is_interactive(const uiBut *but, const bool labeledit) { /* NOTE: #UI_BTYPE_LABEL is included for highlights, this allows drags. */ - if ((but->type == UI_BTYPE_LABEL) && but->dragpoin == nullptr) { + if ((but->type == UI_BTYPE_LABEL) && but->dragpoin == nullptr && but->tip_func == nullptr) { return false; } if (ELEM(but->type, UI_BTYPE_ROUNDBOX, UI_BTYPE_SEPR, UI_BTYPE_SEPR_LINE, UI_BTYPE_LISTBOX)) { diff --git a/source/blender/editors/interface/interface_region_tooltip.c b/source/blender/editors/interface/interface_region_tooltip.c index 29553ff65d1..ca18ec14a54 100644 --- a/source/blender/editors/interface/interface_region_tooltip.c +++ b/source/blender/editors/interface/interface_region_tooltip.c @@ -788,8 +788,10 @@ static uiTooltipData *ui_tooltip_data_from_button_or_extra_icon(bContext *C, } /* Tip Label (only for buttons not already showing the label). - * Check prefix instead of comparing because the button may include the shortcut. */ - if (but_label.strinfo && !STRPREFIX(but->drawstr, but_label.strinfo)) { + * Check prefix instead of comparing because the button may include the shortcut. + * Buttons with dynamic tooltips also don't get their default label here since they + * can already provide more accurate and specific tooltip content. */ + if (but_label.strinfo && !STRPREFIX(but->drawstr, but_label.strinfo) && !but->tip_func) { uiTooltipField *field = text_field_add(data, &(uiTooltipFormat){ .style = UI_TIP_STYLE_HEADER, diff --git a/source/blender/editors/interface/interface_style.cc b/source/blender/editors/interface/interface_style.cc index b4e97f8a396..0156a943015 100644 --- a/source/blender/editors/interface/interface_style.cc +++ b/source/blender/editors/interface/interface_style.cc @@ -161,7 +161,7 @@ void UI_fontstyle_draw_ex(const uiFontStyle *fs, } else { /* Draw from bound-box center. */ - const float height = BLF_ascender(fs->uifont_id) + BLF_descender(fs->uifont_id); + const int height = BLF_ascender(fs->uifont_id) + BLF_descender(fs->uifont_id); yofs = ceil(0.5f * (BLI_rcti_size_y(rect) - height)); } @@ -279,9 +279,9 @@ void UI_fontstyle_draw_simple_backdrop(const uiFontStyle *fs, UI_fontstyle_set(fs); { - const float width = BLF_width(fs->uifont_id, str, BLF_DRAW_STR_DUMMY_MAX); - const float height = BLF_height_max(fs->uifont_id); - const float decent = BLF_descender(fs->uifont_id); + const int width = BLF_width(fs->uifont_id, str, BLF_DRAW_STR_DUMMY_MAX); + const int height = BLF_height_max(fs->uifont_id); + const int decent = BLF_descender(fs->uifont_id); const float margin = height / 4.0f; rctf rect; diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index 38499a7f089..a5e2e9353bf 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -787,7 +787,7 @@ static const char *template_id_browse_tip(const StructRNA *type) case ID_LP: return N_("Browse LightProbe to be linked"); case ID_CV: - return N_("Browse Hair Curves Data to be linked"); + return N_("Browse Curves Data to be linked"); case ID_PT: return N_("Browse Point Cloud Data to be linked"); case ID_VO: diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c index a16c24f63cd..98ecf91adbc 100644 --- a/source/blender/editors/interface/interface_widgets.c +++ b/source/blender/editors/interface/interface_widgets.c @@ -1859,15 +1859,15 @@ static bool widget_draw_text_underline_calc_position(const char *UNUSED(str), const size_t str_step_ofs, const rcti *glyph_step_bounds, const int UNUSED(glyph_advance_x), - const rctf *glyph_bounds, + const rcti *glyph_bounds, const int UNUSED(glyph_bearing[2]), void *user_data) { struct UnderlineData *ul_data = user_data; if (ul_data->str_offset == str_step_ofs) { /* Full width of this glyph including both bearings. */ - const float width = glyph_bounds->xmin + BLI_rctf_size_x(glyph_bounds) + glyph_bounds->xmin; - ul_data->r_offset_px[0] = glyph_step_bounds->xmin + ((width - ul_data->width_px) * 0.5f); + const int width = glyph_bounds->xmin + BLI_rcti_size_x(glyph_bounds) + glyph_bounds->xmin; + ul_data->r_offset_px[0] = glyph_step_bounds->xmin + ((width - ul_data->width_px) / 2); /* One line-width below the lower glyph bounds. */ ul_data->r_offset_px[1] = glyph_bounds->ymin - U.pixelsize; /* Early exit. */ diff --git a/source/blender/editors/io/io_obj.c b/source/blender/editors/io/io_obj.c index 97f1e08fdff..beed4abd52b 100644 --- a/source/blender/editors/io/io_obj.c +++ b/source/blender/editors/io/io_obj.c @@ -378,6 +378,7 @@ static int wm_obj_import_exec(bContext *C, wmOperator *op) import_params.clamp_size = RNA_float_get(op->ptr, "clamp_size"); import_params.forward_axis = RNA_enum_get(op->ptr, "forward_axis"); import_params.up_axis = RNA_enum_get(op->ptr, "up_axis"); + import_params.validate_meshes = RNA_boolean_get(op->ptr, "validate_meshes"); OBJ_import(C, &import_params); @@ -388,8 +389,8 @@ static void ui_obj_import_settings(uiLayout *layout, PointerRNA *imfptr) { uiLayoutSetPropSep(layout, true); uiLayoutSetPropDecorate(layout, false); - uiLayout *box = uiLayoutBox(layout); + uiLayout *box = uiLayoutBox(layout); uiItemL(box, IFACE_("Transform"), ICON_OBJECT_DATA); uiLayout *col = uiLayoutColumn(box, false); uiLayout *sub = uiLayoutColumn(col, false); @@ -397,6 +398,11 @@ static void ui_obj_import_settings(uiLayout *layout, PointerRNA *imfptr) sub = uiLayoutColumn(col, false); uiItemR(sub, imfptr, "forward_axis", 0, IFACE_("Axis Forward"), ICON_NONE); uiItemR(sub, imfptr, "up_axis", 0, IFACE_("Up"), ICON_NONE); + + box = uiLayoutBox(layout); + uiItemL(box, IFACE_("Options"), ICON_EXPORT); + col = uiLayoutColumn(box, false); + uiItemR(col, imfptr, "validate_meshes", 0, NULL, ICON_NONE); } static void wm_obj_import_draw(bContext *C, wmOperator *op) @@ -442,4 +448,9 @@ void WM_OT_obj_import(struct wmOperatorType *ot) "Forward Axis", ""); RNA_def_enum(ot->srna, "up_axis", io_obj_transform_axis_up, OBJ_AXIS_Y_UP, "Up Axis", ""); + RNA_def_boolean(ot->srna, + "validate_meshes", + false, + "Validate Meshes", + "Check imported mesh objects for invalid data (slow)"); } diff --git a/source/blender/editors/io/io_usd.c b/source/blender/editors/io/io_usd.c index bff1bdd0ed8..e0616a0cec3 100644 --- a/source/blender/editors/io/io_usd.c +++ b/source/blender/editors/io/io_usd.c @@ -215,47 +215,43 @@ void WM_OT_usd_export(struct wmOperatorType *ot) "selected_objects_only", false, "Selection Only", - "Only selected objects are exported. Unselected parents of selected objects are " + "Only export selected objects. Unselected parents of selected objects are " "exported as empty transform"); RNA_def_boolean(ot->srna, "visible_objects_only", true, "Visible Only", - "Only visible objects are exported. Invisible parents of exported objects are " - "exported as empty transform"); + "Only export visible objects. Invisible parents of exported objects are " + "exported as empty transforms"); - RNA_def_boolean(ot->srna, - "export_animation", - false, - "Animation", - "When checked, the render frame range is exported. When false, only the current " - "frame is exported"); RNA_def_boolean( - ot->srna, "export_hair", false, "Hair", "When checked, hair is exported as USD curves"); - RNA_def_boolean(ot->srna, - "export_uvmaps", - true, - "UV Maps", - "When checked, all UV maps of exported meshes are included in the export"); + ot->srna, + "export_animation", + false, + "Animation", + "Export all frames in the render frame range, rather than only the current frame"); + RNA_def_boolean( + ot->srna, "export_hair", false, "Hair", "Export hair particle systems as USD curves"); + RNA_def_boolean( + ot->srna, "export_uvmaps", true, "UV Maps", "Include all mesh UV maps in the export"); RNA_def_boolean(ot->srna, "export_normals", true, "Normals", - "When checked, normals of exported meshes are included in the export"); + "Include normals of exported meshes in the export"); RNA_def_boolean(ot->srna, "export_materials", true, "Materials", - "When checked, the viewport settings of materials are exported as USD preview " - "materials, and material assignments are exported as geometry subsets"); + "Export viewport settings of materials as USD preview materials, and export " + "material assignments as geometry subsets"); RNA_def_boolean(ot->srna, "use_instancing", false, "Instancing", - "When checked, instanced objects are exported as references in USD. " - "When unchecked, instanced objects are exported as real objects"); + "Export instanced objects as references in USD rather than real objects"); RNA_def_enum(ot->srna, "evaluation_mode", diff --git a/source/blender/editors/mask/mask_ops.c b/source/blender/editors/mask/mask_ops.c index 595e6d791ef..3c0e7ee399c 100644 --- a/source/blender/editors/mask/mask_ops.c +++ b/source/blender/editors/mask/mask_ops.c @@ -15,6 +15,7 @@ #include "BKE_mask.h" #include "DEG_depsgraph.h" +#include "DEG_depsgraph_query.h" #include "DNA_mask_types.h" #include "DNA_object_types.h" /* SELECT */ @@ -243,7 +244,7 @@ static void mask_point_undistort_pos(SpaceClip *sc, float r_co[2], const float c } static bool spline_under_mouse_get(const bContext *C, - Mask *mask, + Mask *mask_orig, const float co[2], MaskLayer **r_mask_layer, MaskSpline **r_mask_spline) @@ -258,6 +259,9 @@ static bool spline_under_mouse_get(const bContext *C, *r_mask_layer = NULL; *r_mask_spline = NULL; + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + Mask *mask_eval = (Mask *)DEG_get_evaluated_id(depsgraph, &mask_orig->id); + int width, height; ED_mask_get_size(area, &width, &height); float pixel_co[2]; @@ -266,22 +270,25 @@ static bool spline_under_mouse_get(const bContext *C, if (sc != NULL) { undistort = (sc->clip != NULL) && (sc->user.render_flag & MCLIP_PROXY_RENDER_UNDISTORT) != 0; } - for (MaskLayer *mask_layer = mask->masklayers.first; mask_layer != NULL; - mask_layer = mask_layer->next) { - if (mask_layer->visibility_flag & MASK_HIDE_SELECT) { + + for (MaskLayer *mask_layer_orig = mask_orig->masklayers.first, + *mask_layer_eval = mask_eval->masklayers.first; + mask_layer_orig != NULL; + mask_layer_orig = mask_layer_orig->next, mask_layer_eval = mask_layer_eval->next) { + if (mask_layer_orig->visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) { continue; } - - for (MaskSpline *spline = mask_layer->splines.first; spline != NULL; spline = spline->next) { - MaskSplinePoint *points_array; - float min[2], max[2], center[2]; - if ((spline->flag & SELECT) == 0) { + for (MaskSpline *spline_orig = mask_layer_orig->splines.first, + *spline_eval = mask_layer_eval->splines.first; + spline_orig != NULL; + spline_orig = spline_orig->next, spline_eval = spline_eval->next) { + if ((spline_orig->flag & SELECT) == 0) { continue; } - - points_array = BKE_mask_spline_point_array(spline); + MaskSplinePoint *points_array = BKE_mask_spline_point_array(spline_eval); + float min[2], max[2], center[2]; INIT_MINMAX2(min, max); - for (int i = 0; i < spline->tot_point; i++) { + for (int i = 0; i < spline_orig->tot_point; i++) { MaskSplinePoint *point_deform = &points_array[i]; BezTriple *bezt = &point_deform->bezt; @@ -302,8 +309,8 @@ static bool spline_under_mouse_get(const bContext *C, float max_bb_side = min_ff((max[0] - min[0]) * width, (max[1] - min[1]) * height); if (dist_squared <= max_bb_side * max_bb_side * 0.5f && (closest_spline == NULL || dist_squared < closest_dist_squared)) { - closest_layer = mask_layer; - closest_spline = spline; + closest_layer = mask_layer_orig; + closest_spline = spline_orig; closest_dist_squared = dist_squared; } } @@ -311,7 +318,7 @@ static bool spline_under_mouse_get(const bContext *C, if (closest_dist_squared < square_f(threshold) && closest_spline != NULL) { float diff_score; if (ED_mask_find_nearest_diff_point(C, - mask, + mask_orig, co, threshold, false, diff --git a/source/blender/editors/mask/mask_select.c b/source/blender/editors/mask/mask_select.c index 29cd593ce7f..e3c3f1c38a0 100644 --- a/source/blender/editors/mask/mask_select.c +++ b/source/blender/editors/mask/mask_select.c @@ -16,6 +16,9 @@ #include "BKE_context.h" #include "BKE_mask.h" +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_query.h" + #include "DNA_mask_types.h" #include "WM_api.h" @@ -427,7 +430,9 @@ static int box_select_exec(bContext *C, wmOperator *op) ScrArea *area = CTX_wm_area(C); ARegion *region = CTX_wm_region(C); - Mask *mask = CTX_data_edit_mask(C); + Mask *mask_orig = CTX_data_edit_mask(C); + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + Mask *mask_eval = (Mask *)DEG_get_evaluated_id(depsgraph, &mask_orig->id); rcti rect; rctf rectf; @@ -436,7 +441,7 @@ static int box_select_exec(bContext *C, wmOperator *op) const eSelectOp sel_op = RNA_enum_get(op->ptr, "mode"); const bool select = (sel_op != SEL_OP_SUB); if (SEL_OP_USE_PRE_DESELECT(sel_op)) { - ED_mask_select_toggle_all(mask, SEL_DESELECT); + ED_mask_select_toggle_all(mask_orig, SEL_DESELECT); changed = true; } @@ -447,16 +452,22 @@ static int box_select_exec(bContext *C, wmOperator *op) ED_mask_point_pos(area, region, rect.xmax, rect.ymax, &rectf.xmax, &rectf.ymax); /* do actual selection */ - LISTBASE_FOREACH (MaskLayer *, mask_layer, &mask->masklayers) { - if (mask_layer->visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) { + for (MaskLayer *mask_layer_orig = mask_orig->masklayers.first, + *mask_layer_eval = mask_eval->masklayers.first; + mask_layer_orig != NULL; + mask_layer_orig = mask_layer_orig->next, mask_layer_eval = mask_layer_eval->next) { + if (mask_layer_orig->visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) { continue; } + for (MaskSpline *spline_orig = mask_layer_orig->splines.first, + *spline_eval = mask_layer_eval->splines.first; + spline_orig != NULL; + spline_orig = spline_orig->next, spline_eval = spline_eval->next) { - LISTBASE_FOREACH (MaskSpline *, spline, &mask_layer->splines) { - MaskSplinePoint *points_array = BKE_mask_spline_point_array(spline); + MaskSplinePoint *points_array = BKE_mask_spline_point_array(spline_eval); - for (int i = 0; i < spline->tot_point; i++) { - MaskSplinePoint *point = &spline->points[i]; + for (int i = 0; i < spline_orig->tot_point; i++) { + MaskSplinePoint *point = &spline_orig->points[i]; MaskSplinePoint *point_deform = &points_array[i]; /* TODO: handles? */ @@ -471,10 +482,10 @@ static int box_select_exec(bContext *C, wmOperator *op) } if (changed) { - ED_mask_select_flush_all(mask); + ED_mask_select_flush_all(mask_orig); - DEG_id_tag_update(&mask->id, ID_RECALC_SELECT); - WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask); + DEG_id_tag_update(&mask_orig->id, ID_RECALC_SELECT); + WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask_orig); return OPERATOR_FINISHED; } @@ -517,14 +528,16 @@ static bool do_lasso_select_mask(bContext *C, ScrArea *area = CTX_wm_area(C); ARegion *region = CTX_wm_region(C); - Mask *mask = CTX_data_edit_mask(C); + Mask *mask_orig = CTX_data_edit_mask(C); + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + Mask *mask_eval = (Mask *)DEG_get_evaluated_id(depsgraph, &mask_orig->id); rcti rect; bool changed = false; const bool select = (sel_op != SEL_OP_SUB); if (SEL_OP_USE_PRE_DESELECT(sel_op)) { - ED_mask_select_toggle_all(mask, SEL_DESELECT); + ED_mask_select_toggle_all(mask_orig, SEL_DESELECT); changed = true; } @@ -532,16 +545,22 @@ static bool do_lasso_select_mask(bContext *C, BLI_lasso_boundbox(&rect, mcoords, mcoords_len); /* do actual selection */ - LISTBASE_FOREACH (MaskLayer *, mask_layer, &mask->masklayers) { - if (mask_layer->visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) { + for (MaskLayer *mask_layer_orig = mask_orig->masklayers.first, + *mask_layer_eval = mask_eval->masklayers.first; + mask_layer_orig != NULL; + mask_layer_orig = mask_layer_orig->next, mask_layer_eval = mask_layer_eval->next) { + if (mask_layer_orig->visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) { continue; } + for (MaskSpline *spline_orig = mask_layer_orig->splines.first, + *spline_eval = mask_layer_eval->splines.first; + spline_orig != NULL; + spline_orig = spline_orig->next, spline_eval = spline_eval->next) { - LISTBASE_FOREACH (MaskSpline *, spline, &mask_layer->splines) { - MaskSplinePoint *points_array = BKE_mask_spline_point_array(spline); + MaskSplinePoint *points_array = BKE_mask_spline_point_array(spline_eval); - for (int i = 0; i < spline->tot_point; i++) { - MaskSplinePoint *point = &spline->points[i]; + for (int i = 0; i < spline_orig->tot_point; i++) { + MaskSplinePoint *point = &spline_orig->points[i]; MaskSplinePoint *point_deform = &points_array[i]; /* TODO: handles? */ @@ -572,10 +591,10 @@ static bool do_lasso_select_mask(bContext *C, } if (changed) { - ED_mask_select_flush_all(mask); + ED_mask_select_flush_all(mask_orig); - DEG_id_tag_update(&mask->id, ID_RECALC_SELECT); - WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask); + DEG_id_tag_update(&mask_orig->id, ID_RECALC_SELECT); + WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask_orig); } return changed; @@ -643,7 +662,9 @@ static int circle_select_exec(bContext *C, wmOperator *op) ScrArea *area = CTX_wm_area(C); ARegion *region = CTX_wm_region(C); - Mask *mask = CTX_data_edit_mask(C); + Mask *mask_orig = CTX_data_edit_mask(C); + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + Mask *mask_eval = (Mask *)DEG_get_evaluated_id(depsgraph, &mask_orig->id); float zoomx, zoomy, offset[2], ellipse[2]; int width, height; @@ -668,21 +689,27 @@ static int circle_select_exec(bContext *C, wmOperator *op) WM_gesture_is_modal_first(op->customdata)); const bool select = (sel_op != SEL_OP_SUB); if (SEL_OP_USE_PRE_DESELECT(sel_op)) { - ED_mask_select_toggle_all(mask, SEL_DESELECT); + ED_mask_select_toggle_all(mask_orig, SEL_DESELECT); changed = true; } /* do actual selection */ - LISTBASE_FOREACH (MaskLayer *, mask_layer, &mask->masklayers) { - if (mask_layer->visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) { + for (MaskLayer *mask_layer_orig = mask_orig->masklayers.first, + *mask_layer_eval = mask_eval->masklayers.first; + mask_layer_orig != NULL; + mask_layer_orig = mask_layer_orig->next, mask_layer_eval = mask_layer_eval->next) { + if (mask_layer_orig->visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) { continue; } + for (MaskSpline *spline_orig = mask_layer_orig->splines.first, + *spline_eval = mask_layer_eval->splines.first; + spline_orig != NULL; + spline_orig = spline_orig->next, spline_eval = spline_eval->next) { - LISTBASE_FOREACH (MaskSpline *, spline, &mask_layer->splines) { - MaskSplinePoint *points_array = BKE_mask_spline_point_array(spline); + MaskSplinePoint *points_array = BKE_mask_spline_point_array(spline_eval); - for (int i = 0; i < spline->tot_point; i++) { - MaskSplinePoint *point = &spline->points[i]; + for (int i = 0; i < spline_orig->tot_point; i++) { + MaskSplinePoint *point = &spline_orig->points[i]; MaskSplinePoint *point_deform = &points_array[i]; if (mask_spline_point_inside_ellipse(&point_deform->bezt, offset, ellipse)) { @@ -696,10 +723,10 @@ static int circle_select_exec(bContext *C, wmOperator *op) } if (changed) { - ED_mask_select_flush_all(mask); + ED_mask_select_flush_all(mask_orig); - DEG_id_tag_update(&mask->id, ID_RECALC_SELECT); - WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask); + DEG_id_tag_update(&mask_orig->id, ID_RECALC_SELECT); + WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask_orig); return OPERATOR_FINISHED; } diff --git a/source/blender/editors/object/CMakeLists.txt b/source/blender/editors/object/CMakeLists.txt index 02feccc211a..97376a495c1 100644 --- a/source/blender/editors/object/CMakeLists.txt +++ b/source/blender/editors/object/CMakeLists.txt @@ -43,7 +43,7 @@ set(SRC object_gpencil_modifier.c object_hook.c object_modes.c - object_modifier.c + object_modifier.cc object_ops.c object_random.c object_relations.c diff --git a/source/blender/editors/object/object_edit.c b/source/blender/editors/object/object_edit.c index cc8644285c0..2518b2d201d 100644 --- a/source/blender/editors/object/object_edit.c +++ b/source/blender/editors/object/object_edit.c @@ -1828,13 +1828,6 @@ static bool move_to_collection_poll(bContext *C) if (CTX_wm_space_outliner(C) != NULL) { return ED_outliner_collections_editor_poll(C); } - - View3D *v3d = CTX_wm_view3d(C); - - if (v3d && v3d->localvd) { - return false; - } - return ED_operator_objectmode(C); } diff --git a/source/blender/editors/object/object_intern.h b/source/blender/editors/object/object_intern.h index dd6e11abbf9..fb61200be9d 100644 --- a/source/blender/editors/object/object_intern.h +++ b/source/blender/editors/object/object_intern.h @@ -36,6 +36,7 @@ void OBJECT_OT_scale_clear(struct wmOperatorType *ot); void OBJECT_OT_origin_clear(struct wmOperatorType *ot); void OBJECT_OT_visual_transform_apply(struct wmOperatorType *ot); void OBJECT_OT_transform_apply(struct wmOperatorType *ot); +void OBJECT_OT_parent_inverse_apply(wmOperatorType *ot); void OBJECT_OT_transform_axis_target(struct wmOperatorType *ot); void OBJECT_OT_origin_set(struct wmOperatorType *ot); @@ -200,6 +201,7 @@ void OBJECT_OT_skin_armature_create(struct wmOperatorType *ot); void OBJECT_OT_laplaciandeform_bind(struct wmOperatorType *ot); void OBJECT_OT_surfacedeform_bind(struct wmOperatorType *ot); void OBJECT_OT_geometry_nodes_input_attribute_toggle(struct wmOperatorType *ot); +void OBJECT_OT_geometry_node_tree_copy_assign(struct wmOperatorType *ot); /* object_gpencil_modifiers.c */ diff --git a/source/blender/editors/object/object_modifier.c b/source/blender/editors/object/object_modifier.cc index 545265b18b1..f7543c97903 100644 --- a/source/blender/editors/object/object_modifier.c +++ b/source/blender/editors/object/object_modifier.cc @@ -5,9 +5,9 @@ * \ingroup edobj */ -#include <math.h> -#include <stdio.h> -#include <stdlib.h> +#include <cmath> +#include <cstdio> +#include <cstdlib> #include "MEM_guardedalloc.h" @@ -142,19 +142,19 @@ static void object_force_modifier_bind_simple_options(Depsgraph *depsgraph, ModifierData *ED_object_modifier_add( ReportList *reports, Main *bmain, Scene *scene, Object *ob, const char *name, int type) { - ModifierData *md = NULL, *new_md = NULL; - const ModifierTypeInfo *mti = BKE_modifier_get_info(type); + ModifierData *md = nullptr, *new_md = nullptr; + const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)type); /* Check compatibility of modifier [T25291, T50373]. */ if (!BKE_object_support_modifier_type_check(ob, type)) { BKE_reportf(reports, RPT_WARNING, "Modifiers cannot be added to object '%s'", ob->id.name + 2); - return NULL; + return nullptr; } if (mti->flags & eModifierTypeFlag_Single) { - if (BKE_modifiers_findby_type(ob, type)) { + if (BKE_modifiers_findby_type(ob, (ModifierType)type)) { BKE_report(reports, RPT_WARNING, "Only one modifier of this type is allowed"); - return NULL; + return nullptr; } } @@ -169,9 +169,10 @@ ModifierData *ED_object_modifier_add( new_md = BKE_modifier_new(type); if (mti->flags & eModifierTypeFlag_RequiresOriginalData) { - md = ob->modifiers.first; + md = static_cast<ModifierData *>(ob->modifiers.first); - while (md && BKE_modifier_get_info(md->type)->type == eModifierTypeType_OnlyDeform) { + while (md && + BKE_modifier_get_info((ModifierType)md->type)->type == eModifierTypeType_OnlyDeform) { md = md->next; } @@ -217,7 +218,7 @@ ModifierData *ED_object_modifier_add( } else if (type == eModifierType_Skin) { /* ensure skin-node customdata exists */ - BKE_mesh_ensure_skin_customdata(ob->data); + BKE_mesh_ensure_skin_customdata(static_cast<Mesh *>(ob->data)); } } @@ -248,7 +249,7 @@ bool ED_object_iter_other(Main *bmain, bool (*callback)(Object *ob, void *callback_data), void *callback_data) { - ID *ob_data_id = orig_ob->data; + ID *ob_data_id = static_cast<ID *>(orig_ob->data); int users = ob_data_id->us; if (ob_data_id->flag & LIB_FAKEUSER) { @@ -260,7 +261,8 @@ bool ED_object_iter_other(Main *bmain, Object *ob; int totfound = include_orig ? 0 : 1; - for (ob = bmain->objects.first; ob && totfound < users; ob = ob->id.next) { + for (ob = static_cast<Object *>(bmain->objects.first); ob && totfound < users; + ob = reinterpret_cast<Object *>(ob->id.next)) { if (((ob != orig_ob) || include_orig) && (ob->data == orig_ob->data)) { if (callback(ob, callback_data)) { return true; @@ -281,7 +283,7 @@ static bool object_has_modifier_cb(Object *ob, void *data) { ModifierType type = *((ModifierType *)data); - return object_has_modifier(ob, NULL, type); + return object_has_modifier(ob, nullptr, type); } bool ED_object_multires_update_totlevels_cb(Object *ob, void *totlevel_v) @@ -342,7 +344,7 @@ static bool object_modifier_remove( else if (md->type == eModifierType_Multires) { /* Delete MDisps layer if not used by another multires modifier */ if (object_modifier_safe_to_delete(bmain, ob, md, eModifierType_Multires)) { - multires_customdata_delete(ob->data); + multires_customdata_delete(static_cast<Mesh *>(ob->data)); } } else if (md->type == eModifierType_Skin) { @@ -384,7 +386,7 @@ bool ED_object_modifier_remove( void ED_object_modifier_clear(Main *bmain, Scene *scene, Object *ob) { - ModifierData *md = ob->modifiers.first; + ModifierData *md = static_cast<ModifierData *>(ob->modifiers.first); bool sort_depsgraph = false; if (!md) { @@ -406,10 +408,10 @@ void ED_object_modifier_clear(Main *bmain, Scene *scene, Object *ob) bool ED_object_modifier_move_up(ReportList *reports, Object *ob, ModifierData *md) { if (md->prev) { - const ModifierTypeInfo *mti = BKE_modifier_get_info(md->type); + const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md->type); if (mti->type != eModifierTypeType_OnlyDeform) { - const ModifierTypeInfo *nmti = BKE_modifier_get_info(md->prev->type); + const ModifierTypeInfo *nmti = BKE_modifier_get_info((ModifierType)md->prev->type); if (nmti->flags & eModifierTypeFlag_RequiresOriginalData) { BKE_report(reports, RPT_WARNING, "Cannot move above a modifier requiring original data"); @@ -430,10 +432,10 @@ bool ED_object_modifier_move_up(ReportList *reports, Object *ob, ModifierData *m bool ED_object_modifier_move_down(ReportList *reports, Object *ob, ModifierData *md) { if (md->next) { - const ModifierTypeInfo *mti = BKE_modifier_get_info(md->type); + const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md->type); if (mti->flags & eModifierTypeFlag_RequiresOriginalData) { - const ModifierTypeInfo *nmti = BKE_modifier_get_info(md->next->type); + const ModifierTypeInfo *nmti = BKE_modifier_get_info((ModifierType)md->next->type); if (nmti->type != eModifierTypeType_OnlyDeform) { BKE_report(reports, RPT_WARNING, "Cannot move beyond a non-deforming modifier"); @@ -456,7 +458,7 @@ bool ED_object_modifier_move_to_index(ReportList *reports, ModifierData *md, const int index) { - BLI_assert(md != NULL); + BLI_assert(md != nullptr); BLI_assert(index >= 0); if (index >= BLI_listbase_count(&ob->modifiers)) { BKE_report(reports, RPT_WARNING, "Cannot move modifier beyond the end of the stack"); @@ -534,7 +536,7 @@ bool ED_object_modifier_convert(ReportList *UNUSED(reports), return false; } ParticleSystem *psys_eval = psys_eval_get(depsgraph, ob, psys_orig); - if (psys_eval->pathcache == NULL) { + if (psys_eval->pathcache == nullptr) { return false; } @@ -572,15 +574,15 @@ bool ED_object_modifier_convert(ReportList *UNUSED(reports), } /* add new mesh */ - Object *obn = BKE_object_add(bmain, view_layer, OB_MESH, NULL); - Mesh *me = obn->data; + Object *obn = BKE_object_add(bmain, view_layer, OB_MESH, nullptr); + Mesh *me = static_cast<Mesh *>(obn->data); me->totvert = verts_num; me->totedge = edges_num; - me->mvert = CustomData_add_layer(&me->vdata, CD_MVERT, CD_CALLOC, NULL, verts_num); - me->medge = CustomData_add_layer(&me->edata, CD_MEDGE, CD_CALLOC, NULL, edges_num); - me->mface = CustomData_add_layer(&me->fdata, CD_MFACE, CD_CALLOC, NULL, 0); + me->mvert = (MVert *)CustomData_add_layer(&me->vdata, CD_MVERT, CD_CALLOC, nullptr, verts_num); + me->medge = (MEdge *)CustomData_add_layer(&me->edata, CD_MEDGE, CD_CALLOC, nullptr, edges_num); + me->mface = (MFace *)CustomData_add_layer(&me->fdata, CD_MFACE, CD_CALLOC, nullptr, 0); MVert *mvert = me->mvert; MEdge *medge = me->medge; @@ -650,9 +652,9 @@ static bool modifier_apply_shape(Main *bmain, Object *ob, ModifierData *md_eval) { - const ModifierTypeInfo *mti = BKE_modifier_get_info(md_eval->type); + const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md_eval->type); - if (mti->isDisabled && mti->isDisabled(scene, md_eval, 0)) { + if (mti->isDisabled && mti->isDisabled(scene, md_eval, false)) { BKE_report(reports, RPT_ERROR, "Modifier is disabled, skipping apply"); return false; } @@ -668,7 +670,7 @@ static bool modifier_apply_shape(Main *bmain, * we can look into supporting them. */ if (ob->type == OB_MESH) { - Mesh *me = ob->data; + Mesh *me = static_cast<Mesh *>(ob->data); Key *key = me->key; if (!BKE_modifier_is_same_topology(md_eval) || mti->type == eModifierTypeType_NonGeometrical) { @@ -683,22 +685,22 @@ static bool modifier_apply_shape(Main *bmain, return false; } - if (key == NULL) { + if (key == nullptr) { key = me->key = BKE_key_add(bmain, (ID *)me); key->type = KEY_RELATIVE; /* if that was the first key block added, then it was the basis. * Initialize it with the mesh, and add another for the modifier */ - KeyBlock *kb = BKE_keyblock_add(key, NULL); + KeyBlock *kb = BKE_keyblock_add(key, nullptr); BKE_keyblock_convert_from_mesh(me, key, kb); } KeyBlock *kb = BKE_keyblock_add(key, md_eval->name); BKE_mesh_nomain_to_meshkey(mesh_applied, me, kb); - BKE_id_free(NULL, mesh_applied); + BKE_id_free(nullptr, mesh_applied); } else { - /* TODO: implement for hair, point clouds and volumes. */ + /* TODO: implement for curves, point clouds and volumes. */ BKE_report(reports, RPT_ERROR, "Cannot apply modifier for this object type"); return false; } @@ -708,15 +710,15 @@ static bool modifier_apply_shape(Main *bmain, static bool modifier_apply_obdata( ReportList *reports, Depsgraph *depsgraph, Scene *scene, Object *ob, ModifierData *md_eval) { - const ModifierTypeInfo *mti = BKE_modifier_get_info(md_eval->type); + const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md_eval->type); - if (mti->isDisabled && mti->isDisabled(scene, md_eval, 0)) { + if (mti->isDisabled && mti->isDisabled(scene, md_eval, false)) { BKE_report(reports, RPT_ERROR, "Modifier is disabled, skipping apply"); return false; } if (ob->type == OB_MESH) { - Mesh *me = ob->data; + Mesh *me = static_cast<Mesh *>(ob->data); MultiresModifierData *mmd = find_multires_modifier_before(scene, md_eval); if (me->key && mti->type != eModifierTypeType_NonGeometrical) { @@ -757,9 +759,9 @@ static bool modifier_apply_obdata( } else if (ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF)) { Object *object_eval = DEG_get_evaluated_object(depsgraph, ob); - Curve *curve = ob->data; - Curve *curve_eval = (Curve *)object_eval->data; - ModifierEvalContext mectx = {depsgraph, object_eval, 0}; + Curve *curve = static_cast<Curve *>(ob->data); + Curve *curve_eval = static_cast<Curve *>(object_eval->data); + ModifierEvalContext mectx = {depsgraph, object_eval, (ModifierApplyFlag)0}; if (ELEM(mti->type, eModifierTypeType_Constructive, eModifierTypeType_Nonconstructive)) { BKE_report( @@ -773,7 +775,7 @@ static bool modifier_apply_obdata( int verts_num; float(*vertexCos)[3] = BKE_curve_nurbs_vert_coords_alloc(&curve_eval->nurb, &verts_num); - mti->deformVerts(md_eval, &mectx, NULL, vertexCos, verts_num); + mti->deformVerts(md_eval, &mectx, nullptr, vertexCos, verts_num); BKE_curve_nurbs_vert_coords_apply(&curve->nurb, vertexCos, false); MEM_freeN(vertexCos); @@ -782,8 +784,8 @@ static bool modifier_apply_obdata( } else if (ob->type == OB_LATTICE) { Object *object_eval = DEG_get_evaluated_object(depsgraph, ob); - Lattice *lattice = ob->data; - ModifierEvalContext mectx = {depsgraph, object_eval, 0}; + Lattice *lattice = static_cast<Lattice *>(ob->data); + ModifierEvalContext mectx = {depsgraph, object_eval, (ModifierApplyFlag)0}; if (ELEM(mti->type, eModifierTypeType_Constructive, eModifierTypeType_Nonconstructive)) { BKE_report(reports, RPT_ERROR, "Constructive modifiers cannot be applied"); @@ -792,7 +794,7 @@ static bool modifier_apply_obdata( int verts_num; float(*vertexCos)[3] = BKE_lattice_vert_coords_alloc(lattice, &verts_num); - mti->deformVerts(md_eval, &mectx, NULL, vertexCos, verts_num); + mti->deformVerts(md_eval, &mectx, nullptr, vertexCos, verts_num); BKE_lattice_vert_coords_apply(lattice, vertexCos); MEM_freeN(vertexCos); @@ -800,7 +802,7 @@ static bool modifier_apply_obdata( DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); } else { - /* TODO: implement for hair, point clouds and volumes. */ + /* TODO: implement for curves, point clouds and volumes. */ BKE_report(reports, RPT_ERROR, "Cannot apply modifier for this object type"); return false; } @@ -918,7 +920,7 @@ static int modifier_add_exec(bContext *C, wmOperator *op) Object *ob = ED_object_active_context(C); int type = RNA_enum_get(op->ptr, "type"); - if (!ED_object_modifier_add(op->reports, bmain, scene, ob, NULL, type)) { + if (!ED_object_modifier_add(op->reports, bmain, scene, ob, nullptr, type)) { return OPERATOR_CANCELLED; } @@ -938,15 +940,15 @@ static const EnumPropertyItem *modifier_add_itemf(bContext *C, return rna_enum_object_modifier_type_items; } - EnumPropertyItem *items = NULL; + EnumPropertyItem *items = nullptr; int totitem = 0; - const EnumPropertyItem *group_item = NULL; + const EnumPropertyItem *group_item = nullptr; for (int a = 0; rna_enum_object_modifier_type_items[a].identifier; a++) { const EnumPropertyItem *md_item = &rna_enum_object_modifier_type_items[a]; if (md_item->identifier[0]) { - const ModifierTypeInfo *mti = BKE_modifier_get_info(md_item->value); + const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md_item->value); if (mti->flags & eModifierTypeFlag_NoUserAdd) { continue; @@ -963,7 +965,7 @@ static const EnumPropertyItem *modifier_add_itemf(bContext *C, if (group_item) { RNA_enum_item_add(&items, &totitem, group_item); - group_item = NULL; + group_item = nullptr; } RNA_enum_item_add(&items, &totitem, md_item); @@ -1016,9 +1018,9 @@ bool edit_modifier_poll_generic(bContext *C, Main *bmain = CTX_data_main(C); PointerRNA ptr = CTX_data_pointer_get_type(C, "modifier", rna_type); Object *ob = (ptr.owner_id) ? (Object *)ptr.owner_id : ED_object_active_context(C); - ModifierData *mod = ptr.data; /* May be NULL. */ + ModifierData *mod = static_cast<ModifierData *>(ptr.data); /* May be nullptr. */ - if (mod == NULL && ob != NULL) { + if (mod == nullptr && ob != nullptr) { mod = BKE_object_active_modifier(ob); } @@ -1038,7 +1040,7 @@ bool edit_modifier_poll_generic(bContext *C, return false; } - if (!is_editmode_allowed && CTX_data_edit_object(C) != NULL) { + if (!is_editmode_allowed && CTX_data_edit_object(C) != nullptr) { CTX_wm_operator_poll_msg_set(C, "This modifier operation is not allowed from Edit mode"); return false; } @@ -1061,7 +1063,7 @@ static bool edit_modifier_liboverride_allowed_poll(bContext *C) void edit_modifier_properties(wmOperatorType *ot) { PropertyRNA *prop = RNA_def_string( - ot->srna, "modifier", NULL, MAX_NAME, "Modifier", "Name of the modifier to edit"); + ot->srna, "modifier", nullptr, MAX_NAME, "Modifier", "Name of the modifier to edit"); RNA_def_property_flag(prop, PROP_HIDDEN); } @@ -1087,8 +1089,8 @@ bool edit_modifier_invoke_properties(bContext *C, wmOperator *op) } PointerRNA ctx_ptr = CTX_data_pointer_get_type(C, "modifier", &RNA_Modifier); - if (ctx_ptr.data != NULL) { - ModifierData *md = ctx_ptr.data; + if (ctx_ptr.data != nullptr) { + ModifierData *md = static_cast<ModifierData *>(ctx_ptr.data); RNA_string_set(op->ptr, "modifier", md->name); return true; } @@ -1112,14 +1114,14 @@ static bool edit_modifier_invoke_properties_with_hover(bContext *C, /* Note that the context pointer is *not* the active modifier, it is set in UI layouts. */ PointerRNA ctx_ptr = CTX_data_pointer_get_type(C, "modifier", &RNA_Modifier); - if (ctx_ptr.data != NULL) { - ModifierData *md = ctx_ptr.data; + if (ctx_ptr.data != nullptr) { + ModifierData *md = static_cast<ModifierData *>(ctx_ptr.data); RNA_string_set(op->ptr, "modifier", md->name); return true; } PointerRNA *panel_ptr = UI_region_panel_custom_data_under_cursor(C, event); - if (panel_ptr == NULL || RNA_pointer_is_null(panel_ptr)) { + if (panel_ptr == nullptr || RNA_pointer_is_null(panel_ptr)) { *r_retval = OPERATOR_CANCELLED; return false; } @@ -1132,7 +1134,7 @@ static bool edit_modifier_invoke_properties_with_hover(bContext *C, return false; } - const ModifierData *md = panel_ptr->data; + const ModifierData *md = static_cast<const ModifierData *>(panel_ptr->data); RNA_string_set(op->ptr, "modifier", md->name); return true; } @@ -1145,7 +1147,7 @@ ModifierData *edit_modifier_property_get(wmOperator *op, Object *ob, int type) ModifierData *md = BKE_modifiers_findby_name(ob, modifier_name); if (md && type != 0 && md->type != type) { - md = NULL; + md = nullptr; } return md; @@ -1166,7 +1168,7 @@ static int modifier_remove_exec(bContext *C, wmOperator *op) ModifierData *md = edit_modifier_property_get(op, ob, 0); int mode_orig = ob->mode; - if (md == NULL) { + if (md == nullptr) { return OPERATOR_CANCELLED; } @@ -1184,7 +1186,7 @@ static int modifier_remove_exec(bContext *C, wmOperator *op) if (mode_orig & OB_MODE_PARTICLE_EDIT) { if ((ob->mode & OB_MODE_PARTICLE_EDIT) == 0) { if (ob == OBACT(view_layer)) { - WM_event_add_notifier(C, NC_SCENE | ND_MODE | NS_MODE_OBJECT, NULL); + WM_event_add_notifier(C, NC_SCENE | ND_MODE | NS_MODE_OBJECT, nullptr); } } } @@ -1371,14 +1373,14 @@ static bool modifier_apply_poll(bContext *C) Scene *scene = CTX_data_scene(C); PointerRNA ptr = CTX_data_pointer_get_type(C, "modifier", &RNA_Modifier); - Object *ob = (ptr.owner_id != NULL) ? (Object *)ptr.owner_id : ED_object_active_context(C); - ModifierData *md = ptr.data; /* May be NULL. */ + Object *ob = (ptr.owner_id != nullptr) ? (Object *)ptr.owner_id : ED_object_active_context(C); + ModifierData *md = static_cast<ModifierData *>(ptr.data); /* May be nullptr. */ - if (ID_IS_OVERRIDE_LIBRARY(ob) || ((ob->data != NULL) && ID_IS_OVERRIDE_LIBRARY(ob->data))) { + if (ID_IS_OVERRIDE_LIBRARY(ob) || ((ob->data != nullptr) && ID_IS_OVERRIDE_LIBRARY(ob->data))) { CTX_wm_operator_poll_msg_set(C, "Modifiers cannot be applied on override data"); return false; } - if (md != NULL) { + if (md != nullptr) { if ((ob->mode & OB_MODE_SCULPT) && (find_multires_modifier_before(scene, md)) && (BKE_modifier_is_same_topology(md) == false)) { CTX_wm_operator_poll_msg_set( @@ -1399,14 +1401,14 @@ static int modifier_apply_exec_ex(bContext *C, wmOperator *op, int apply_as, boo const bool do_report = RNA_boolean_get(op->ptr, "report"); const bool do_single_user = RNA_boolean_get(op->ptr, "single_user"); - if (md == NULL) { + if (md == nullptr) { return OPERATOR_CANCELLED; } if (do_single_user && ID_REAL_USERS(ob->data) > 1) { ED_object_single_obdata_user(bmain, scene, ob); BKE_main_id_newptr_and_tag_clear(bmain); - WM_event_add_notifier(C, NC_WINDOW, NULL); + WM_event_add_notifier(C, NC_WINDOW, nullptr); DEG_relations_tag_update(bmain); } @@ -1447,9 +1449,9 @@ static int modifier_apply_invoke(bContext *C, wmOperator *op, const wmEvent *eve int retval; if (edit_modifier_invoke_properties_with_hover(C, op, event, &retval)) { PointerRNA ptr = CTX_data_pointer_get_type(C, "modifier", &RNA_Modifier); - Object *ob = (ptr.owner_id != NULL) ? (Object *)ptr.owner_id : ED_object_active_context(C); + Object *ob = (ptr.owner_id != nullptr) ? (Object *)ptr.owner_id : ED_object_active_context(C); - if ((ob->data != NULL) && ID_REAL_USERS(ob->data) > 1) { + if ((ob->data != nullptr) && ID_REAL_USERS(ob->data) > 1) { PropertyRNA *prop = RNA_struct_find_property(op->ptr, "single_user"); if (!RNA_property_is_set(op->ptr, prop)) { RNA_property_boolean_set(op->ptr, prop, true); @@ -1485,7 +1487,7 @@ void OBJECT_OT_modifier_apply(wmOperatorType *ot) false, "Make Data Single User", "Make the object's data single user if needed"); - RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + RNA_def_property_flag(prop, (PropertyFlag)(PROP_HIDDEN | PROP_SKIP_SAVE)); } /** \} */ @@ -1525,7 +1527,7 @@ static char *modifier_apply_as_shapekey_get_description(struct bContext *UNUSED( return BLI_strdup(TIP_("Apply modifier as a new shapekey and keep it in the stack")); } - return NULL; + return nullptr; } void OBJECT_OT_modifier_apply_as_shapekey(wmOperatorType *ot) @@ -1703,7 +1705,7 @@ static int modifier_copy_to_selected_exec(bContext *C, wmOperator *op) } int num_copied = 0; - const ModifierTypeInfo *mti = BKE_modifier_get_info(md->type); + const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md->type); CTX_DATA_BEGIN (C, Object *, ob, selected_objects) { if (ob == obact) { @@ -1721,7 +1723,7 @@ static int modifier_copy_to_selected_exec(bContext *C, wmOperator *op) } if (mti->flags & eModifierTypeFlag_Single) { - if (BKE_modifiers_findby_type(ob, md->type)) { + if (BKE_modifiers_findby_type(ob, (ModifierType)md->type)) { BKE_reportf(op->reports, RPT_WARNING, "Modifier can only be added once to object '%s'", @@ -1768,12 +1770,12 @@ static bool modifier_copy_to_selected_poll(bContext *C) { PointerRNA ptr = CTX_data_pointer_get_type(C, "modifier", &RNA_Modifier); Object *obact = (ptr.owner_id) ? (Object *)ptr.owner_id : ED_object_active_context(C); - ModifierData *md = ptr.data; + ModifierData *md = static_cast<ModifierData *>(ptr.data); /* This just mirrors the check in #BKE_object_copy_modifier, * but there is no reasoning for it there. */ if (md && ELEM(md->type, eModifierType_Hook, eModifierType_Collision)) { - CTX_wm_operator_poll_msg_set(C, "Not supported for \"Collision\" or \"Hook\" modifiers"); + CTX_wm_operator_poll_msg_set(C, R"(Not supported for "Collision" or "Hook" modifiers)"); return false; } @@ -1909,7 +1911,7 @@ static EnumPropertyItem prop_multires_subdivide_mode_type[] = { 0, "Linear", "Create a new level using linear interpolation of the sculpted displacement"}, - {0, NULL, 0, NULL, NULL}, + {0, nullptr, 0, nullptr, nullptr}, }; static int multires_subdivide_exec(bContext *C, wmOperator *op) @@ -1978,7 +1980,7 @@ void OBJECT_OT_multires_subdivide(wmOperatorType *ot) static int multires_reshape_exec(bContext *C, wmOperator *op) { Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - Object *ob = ED_object_active_context(C), *secondob = NULL; + Object *ob = ED_object_active_context(C), *secondob = nullptr; MultiresModifierData *mmd = (MultiresModifierData *)edit_modifier_property_get( op, ob, eModifierType_Multires); @@ -2048,7 +2050,7 @@ static int multires_external_save_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); Object *ob = ED_object_active_context(C); - Mesh *me = (ob) ? ob->data : op->customdata; + Mesh *me = (ob) ? static_cast<Mesh *>(ob->data) : static_cast<Mesh *>(op->customdata); char path[FILE_MAX]; const bool relative = RNA_boolean_get(op->ptr, "relative_path"); @@ -2075,7 +2077,7 @@ static int multires_external_save_exec(bContext *C, wmOperator *op) static int multires_external_save_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) { Object *ob = ED_object_active_context(C); - Mesh *me = ob->data; + Mesh *me = static_cast<Mesh *>(ob->data); char path[FILE_MAX]; if (!edit_modifier_invoke_properties(C, op)) { @@ -2140,7 +2142,7 @@ void OBJECT_OT_multires_external_save(wmOperatorType *ot) static int multires_external_pack_exec(bContext *C, wmOperator *UNUSED(op)) { Object *ob = ED_object_active_context(C); - Mesh *me = ob->data; + Mesh *me = static_cast<Mesh *>(ob->data); if (!CustomData_external_test(&me->ldata, CD_MDISPS)) { return OPERATOR_CANCELLED; @@ -2334,7 +2336,7 @@ void OBJECT_OT_multires_rebuild_subdiv(wmOperatorType *ot) static void modifier_skin_customdata_delete(Object *ob) { - Mesh *me = ob->data; + Mesh *me = static_cast<Mesh *>(ob->data); BMEditMesh *em = me->edit_mesh; if (em) { @@ -2353,7 +2355,7 @@ static bool skin_poll(bContext *C) static bool skin_edit_poll(bContext *C) { Object *ob = CTX_data_edit_object(C); - return (ob != NULL && + return (ob != nullptr && edit_modifier_poll_generic(C, &RNA_SkinModifier, (1 << OB_MESH), true, false) && !ID_IS_OVERRIDE_LIBRARY(ob) && !ID_IS_OVERRIDE_LIBRARY(ob->data)); } @@ -2367,7 +2369,7 @@ static void skin_root_clear(BMVert *bm_vert, GSet *visited, const int cd_vert_sk BMVert *v2 = BM_edge_other_vert(bm_edge, bm_vert); if (BLI_gset_add(visited, v2)) { - MVertSkin *vs = BM_ELEM_CD_GET_VOID_P(v2, cd_vert_skin_offset); + MVertSkin *vs = static_cast<MVertSkin *>(BM_ELEM_CD_GET_VOID_P(v2, cd_vert_skin_offset)); /* clear vertex root flag and add to visited set */ vs->flag &= ~MVERT_SKIN_ROOT; @@ -2385,7 +2387,7 @@ static int skin_root_mark_exec(bContext *C, wmOperator *UNUSED(op)) GSet *visited = BLI_gset_ptr_new(__func__); - BKE_mesh_ensure_skin_customdata(ob->data); + BKE_mesh_ensure_skin_customdata(static_cast<Mesh *>(ob->data)); const int cd_vert_skin_offset = CustomData_get_offset(&bm->vdata, CD_MVERT_SKIN); @@ -2393,7 +2395,8 @@ static int skin_root_mark_exec(bContext *C, wmOperator *UNUSED(op)) BMIter bm_iter; BM_ITER_MESH (bm_vert, &bm_iter, bm, BM_VERTS_OF_MESH) { if (BM_elem_flag_test(bm_vert, BM_ELEM_SELECT) && BLI_gset_add(visited, bm_vert)) { - MVertSkin *vs = BM_ELEM_CD_GET_VOID_P(bm_vert, cd_vert_skin_offset); + MVertSkin *vs = static_cast<MVertSkin *>( + BM_ELEM_CD_GET_VOID_P(bm_vert, cd_vert_skin_offset)); /* mark vertex as root and add to visited set */ vs->flag |= MVERT_SKIN_ROOT; @@ -2403,7 +2406,7 @@ static int skin_root_mark_exec(bContext *C, wmOperator *UNUSED(op)) } } - BLI_gset_free(visited, NULL); + BLI_gset_free(visited, nullptr); DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob); @@ -2424,17 +2427,17 @@ void OBJECT_OT_skin_root_mark(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -typedef enum { +enum SkinLooseAction { SKIN_LOOSE_MARK, SKIN_LOOSE_CLEAR, -} SkinLooseAction; +}; static int skin_loose_mark_clear_exec(bContext *C, wmOperator *op) { Object *ob = CTX_data_edit_object(C); BMEditMesh *em = BKE_editmesh_from_object(ob); BMesh *bm = em->bm; - SkinLooseAction action = RNA_enum_get(op->ptr, "action"); + SkinLooseAction action = static_cast<SkinLooseAction>(RNA_enum_get(op->ptr, "action")); if (!CustomData_has_layer(&bm->vdata, CD_MVERT_SKIN)) { return OPERATOR_CANCELLED; @@ -2444,7 +2447,8 @@ static int skin_loose_mark_clear_exec(bContext *C, wmOperator *op) BMIter bm_iter; BM_ITER_MESH (bm_vert, &bm_iter, bm, BM_VERTS_OF_MESH) { if (BM_elem_flag_test(bm_vert, BM_ELEM_SELECT)) { - MVertSkin *vs = CustomData_bmesh_get(&bm->vdata, bm_vert->head.data, CD_MVERT_SKIN); + MVertSkin *vs = static_cast<MVertSkin *>( + CustomData_bmesh_get(&bm->vdata, bm_vert->head.data, CD_MVERT_SKIN)); switch (action) { case SKIN_LOOSE_MARK: @@ -2468,7 +2472,7 @@ void OBJECT_OT_skin_loose_mark_clear(wmOperatorType *ot) static const EnumPropertyItem action_items[] = { {SKIN_LOOSE_MARK, "MARK", 0, "Mark", "Mark selected vertices as loose"}, {SKIN_LOOSE_CLEAR, "CLEAR", 0, "Clear", "Set selected vertices as not loose"}, - {0, NULL, 0, NULL, NULL}, + {0, nullptr, 0, nullptr, nullptr}, }; ot->name = "Skin Mark/Clear Loose"; @@ -2481,7 +2485,7 @@ void OBJECT_OT_skin_loose_mark_clear(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - RNA_def_enum(ot->srna, "action", action_items, SKIN_LOOSE_MARK, "Action", NULL); + RNA_def_enum(ot->srna, "action", action_items, SKIN_LOOSE_MARK, "Action", nullptr); } static int skin_radii_equalize_exec(bContext *C, wmOperator *UNUSED(op)) @@ -2498,7 +2502,8 @@ static int skin_radii_equalize_exec(bContext *C, wmOperator *UNUSED(op)) BMIter bm_iter; BM_ITER_MESH (bm_vert, &bm_iter, bm, BM_VERTS_OF_MESH) { if (BM_elem_flag_test(bm_vert, BM_ELEM_SELECT)) { - MVertSkin *vs = CustomData_bmesh_get(&bm->vdata, bm_vert->head.data, CD_MVERT_SKIN); + MVertSkin *vs = static_cast<MVertSkin *>( + CustomData_bmesh_get(&bm->vdata, bm_vert->head.data, CD_MVERT_SKIN)); float avg = (vs->radius[0] + vs->radius[1]) * 0.5f; vs->radius[0] = vs->radius[1] = avg; @@ -2548,7 +2553,7 @@ static void skin_armature_bone_create(Object *skin_ob, EditBone *bone = ED_armature_ebone_add(arm, "Bone"); bone->parent = parent_bone; - if (parent_bone != NULL) { + if (parent_bone != nullptr) { bone->flag |= BONE_CONNECTED; } @@ -2559,7 +2564,7 @@ static void skin_armature_bone_create(Object *skin_ob, /* add bDeformGroup */ bDeformGroup *dg = BKE_object_defgroup_add_name(skin_ob, bone->name); - if (dg != NULL) { + if (dg != nullptr) { ED_vgroup_vert_add(skin_ob, dg, parent_v, 1, WEIGHT_REPLACE); ED_vgroup_vert_add(skin_ob, dg, v, 1, WEIGHT_REPLACE); } @@ -2570,7 +2575,7 @@ static void skin_armature_bone_create(Object *skin_ob, static Object *modifier_skin_armature_create(Depsgraph *depsgraph, Main *bmain, Object *skin_ob) { - Mesh *me = skin_ob->data; + Mesh *me = static_cast<Mesh *>(skin_ob->data); Scene *scene_eval = DEG_get_evaluated_scene(depsgraph); Object *ob_eval = DEG_get_evaluated_object(depsgraph, skin_ob); @@ -2579,18 +2584,19 @@ static Object *modifier_skin_armature_create(Depsgraph *depsgraph, Main *bmain, MVert *mvert = me_eval_deform->mvert; /* add vertex weights to original mesh */ - CustomData_add_layer(&me->vdata, CD_MDEFORMVERT, CD_CALLOC, NULL, me->totvert); + CustomData_add_layer(&me->vdata, CD_MDEFORMVERT, CD_CALLOC, nullptr, me->totvert); ViewLayer *view_layer = DEG_get_input_view_layer(depsgraph); - Object *arm_ob = BKE_object_add(bmain, view_layer, OB_ARMATURE, NULL); + Object *arm_ob = BKE_object_add(bmain, view_layer, OB_ARMATURE, nullptr); BKE_object_transform_copy(arm_ob, skin_ob); - bArmature *arm = arm_ob->data; + bArmature *arm = static_cast<bArmature *>(arm_ob->data); arm->layer = 1; arm_ob->dtx |= OB_DRAW_IN_FRONT; arm->drawtype = ARM_LINE; - arm->edbo = MEM_callocN(sizeof(ListBase), "edbo armature"); + arm->edbo = MEM_cnew<ListBase>("edbo armature"); - MVertSkin *mvert_skin = CustomData_get_layer(&me->vdata, CD_MVERT_SKIN); + MVertSkin *mvert_skin = static_cast<MVertSkin *>( + CustomData_get_layer(&me->vdata, CD_MVERT_SKIN)); int *emap_mem; MeshElemMap *emap; BKE_mesh_vert_edge_map_create(&emap, &emap_mem, me->medge, me->totvert, me->totedge); @@ -2601,7 +2607,7 @@ static Object *modifier_skin_armature_create(Depsgraph *depsgraph, Main *bmain, * edit-armature functions to convert back to regular bones */ for (int v = 0; v < me->totvert; v++) { if (mvert_skin[v].flag & MVERT_SKIN_ROOT) { - EditBone *bone = NULL; + EditBone *bone = nullptr; /* Unless the skin root has just one adjacent edge, create * a fake root bone (have it going off in the Y direction @@ -2637,7 +2643,7 @@ static int skin_armature_create_exec(bContext *C, wmOperator *op) Main *bmain = CTX_data_main(C); Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); Object *ob = CTX_data_active_object(C); - Mesh *me = ob->data; + Mesh *me = static_cast<Mesh *>(ob->data); ModifierData *skin_md; if (!CustomData_has_layer(&me->vdata, CD_MVERT_SKIN)) { @@ -2716,7 +2722,7 @@ static int correctivesmooth_bind_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - const bool is_bind = (csmd->bind_coords != NULL); + const bool is_bind = (csmd->bind_coords != nullptr); MEM_SAFE_FREE(csmd->bind_coords); MEM_SAFE_FREE(csmd->delta_cache.deltas); @@ -2785,11 +2791,11 @@ static int meshdeform_bind_exec(bContext *C, wmOperator *op) MeshDeformModifierData *mmd = (MeshDeformModifierData *)edit_modifier_property_get( op, ob, eModifierType_MeshDeform); - if (mmd == NULL) { + if (mmd == nullptr) { return OPERATOR_CANCELLED; } - if (mmd->bindcagecos != NULL) { + if (mmd->bindcagecos != nullptr) { MEM_SAFE_FREE(mmd->bindcagecos); MEM_SAFE_FREE(mmd->dyngrid); MEM_SAFE_FREE(mmd->dyninfluences); @@ -2809,7 +2815,7 @@ static int meshdeform_bind_exec(bContext *C, wmOperator *op) depsgraph, ob, &mmd->modifier); mmd_eval->bindfunc = ED_mesh_deform_bind_callback; object_force_modifier_bind_simple_options(depsgraph, ob, &mmd->modifier); - mmd_eval->bindfunc = NULL; + mmd_eval->bindfunc = nullptr; } DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); @@ -2905,7 +2911,7 @@ static bool ocean_bake_poll(bContext *C) return edit_modifier_poll_generic(C, &RNA_OceanModifier, 0, true, false); } -typedef struct OceanBakeJob { +struct OceanBakeJob { /* from wmJob */ struct Object *owner; short *stop, *do_update; @@ -2914,12 +2920,12 @@ typedef struct OceanBakeJob { struct OceanCache *och; struct Ocean *ocean; struct OceanModifierData *omd; -} OceanBakeJob; +}; static void oceanbake_free(void *customdata) { - OceanBakeJob *oj = customdata; - MEM_freeN(oj); + OceanBakeJob *oj = static_cast<OceanBakeJob *>(customdata); + MEM_delete(oj); } /* called by oceanbake, only to check job 'stop' value */ @@ -2937,7 +2943,7 @@ static int oceanbake_breakjob(void *UNUSED(customdata)) /* called by oceanbake, wmJob sends notifier */ static void oceanbake_update(void *customdata, float progress, int *cancel) { - OceanBakeJob *oj = customdata; + OceanBakeJob *oj = static_cast<OceanBakeJob *>(customdata); if (oceanbake_breakjob(oj)) { *cancel = 1; @@ -2949,7 +2955,7 @@ static void oceanbake_update(void *customdata, float progress, int *cancel) static void oceanbake_startjob(void *customdata, short *stop, short *do_update, float *progress) { - OceanBakeJob *oj = customdata; + OceanBakeJob *oj = static_cast<OceanBakeJob *>(customdata); oj->stop = stop; oj->do_update = do_update; @@ -2965,11 +2971,11 @@ static void oceanbake_startjob(void *customdata, short *stop, short *do_update, static void oceanbake_endjob(void *customdata) { - OceanBakeJob *oj = customdata; + OceanBakeJob *oj = static_cast<OceanBakeJob *>(customdata); if (oj->ocean) { BKE_ocean_free(oj->ocean); - oj->ocean = NULL; + oj->ocean = nullptr; } oj->omd->oceancache = oj->och; @@ -3009,7 +3015,7 @@ static int ocean_bake_exec(bContext *C, wmOperator *op) omd->foam_fade, omd->resolution); - och->time = MEM_mallocN(och->duration * sizeof(float), "foam bake time"); + och->time = static_cast<float *>(MEM_mallocN(och->duration * sizeof(float), "foam bake time")); int cfra = scene->r.cfra; @@ -3057,7 +3063,7 @@ static int ocean_bake_exec(bContext *C, wmOperator *op) "Ocean Simulation", WM_JOB_PROGRESS, WM_JOB_TYPE_OBJECT_SIM_OCEAN); - OceanBakeJob *oj = MEM_callocN(sizeof(OceanBakeJob), "ocean bake job"); + OceanBakeJob *oj = MEM_cnew<OceanBakeJob>("ocean bake job"); oj->owner = ob; oj->ocean = ocean; oj->och = och; @@ -3065,7 +3071,7 @@ static int ocean_bake_exec(bContext *C, wmOperator *op) WM_jobs_customdata_set(wm_job, oj, oceanbake_free); WM_jobs_timer(wm_job, 0.1, NC_OBJECT | ND_MODIFIER, NC_OBJECT | ND_MODIFIER); - WM_jobs_callbacks(wm_job, oceanbake_startjob, NULL, NULL, oceanbake_endjob); + WM_jobs_callbacks(wm_job, oceanbake_startjob, nullptr, nullptr, oceanbake_endjob); WM_jobs_start(CTX_wm_manager(C), wm_job); @@ -3115,7 +3121,7 @@ static int laplaciandeform_bind_exec(bContext *C, wmOperator *op) LaplacianDeformModifierData *lmd = (LaplacianDeformModifierData *)edit_modifier_property_get( op, ob, eModifierType_LaplacianDeform); - if (lmd == NULL) { + if (lmd == nullptr) { return OPERATOR_CANCELLED; } @@ -3137,11 +3143,11 @@ static int laplaciandeform_bind_exec(bContext *C, wmOperator *op) /* This is hard to know from the modifier itself whether the evaluation is * happening for binding or not. So we copy all the required data here. */ lmd->verts_num = lmd_eval->verts_num; - if (lmd_eval->vertexco == NULL) { + if (lmd_eval->vertexco == nullptr) { MEM_SAFE_FREE(lmd->vertexco); } else { - lmd->vertexco = MEM_dupallocN(lmd_eval->vertexco); + lmd->vertexco = static_cast<float *>(MEM_dupallocN(lmd_eval->vertexco)); } DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); @@ -3192,7 +3198,7 @@ static int surfacedeform_bind_exec(bContext *C, wmOperator *op) SurfaceDeformModifierData *smd = (SurfaceDeformModifierData *)edit_modifier_property_get( op, ob, eModifierType_SurfaceDeform); - if (smd == NULL) { + if (smd == nullptr) { return OPERATOR_CANCELLED; } @@ -3258,7 +3264,7 @@ static int geometry_nodes_input_attribute_toggle_exec(bContext *C, wmOperator *o char modifier_name[MAX_NAME]; RNA_string_get(op->ptr, "modifier_name", modifier_name); NodesModifierData *nmd = (NodesModifierData *)BKE_modifiers_findby_name(ob, modifier_name); - if (nmd == NULL) { + if (nmd == nullptr) { return OPERATOR_CANCELLED; } @@ -3288,8 +3294,55 @@ void OBJECT_OT_geometry_nodes_input_attribute_toggle(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; - RNA_def_string(ot->srna, "prop_path", NULL, 0, "Prop Path", ""); - RNA_def_string(ot->srna, "modifier_name", NULL, MAX_NAME, "Modifier Name", ""); + RNA_def_string(ot->srna, "prop_path", nullptr, 0, "Prop Path", ""); + RNA_def_string(ot->srna, "modifier_name", nullptr, MAX_NAME, "Modifier Name", ""); +} + +/** \} */ + +/* ------------------------------------------------------------------- */ +/** \name Copy and Assign Geometry Node Group operator + * \{ */ + +static int geometry_node_tree_copy_assign_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Main *bmain = CTX_data_main(C); + Object *ob = ED_object_active_context(C); + + ModifierData *md = BKE_object_active_modifier(ob); + if (md->type != eModifierType_Nodes) { + return OPERATOR_CANCELLED; + } + + NodesModifierData *nmd = (NodesModifierData *)md; + bNodeTree *tree = nmd->node_group; + if (tree == nullptr) { + return OPERATOR_CANCELLED; + } + + bNodeTree *new_tree = (bNodeTree *)BKE_id_copy_ex( + bmain, &tree->id, nullptr, LIB_ID_COPY_ACTIONS | LIB_ID_COPY_DEFAULT); + + if (new_tree == nullptr) { + return OPERATOR_CANCELLED; + } + + nmd->node_group = new_tree; + id_us_min(&tree->id); + + WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob); + return OPERATOR_FINISHED; +} + +void OBJECT_OT_geometry_node_tree_copy_assign(wmOperatorType *ot) +{ + ot->name = "Copy Geometry Node Group"; + ot->description = "Copy the active geometry node group and assign it to the active modifier"; + ot->idname = "OBJECT_OT_geometry_node_tree_copy_assign"; + + ot->exec = geometry_node_tree_copy_assign_exec; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } /** \} */ diff --git a/source/blender/editors/object/object_ops.c b/source/blender/editors/object/object_ops.c index cf5c349228f..9b21dabb4bb 100644 --- a/source/blender/editors/object/object_ops.c +++ b/source/blender/editors/object/object_ops.c @@ -33,6 +33,7 @@ void ED_operatortypes_object(void) WM_operatortype_append(OBJECT_OT_origin_clear); WM_operatortype_append(OBJECT_OT_visual_transform_apply); WM_operatortype_append(OBJECT_OT_transform_apply); + WM_operatortype_append(OBJECT_OT_parent_inverse_apply); WM_operatortype_append(OBJECT_OT_transform_axis_target); WM_operatortype_append(OBJECT_OT_origin_set); @@ -132,6 +133,7 @@ void ED_operatortypes_object(void) WM_operatortype_append(OBJECT_OT_skin_radii_equalize); WM_operatortype_append(OBJECT_OT_skin_armature_create); WM_operatortype_append(OBJECT_OT_geometry_nodes_input_attribute_toggle); + WM_operatortype_append(OBJECT_OT_geometry_node_tree_copy_assign); /* grease pencil modifiers */ WM_operatortype_append(OBJECT_OT_gpencil_modifier_add); diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c index 7be46bdb24b..5ef8e573e27 100644 --- a/source/blender/editors/object/object_relations.c +++ b/source/blender/editors/object/object_relations.c @@ -936,8 +936,19 @@ static int parent_set_invoke_menu(bContext *C, wmOperatorType *ot) RNA_boolean_set(&opptr, "keep_transform", true); #endif - uiItemO( - layout, IFACE_("Object (Without Inverse)"), ICON_NONE, "OBJECT_OT_parent_no_inverse_set"); + uiItemBooleanO(layout, + IFACE_("Object (Without Inverse)"), + ICON_NONE, + "OBJECT_OT_parent_no_inverse_set", + "keep_transform", + 0); + + uiItemBooleanO(layout, + IFACE_("Object (Keep Transform Without Inverse)"), + ICON_NONE, + "OBJECT_OT_parent_no_inverse_set", + "keep_transform", + 1); struct { bool mesh, gpencil; @@ -1055,6 +1066,8 @@ static int parent_noinv_set_exec(bContext *C, wmOperator *op) Main *bmain = CTX_data_main(C); Object *par = ED_object_active_context(C); + const bool keep_transform = RNA_boolean_get(op->ptr, "keep_transform"); + DEG_id_tag_update(&par->id, ID_RECALC_TRANSFORM); /* context iterator */ @@ -1064,16 +1077,21 @@ static int parent_noinv_set_exec(bContext *C, wmOperator *op) BKE_report(op->reports, RPT_ERROR, "Loop in parents"); } else { - /* clear inverse matrix and also the object location */ - unit_m4(ob->parentinv); - memset(ob->loc, 0, sizeof(float[3])); - /* set recalc flags */ DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); /* set parenting type for object - object only... */ ob->parent = par; ob->partype = PAROBJECT; /* NOTE: DNA define, not operator property. */ + + if (keep_transform) { + BKE_object_apply_parent_inverse(ob); + continue; + } + + /* clear inverse matrix and also the object location */ + unit_m4(ob->parentinv); + memset(ob->loc, 0, sizeof(float[3])); } } } @@ -1100,6 +1118,12 @@ void OBJECT_OT_parent_no_inverse_set(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + RNA_def_boolean(ot->srna, + "keep_transform", + false, + "Keep Transform", + "Preserve the world transform throughout parenting"); } /** \} */ diff --git a/source/blender/editors/object/object_select.c b/source/blender/editors/object/object_select.c index 3ebf578ca6b..c3d8fb9cfe5 100644 --- a/source/blender/editors/object/object_select.c +++ b/source/blender/editors/object/object_select.c @@ -1449,7 +1449,7 @@ void OBJECT_OT_select_random(wmOperatorType *ot) ot->idname = "OBJECT_OT_select_random"; /* api callbacks */ - /*ot->invoke = object_select_random_invoke XXX: need a number popup ;*/ + // ot->invoke = object_select_random_invoke; /* TODO: need a number popup. */ ot->exec = object_select_random_exec; ot->poll = objects_selectable_poll; diff --git a/source/blender/editors/object/object_transform.cc b/source/blender/editors/object/object_transform.cc index 3d4d543012d..976fe683f95 100644 --- a/source/blender/editors/object/object_transform.cc +++ b/source/blender/editors/object/object_transform.cc @@ -597,7 +597,7 @@ static bool apply_objects_internal_can_multiuser(bContext *C) { Object *obact = CTX_data_active_object(C); - if (ELEM(NULL, obact, obact->data)) { + if (ELEM(nullptr, obact, obact->data)) { return false; } @@ -1173,6 +1173,38 @@ void OBJECT_OT_transform_apply(wmOperatorType *ot) RNA_def_property_flag(prop, PROP_SKIP_SAVE); } +static int object_parent_inverse_apply_exec(bContext *C, wmOperator *UNUSED(op)) +{ + CTX_DATA_BEGIN (C, Object *, ob, selected_editable_objects) { + if (ob->parent == nullptr) { + continue; + } + + DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM); + BKE_object_apply_parent_inverse(ob); + } + CTX_DATA_END; + + WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, nullptr); + + return OPERATOR_FINISHED; +} + +void OBJECT_OT_parent_inverse_apply(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Apply Parent Inverse"; + ot->description = "Apply the object's parent inverse to the its data"; + ot->idname = "OBJECT_OT_parent_inverse_apply"; + + /* api callbacks */ + ot->exec = object_parent_inverse_apply_exec; + ot->poll = ED_operator_objectmode; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/editors/physics/particle_edit.c b/source/blender/editors/physics/particle_edit.c index eba647a1b17..0e03feba340 100644 --- a/source/blender/editors/physics/particle_edit.c +++ b/source/blender/editors/physics/particle_edit.c @@ -4693,10 +4693,6 @@ static int brush_edit_init(bContext *C, wmOperator *op) BrushEdit *bedit; float min[3], max[3]; - if (!WM_toolsystem_active_tool_is_brush(C)) { - return 0; - } - /* set the 'distance factor' for grabbing (used in comb etc) */ INIT_MINMAX(min, max); PE_minmax(depsgraph, scene, view_layer, min, max); @@ -5033,6 +5029,11 @@ static void brush_edit_cancel(bContext *UNUSED(C), wmOperator *op) brush_edit_exit(op); } +static bool brush_edit_poll(bContext *C) +{ + return PE_poll_view3d(C) && WM_toolsystem_active_tool_is_brush(C); +} + void PARTICLE_OT_brush_edit(wmOperatorType *ot) { /* identifiers */ @@ -5045,7 +5046,7 @@ void PARTICLE_OT_brush_edit(wmOperatorType *ot) ot->invoke = brush_edit_invoke; ot->modal = brush_edit_modal; ot->cancel = brush_edit_cancel; - ot->poll = PE_poll_view3d; + ot->poll = brush_edit_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING; diff --git a/source/blender/editors/render/render_shading.cc b/source/blender/editors/render/render_shading.cc index f5bd60df2b4..da2290f7372 100644 --- a/source/blender/editors/render/render_shading.cc +++ b/source/blender/editors/render/render_shading.cc @@ -1167,7 +1167,7 @@ void SCENE_OT_view_layer_add_lightgroup(wmOperatorType *ot) ot->prop = RNA_def_string(ot->srna, "name", nullptr, - sizeof(((ViewLayerLightgroup *)NULL)->name), + sizeof(((ViewLayerLightgroup *)nullptr)->name), "Name", "Name of newly created lightgroup"); } @@ -2613,7 +2613,7 @@ static void copy_mtex_copybuf(ID *id) } if (mtex && *mtex) { - memcpy(&mtexcopybuf, *mtex, sizeof(MTex)); + mtexcopybuf = blender::dna::shallow_copy(**mtex); mtexcopied = 1; } else { @@ -2649,7 +2649,7 @@ static void paste_mtex_copybuf(ID *id) id_us_min(&(*mtex)->tex->id); } - memcpy(*mtex, &mtexcopybuf, sizeof(MTex)); + **mtex = blender::dna::shallow_copy(mtexcopybuf); id_us_plus((ID *)mtexcopybuf.tex); } diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c index 30bf23e0987..ad815f0d998 100644 --- a/source/blender/editors/screen/area.c +++ b/source/blender/editors/screen/area.c @@ -2176,12 +2176,12 @@ struct RegionTypeAlignInfo { * Needed for detecting which header displays the space-type switcher. */ bool hidden; - } by_type[RGN_TYPE_LEN]; + } by_type[RGN_TYPE_NUM]; }; static void region_align_info_from_area(ScrArea *area, struct RegionTypeAlignInfo *r_align_info) { - for (int index = 0; index < RGN_TYPE_LEN; index++) { + for (int index = 0; index < RGN_TYPE_NUM; index++) { r_align_info->by_type[index].alignment = -1; /* Default to true, when it doesn't exist - it's effectively hidden. */ r_align_info->by_type[index].hidden = true; @@ -2189,7 +2189,7 @@ static void region_align_info_from_area(ScrArea *area, struct RegionTypeAlignInf LISTBASE_FOREACH (ARegion *, region, &area->regionbase) { const int index = region->regiontype; - if ((uint)index < RGN_TYPE_LEN) { + if ((uint)index < RGN_TYPE_NUM) { r_align_info->by_type[index].alignment = RGN_ALIGN_ENUM_FROM_MASK(region->alignment); r_align_info->by_type[index].hidden = (region->flag & RGN_FLAG_HIDDEN) != 0; } @@ -2252,7 +2252,7 @@ static short region_alignment_from_header_and_tool_header_state( static void region_align_info_to_area_for_headers( const struct RegionTypeAlignInfo *region_align_info_src, const struct RegionTypeAlignInfo *region_align_info_dst, - ARegion *region_by_type[RGN_TYPE_LEN]) + ARegion *region_by_type[RGN_TYPE_NUM]) { /* Abbreviate access. */ const short header_alignment_src = region_align_info_src->by_type[RGN_TYPE_HEADER].alignment; @@ -2365,12 +2365,12 @@ static void region_align_info_to_area_for_headers( } static void region_align_info_to_area( - ScrArea *area, const struct RegionTypeAlignInfo region_align_info_src[RGN_TYPE_LEN]) + ScrArea *area, const struct RegionTypeAlignInfo region_align_info_src[RGN_TYPE_NUM]) { - ARegion *region_by_type[RGN_TYPE_LEN] = {NULL}; + ARegion *region_by_type[RGN_TYPE_NUM] = {NULL}; LISTBASE_FOREACH (ARegion *, region, &area->regionbase) { const int index = region->regiontype; - if ((uint)index < RGN_TYPE_LEN) { + if ((uint)index < RGN_TYPE_NUM) { region_by_type[index] = region; } } @@ -2437,7 +2437,7 @@ void ED_area_newspace(bContext *C, ScrArea *area, int type, const bool skip_regi */ bool sync_header_alignment = false; - struct RegionTypeAlignInfo region_align_info[RGN_TYPE_LEN]; + struct RegionTypeAlignInfo region_align_info[RGN_TYPE_NUM]; if ((slold != NULL) && (slold->link_flag & SPACE_FLAG_TYPE_TEMPORARY) == 0) { region_align_info_from_area(area, region_align_info); sync_header_alignment = true; diff --git a/source/blender/editors/sculpt_paint/CMakeLists.txt b/source/blender/editors/sculpt_paint/CMakeLists.txt index b89cbcf87fa..abdae5c44f9 100644 --- a/source/blender/editors/sculpt_paint/CMakeLists.txt +++ b/source/blender/editors/sculpt_paint/CMakeLists.txt @@ -72,6 +72,7 @@ set(SRC sculpt_multiplane_scrape.c sculpt_ops.c sculpt_paint_color.c + sculpt_paint_image.cc sculpt_pose.c sculpt_smooth.c sculpt_transform.c diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc b/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc index dcfda658bbd..992ac77803a 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc @@ -74,431 +74,6 @@ using blender::bke::CurvesGeometry; /** \name * SCULPT_CURVES_OT_brush_stroke * \{ */ -class DensityAddOperation : public CurvesSculptStrokeOperation { - private: - /** Contains the root points of the curves that existed before this operation started. */ - KDTree_3d *old_kdtree_ = nullptr; - /** Number of points in the kdtree above. */ - int old_kdtree_size_ = 0; - - /** - * Indicates that the corresponding curve has already been created and can't be changed by this - * operation anymore. - */ - static constexpr int ExistsAlreadyIndex = INT32_MAX; - - struct NewPointsData { - Vector<float3> bary_coords; - Vector<int> looptri_indices; - Vector<float3> positions; - Vector<float3> normals; - }; - - public: - ~DensityAddOperation() override - { - if (old_kdtree_ != nullptr) { - BLI_kdtree_3d_free(old_kdtree_); - } - } - - void on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension) override - { - Depsgraph &depsgraph = *CTX_data_depsgraph_pointer(C); - Scene &scene = *CTX_data_scene(C); - Object &object = *CTX_data_active_object(C); - ARegion *region = CTX_wm_region(C); - View3D *v3d = CTX_wm_view3d(C); - - Curves &curves_id = *static_cast<Curves *>(object.data); - CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry); - - if (curves_id.surface == nullptr || curves_id.surface->type != OB_MESH) { - return; - } - - const Object &surface_ob = *curves_id.surface; - const Mesh &surface = *static_cast<const Mesh *>(surface_ob.data); - const float4x4 surface_ob_mat = surface_ob.obmat; - const float4x4 surface_ob_imat = surface_ob_mat.inverted(); - - ToolSettings &tool_settings = *scene.toolsettings; - CurvesSculpt &curves_sculpt = *tool_settings.curves_sculpt; - Brush &brush = *BKE_paint_brush(&curves_sculpt.paint); - const float brush_radius_screen = BKE_brush_size_get(&scene, &brush); - const float strength = BKE_brush_alpha_get(&scene, &brush); - const float minimum_distance = curves_sculpt.distance; - - /* This is the main ray that is used to determine the brush position in 3D space. */ - float3 ray_start, ray_end; - ED_view3d_win_to_segment_clipped( - &depsgraph, region, v3d, stroke_extension.mouse_position, ray_start, ray_end, true); - ray_start = surface_ob_imat * ray_start; - ray_end = surface_ob_imat * ray_end; - const float3 ray_direction = math::normalize(ray_end - ray_start); - - /* This ray is used to determine the brush radius in 3d space. */ - float3 offset_ray_start, offset_ray_end; - ED_view3d_win_to_segment_clipped(&depsgraph, - region, - v3d, - stroke_extension.mouse_position + - float2(0, brush_radius_screen), - offset_ray_start, - offset_ray_end, - true); - offset_ray_start = surface_ob_imat * offset_ray_start; - offset_ray_end = surface_ob_imat * offset_ray_end; - - float4x4 ob_imat; - invert_m4_m4(ob_imat.values, object.obmat); - - const float4x4 transform = ob_imat * surface_ob_mat; - - BVHTreeFromMesh bvhtree; - BKE_bvhtree_from_mesh_get(&bvhtree, &surface, BVHTREE_FROM_LOOPTRI, 2); - - /* Do a raycast against the surface object to find the brush position. */ - BVHTreeRayHit ray_hit; - ray_hit.dist = FLT_MAX; - ray_hit.index = -1; - BLI_bvhtree_ray_cast(bvhtree.tree, - ray_start, - ray_direction, - 0.0f, - &ray_hit, - bvhtree.raycast_callback, - &bvhtree); - - if (ray_hit.index == -1) { - /* The ray did not hit the surface. */ - free_bvhtree_from_mesh(&bvhtree); - return; - } - /* Brush position in the space of the surface object. */ - const float3 brush_pos_3d_surface = ray_hit.co; - const float brush_radius_3d_surface = dist_to_line_v3( - brush_pos_3d_surface, offset_ray_start, offset_ray_end); - - /* Brush position in the space of the curves object. */ - const float3 brush_pos_3d_curves = transform * brush_pos_3d_surface; - const float brush_radius_3d_curves = dist_to_line_v3( - brush_pos_3d_curves, transform * offset_ray_start, transform * offset_ray_end); - - Vector<int> looptri_indices = this->find_looptri_indices_to_consider( - bvhtree, brush_pos_3d_surface, brush_radius_3d_surface); - - free_bvhtree_from_mesh(&bvhtree); - - if (old_kdtree_ == nullptr && minimum_distance > 0.0f) { - old_kdtree_ = this->kdtree_from_curve_roots_and_positions(curves, curves.curves_range(), {}); - old_kdtree_size_ = curves.curves_num(); - } - - float density; - if (minimum_distance > 0.0f) { - /* Estimate the sampling density based on the target minimum distance. */ - density = strength * pow2f(1.0f / minimum_distance); - } - else { - /* Sample a somewhat constant amount of points based on the strength. */ - const float brush_circle_area_3d = M_PI * pow2f(brush_radius_3d_curves); - density = strength * 100.0f / brush_circle_area_3d; - } - - NewPointsData new_points = this->sample_new_points(density, - minimum_distance, - brush_radius_3d_curves, - brush_pos_3d_curves, - looptri_indices, - transform, - surface); - if (minimum_distance > 0.0f) { - this->eliminate_too_close_points(new_points, curves, minimum_distance); - } - this->insert_new_curves(new_points, curves); - - DEG_id_tag_update(&curves_id.id, ID_RECALC_GEOMETRY); - ED_region_tag_redraw(region); - } - - private: - Vector<int> find_looptri_indices_to_consider(BVHTreeFromMesh &bvhtree, - const float3 &brush_pos, - const float brush_radius_3d) - { - Vector<int> looptri_indices; - - struct RangeQueryUserData { - Vector<int> &indices; - } range_query_user_data = {looptri_indices}; - - BLI_bvhtree_range_query( - bvhtree.tree, - brush_pos, - brush_radius_3d, - [](void *userdata, int index, const float co[3], float dist_sq) { - UNUSED_VARS(co, dist_sq); - RangeQueryUserData &data = *static_cast<RangeQueryUserData *>(userdata); - data.indices.append(index); - }, - &range_query_user_data); - - return looptri_indices; - } - - KDTree_3d *kdtree_from_curve_roots_and_positions(const CurvesGeometry &curves, - const IndexRange curves_range, - Span<float3> extra_positions) - { - const int tot_points = curves_range.size() + extra_positions.size(); - KDTree_3d *kdtree = BLI_kdtree_3d_new(tot_points); - for (const int curve_i : curves_range) { - const int first_point_i = curves.offsets()[curve_i]; - const float3 root_position = curves.positions()[first_point_i]; - BLI_kdtree_3d_insert(kdtree, ExistsAlreadyIndex, root_position); - } - for (const int i : extra_positions.index_range()) { - BLI_kdtree_3d_insert(kdtree, i, extra_positions[i]); - } - BLI_kdtree_3d_balance(kdtree); - return kdtree; - } - - bool is_too_close_to_existing_point(const float3 position, const float minimum_distance) const - { - if (old_kdtree_ == nullptr) { - return false; - } - KDTreeNearest_3d nearest; - nearest.index = -1; - BLI_kdtree_3d_find_nearest(old_kdtree_, position, &nearest); - if (nearest.index >= 0 && nearest.dist < minimum_distance) { - return true; - } - return false; - } - - NewPointsData sample_new_points(const float density, - const float minimum_distance, - const float brush_radius_3d, - const float3 &brush_pos, - const Span<int> looptri_indices, - const float4x4 &transform, - const Mesh &surface) - { - const float brush_radius_3d_sq = brush_radius_3d * brush_radius_3d; - const float area_threshold = M_PI * brush_radius_3d_sq; - - const Span<MLoopTri> looptris{BKE_mesh_runtime_looptri_ensure(&surface), - BKE_mesh_runtime_looptri_len(&surface)}; - - threading::EnumerableThreadSpecific<NewPointsData> new_points_per_thread; - - const double time = PIL_check_seconds_timer(); - const uint64_t time_as_int = *reinterpret_cast<const uint64_t *>(&time); - const uint32_t rng_base_seed = time_as_int ^ (time_as_int >> 32); - - RandomNumberGenerator rng{rng_base_seed}; - - threading::parallel_for(looptri_indices.index_range(), 512, [&](const IndexRange range) { - RandomNumberGenerator looptri_rng{rng_base_seed + (uint32_t)range.start()}; - - for (const int looptri_index : looptri_indices.slice(range)) { - const MLoopTri &looptri = looptris[looptri_index]; - const float3 &v0 = transform * float3(surface.mvert[surface.mloop[looptri.tri[0]].v].co); - const float3 &v1 = transform * float3(surface.mvert[surface.mloop[looptri.tri[1]].v].co); - const float3 &v2 = transform * float3(surface.mvert[surface.mloop[looptri.tri[2]].v].co); - const float looptri_area = area_tri_v3(v0, v1, v2); - - float3 normal; - normal_tri_v3(normal, v0, v1, v2); - - /* Use a different sampling strategy depending on whether the triangle is large or small - * compared to the brush size. When the triangle is small, points are distributed within - * the triangle directly. If the triangle is larger than the brush, distribute new points - * in a circle on the triangle plane. */ - if (looptri_area < area_threshold) { - const int amount = looptri_rng.round_probabilistic(looptri_area * density); - - threading::parallel_for(IndexRange(amount), 512, [&](const IndexRange amount_range) { - RandomNumberGenerator point_rng{rng_base_seed + looptri_index * 1000 + - (uint32_t)amount_range.start()}; - NewPointsData &new_points = new_points_per_thread.local(); - - for ([[maybe_unused]] const int i : amount_range) { - const float3 bary_coord = point_rng.get_barycentric_coordinates(); - const float3 point_pos = attribute_math::mix3(bary_coord, v0, v1, v2); - - if (math::distance(point_pos, brush_pos) > brush_radius_3d) { - continue; - } - if (minimum_distance > 0.0f && - this->is_too_close_to_existing_point(point_pos, minimum_distance)) { - continue; - } - - new_points.bary_coords.append(bary_coord); - new_points.looptri_indices.append(looptri_index); - new_points.positions.append(point_pos); - new_points.normals.append(normal); - } - }); - } - else { - float3 hit_pos_proj = brush_pos; - project_v3_plane(hit_pos_proj, normal, v0); - const float proj_distance_sq = math::distance_squared(hit_pos_proj, brush_pos); - const float brush_radius_factor_sq = 1.0f - - std::min(1.0f, - proj_distance_sq / brush_radius_3d_sq); - const float radius_proj_sq = brush_radius_3d_sq * brush_radius_factor_sq; - const float radius_proj = std::sqrt(radius_proj_sq); - const float circle_area = M_PI * radius_proj_sq; - - const int amount = rng.round_probabilistic(circle_area * density); - - const float3 axis_1 = math::normalize(v1 - v0) * radius_proj; - const float3 axis_2 = math::normalize( - math::cross(axis_1, math::cross(axis_1, v2 - v0))) * - radius_proj; - - threading::parallel_for(IndexRange(amount), 512, [&](const IndexRange amount_range) { - RandomNumberGenerator point_rng{rng_base_seed + looptri_index * 1000 + - (uint32_t)amount_range.start()}; - NewPointsData &new_points = new_points_per_thread.local(); - - for ([[maybe_unused]] const int i : amount_range) { - const float r = std::sqrt(rng.get_float()); - const float angle = rng.get_float() * 2 * M_PI; - const float x = r * std::cos(angle); - const float y = r * std::sin(angle); - - const float3 point_pos = hit_pos_proj + axis_1 * x + axis_2 * y; - - if (!isect_point_tri_prism_v3(point_pos, v0, v1, v2)) { - continue; - } - if (minimum_distance > 0.0f && - this->is_too_close_to_existing_point(point_pos, minimum_distance)) { - continue; - } - - float3 bary_coord; - interp_weights_tri_v3(bary_coord, v0, v1, v2, point_pos); - - new_points.bary_coords.append(bary_coord); - new_points.looptri_indices.append(looptri_index); - new_points.positions.append(point_pos); - new_points.normals.append(normal); - } - }); - } - } - }); - - NewPointsData new_points; - for (const NewPointsData &local_new_points : new_points_per_thread) { - new_points.bary_coords.extend(local_new_points.bary_coords); - new_points.looptri_indices.extend(local_new_points.looptri_indices); - new_points.positions.extend(local_new_points.positions); - new_points.normals.extend(local_new_points.normals); - } - return new_points; - } - - void eliminate_too_close_points(NewPointsData &points, - const CurvesGeometry &curves, - const float minimum_distance) - { - Array<bool> elimination_mask(points.positions.size(), false); - - const int curves_added_previously = curves.curves_num() - old_kdtree_size_; - KDTree_3d *new_points_kdtree = this->kdtree_from_curve_roots_and_positions( - curves, IndexRange(old_kdtree_size_, curves_added_previously), points.positions); - - Array<Vector<int>> points_in_range(points.positions.size()); - threading::parallel_for(points.positions.index_range(), 256, [&](const IndexRange range) { - for (const int point_i : range) { - const float3 query_position = points.positions[point_i]; - - struct CallbackData { - int point_i; - Vector<int> &found_indices; - MutableSpan<bool> elimination_mask; - } callback_data = {point_i, points_in_range[point_i], elimination_mask}; - - BLI_kdtree_3d_range_search_cb( - new_points_kdtree, - query_position, - minimum_distance, - [](void *user_data, int index, const float *UNUSED(co), float UNUSED(dist_sq)) { - CallbackData &data = *static_cast<CallbackData *>(user_data); - if (index == data.point_i) { - /* Ignore self. */ - return true; - } - if (index == ExistsAlreadyIndex) { - /* An already existing point is too close, so this new point will be eliminated. */ - data.elimination_mask[data.point_i] = true; - return false; - } - data.found_indices.append(index); - return true; - }, - &callback_data); - } - }); - - for (const int point_i : points.positions.index_range()) { - if (elimination_mask[point_i]) { - /* Point is eliminated already. */ - continue; - } - - for (const int other_point_i : points_in_range[point_i]) { - elimination_mask[other_point_i] = true; - } - } - - BLI_kdtree_3d_free(new_points_kdtree); - for (int i = points.positions.size() - 1; i >= 0; i--) { - if (elimination_mask[i]) { - points.positions.remove_and_reorder(i); - points.bary_coords.remove_and_reorder(i); - points.looptri_indices.remove_and_reorder(i); - points.normals.remove_and_reorder(i); - } - } - } - - void insert_new_curves(const NewPointsData &new_points, CurvesGeometry &curves) - { - const int tot_new_curves = new_points.positions.size(); - - const int points_per_curve = 8; - curves.resize(curves.points_num() + tot_new_curves * points_per_curve, - curves.curves_num() + tot_new_curves); - - MutableSpan<int> offsets = curves.offsets_for_write(); - MutableSpan<float3> positions = curves.positions_for_write(); - - for (const int i : IndexRange(tot_new_curves)) { - const int curve_i = curves.curves_num() - tot_new_curves + i; - const int first_point_i = offsets[curve_i]; - offsets[curve_i + 1] = offsets[curve_i] + points_per_curve; - - const float3 root = new_points.positions[i]; - const float3 tip = root + 0.1f * new_points.normals[i]; - - for (const int j : IndexRange(points_per_curve)) { - positions[first_point_i + j] = math::interpolate( - root, tip, j / (float)(points_per_curve - 1)); - } - } - } -}; - static std::unique_ptr<CurvesSculptStrokeOperation> start_brush_operation(bContext *C, wmOperator *op) { @@ -518,8 +93,6 @@ static std::unique_ptr<CurvesSculptStrokeOperation> start_brush_operation(bConte return new_add_operation(); case CURVES_SCULPT_TOOL_GROW_SHRINK: return new_grow_shrink_operation(mode, C); - case CURVES_SCULPT_TOOL_TEST1: - return std::make_unique<DensityAddOperation>(); } BLI_assert_unreachable(); return {}; diff --git a/source/blender/editors/sculpt_paint/paint_canvas.cc b/source/blender/editors/sculpt_paint/paint_canvas.cc index 5683e3ff741..9262cbebcac 100644 --- a/source/blender/editors/sculpt_paint/paint_canvas.cc +++ b/source/blender/editors/sculpt_paint/paint_canvas.cc @@ -2,20 +2,11 @@ #include "BLI_compiler_compat.h" #include "DNA_material_types.h" -#include "DNA_mesh_types.h" -#include "DNA_node_types.h" -#include "DNA_screen_types.h" +#include "DNA_scene_types.h" #include "DNA_workspace_types.h" -#include "BKE_context.h" -#include "BKE_customdata.h" #include "BKE_material.h" #include "BKE_paint.h" -#include "BKE_pbvh.h" - -#include "DEG_depsgraph.h" - -#include "NOD_shader.h" #include "WM_toolsystem.h" @@ -43,16 +34,15 @@ static TexPaintSlot *get_active_slot(Object *ob) extern "C" { -using namespace blender; using namespace blender::ed::sculpt_paint::canvas; /* Does the paint tool with the given idname uses a canvas. */ -static bool paint_tool_uses_canvas(StringRef idname) +static bool paint_tool_uses_canvas(blender::StringRef idname) { return ELEM(idname, "builtin_brush.Paint", "builtin_brush.Smear", "builtin.color_filter"); } -static bool paint_tool_shading_color_follows_last_used(StringRef idname) +static bool paint_tool_shading_color_follows_last_used(blender::StringRef idname) { /* TODO(jbakker): complete this list. */ return ELEM(idname, "builtin_brush.Mask"); @@ -147,59 +137,4 @@ eV3DShadingColorType ED_paint_shading_color_override(bContext *C, return color_type; } - -Image *ED_paint_canvas_image_get(const struct PaintModeSettings *settings, struct Object *ob) -{ - switch (settings->canvas_source) { - case PAINT_CANVAS_SOURCE_COLOR_ATTRIBUTE: - return nullptr; - case PAINT_CANVAS_SOURCE_IMAGE: - return settings->canvas_image; - case PAINT_CANVAS_SOURCE_MATERIAL: { - TexPaintSlot *slot = get_active_slot(ob); - if (slot == nullptr) { - break; - } - return slot->ima; - } - } - return nullptr; -} - -int ED_paint_canvas_uvmap_layer_index_get(const struct PaintModeSettings *settings, - struct Object *ob) -{ - switch (settings->canvas_source) { - case PAINT_CANVAS_SOURCE_COLOR_ATTRIBUTE: - return -1; - case PAINT_CANVAS_SOURCE_IMAGE: { - /* Use active uv map of the object. */ - if (ob->type != OB_MESH) { - return -1; - } - - const Mesh *mesh = static_cast<Mesh *>(ob->data); - return CustomData_get_active_layer_index(&mesh->ldata, CD_MLOOPUV); - } - case PAINT_CANVAS_SOURCE_MATERIAL: { - /* Use uv map of the canvas. */ - TexPaintSlot *slot = get_active_slot(ob); - if (slot == nullptr) { - break; - } - - if (ob->type != OB_MESH) { - return -1; - } - - if (slot->uvname == nullptr) { - return -1; - } - - const Mesh *mesh = static_cast<Mesh *>(ob->data); - return CustomData_get_named_layer_index(&mesh->ldata, CD_MLOOPUV, slot->uvname); - } - } - return -1; -} } diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index 24c5a9fce52..bbbed32e316 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -2188,7 +2188,8 @@ void SCULPT_calc_area_normal_and_center( static float brush_strength(const Sculpt *sd, const StrokeCache *cache, const float feather, - const UnifiedPaintSettings *ups) + const UnifiedPaintSettings *ups, + const PaintModeSettings *UNUSED(paint_mode_settings)) { const Scene *scene = cache->vc->scene; const Brush *brush = BKE_paint_brush((Paint *)&sd->paint); @@ -2750,6 +2751,41 @@ static void update_brush_local_mat(Sculpt *sd, Object *ob) /** \} */ /* -------------------------------------------------------------------- */ +/** \name Texture painting + * \{ */ + +static bool sculpt_needs_pbvh_pixels(PaintModeSettings *paint_mode_settings, + const Brush *brush, + Object *ob) +{ + if (brush->sculpt_tool == SCULPT_TOOL_PAINT && U.experimental.use_sculpt_texture_paint) { + Image *image; + ImageUser *image_user; + return SCULPT_paint_image_canvas_get(paint_mode_settings, ob, &image, &image_user); + } + + return false; +} + +static void sculpt_pbvh_update_pixels(PaintModeSettings *paint_mode_settings, + SculptSession *ss, + Object *ob) +{ + BLI_assert(ob->type == OB_MESH); + Mesh *mesh = (Mesh *)ob->data; + + Image *image; + ImageUser *image_user; + if (!SCULPT_paint_image_canvas_get(paint_mode_settings, ob, &image, &image_user)) { + return; + } + + BKE_pbvh_build_pixels(ss->pbvh, mesh->mloop, &mesh->ldata, image, image_user); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name Generic Brush Plane & Symmetry Utilities * \{ */ @@ -3075,7 +3111,8 @@ void SCULPT_vertcos_to_key(Object *ob, KeyBlock *kb, const float (*vertCos)[3]) static void sculpt_topology_update(Sculpt *sd, Object *ob, Brush *brush, - UnifiedPaintSettings *UNUSED(ups)) + UnifiedPaintSettings *UNUSED(ups), + PaintModeSettings *UNUSED(paint_mode_settings)) { SculptSession *ss = ob->sculpt; @@ -3170,7 +3207,11 @@ static void do_brush_action_task_cb(void *__restrict userdata, } } -static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSettings *ups) +static void do_brush_action(Sculpt *sd, + Object *ob, + Brush *brush, + UnifiedPaintSettings *ups, + PaintModeSettings *paint_mode_settings) { SculptSession *ss = ob->sculpt; int totnode; @@ -3209,6 +3250,10 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe nodes = sculpt_pbvh_gather_generic(ob, sd, brush, use_original, radius_scale, &totnode); } + if (sculpt_needs_pbvh_pixels(paint_mode_settings, brush, ob)) { + sculpt_pbvh_update_pixels(paint_mode_settings, ss, ob); + } + /* Draw Face Sets in draw mode makes a single undo push, in alt-smooth mode deforms the * vertices and uses regular coords undo. */ /* It also assigns the paint_face_set here as it needs to be done regardless of the stroke type @@ -3399,7 +3444,7 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe SCULPT_do_displacement_smear_brush(sd, ob, nodes, totnode); break; case SCULPT_TOOL_PAINT: - SCULPT_do_paint_brush(sd, ob, nodes, totnode); + SCULPT_do_paint_brush(paint_mode_settings, sd, ob, nodes, totnode); break; case SCULPT_TOOL_SMEAR: SCULPT_do_smear_brush(sd, ob, nodes, totnode); @@ -3704,10 +3749,18 @@ void SCULPT_cache_calc_brushdata_symm(StrokeCache *cache, } } -typedef void (*BrushActionFunc)(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSettings *ups); +typedef void (*BrushActionFunc)(Sculpt *sd, + Object *ob, + Brush *brush, + UnifiedPaintSettings *ups, + PaintModeSettings *paint_mode_settings); -static void do_tiled( - Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSettings *ups, BrushActionFunc action) +static void do_tiled(Sculpt *sd, + Object *ob, + Brush *brush, + UnifiedPaintSettings *ups, + PaintModeSettings *paint_mode_settings, + BrushActionFunc action) { SculptSession *ss = ob->sculpt; StrokeCache *cache = ss->cache; @@ -3741,7 +3794,7 @@ static void do_tiled( /* First do the "un-tiled" position to initialize the stroke for this location. */ cache->tile_pass = 0; - action(sd, ob, brush, ups); + action(sd, ob, brush, ups, paint_mode_settings); /* Now do it for all the tiles. */ copy_v3_v3_int(cur, start); @@ -3760,7 +3813,7 @@ static void do_tiled( cache->plane_offset[dim] = cur[dim] * step[dim]; cache->initial_location[dim] = cur[dim] * step[dim] + original_initial_location[dim]; } - action(sd, ob, brush, ups); + action(sd, ob, brush, ups, paint_mode_settings); } } } @@ -3770,6 +3823,7 @@ static void do_radial_symmetry(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSettings *ups, + PaintModeSettings *paint_mode_settings, BrushActionFunc action, const char symm, const int axis, @@ -3781,7 +3835,7 @@ static void do_radial_symmetry(Sculpt *sd, const float angle = 2.0f * M_PI * i / sd->radial_symm[axis - 'X']; ss->cache->radial_symmetry_pass = i; SCULPT_cache_calc_brushdata_symm(ss->cache, symm, axis, angle); - do_tiled(sd, ob, brush, ups, action); + do_tiled(sd, ob, brush, ups, paint_mode_settings, action); } } @@ -3803,7 +3857,8 @@ static void sculpt_fix_noise_tear(Sculpt *sd, Object *ob) static void do_symmetrical_brush_actions(Sculpt *sd, Object *ob, BrushActionFunc action, - UnifiedPaintSettings *ups) + UnifiedPaintSettings *ups, + PaintModeSettings *paint_mode_settings) { Brush *brush = BKE_paint_brush(&sd->paint); SculptSession *ss = ob->sculpt; @@ -3812,7 +3867,7 @@ static void do_symmetrical_brush_actions(Sculpt *sd, float feather = calc_symmetry_feather(sd, ss->cache); - cache->bstrength = brush_strength(sd, cache, feather, ups); + cache->bstrength = brush_strength(sd, cache, feather, ups, paint_mode_settings); cache->symmetry = symm; /* `symm` is a bit combination of XYZ - @@ -3825,11 +3880,11 @@ static void do_symmetrical_brush_actions(Sculpt *sd, cache->radial_symmetry_pass = 0; SCULPT_cache_calc_brushdata_symm(cache, i, 0, 0); - do_tiled(sd, ob, brush, ups, action); + do_tiled(sd, ob, brush, ups, paint_mode_settings, action); - do_radial_symmetry(sd, ob, brush, ups, action, i, 'X', feather); - do_radial_symmetry(sd, ob, brush, ups, action, i, 'Y', feather); - do_radial_symmetry(sd, ob, brush, ups, action, i, 'Z', feather); + do_radial_symmetry(sd, ob, brush, ups, paint_mode_settings, action, i, 'X', feather); + do_radial_symmetry(sd, ob, brush, ups, paint_mode_settings, action, i, 'Y', feather); + do_radial_symmetry(sd, ob, brush, ups, paint_mode_settings, action, i, 'Z', feather); } } @@ -4609,7 +4664,8 @@ static bool sculpt_needs_connectivity_info(const Sculpt *sd, SCULPT_TOOL_NEEDS_COLOR(brush->sculpt_tool) || (brush->sculpt_tool == SCULPT_TOOL_CLOTH) || (brush->sculpt_tool == SCULPT_TOOL_SMEAR) || (brush->sculpt_tool == SCULPT_TOOL_DRAW_FACE_SETS) || - (brush->sculpt_tool == SCULPT_TOOL_DISPLACEMENT_SMEAR)); + (brush->sculpt_tool == SCULPT_TOOL_DISPLACEMENT_SMEAR) || + (brush->sculpt_tool == SCULPT_TOOL_PAINT)); } void SCULPT_stroke_modifiers_check(const bContext *C, Object *ob, const Brush *brush) @@ -5057,6 +5113,15 @@ void SCULPT_flush_update_step(bContext *C, SculptUpdateType update_flags) multires_mark_as_modified(depsgraph, ob, MULTIRES_COORDS_MODIFIED); } + if ((update_flags & SCULPT_UPDATE_IMAGE) != 0) { + ED_region_tag_redraw(region); + if (update_flags == SCULPT_UPDATE_IMAGE) { + /* Early exit when only need to update the images. We don't want to tag any geometry updates + * that would rebuilt the PBVH. */ + return; + } + } + DEG_id_tag_update(&ob->id, ID_RECALC_SHADING); /* Only current viewport matters, slower update for all viewports will @@ -5136,6 +5201,16 @@ void SCULPT_flush_update_done(const bContext *C, Object *ob, SculptUpdateType up } } } + + if (update_flags & SCULPT_UPDATE_IMAGE) { + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { + SpaceLink *sl = area->spacedata.first; + if (sl->spacetype != SPACE_IMAGE) { + continue; + } + ED_area_tag_redraw_regiontype(area, RGN_TYPE_WINDOW); + } + } } if (update_flags & SCULPT_UPDATE_COORDS) { @@ -5227,6 +5302,7 @@ static void sculpt_stroke_update_step(bContext *C, Object *ob = CTX_data_active_object(C); SculptSession *ss = ob->sculpt; const Brush *brush = BKE_paint_brush(&sd->paint); + ToolSettings *tool_settings = CTX_data_tool_settings(C); SCULPT_stroke_modifiers_check(C, ob, brush); sculpt_update_cache_variants(C, sd, ob, itemptr); @@ -5246,10 +5322,10 @@ static void sculpt_stroke_update_step(bContext *C, } if (SCULPT_stroke_is_dynamic_topology(ss, brush)) { - do_symmetrical_brush_actions(sd, ob, sculpt_topology_update, ups); + do_symmetrical_brush_actions(sd, ob, sculpt_topology_update, ups, &tool_settings->paint_mode); } - do_symmetrical_brush_actions(sd, ob, do_brush_action, ups); + do_symmetrical_brush_actions(sd, ob, do_brush_action, ups, &tool_settings->paint_mode); sculpt_combine_proxies(sd, ob); /* Hack to fix noise texture tearing mesh. */ @@ -5280,7 +5356,12 @@ static void sculpt_stroke_update_step(bContext *C, SCULPT_flush_update_step(C, SCULPT_UPDATE_MASK); } else if (ELEM(brush->sculpt_tool, SCULPT_TOOL_PAINT, SCULPT_TOOL_SMEAR)) { - SCULPT_flush_update_step(C, SCULPT_UPDATE_COLOR); + if (SCULPT_use_image_paint_brush(&tool_settings->paint_mode, ob)) { + SCULPT_flush_update_step(C, SCULPT_UPDATE_IMAGE); + } + else { + SCULPT_flush_update_step(C, SCULPT_UPDATE_COLOR); + } } else { SCULPT_flush_update_step(C, SCULPT_UPDATE_COORDS); @@ -5302,6 +5383,7 @@ static void sculpt_stroke_done(const bContext *C, struct PaintStroke *UNUSED(str Object *ob = CTX_data_active_object(C); SculptSession *ss = ob->sculpt; Sculpt *sd = CTX_data_tool_settings(C)->sculpt; + ToolSettings *tool_settings = CTX_data_tool_settings(C); /* Finished. */ if (!ss->cache) { @@ -5335,6 +5417,11 @@ static void sculpt_stroke_done(const bContext *C, struct PaintStroke *UNUSED(str if (brush->sculpt_tool == SCULPT_TOOL_MASK) { SCULPT_flush_update_done(C, ob, SCULPT_UPDATE_MASK); } + else if (brush->sculpt_tool == SCULPT_TOOL_PAINT) { + if (SCULPT_use_image_paint_brush(&tool_settings->paint_mode, ob)) { + SCULPT_flush_update_done(C, ob, SCULPT_UPDATE_IMAGE); + } + } else { SCULPT_flush_update_done(C, ob, SCULPT_UPDATE_COORDS); } diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_color.c b/source/blender/editors/sculpt_paint/sculpt_filter_color.c index e4180a36a98..5d4a2c54832 100644 --- a/source/blender/editors/sculpt_paint/sculpt_filter_color.c +++ b/source/blender/editors/sculpt_paint/sculpt_filter_color.c @@ -109,6 +109,7 @@ static void color_filter_task_cb(void *__restrict userdata, } copy_v3_v3(orig_color, orig_data.col); + final_color[3] = orig_data.col[3]; /* Copy alpha */ switch (mode) { case COLOR_FILTER_FILL: { @@ -128,8 +129,14 @@ static void color_filter_task_cb(void *__restrict userdata, break; case COLOR_FILTER_SATURATION: rgb_to_hsv_v(orig_color, hsv_color); - hsv_color[1] = clamp_f(hsv_color[1] + fade, 0.0f, 1.0f); - hsv_to_rgb_v(hsv_color, final_color); + + if (hsv_color[1] != 0.0f) { + hsv_color[1] = clamp_f(hsv_color[1] + fade, 0.0f, 1.0f); + hsv_to_rgb_v(hsv_color, final_color); + } + else { + copy_v3_v3(final_color, orig_color); + } break; case COLOR_FILTER_VALUE: rgb_to_hsv_v(orig_color, hsv_color); @@ -186,7 +193,32 @@ static void color_filter_task_cb(void *__restrict userdata, float col[4]; SCULPT_vertex_color_get(ss, vd.index, col); - blend_color_interpolate_float(final_color, col, smooth_color, fade); + if (fade < 0.0f) { + interp_v4_v4v4(smooth_color, smooth_color, col, 0.5f); + } + + bool copy_alpha = col[3] == smooth_color[3]; + + if (fade < 0.0f) { + float delta_color[4]; + + /* Unsharp mask. */ + copy_v4_v4(delta_color, ss->filter_cache->pre_smoothed_color[vd.index]); + sub_v4_v4(delta_color, smooth_color); + + copy_v4_v4(final_color, col); + madd_v4_v4fl(final_color, delta_color, fade); + } + else { + blend_color_interpolate_float(final_color, col, smooth_color, fade); + } + + CLAMP4(final_color, 0.0f, 1.0f); + + /* Prevent accumulated numeric error from corrupting alpha. */ + if (copy_alpha) { + final_color[3] = smooth_color[3]; + } break; } } @@ -201,6 +233,46 @@ static void color_filter_task_cb(void *__restrict userdata, BKE_pbvh_node_mark_update_color(data->nodes[n]); } +static void sculpt_color_presmooth_init(SculptSession *ss) +{ + int totvert = SCULPT_vertex_count_get(ss); + + if (!ss->filter_cache->pre_smoothed_color) { + ss->filter_cache->pre_smoothed_color = MEM_malloc_arrayN( + totvert, sizeof(float) * 4, "ss->filter_cache->pre_smoothed_color"); + } + + for (int i = 0; i < totvert; i++) { + SCULPT_vertex_color_get(ss, i, ss->filter_cache->pre_smoothed_color[i]); + } + + for (int iteration = 0; iteration < 2; iteration++) { + for (int i = 0; i < totvert; i++) { + float avg[4] = {0.0f, 0.0f, 0.0f, 0.0f}; + int total = 0; + + SculptVertexNeighborIter ni; + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, i, ni) { + float col[4] = {0}; + + copy_v4_v4(col, ss->filter_cache->pre_smoothed_color[ni.index]); + + add_v4_v4(avg, col); + total++; + } + SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); + + if (total > 0) { + mul_v4_fl(avg, 1.0f / (float)total); + interp_v4_v4v4(ss->filter_cache->pre_smoothed_color[i], + ss->filter_cache->pre_smoothed_color[i], + avg, + 0.5f); + } + } + } +} + static int sculpt_color_filter_modal(bContext *C, wmOperator *op, const wmEvent *event) { Object *ob = CTX_data_active_object(C); @@ -227,6 +299,10 @@ static int sculpt_color_filter_modal(bContext *C, wmOperator *op, const wmEvent RNA_float_get_array(op->ptr, "fill_color", fill_color); IMB_colormanagement_srgb_to_scene_linear_v3(fill_color); + if (filter_strength < 0.0 && !ss->filter_cache->pre_smoothed_color) { + sculpt_color_presmooth_init(ss); + } + SculptThreadedTaskData data = { .sd = sd, .ob = ob, @@ -273,12 +349,7 @@ static int sculpt_color_filter_invoke(bContext *C, wmOperator *op, const wmEvent return OPERATOR_CANCELLED; } - if (!SCULPT_has_colors(ss)) { - return OPERATOR_CANCELLED; - } - SCULPT_undo_push_begin(ob, "color filter"); - BKE_sculpt_color_layer_create_if_needed(ob); /* CTX_data_ensure_evaluated_depsgraph should be used at the end to include the updates of @@ -310,7 +381,7 @@ void SCULPT_OT_color_filter(struct wmOperatorType *ot) /* api callbacks */ ot->invoke = sculpt_color_filter_invoke; ot->modal = sculpt_color_filter_modal; - ot->poll = SCULPT_vertex_colors_poll; + ot->poll = SCULPT_mode_poll; ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c index 4b832256dae..a32b1c3015b 100644 --- a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c +++ b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c @@ -178,6 +178,7 @@ void SCULPT_filter_cache_free(SculptSession *ss) MEM_SAFE_FREE(ss->filter_cache->sharpen_factor); MEM_SAFE_FREE(ss->filter_cache->detail_directions); MEM_SAFE_FREE(ss->filter_cache->limit_surface_co); + MEM_SAFE_FREE(ss->filter_cache->pre_smoothed_color); MEM_SAFE_FREE(ss->filter_cache); } diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h index 38905b50c59..3839c0e71e4 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.h +++ b/source/blender/editors/sculpt_paint/sculpt_intern.h @@ -11,11 +11,13 @@ #include "DNA_key_types.h" #include "DNA_listBase.h" #include "DNA_meshdata_types.h" +#include "DNA_scene_types.h" #include "DNA_vec_types.h" #include "BKE_paint.h" #include "BKE_pbvh.h" #include "BLI_bitmap.h" +#include "BLI_compiler_attrs.h" #include "BLI_compiler_compat.h" #include "BLI_gsqueue.h" #include "BLI_threads.h" @@ -25,12 +27,13 @@ extern "C" { #endif struct AutomaskingCache; +struct Image; +struct ImageUser; struct KeyBlock; struct Object; struct SculptUndoNode; struct bContext; - -enum ePaintSymmetryFlags; +struct PaintModeSettings; /* Updates */ @@ -43,6 +46,7 @@ typedef enum SculptUpdateType { SCULPT_UPDATE_MASK = 1 << 1, SCULPT_UPDATE_VISIBILITY = 1 << 2, SCULPT_UPDATE_COLOR = 1 << 3, + SCULPT_UPDATE_IMAGE = 1 << 4, } SculptUpdateType; typedef struct SculptCursorGeometryInfo { @@ -439,6 +443,9 @@ typedef struct FilterCache { /* Auto-masking. */ AutomaskingCache *automasking; + + /* Pre-smoothed colors used by sharpening. Colors are HSL. */ + float (*pre_smoothed_color)[4]; } FilterCache; /** @@ -1623,7 +1630,29 @@ void SCULPT_multiplane_scrape_preview_draw(uint gpuattr, void SCULPT_do_draw_face_sets_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode); /* Paint Brush. */ -void SCULPT_do_paint_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode); +void SCULPT_do_paint_brush(struct PaintModeSettings *paint_mode_settings, + Sculpt *sd, + Object *ob, + PBVHNode **nodes, + int totnode) ATTR_NONNULL(); + +/** + * @brief Get the image canvas for painting on the given object. + * + * @return #true if an image is found. The #r_image and #r_image_user fields are filled with the + * image and image user. Returns false when the image isn't found. In the later case the r_image + * and r_image_user are set to nullptr/NULL. + */ +bool SCULPT_paint_image_canvas_get(struct PaintModeSettings *paint_mode_settings, + struct Object *ob, + struct Image **r_image, + struct ImageUser **r_image_user) ATTR_NONNULL(); +void SCULPT_do_paint_brush_image(struct PaintModeSettings *paint_mode_settings, + Sculpt *sd, + Object *ob, + PBVHNode **nodes, + int totnode) ATTR_NONNULL(); +bool SCULPT_use_image_paint_brush(struct PaintModeSettings *settings, Object *ob) ATTR_NONNULL(); /* Smear Brush. */ void SCULPT_do_smear_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode); diff --git a/source/blender/editors/sculpt_paint/sculpt_ops.c b/source/blender/editors/sculpt_paint/sculpt_ops.c index f84852d1d0e..9581bf2071c 100644 --- a/source/blender/editors/sculpt_paint/sculpt_ops.c +++ b/source/blender/editors/sculpt_paint/sculpt_ops.c @@ -1030,14 +1030,6 @@ static int sculpt_mask_by_color_invoke(bContext *C, wmOperator *op, const wmEven return OPERATOR_CANCELLED; } - if (!SCULPT_has_colors(ss)) { - return OPERATOR_CANCELLED; - } - - if (SCULPT_has_loop_colors(ob)) { - BKE_pbvh_ensure_node_loops(ss->pbvh); - } - SCULPT_vertex_random_access_ensure(ss); /* Tools that are not brushes do not have the brush gizmo to update the vertex as the mouse move, @@ -1049,12 +1041,17 @@ static int sculpt_mask_by_color_invoke(bContext *C, wmOperator *op, const wmEven SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false); SCULPT_undo_push_begin(ob, "Mask by color"); + BKE_sculpt_color_layer_create_if_needed(ob); const int active_vertex = SCULPT_active_vertex_get(ss); const float threshold = RNA_float_get(op->ptr, "threshold"); const bool invert = RNA_boolean_get(op->ptr, "invert"); const bool preserve_mask = RNA_boolean_get(op->ptr, "preserve_previous_mask"); + if (SCULPT_has_loop_colors(ob)) { + BKE_pbvh_ensure_node_loops(ss->pbvh); + } + if (RNA_boolean_get(op->ptr, "contiguous")) { sculpt_mask_by_color_contiguous(ob, active_vertex, threshold, invert, preserve_mask); } @@ -1080,7 +1077,7 @@ static void SCULPT_OT_mask_by_color(wmOperatorType *ot) /* api callbacks */ ot->invoke = sculpt_mask_by_color_invoke; - ot->poll = SCULPT_vertex_colors_poll; + ot->poll = SCULPT_mode_poll; ot->flag = OPTYPE_REGISTER; diff --git a/source/blender/editors/sculpt_paint/sculpt_paint_color.c b/source/blender/editors/sculpt_paint/sculpt_paint_color.c index e7a713efa14..7a8a6e8e484 100644 --- a/source/blender/editors/sculpt_paint/sculpt_paint_color.c +++ b/source/blender/editors/sculpt_paint/sculpt_paint_color.c @@ -240,8 +240,14 @@ static void sample_wet_paint_reduce(const void *__restrict UNUSED(userdata), add_v4_v4(join->color, swptd->color); } -void SCULPT_do_paint_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) +void SCULPT_do_paint_brush( + PaintModeSettings *paint_mode_settings, Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) { + if (SCULPT_use_image_paint_brush(paint_mode_settings, ob)) { + SCULPT_do_paint_brush_image(paint_mode_settings, sd, ob, nodes, totnode); + return; + } + Brush *brush = BKE_paint_brush(&sd->paint); SculptSession *ss = ob->sculpt; diff --git a/source/blender/editors/sculpt_paint/sculpt_paint_image.cc b/source/blender/editors/sculpt_paint/sculpt_paint_image.cc new file mode 100644 index 00000000000..1fc7551f545 --- /dev/null +++ b/source/blender/editors/sculpt_paint/sculpt_paint_image.cc @@ -0,0 +1,430 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2022 Blender Foundation. All rights reserved. */ + +#include "DNA_image_types.h" +#include "DNA_material_types.h" +#include "DNA_mesh_types.h" +#include "DNA_node_types.h" +#include "DNA_object_types.h" + +#include "ED_paint.h" +#include "ED_uvedit.h" + +#include "BLI_math.h" +#include "BLI_math_color_blend.h" +#include "BLI_task.h" + +#include "IMB_colormanagement.h" +#include "IMB_imbuf.h" + +#include "BKE_brush.h" +#include "BKE_image_wrappers.hh" +#include "BKE_material.h" +#include "BKE_pbvh.h" +#include "BKE_pbvh_pixels.hh" + +#include "bmesh.h" + +#include "NOD_shader.h" + +#include "sculpt_intern.h" + +namespace blender::ed::sculpt_paint::paint::image { + +using namespace blender::bke::pbvh::pixels; +using namespace blender::bke::image; + +struct ImageData { + Image *image = nullptr; + ImageUser *image_user = nullptr; + + ~ImageData() = default; + + static bool init_active_image(Object *ob, + ImageData *r_image_data, + PaintModeSettings *paint_mode_settings) + { + return BKE_paint_canvas_image_get( + paint_mode_settings, ob, &r_image_data->image, &r_image_data->image_user); + } +}; + +struct TexturePaintingUserData { + Object *ob; + Brush *brush; + PBVHNode **nodes; + ImageData image_data; +}; + +/** Reading and writing to image buffer with 4 float channels. */ +class ImageBufferFloat4 { + private: + int pixel_offset; + + public: + void set_image_position(ImBuf *image_buffer, ushort2 image_pixel_position) + { + pixel_offset = int(image_pixel_position.y) * image_buffer->x + int(image_pixel_position.x); + } + + void next_pixel() + { + pixel_offset += 1; + } + + float4 read_pixel(ImBuf *image_buffer) const + { + return &image_buffer->rect_float[pixel_offset * 4]; + } + + void write_pixel(ImBuf *image_buffer, const float4 pixel_data) const + { + copy_v4_v4(&image_buffer->rect_float[pixel_offset * 4], pixel_data); + } + + const char *get_colorspace_name(ImBuf *image_buffer) + { + return IMB_colormanagement_get_float_colorspace(image_buffer); + } +}; + +/** Reading and writing to image buffer with 4 byte channels. */ +class ImageBufferByte4 { + private: + int pixel_offset; + + public: + void set_image_position(ImBuf *image_buffer, ushort2 image_pixel_position) + { + pixel_offset = int(image_pixel_position.y) * image_buffer->x + int(image_pixel_position.x); + } + + void next_pixel() + { + pixel_offset += 1; + } + + float4 read_pixel(ImBuf *image_buffer) const + { + float4 result; + rgba_uchar_to_float(result, + static_cast<const uchar *>( + static_cast<const void *>(&(image_buffer->rect[pixel_offset])))); + return result; + } + + void write_pixel(ImBuf *image_buffer, const float4 pixel_data) const + { + rgba_float_to_uchar( + static_cast<uchar *>(static_cast<void *>(&image_buffer->rect[pixel_offset])), pixel_data); + } + + const char *get_colorspace_name(ImBuf *image_buffer) + { + return IMB_colormanagement_get_rect_colorspace(image_buffer); + } +}; + +template<typename ImageBuffer> class PaintingKernel { + ImageBuffer image_accessor; + + SculptSession *ss; + const Brush *brush; + const int thread_id; + const MVert *mvert; + + float4 brush_color; + float brush_strength; + + SculptBrushTestFn brush_test_fn; + SculptBrushTest test; + /* Pointer to the last used image buffer to detect when buffers are switched. */ + void *last_used_image_buffer_ptr = nullptr; + const char *last_used_color_space = nullptr; + + public: + explicit PaintingKernel(SculptSession *ss, + const Brush *brush, + const int thread_id, + const MVert *mvert) + : ss(ss), brush(brush), thread_id(thread_id), mvert(mvert) + { + init_brush_strength(); + init_brush_test(); + } + + bool paint(const Triangles &triangles, const PackedPixelRow &pixel_row, ImBuf *image_buffer) + { + image_accessor.set_image_position(image_buffer, pixel_row.start_image_coordinate); + const TrianglePaintInput triangle = triangles.get_paint_input(pixel_row.triangle_index); + float3 pixel_pos = get_start_pixel_pos(triangle, pixel_row); + const float3 delta_pixel_pos = get_delta_pixel_pos(triangle, pixel_row, pixel_pos); + bool pixels_painted = false; + for (int x = 0; x < pixel_row.num_pixels; x++) { + if (!brush_test_fn(&test, pixel_pos)) { + pixel_pos += delta_pixel_pos; + image_accessor.next_pixel(); + continue; + } + + float4 color = image_accessor.read_pixel(image_buffer); + const float3 normal(0.0f, 0.0f, 0.0f); + const float3 face_normal(0.0f, 0.0f, 0.0f); + const float mask = 0.0f; + const float falloff_strength = SCULPT_brush_strength_factor( + ss, brush, pixel_pos, sqrtf(test.dist), normal, face_normal, mask, 0, thread_id); + float4 paint_color = brush_color * falloff_strength * brush_strength; + float4 buffer_color; + blend_color_mix_float(buffer_color, color, paint_color); + buffer_color *= brush->alpha; + IMB_blend_color_float(color, color, buffer_color, static_cast<IMB_BlendMode>(brush->blend)); + image_accessor.write_pixel(image_buffer, color); + pixels_painted = true; + + image_accessor.next_pixel(); + pixel_pos += delta_pixel_pos; + } + return pixels_painted; + } + + void init_brush_color(ImBuf *image_buffer) + { + const char *to_colorspace = image_accessor.get_colorspace_name(image_buffer); + if (last_used_color_space == to_colorspace) { + return; + } + copy_v3_v3(brush_color, + ss->cache->invert ? BKE_brush_secondary_color_get(ss->scene, brush) : + BKE_brush_color_get(ss->scene, brush)); + /* NOTE: Brush colors are stored in sRGB. We use math color to follow other areas that + * use brush colors. From there on we use IMB_colormanagement to convert the brush color to the + * colorspace of the texture. This isn't ideal, but would need more refactoring to make sure + * that brush colors are stored in scene linear by default. */ + srgb_to_linearrgb_v3_v3(brush_color, brush_color); + brush_color[3] = 1.0f; + + const char *from_colorspace = IMB_colormanagement_role_colorspace_name_get( + COLOR_ROLE_SCENE_LINEAR); + ColormanageProcessor *cm_processor = IMB_colormanagement_colorspace_processor_new( + from_colorspace, to_colorspace); + IMB_colormanagement_processor_apply_v4(cm_processor, brush_color); + IMB_colormanagement_processor_free(cm_processor); + last_used_color_space = to_colorspace; + } + + private: + void init_brush_strength() + { + brush_strength = ss->cache->bstrength; + } + void init_brush_test() + { + brush_test_fn = SCULPT_brush_test_init_with_falloff_shape(ss, &test, brush->falloff_shape); + } + + /** + * Extract the starting pixel position from the given encoded_pixels belonging to the triangle. + */ + float3 get_start_pixel_pos(const TrianglePaintInput &triangle, + const PackedPixelRow &encoded_pixels) const + { + return init_pixel_pos(triangle, encoded_pixels.start_barycentric_coord); + } + + /** + * Extract the delta pixel position that will be used to advance a Pixel instance to the next + * pixel. + */ + float3 get_delta_pixel_pos(const TrianglePaintInput &triangle, + const PackedPixelRow &encoded_pixels, + const float3 &start_pixel) const + { + float3 result = init_pixel_pos( + triangle, encoded_pixels.start_barycentric_coord + triangle.delta_barycentric_coord_u); + return result - start_pixel; + } + + float3 init_pixel_pos(const TrianglePaintInput &triangle, + const float2 &barycentric_weights) const + { + const int3 &vert_indices = triangle.vert_indices; + float3 result; + const float3 barycentric(barycentric_weights.x, + barycentric_weights.y, + 1.0f - barycentric_weights.x - barycentric_weights.y); + interp_v3_v3v3v3(result, + mvert[vert_indices[0]].co, + mvert[vert_indices[1]].co, + mvert[vert_indices[2]].co, + barycentric); + return result; + } +}; + +static std::vector<bool> init_triangle_brush_test(SculptSession *ss, + Triangles &triangles, + const MVert *mvert) +{ + std::vector<bool> brush_test(triangles.size()); + SculptBrushTest test; + SCULPT_brush_test_init(ss, &test); + float3 brush_min_bounds(test.location[0] - test.radius, + test.location[1] - test.radius, + test.location[2] - test.radius); + float3 brush_max_bounds(test.location[0] + test.radius, + test.location[1] + test.radius, + test.location[2] + test.radius); + for (int triangle_index = 0; triangle_index < triangles.size(); triangle_index++) { + TrianglePaintInput &triangle = triangles.get_paint_input(triangle_index); + + float3 triangle_min_bounds(mvert[triangle.vert_indices[0]].co); + float3 triangle_max_bounds(triangle_min_bounds); + for (int i = 1; i < 3; i++) { + const float3 &pos = mvert[triangle.vert_indices[i]].co; + triangle_min_bounds.x = min_ff(triangle_min_bounds.x, pos.x); + triangle_min_bounds.y = min_ff(triangle_min_bounds.y, pos.y); + triangle_min_bounds.z = min_ff(triangle_min_bounds.z, pos.z); + triangle_max_bounds.x = max_ff(triangle_max_bounds.x, pos.x); + triangle_max_bounds.y = max_ff(triangle_max_bounds.y, pos.y); + triangle_max_bounds.z = max_ff(triangle_max_bounds.z, pos.z); + } + brush_test[triangle_index] = isect_aabb_aabb_v3( + brush_min_bounds, brush_max_bounds, triangle_min_bounds, triangle_max_bounds); + } + return brush_test; +} + +static void do_paint_pixels(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict tls) +{ + TexturePaintingUserData *data = static_cast<TexturePaintingUserData *>(userdata); + Object *ob = data->ob; + SculptSession *ss = ob->sculpt; + const Brush *brush = data->brush; + PBVHNode *node = data->nodes[n]; + + NodeData &node_data = BKE_pbvh_pixels_node_data_get(*node); + const int thread_id = BLI_task_parallel_thread_id(tls); + MVert *mvert = SCULPT_mesh_deformed_mverts_get(ss); + + std::vector<bool> brush_test = init_triangle_brush_test(ss, node_data.triangles, mvert); + + PaintingKernel<ImageBufferFloat4> kernel_float4(ss, brush, thread_id, mvert); + PaintingKernel<ImageBufferByte4> kernel_byte4(ss, brush, thread_id, mvert); + + ImageUser image_user = *data->image_data.image_user; + bool pixels_updated = false; + for (UDIMTilePixels &tile_data : node_data.tiles) { + LISTBASE_FOREACH (ImageTile *, tile, &data->image_data.image->tiles) { + ImageTileWrapper image_tile(tile); + if (image_tile.get_tile_number() == tile_data.tile_number) { + image_user.tile = image_tile.get_tile_number(); + + ImBuf *image_buffer = BKE_image_acquire_ibuf(data->image_data.image, &image_user, nullptr); + if (image_buffer == nullptr) { + continue; + } + + if (image_buffer->rect_float != nullptr) { + kernel_float4.init_brush_color(image_buffer); + } + else { + kernel_float4.init_brush_color(image_buffer); + } + + for (const PackedPixelRow &pixel_row : tile_data.pixel_rows) { + if (!brush_test[pixel_row.triangle_index]) { + continue; + } + bool pixels_painted = false; + if (image_buffer->rect_float != nullptr) { + pixels_painted = kernel_float4.paint(node_data.triangles, pixel_row, image_buffer); + } + else { + pixels_painted = kernel_byte4.paint(node_data.triangles, pixel_row, image_buffer); + } + + if (pixels_painted) { + tile_data.mark_dirty(pixel_row); + } + } + + BKE_image_release_ibuf(data->image_data.image, image_buffer, nullptr); + pixels_updated |= tile_data.flags.dirty; + break; + } + } + } + + node_data.flags.dirty |= pixels_updated; +} + +static void do_mark_dirty_regions(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict UNUSED(tls)) +{ + TexturePaintingUserData *data = static_cast<TexturePaintingUserData *>(userdata); + PBVHNode *node = data->nodes[n]; + BKE_pbvh_pixels_mark_image_dirty(*node, *data->image_data.image, *data->image_data.image_user); +} + +} // namespace blender::ed::sculpt_paint::paint::image + +extern "C" { + +using namespace blender::ed::sculpt_paint::paint::image; + +bool SCULPT_paint_image_canvas_get(PaintModeSettings *paint_mode_settings, + Object *ob, + Image **r_image, + ImageUser **r_image_user) +{ + BLI_assert(r_image); + BLI_assert(r_image_user); + ImageData image_data; + if (!ImageData::init_active_image(ob, &image_data, paint_mode_settings)) { + return false; + } + + *r_image = image_data.image; + *r_image_user = image_data.image_user; + return true; +} + +bool SCULPT_use_image_paint_brush(PaintModeSettings *settings, Object *ob) +{ + if (!U.experimental.use_sculpt_texture_paint) { + return false; + } + if (ob->type != OB_MESH) { + return false; + } + Image *image; + ImageUser *image_user; + return BKE_paint_canvas_image_get(settings, ob, &image, &image_user); +} + +void SCULPT_do_paint_brush_image( + PaintModeSettings *paint_mode_settings, Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) +{ + Brush *brush = BKE_paint_brush(&sd->paint); + + TexturePaintingUserData data = {nullptr}; + data.ob = ob; + data.brush = brush; + data.nodes = nodes; + + if (!ImageData::init_active_image(ob, &data.image_data, paint_mode_settings)) { + return; + } + + TaskParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, true, totnode); + BLI_task_parallel_range(0, totnode, &data, do_paint_pixels, &settings); + + TaskParallelSettings settings_flush; + BKE_pbvh_parallel_range_settings(&settings_flush, false, totnode); + BLI_task_parallel_range(0, totnode, &data, do_mark_dirty_regions, &settings_flush); +} +} diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.c b/source/blender/editors/sculpt_paint/sculpt_undo.c index 1354277fbdd..eae90359dfd 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.c +++ b/source/blender/editors/sculpt_paint/sculpt_undo.c @@ -343,7 +343,7 @@ static bool sculpt_undo_restore_color(bContext *C, SculptUndoNode *unode) bool modified = false; /* NOTE: even with loop colors we still store derived - * vertex colors for original data lookup.*/ + * vertex colors for original data lookup. */ if (unode->col && !unode->loop_col) { BKE_pbvh_swap_colors(ss->pbvh, unode->index, unode->totvert, unode->col); modified = true; diff --git a/source/blender/editors/space_action/CMakeLists.txt b/source/blender/editors/space_action/CMakeLists.txt index 9e292062f27..841bd5cf91b 100644 --- a/source/blender/editors/space_action/CMakeLists.txt +++ b/source/blender/editors/space_action/CMakeLists.txt @@ -5,6 +5,7 @@ set(INC ../../blenkernel ../../blenlib ../../blentranslation + ../../depsgraph ../../gpu ../../makesdna ../../makesrna diff --git a/source/blender/editors/space_action/action_data.c b/source/blender/editors/space_action/action_data.c index 36858d57446..8f97a58451e 100644 --- a/source/blender/editors/space_action/action_data.c +++ b/source/blender/editors/space_action/action_data.c @@ -45,6 +45,8 @@ #include "ED_mask.h" #include "ED_screen.h" +#include "DEG_depsgraph.h" + #include "WM_api.h" #include "WM_types.h" @@ -335,6 +337,13 @@ static int action_pushdown_exec(bContext *C, wmOperator *op) /* action can be safely added */ BKE_nla_action_pushdown(adt, ID_IS_OVERRIDE_LIBRARY(adt_id_owner)); + struct Main *bmain = CTX_data_main(C); + DEG_id_tag_update_ex(bmain, adt_id_owner, ID_RECALC_ANIMATION); + + /* The action needs updating too, as FCurve modifiers are to be reevaluated. They won't extend + * beyond the NLA strip after pushing down to the NLA. */ + DEG_id_tag_update_ex(bmain, &adt->action->id, ID_RECALC_ANIMATION); + /* Stop displaying this action in this editor * NOTE: The editor itself doesn't set a user... */ diff --git a/source/blender/editors/space_buttons/space_buttons.c b/source/blender/editors/space_buttons/space_buttons.c index 1ec6832c6c3..1d0061ab7d8 100644 --- a/source/blender/editors/space_buttons/space_buttons.c +++ b/source/blender/editors/space_buttons/space_buttons.c @@ -861,12 +861,11 @@ static void buttons_id_remap(ScrArea *UNUSED(area), for (int i = 0; i < path->len; i++) { switch (BKE_id_remapper_apply(mappings, &path->ptr[i].owner_id, ID_REMAP_APPLY_DEFAULT)) { case ID_REMAP_RESULT_SOURCE_UNASSIGNED: { - if (i == 0) { - MEM_SAFE_FREE(sbuts->path); - } - else { + path->len = i; + if (i != 0) { + /* If the first item in the path is cleared, the whole path is cleared, so no need to + * clear further items here, see also at the end of this block. */ memset(&path->ptr[i], 0, sizeof(path->ptr[i]) * (path->len - i)); - path->len = i; } break; } @@ -887,6 +886,9 @@ static void buttons_id_remap(ScrArea *UNUSED(area), } } } + if (path->len == 0) { + MEM_SAFE_FREE(sbuts->path); + } } if (sbuts->texuser) { diff --git a/source/blender/editors/space_file/file_draw.c b/source/blender/editors/space_file/file_draw.c index 0438ba8dcd2..0e2b98ca349 100644 --- a/source/blender/editors/space_file/file_draw.c +++ b/source/blender/editors/space_file/file_draw.c @@ -248,7 +248,7 @@ static void file_draw_string_multiline(int sx, int font_id = style->widgetlabel.uifont_id; int len = strlen(string); - rctf textbox; + rcti textbox; BLF_wordwrap(font_id, wrap_width); BLF_enable(font_id, BLF_WORD_WRAP); BLF_boundbox(font_id, string, len, &textbox); @@ -260,7 +260,7 @@ static void file_draw_string_multiline(int sx, rect.xmax = sx + wrap_width; /* Need to increase the clipping rect by one more line, since the #UI_fontstyle_draw_ex() will * actually start drawing at (ymax - line-height). */ - rect.ymin = sy - round_fl_to_int(BLI_rctf_size_y(&textbox)) - line_height; + rect.ymin = sy - BLI_rcti_size_y(&textbox) - line_height; rect.ymax = sy; struct ResultBLF result; diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c index 08f4ecc0dd9..9f71d6f77c7 100644 --- a/source/blender/editors/space_file/filelist.c +++ b/source/blender/editors/space_file/filelist.c @@ -3403,7 +3403,7 @@ static void filelist_readjob_main_recursive(Main *bmain, FileList *filelist) filelist->filelist.entries[20].entry->relpath = BLI_strdup("Action"); filelist->filelist.entries[21].entry->relpath = BLI_strdup("NodeTree"); filelist->filelist.entries[22].entry->relpath = BLI_strdup("Speaker"); - filelist->filelist.entries[23].entry->relpath = BLI_strdup("Hair"); + filelist->filelist.entries[23].entry->relpath = BLI_strdup("Curves"); filelist->filelist.entries[24].entry->relpath = BLI_strdup("Point Cloud"); filelist->filelist.entries[25].entry->relpath = BLI_strdup("Volume"); # ifdef WITH_FREESTYLE diff --git a/source/blender/editors/space_nla/nla_edit.c b/source/blender/editors/space_nla/nla_edit.c index 79bfaa92f80..2aa9b347ed7 100644 --- a/source/blender/editors/space_nla/nla_edit.c +++ b/source/blender/editors/space_nla/nla_edit.c @@ -98,6 +98,7 @@ static int nlaedit_enable_tweakmode_exec(bContext *C, wmOperator *op) int filter; const bool do_solo = RNA_boolean_get(op->ptr, "isolate_action"); + const bool use_upper_stack_evaluation = RNA_boolean_get(op->ptr, "use_upper_stack_evaluation"); bool ok = false; /* get editor data */ @@ -119,6 +120,13 @@ static int nlaedit_enable_tweakmode_exec(bContext *C, wmOperator *op) for (ale = anim_data.first; ale; ale = ale->next) { AnimData *adt = ale->data; + if (use_upper_stack_evaluation) { + adt->flag |= ADT_NLA_EVAL_UPPER_TRACKS; + } + else { + adt->flag &= ~ADT_NLA_EVAL_UPPER_TRACKS; + } + /* Try entering tweak-mode if valid. */ ok |= BKE_nla_tweakmode_enter(adt); @@ -181,6 +189,13 @@ void NLA_OT_tweakmode_enter(wmOperatorType *ot) "Enable 'solo' on the NLA Track containing the active strip, " "to edit it without seeing the effects of the NLA stack"); RNA_def_property_flag(prop, PROP_SKIP_SAVE); + + prop = RNA_def_boolean(ot->srna, + "use_upper_stack_evaluation", + false, + "Evaluate Upper Stack", + "In tweak mode, display the effects of the tracks above the tweak strip"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); } /** \} */ diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc index 88ab7617932..47d35ced371 100644 --- a/source/blender/editors/space_node/node_draw.cc +++ b/source/blender/editors/space_node/node_draw.cc @@ -26,12 +26,10 @@ #include "BLI_span.hh" #include "BLI_string_ref.hh" #include "BLI_vector.hh" -#include "BLI_vector_set.hh" #include "BLT_translation.h" #include "BKE_context.h" -#include "BKE_geometry_set.hh" #include "BKE_idtype.h" #include "BKE_lib_id.h" #include "BKE_main.h" @@ -75,10 +73,9 @@ #include "node_intern.hh" /* own include */ using blender::GPointer; -using blender::fn::FieldCPPType; -using blender::fn::FieldInput; using blender::fn::GField; namespace geo_log = blender::nodes::geometry_nodes_eval_log; +using geo_log::NamedAttributeUsage; extern "C" { /* XXX interface.h */ @@ -374,6 +371,8 @@ static void node_update_basis(const bContext &C, bNodeTree &ntree, bNode &node, const char *socket_label = nodeSocketLabel(nsock); nsock->typeinfo->draw((bContext *)&C, row, &sockptr, &nodeptr, IFACE_(socket_label)); + node_socket_add_tooltip(&ntree, &node, nsock, row); + UI_block_align_end(&block); UI_block_layout_resolve(&block, nullptr, &buty); @@ -504,6 +503,8 @@ static void node_update_basis(const bContext &C, bNodeTree &ntree, bNode &node, const char *socket_label = nodeSocketLabel(nsock); nsock->typeinfo->draw((bContext *)&C, row, &sockptr, &nodeptr, IFACE_(socket_label)); + node_socket_add_tooltip(&ntree, &node, nsock, row); + UI_block_align_end(&block); UI_block_layout_resolve(&block, nullptr, &buty); @@ -943,6 +944,10 @@ static std::optional<std::string> create_socket_inspection_string(bContext *C, bNodeSocket &socket) { SpaceNode *snode = CTX_wm_space_node(C); + if (snode == nullptr) { + return {}; + }; + const geo_log::SocketLog *socket_log = geo_log::ModifierLog::find_socket_by_node_editor_context( *snode, node, socket); if (socket_log == nullptr) { @@ -970,6 +975,78 @@ static std::optional<std::string> create_socket_inspection_string(bContext *C, return ss.str(); } +static bool node_socket_has_tooltip(bNodeTree *ntree, bNodeSocket *socket) +{ + if (ntree->type == NTREE_GEOMETRY) { + return true; + } + + if (socket->declaration != nullptr) { + const blender::nodes::SocketDeclaration &socket_decl = *socket->declaration; + return !socket_decl.description().is_empty(); + } + + return false; +} + +static char *node_socket_get_tooltip(bContext *C, + bNodeTree *ntree, + bNode *node, + bNodeSocket *socket) +{ + std::stringstream output; + if (socket->declaration != nullptr) { + const blender::nodes::SocketDeclaration &socket_decl = *socket->declaration; + blender::StringRef description = socket_decl.description(); + if (!description.is_empty()) { + output << TIP_(description.data()); + } + } + + if (ntree->type == NTREE_GEOMETRY) { + if (!output.str().empty()) { + output << ".\n\n"; + } + + std::optional<std::string> socket_inspection_str = create_socket_inspection_string( + C, *node, *socket); + if (socket_inspection_str.has_value()) { + output << *socket_inspection_str; + } + else { + output << TIP_("The socket value has not been computed yet"); + } + } + + if (output.str().empty()) { + output << nodeSocketLabel(socket); + } + + return BLI_strdup(output.str().c_str()); +} + +void node_socket_add_tooltip(bNodeTree *ntree, bNode *node, bNodeSocket *sock, uiLayout *layout) +{ + if (!node_socket_has_tooltip(ntree, sock)) { + return; + } + + SocketTooltipData *data = MEM_cnew<SocketTooltipData>(__func__); + data->ntree = ntree; + data->node = node; + data->socket = sock; + + uiLayoutSetTooltipFunc( + layout, + [](bContext *C, void *argN, const char *UNUSED(tip)) { + SocketTooltipData *data = static_cast<SocketTooltipData *>(argN); + return node_socket_get_tooltip(C, data->ntree, data->node, data->socket); + }, + data, + MEM_dupallocN, + MEM_freeN); +} + static void node_socket_draw_nested(const bContext &C, bNodeTree &ntree, PointerRNA &node_ptr, @@ -1001,8 +1078,7 @@ static void node_socket_draw_nested(const bContext &C, size_id, outline_col_id); - if (ntree.type != NTREE_GEOMETRY) { - /* Only geometry nodes has socket value tooltips currently. */ + if (!node_socket_has_tooltip(&ntree, &sock)) { return; } @@ -1034,24 +1110,7 @@ static void node_socket_draw_nested(const bContext &C, but, [](bContext *C, void *argN, const char *UNUSED(tip)) { SocketTooltipData *data = (SocketTooltipData *)argN; - std::optional<std::string> socket_inspection_str = create_socket_inspection_string( - C, *data->node, *data->socket); - - std::stringstream output; - if (data->socket->declaration != nullptr) { - const blender::nodes::SocketDeclaration &socket_decl = *data->socket->declaration; - blender::StringRef description = socket_decl.description(); - if (!description.is_empty()) { - output << TIP_(description.data()) << ".\n\n"; - } - } - 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()); + return node_socket_get_tooltip(C, data->ntree, data->node, data->socket); }, data, MEM_freeN); @@ -1626,10 +1685,127 @@ static std::string node_get_execution_time_label(const SpaceNode &snode, const b struct NodeExtraInfoRow { std::string text; - const char *tooltip; int icon; + const char *tooltip = nullptr; + + uiButToolTipFunc tooltip_fn = nullptr; + void *tooltip_fn_arg = nullptr; + void (*tooltip_fn_free_arg)(void *) = nullptr; }; +struct NamedAttributeTooltipArg { + Map<std::string, NamedAttributeUsage> usage_by_attribute; +}; + +static char *named_attribute_tooltip(bContext *UNUSED(C), void *argN, const char *UNUSED(tip)) +{ + NamedAttributeTooltipArg &arg = *static_cast<NamedAttributeTooltipArg *>(argN); + + std::stringstream ss; + ss << TIP_("Accessed attribute names:\n"); + Vector<std::pair<StringRefNull, NamedAttributeUsage>> sorted_used_attribute; + for (auto &&item : arg.usage_by_attribute.items()) { + sorted_used_attribute.append({item.key, item.value}); + } + std::sort(sorted_used_attribute.begin(), sorted_used_attribute.end()); + for (const std::pair<StringRefNull, NamedAttributeUsage> &attribute : sorted_used_attribute) { + const StringRefNull name = attribute.first; + const NamedAttributeUsage usage = attribute.second; + ss << " \u2022 \"" << name << "\": "; + Vector<std::string> usages; + if ((usage & NamedAttributeUsage::Read) != NamedAttributeUsage::None) { + usages.append(TIP_("read")); + } + if ((usage & NamedAttributeUsage::Write) != NamedAttributeUsage::None) { + usages.append(TIP_("write")); + } + if ((usage & NamedAttributeUsage::Remove) != NamedAttributeUsage::None) { + usages.append(TIP_("remove")); + } + for (const int i : usages.index_range()) { + ss << usages[i]; + if (i < usages.size() - 1) { + ss << ", "; + } + } + ss << "\n"; + } + ss << "\n"; + ss << TIP_( + "Attributes with these names used within the group may conflict with existing attributes"); + return BLI_strdup(ss.str().c_str()); +} + +static NodeExtraInfoRow row_from_used_named_attribute( + const Map<std::string, NamedAttributeUsage> &usage_by_attribute_name) +{ + NodeExtraInfoRow row; + row.text = TIP_("Attributes"); + row.icon = ICON_SPREADSHEET; + row.tooltip_fn = named_attribute_tooltip; + row.tooltip_fn_arg = new NamedAttributeTooltipArg{usage_by_attribute_name}; + row.tooltip_fn_free_arg = [](void *arg) { delete static_cast<NamedAttributeTooltipArg *>(arg); }; + return row; +} + +static std::optional<NodeExtraInfoRow> node_get_accessed_attributes_row(const SpaceNode &snode, + const bNode &node) +{ + if (node.type == NODE_GROUP) { + const geo_log::TreeLog *root_tree_log = geo_log::ModifierLog::find_tree_by_node_editor_context( + snode); + if (root_tree_log == nullptr) { + return std::nullopt; + } + const geo_log::TreeLog *tree_log = root_tree_log->lookup_child_log(node.name); + if (tree_log == nullptr) { + return std::nullopt; + } + + Map<std::string, NamedAttributeUsage> usage_by_attribute; + tree_log->foreach_node_log([&](const geo_log::NodeLog &node_log) { + for (const geo_log::UsedNamedAttribute &used_attribute : node_log.used_named_attributes()) { + usage_by_attribute.lookup_or_add_as(used_attribute.name, + used_attribute.usage) |= used_attribute.usage; + } + }); + if (usage_by_attribute.is_empty()) { + return std::nullopt; + } + + return row_from_used_named_attribute(usage_by_attribute); + } + if (ELEM(node.type, + GEO_NODE_STORE_NAMED_ATTRIBUTE, + GEO_NODE_REMOVE_ATTRIBUTE, + GEO_NODE_INPUT_NAMED_ATTRIBUTE)) { + /* Only show the overlay when the name is passed in from somewhere else. */ + LISTBASE_FOREACH (bNodeSocket *, socket, &node.inputs) { + if (STREQ(socket->name, "Name")) { + if ((socket->flag & SOCK_IN_USE) == 0) { + return std::nullopt; + } + } + } + const geo_log::NodeLog *node_log = geo_log::ModifierLog::find_node_by_node_editor_context( + snode, node.name); + if (node_log == nullptr) { + return std::nullopt; + } + Map<std::string, NamedAttributeUsage> usage_by_attribute; + for (const geo_log::UsedNamedAttribute &used_attribute : node_log->used_named_attributes()) { + usage_by_attribute.lookup_or_add_as(used_attribute.name, + used_attribute.usage) |= used_attribute.usage; + } + if (usage_by_attribute.is_empty()) { + return std::nullopt; + } + return row_from_used_named_attribute(usage_by_attribute); + } + + return std::nullopt; +} + static Vector<NodeExtraInfoRow> node_get_extra_info(const SpaceNode &snode, const bNode &node) { Vector<NodeExtraInfoRow> rows; @@ -1660,6 +1836,14 @@ static Vector<NodeExtraInfoRow> node_get_extra_info(const SpaceNode &snode, cons rows.append(std::move(row)); } } + + if (snode.overlay.flag & SN_OVERLAY_SHOW_NAMED_ATTRIBUTES && + snode.edittree->type == NTREE_GEOMETRY) { + if (std::optional<NodeExtraInfoRow> row = node_get_accessed_attributes_row(snode, node)) { + rows.append(std::move(*row)); + } + } + return rows; } @@ -1669,20 +1853,20 @@ static void node_draw_extra_info_row(const bNode &node, const int row, const NodeExtraInfoRow &extra_info_row) { - uiBut *but_timing = uiDefBut(&block, - UI_BTYPE_LABEL, - 0, - extra_info_row.text.c_str(), - (int)(rect.xmin + 4.0f * U.dpi_fac + NODE_MARGIN_X + 0.4f), - (int)(rect.ymin + row * (20.0f * U.dpi_fac)), - (short)(rect.xmax - rect.xmin), - (short)NODE_DY, - nullptr, - 0, - 0, - 0, - 0, - ""); + uiBut *but_text = uiDefBut(&block, + UI_BTYPE_LABEL, + 0, + extra_info_row.text.c_str(), + (int)(rect.xmin + 4.0f * U.dpi_fac + NODE_MARGIN_X + 0.4f), + (int)(rect.ymin + row * (20.0f * U.dpi_fac)), + (short)(rect.xmax - rect.xmin), + (short)NODE_DY, + nullptr, + 0, + 0, + 0, + 0, + ""); UI_block_emboss_set(&block, UI_EMBOSS_NONE); uiBut *but_icon = uiDefIconBut(&block, UI_BTYPE_BUT, @@ -1698,9 +1882,15 @@ static void node_draw_extra_info_row(const bNode &node, 0, 0, extra_info_row.tooltip); + if (extra_info_row.tooltip_fn != nullptr) { + UI_but_func_tooltip_set(but_icon, + extra_info_row.tooltip_fn, + extra_info_row.tooltip_fn_arg, + extra_info_row.tooltip_fn_free_arg); + } UI_block_emboss_set(&block, UI_EMBOSS); if (node.flag & NODE_MUTED) { - UI_but_flag_enable(but_timing, UI_BUT_INACTIVE); + UI_but_flag_enable(but_text, UI_BUT_INACTIVE); UI_but_flag_enable(but_icon, UI_BUT_INACTIVE); } } diff --git a/source/blender/editors/space_node/node_intern.hh b/source/blender/editors/space_node/node_intern.hh index 44163cd5ae3..10d4ad36d95 100644 --- a/source/blender/editors/space_node/node_intern.hh +++ b/source/blender/editors/space_node/node_intern.hh @@ -134,6 +134,8 @@ void node_socket_color_get(const bContext &C, void node_draw_space(const bContext &C, ARegion ®ion); +void node_socket_add_tooltip(bNodeTree *ntree, bNode *node, bNodeSocket *sock, uiLayout *layout); + /** * Sort nodes by selection: unselected nodes first, then selected, * then the active node at the very end. Relative order is kept intact. diff --git a/source/blender/editors/space_node/node_templates.cc b/source/blender/editors/space_node/node_templates.cc index e92e0571157..58a313c328e 100644 --- a/source/blender/editors/space_node/node_templates.cc +++ b/source/blender/editors/space_node/node_templates.cc @@ -852,23 +852,19 @@ static void ui_node_draw_input( } } else { - row = uiLayoutRow(row, true); + uiLayout *sub = uiLayoutRow(row, true); - uiTemplateNodeLink(row, C, ntree, node, input); + uiTemplateNodeLink(sub, C, ntree, node, input); if (input->flag & SOCK_HIDE_VALUE) { add_dummy_decorator = true; } /* input not linked, show value */ else { - uiLayout *sub = row; - switch (input->type) { case SOCK_VECTOR: - if (input->type == SOCK_VECTOR) { - uiItemS(row); - sub = uiLayoutColumn(row, true); - } + uiItemS(sub); + sub = uiLayoutColumn(sub, true); ATTR_FALLTHROUGH; case SOCK_FLOAT: case SOCK_INT: @@ -884,7 +880,7 @@ static void ui_node_draw_input( if (node_tree->type == NTREE_GEOMETRY && snode != nullptr) { /* Only add the attribute search in the node editor, in other places there is not * enough context. */ - node_geometry_add_attribute_search_button(*C, *node, inputptr, *row); + node_geometry_add_attribute_search_button(*C, *node, inputptr, *sub); } else { uiItemR(sub, &inputptr, "default_value", 0, "", ICON_NONE); @@ -903,6 +899,8 @@ static void ui_node_draw_input( uiItemDecoratorR(split_wrapper.decorate_column, nullptr, nullptr, 0); } + node_socket_add_tooltip(ntree, node, input, row); + /* clear */ node->flag &= ~NODE_TEST; } diff --git a/source/blender/editors/space_outliner/outliner_tools.cc b/source/blender/editors/space_outliner/outliner_tools.cc index 67bc0e91053..5da64177e51 100644 --- a/source/blender/editors/space_outliner/outliner_tools.cc +++ b/source/blender/editors/space_outliner/outliner_tools.cc @@ -217,6 +217,7 @@ static void unlink_action_fn(bContext *C, { /* just set action to nullptr */ BKE_animdata_set_action(CTX_wm_reports(C), tsep->id, nullptr); + DEG_id_tag_update(tsep->id, ID_RECALC_ANIMATION); } static void unlink_material_fn(bContext *UNUSED(C), @@ -2016,7 +2017,7 @@ static const EnumPropertyItem prop_id_op_types[] = { {OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET, "OVERRIDE_LIBRARY_RESET", 0, - "Reset Library Override", + "Reset Library Override Single", "Reset this local override to its linked values"}, {OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET_HIERARCHY, "OVERRIDE_LIBRARY_RESET_HIERARCHY", @@ -2036,18 +2037,18 @@ static const EnumPropertyItem prop_id_op_types[] = { "Rebuild this local override from its linked reference, as well as its hierarchy of " "dependencies, enforcing that hierarchy to match the linked data (i.e. ignoring exiting " "overrides on data-blocks pointer properties)"}, + {OUTLINER_IDOP_OVERRIDE_LIBRARY_CLEAR_SINGLE, + "OVERRIDE_LIBRARY_CLEAR_SINGLE", + 0, + "Clear Library Override Single", + "Delete this local override and relink its usages to the linked data-blocks if possible, " + "else reset it and mark it as non editable"}, {OUTLINER_IDOP_OVERRIDE_LIBRARY_CLEAR_HIERARCHY, "OVERRIDE_LIBRARY_CLEAR_HIERARCHY", 0, "Clear Library Override Hierarchy", "Delete this local override (including its hierarchy of override dependencies) and relink " "its usages to the linked data-blocks"}, - {OUTLINER_IDOP_OVERRIDE_LIBRARY_CLEAR_SINGLE, - "OVERRIDE_LIBRARY_CLEAR_SINGLE", - 0, - "Clear Single Library Override", - "Delete this local override if possible, else reset it and mark it as non editable, and " - "relink its usages to the linked data-blocks"}, {0, "", 0, nullptr, nullptr}, {OUTLINER_IDOP_COPY, "COPY", ICON_COPYDOWN, "Copy", ""}, {OUTLINER_IDOP_PASTE, "PASTE", ICON_PASTEDOWN, "Paste", ""}, diff --git a/source/blender/editors/space_spreadsheet/space_spreadsheet.cc b/source/blender/editors/space_spreadsheet/space_spreadsheet.cc index fbdc451cf06..14b9dbe4b44 100644 --- a/source/blender/editors/space_spreadsheet/space_spreadsheet.cc +++ b/source/blender/editors/space_spreadsheet/space_spreadsheet.cc @@ -264,7 +264,13 @@ Object *spreadsheet_get_object_eval(const SpaceSpreadsheet *sspreadsheet, return nullptr; } Object *object_orig = (Object *)used_id; - if (!ELEM(object_orig->type, OB_MESH, OB_POINTCLOUD, OB_VOLUME, OB_CURVES_LEGACY, OB_FONT)) { + if (!ELEM(object_orig->type, + OB_MESH, + OB_POINTCLOUD, + OB_VOLUME, + OB_CURVES_LEGACY, + OB_FONT, + OB_CURVES)) { return nullptr; } diff --git a/source/blender/editors/space_view3d/view3d_select.c b/source/blender/editors/space_view3d/view3d_select.c index 520d234e261..03862c6bdff 100644 --- a/source/blender/editors/space_view3d/view3d_select.c +++ b/source/blender/editors/space_view3d/view3d_select.c @@ -2125,49 +2125,6 @@ static Base *mouse_select_eval_buffer(ViewContext *vc, hit_index = a; } } - - /* Find the best active & non-active hits. - * NOTE(@campbellbarton): Checking if `hits > 1` isn't a reliable way to know - * if there are multiple objects selected since it's possible the same object - * generates multiple hits, either from: - * - Multiple sub-components (bones & camera tracks). - * - Multiple selectable elements such as the object center and the geometry. - * - * For this reason, keep track of the best hit as well as the best hit that - * excludes the selected & active object, using this value when it's valid. */ - if ((hit_index != -1) && - /* Special case, cycling away from the active object should only be done when it - * doesn't have a bone selection, otherwise selecting sub-elements is difficult. */ - ((buffer[hit_index].id & 0xFFFF0000) == 0) && - /* Only exclude active object when it is selected. */ - (BASACT(view_layer) && (BASACT(view_layer)->flag & BASE_SELECTED)) && - /* Allow disabling this behavior entirely. */ - (U.experimental.use_select_nearest_on_first_click == false)) { - - const int select_id_active = BASACT(view_layer)->object->runtime.select_id; - - /* Check if `hit_index` is the current active object. */ - if ((buffer[hit_index].id & 0xFFFF) == select_id_active) { - uint min_not_active = 0xFFFFFFFF; - int hit_index_not_active = -1; - for (a = 0; a < hits; a++) { - /* Any object other than the active-selected. */ - if (select_id_active == (buffer[a].id & 0xFFFF)) { - continue; - } - if (min_not_active > buffer[a].depth) { - min_not_active = buffer[a].depth; - hit_index_not_active = a; - } - } - - /* When the active was selected, first try to use the index - * for the best non-active hit that was found. */ - if (hit_index_not_active != -1) { - hit_index = hit_index_not_active; - } - } - } } if (hit_index != -1) { diff --git a/source/blender/editors/space_view3d/view3d_view.c b/source/blender/editors/space_view3d/view3d_view.c index a56bbb1c1df..fc88737ca70 100644 --- a/source/blender/editors/space_view3d/view3d_view.c +++ b/source/blender/editors/space_view3d/view3d_view.c @@ -1129,7 +1129,6 @@ void VIEW3D_OT_localview_remove_from(wmOperatorType *ot) /* api callbacks */ ot->exec = localview_remove_from_exec; - ot->invoke = WM_operator_confirm; ot->poll = localview_remove_from_poll; ot->flag = OPTYPE_UNDO; } diff --git a/source/blender/editors/transform/transform_convert_sequencer.c b/source/blender/editors/transform/transform_convert_sequencer.c index c7f086e7d4b..d7d85646b08 100644 --- a/source/blender/editors/transform/transform_convert_sequencer.c +++ b/source/blender/editors/transform/transform_convert_sequencer.c @@ -669,8 +669,10 @@ static Sequence *effect_base_input_get(Sequence *effect, SeqInputSide side) return input; } -/* Strips that aren't selected, but their position entirely depends on transformed strips. - * This collection is used to offset animation.*/ +/** + * Strips that aren't selected, but their position entirely depends on transformed strips. + * This collection is used to offset animation. + */ static SeqCollection *query_time_dependent_strips_strips(TransInfo *t) { ListBase *seqbase = seqbase_active_get(t); diff --git a/source/blender/editors/util/ed_draw.c b/source/blender/editors/util/ed_draw.c index eaa1684930d..1b6a3efe19c 100644 --- a/source/blender/editors/util/ed_draw.c +++ b/source/blender/editors/util/ed_draw.c @@ -711,7 +711,7 @@ static float metadata_box_height_get(ImBuf *ibuf, int fontid, const bool is_top) if (i == 4) { struct { struct ResultBLF info; - rctf rect; + rcti rect; } wrap; BLF_enable(fontid, BLF_WORD_WRAP); diff --git a/source/blender/editors/util/ed_transverts.c b/source/blender/editors/util/ed_transverts.c index 8d8f7733877..ef57f9e9b33 100644 --- a/source/blender/editors/util/ed_transverts.c +++ b/source/blender/editors/util/ed_transverts.c @@ -203,7 +203,7 @@ void ED_transverts_create_from_obedit(TransVertStore *tvs, const Object *obedit, BMesh *bm = em->bm; BMIter iter; void *userdata[2] = {em, NULL}; - /*int proptrans = 0; */ /*UNUSED*/ + // int proptrans = 0; /*UNUSED*/ /* abuses vertex index all over, set, just set dirty here, * perhaps this could use its own array instead? - campbell */ diff --git a/source/blender/functions/FN_multi_function_builder.hh b/source/blender/functions/FN_multi_function_builder.hh index e0ccd2a482d..088a906ce02 100644 --- a/source/blender/functions/FN_multi_function_builder.hh +++ b/source/blender/functions/FN_multi_function_builder.hh @@ -390,39 +390,6 @@ template<typename Mut1> class CustomMF_SM : public MultiFunction { }; /** - * Generates a multi-function that converts between two types. - */ -template<typename From, typename To> class CustomMF_Convert : public MultiFunction { - public: - CustomMF_Convert() - { - static MFSignature signature = create_signature(); - this->set_signature(&signature); - } - - static MFSignature create_signature() - { - static std::string name = CPPType::get<From>().name() + " to " + CPPType::get<To>().name(); - MFSignatureBuilder signature{name.c_str()}; - signature.single_input<From>("Input"); - signature.single_output<To>("Output"); - return signature.build(); - } - - void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override - { - const VArray<From> &inputs = params.readonly_single_input<From>(0); - MutableSpan<To> outputs = params.uninitialized_single_output<To>(1); - - mask.to_best_mask_type([&](const auto &mask) { - for (int64_t i : mask) { - new (static_cast<void *>(&outputs[i])) To(inputs[i]); - } - }); - } -}; - -/** * A multi-function that outputs the same value every time. The value is not owned by an instance * of this function. If #make_value_copy is false, the caller is responsible for destructing and * freeing the value. diff --git a/source/blender/functions/tests/FN_multi_function_test.cc b/source/blender/functions/tests/FN_multi_function_test.cc index 59d797605e7..577b09cb014 100644 --- a/source/blender/functions/tests/FN_multi_function_test.cc +++ b/source/blender/functions/tests/FN_multi_function_test.cc @@ -307,25 +307,6 @@ TEST(multi_function, CustomMF_GenericConstantArray) } } -TEST(multi_function, CustomMF_Convert) -{ - CustomMF_Convert<float, int> fn; - - Array<float> inputs = {5.4f, 7.1f, 9.0f}; - Array<int> outputs(inputs.size(), 0); - - MFParamsBuilder params(fn, inputs.size()); - params.add_readonly_single_input(inputs.as_span()); - params.add_uninitialized_single_output(outputs.as_mutable_span()); - - MFContextBuilder context; - fn.call({0, 2}, params, context); - - EXPECT_EQ(outputs[0], 5); - EXPECT_EQ(outputs[1], 0); - EXPECT_EQ(outputs[2], 9); -} - TEST(multi_function, IgnoredOutputs) { OptionalOutputsFunction fn; diff --git a/source/blender/geometry/intern/mesh_merge_by_distance.cc b/source/blender/geometry/intern/mesh_merge_by_distance.cc index 6dc6271194b..9bb1cbb324e 100644 --- a/source/blender/geometry/intern/mesh_merge_by_distance.cc +++ b/source/blender/geometry/intern/mesh_merge_by_distance.cc @@ -104,7 +104,7 @@ struct WeldMesh { /* References all polygons and loops that will be affected. */ Vector<WeldLoop> wloop; Vector<WeldPoly> wpoly; - WeldPoly *wpoly_new; + MutableSpan<WeldPoly> wpoly_new; int wloop_len; int wpoly_len; int wpoly_new_len; @@ -806,11 +806,9 @@ static void weld_poly_loop_ctx_alloc(Span<MPoly> mpoly, wpoly.resize(wpoly_len + maybe_new_poly); } - WeldPoly *poly_new = wpoly.data() + wpoly_len; - r_weld_mesh->wloop = std::move(wloop); r_weld_mesh->wpoly = std::move(wpoly); - r_weld_mesh->wpoly_new = poly_new; + r_weld_mesh->wpoly_new = r_weld_mesh->wpoly.as_mutable_span().drop_front(wpoly_len); r_weld_mesh->wloop_len = wloop_len; r_weld_mesh->wpoly_len = wpoly_len; r_weld_mesh->wpoly_new_len = 0; @@ -830,133 +828,134 @@ static void weld_poly_split_recursive(Span<int> vert_dest_map, int *r_loop_kill) { int poly_len = r_wp->len; - if (poly_len > 3 && ctx_verts_len > 1) { - const int ctx_loops_len = r_wp->loops.len; - const int ctx_loops_ofs = r_wp->loops.ofs; - MutableSpan<WeldLoop> wloop = r_weld_mesh->wloop; - WeldPoly *wpoly_new = r_weld_mesh->wpoly_new; - - int loop_kill = 0; - - WeldLoop *poly_loops = &wloop[ctx_loops_ofs]; - WeldLoop *wla = &poly_loops[0]; - WeldLoop *wla_prev = &poly_loops[ctx_loops_len - 1]; - while (wla_prev->flag == ELEM_COLLAPSED) { - wla_prev--; - } - const int la_len = ctx_loops_len - 1; - for (int la = 0; la < la_len; la++, wla++) { - wa_continue: - if (wla->flag == ELEM_COLLAPSED) { - continue; - } - int vert_a = wla->vert; - /* Only test vertices that will be merged. */ - if (vert_dest_map[vert_a] != OUT_OF_CONTEXT) { - int lb = la + 1; - WeldLoop *wlb = wla + 1; - WeldLoop *wlb_prev = wla; - int killed_ab = 0; - ctx_verts_len = 1; - for (; lb < ctx_loops_len; lb++, wlb++) { - BLI_assert(wlb->loop_skip_to == OUT_OF_CONTEXT); - if (wlb->flag == ELEM_COLLAPSED) { - killed_ab++; - continue; - } - int vert_b = wlb->vert; - if (vert_dest_map[vert_b] != OUT_OF_CONTEXT) { - ctx_verts_len++; + if (poly_len < 3 || ctx_verts_len < 1) { + return; + } + + const int ctx_loops_len = r_wp->loops.len; + const int ctx_loops_ofs = r_wp->loops.ofs; + MutableSpan<WeldLoop> wloop = r_weld_mesh->wloop; + + int loop_kill = 0; + + WeldLoop *poly_loops = &wloop[ctx_loops_ofs]; + WeldLoop *wla = &poly_loops[0]; + WeldLoop *wla_prev = &poly_loops[ctx_loops_len - 1]; + while (wla_prev->flag == ELEM_COLLAPSED) { + wla_prev--; + } + const int la_len = ctx_loops_len - 1; + for (int la = 0; la < la_len; la++, wla++) { + wa_continue: + if (wla->flag == ELEM_COLLAPSED) { + continue; + } + int vert_a = wla->vert; + /* Only test vertices that will be merged. */ + if (vert_dest_map[vert_a] != OUT_OF_CONTEXT) { + int lb = la + 1; + WeldLoop *wlb = wla + 1; + WeldLoop *wlb_prev = wla; + int killed_ab = 0; + ctx_verts_len = 1; + for (; lb < ctx_loops_len; lb++, wlb++) { + BLI_assert(wlb->loop_skip_to == OUT_OF_CONTEXT); + if (wlb->flag == ELEM_COLLAPSED) { + killed_ab++; + continue; + } + int vert_b = wlb->vert; + if (vert_dest_map[vert_b] != OUT_OF_CONTEXT) { + ctx_verts_len++; + } + if (vert_a == vert_b) { + const int dist_a = wlb->loop_orig - wla->loop_orig - killed_ab; + const int dist_b = poly_len - dist_a; + + BLI_assert(dist_a != 0 && dist_b != 0); + if (dist_a == 1 || dist_b == 1) { + BLI_assert(dist_a != dist_b); + BLI_assert((wla->flag == ELEM_COLLAPSED) || (wlb->flag == ELEM_COLLAPSED)); } - if (vert_a == vert_b) { - const int dist_a = wlb->loop_orig - wla->loop_orig - killed_ab; - const int dist_b = poly_len - dist_a; - - BLI_assert(dist_a != 0 && dist_b != 0); - if (dist_a == 1 || dist_b == 1) { - BLI_assert(dist_a != dist_b); - BLI_assert((wla->flag == ELEM_COLLAPSED) || (wlb->flag == ELEM_COLLAPSED)); + else { + WeldLoop *wl_tmp = nullptr; + if (dist_a == 2) { + wl_tmp = wlb_prev; + BLI_assert(wla->flag != ELEM_COLLAPSED); + BLI_assert(wl_tmp->flag != ELEM_COLLAPSED); + wla->flag = ELEM_COLLAPSED; + wl_tmp->flag = ELEM_COLLAPSED; + loop_kill += 2; + poly_len -= 2; } - else { - WeldLoop *wl_tmp = nullptr; - if (dist_a == 2) { - wl_tmp = wlb_prev; - BLI_assert(wla->flag != ELEM_COLLAPSED); + if (dist_b == 2) { + if (wl_tmp != nullptr) { + r_wp->flag = ELEM_COLLAPSED; + *r_poly_kill += 1; + } + else { + wl_tmp = wla_prev; + BLI_assert(wlb->flag != ELEM_COLLAPSED); BLI_assert(wl_tmp->flag != ELEM_COLLAPSED); - wla->flag = ELEM_COLLAPSED; + wlb->flag = ELEM_COLLAPSED; wl_tmp->flag = ELEM_COLLAPSED; - loop_kill += 2; - poly_len -= 2; - } - if (dist_b == 2) { - if (wl_tmp != nullptr) { - r_wp->flag = ELEM_COLLAPSED; - *r_poly_kill += 1; - } - else { - wl_tmp = wla_prev; - BLI_assert(wlb->flag != ELEM_COLLAPSED); - BLI_assert(wl_tmp->flag != ELEM_COLLAPSED); - wlb->flag = ELEM_COLLAPSED; - wl_tmp->flag = ELEM_COLLAPSED; - } - loop_kill += 2; - poly_len -= 2; } - if (wl_tmp == nullptr) { - const int new_loops_len = lb - la; - const int new_loops_ofs = ctx_loops_ofs + la; - - WeldPoly *new_wp = &wpoly_new[r_weld_mesh->wpoly_new_len++]; - new_wp->poly_dst = OUT_OF_CONTEXT; - new_wp->poly_orig = r_wp->poly_orig; - new_wp->loops.len = new_loops_len; - new_wp->loops.ofs = new_loops_ofs; - new_wp->loop_start = wla->loop_orig; - new_wp->loop_end = wlb_prev->loop_orig; - new_wp->len = dist_a; - weld_poly_split_recursive(vert_dest_map, + loop_kill += 2; + poly_len -= 2; + } + if (wl_tmp == nullptr) { + const int new_loops_len = lb - la; + const int new_loops_ofs = ctx_loops_ofs + la; + + WeldPoly *new_wp = &r_weld_mesh->wpoly_new[r_weld_mesh->wpoly_new_len++]; + new_wp->poly_dst = OUT_OF_CONTEXT; + new_wp->poly_orig = r_wp->poly_orig; + new_wp->loops.len = new_loops_len; + new_wp->loops.ofs = new_loops_ofs; + new_wp->loop_start = wla->loop_orig; + new_wp->loop_end = wlb_prev->loop_orig; + new_wp->len = dist_a; + weld_poly_split_recursive(vert_dest_map, #ifdef USE_WELD_DEBUG - mloop, + mloop, #endif - ctx_verts_len, - new_wp, - r_weld_mesh, - r_poly_kill, - r_loop_kill); - BLI_assert(dist_b == poly_len - dist_a); - poly_len = dist_b; - if (wla_prev->loop_orig > wla->loop_orig) { - /* New start. */ - r_wp->loop_start = wlb->loop_orig; - } - else { - /* The `loop_start` doesn't change but some loops must be skipped. */ - wla_prev->loop_skip_to = wlb->loop_orig; - } - wla = wlb; - la = lb; - goto wa_continue; + ctx_verts_len, + new_wp, + r_weld_mesh, + r_poly_kill, + r_loop_kill); + BLI_assert(dist_b == poly_len - dist_a); + poly_len = dist_b; + if (wla_prev->loop_orig > wla->loop_orig) { + /* New start. */ + r_wp->loop_start = wlb->loop_orig; } - break; + else { + /* The `loop_start` doesn't change but some loops must be skipped. */ + wla_prev->loop_skip_to = wlb->loop_orig; + } + wla = wlb; + la = lb; + goto wa_continue; } - } - if (wlb->flag != ELEM_COLLAPSED) { - wlb_prev = wlb; + break; } } + if (wlb->flag != ELEM_COLLAPSED) { + wlb_prev = wlb; + } } - if (wla->flag != ELEM_COLLAPSED) { - wla_prev = wla; - } } - r_wp->len = poly_len; - *r_loop_kill += loop_kill; + if (wla->flag != ELEM_COLLAPSED) { + wla_prev = wla; + } + } + r_wp->len = poly_len; + *r_loop_kill += loop_kill; #ifdef USE_WELD_DEBUG - weld_assert_poly_no_vert_repetition(*r_wp, wloop, mloop, r_weld_mesh->loop_map); + weld_assert_poly_no_vert_repetition(*r_wp, wloop, mloop, r_weld_mesh->loop_map); #endif - } } static void weld_poly_loop_ctx_setup(Span<MLoop> mloop, @@ -971,7 +970,6 @@ static void weld_poly_loop_ctx_setup(Span<MLoop> mloop, { MutableSpan<WeldPoly> wpoly = r_weld_mesh->wpoly; MutableSpan<WeldLoop> wloop = r_weld_mesh->wloop; - WeldPoly *wpoly_new = r_weld_mesh->wpoly_new; int wpoly_len = r_weld_mesh->wpoly_len; int wpoly_new_len = 0; int poly_kill_len = 0; @@ -1034,7 +1032,7 @@ static void weld_poly_loop_ctx_setup(Span<MLoop> mloop, #ifdef USE_WELD_DEBUG weld_assert_poly_and_loop_kill_len(wpoly, - {wpoly_new, wpoly_new_len}, + r_weld_mesh->wpoly_new, wloop, mloop, loop_map, @@ -1170,7 +1168,7 @@ static void weld_poly_loop_ctx_setup(Span<MLoop> mloop, #ifdef USE_WELD_DEBUG weld_assert_poly_and_loop_kill_len(wpoly, - {wpoly_new, wpoly_new_len}, + r_weld_mesh->wpoly_new, wloop, mloop, loop_map, @@ -1180,7 +1178,6 @@ static void weld_poly_loop_ctx_setup(Span<MLoop> mloop, loop_kill_len); #endif - r_weld_mesh->wpoly_new = wpoly_new; r_weld_mesh->poly_kill_len = poly_kill_len; r_weld_mesh->loop_kill_len = loop_kill_len; } diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilenvelope.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilenvelope.c index efbec4222aa..8b0a6ee84a2 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilenvelope.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilenvelope.c @@ -344,6 +344,7 @@ static void add_stroke(Object *ob, const int size = size1 + size2; bGPdata *gpd = ob->data; bGPDstroke *gps_dst = BKE_gpencil_stroke_new(mat_nr, size, gps->thickness); + gps_dst->runtime.gps_orig = gps->runtime.gps_orig; memcpy(&gps_dst->points[0], &gps->points[connection_index], size1 * sizeof(bGPDspoint)); memcpy(&gps_dst->points[size1], &gps->points[point_index], size2 * sizeof(bGPDspoint)); @@ -351,7 +352,6 @@ static void add_stroke(Object *ob, for (int i = 0; i < size; i++) { gps_dst->points[i].pressure *= thickness; gps_dst->points[i].strength *= strength; - memset(&gps_dst->points[i].runtime, 0, sizeof(bGPDspoint_Runtime)); } if (gps->dvert != NULL) { @@ -378,6 +378,8 @@ static void add_stroke_cyclic(Object *ob, { bGPdata *gpd = ob->data; bGPDstroke *gps_dst = BKE_gpencil_stroke_new(mat_nr, size * 2, gps->thickness); + gps_dst->runtime.gps_orig = gps->runtime.gps_orig; + if (gps->dvert != NULL) { gps_dst->dvert = MEM_malloc_arrayN(size * 2, sizeof(MDeformVert), __func__); } @@ -387,7 +389,16 @@ static void add_stroke_cyclic(Object *ob, int b = (point_index + i) % gps->totpoints; gps_dst->points[i] = gps->points[a]; + bGPDspoint *pt_dst = &gps_dst->points[i]; + bGPDspoint *pt_orig = &gps->points[a]; + pt_dst->runtime.pt_orig = pt_orig->runtime.pt_orig; + pt_dst->runtime.idx_orig = pt_orig->runtime.idx_orig; + gps_dst->points[size + i] = gps->points[b]; + pt_dst = &gps_dst->points[size + i]; + pt_orig = &gps->points[b]; + pt_dst->runtime.pt_orig = pt_orig->runtime.pt_orig; + pt_dst->runtime.idx_orig = pt_orig->runtime.idx_orig; if (gps->dvert != NULL) { BKE_defvert_array_copy(&gps_dst->dvert[i], &gps->dvert[a], 1); @@ -417,15 +428,23 @@ static void add_stroke_simple(Object *ob, { bGPdata *gpd = ob->data; bGPDstroke *gps_dst = BKE_gpencil_stroke_new(mat_nr, 2, gps->thickness); + gps_dst->runtime.gps_orig = gps->runtime.gps_orig; gps_dst->points[0] = gps->points[connection_index]; gps_dst->points[0].pressure *= thickness; gps_dst->points[0].strength *= strength; - memset(&gps_dst->points[0].runtime, 0, sizeof(bGPDspoint_Runtime)); + bGPDspoint *pt_dst = &gps_dst->points[0]; + bGPDspoint *pt_orig = &gps->points[connection_index]; + pt_dst->runtime.pt_orig = pt_orig->runtime.pt_orig; + pt_dst->runtime.idx_orig = pt_orig->runtime.idx_orig; + gps_dst->points[1] = gps->points[point_index]; gps_dst->points[1].pressure *= thickness; gps_dst->points[1].strength *= strength; - memset(&gps_dst->points[1].runtime, 0, sizeof(bGPDspoint_Runtime)); + pt_dst = &gps_dst->points[1]; + pt_orig = &gps->points[point_index]; + pt_dst->runtime.pt_orig = pt_orig->runtime.pt_orig; + pt_dst->runtime.idx_orig = pt_orig->runtime.idx_orig; if (gps->dvert != NULL) { gps_dst->dvert = MEM_malloc_arrayN(2, sizeof(MDeformVert), __func__); diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt index 3e12426acdc..98d15a1024c 100644 --- a/source/blender/gpu/CMakeLists.txt +++ b/source/blender/gpu/CMakeLists.txt @@ -46,7 +46,7 @@ set(SRC intern/gpu_batch_utils.c intern/gpu_buffers.c intern/gpu_capabilities.cc - intern/gpu_codegen.c + intern/gpu_codegen.cc intern/gpu_compute.cc intern/gpu_context.cc intern/gpu_debug.cc @@ -57,7 +57,6 @@ set(SRC intern/gpu_index_buffer.cc intern/gpu_init_exit.c intern/gpu_material.c - intern/gpu_material_library.c intern/gpu_matrix.cc intern/gpu_node_graph.c intern/gpu_platform.cc @@ -312,6 +311,7 @@ set(GLSL_SRC shaders/material/gpu_shader_material_glass.glsl shaders/material/gpu_shader_material_glossy.glsl shaders/material/gpu_shader_material_hair_info.glsl + shaders/material/gpu_shader_material_hair.glsl shaders/material/gpu_shader_material_hash.glsl shaders/material/gpu_shader_material_holdout.glsl shaders/material/gpu_shader_material_hue_sat_val.glsl diff --git a/source/blender/gpu/GPU_immediate_util.h b/source/blender/gpu/GPU_immediate_util.h index 924a735af79..cb7ca877b1d 100644 --- a/source/blender/gpu/GPU_immediate_util.h +++ b/source/blender/gpu/GPU_immediate_util.h @@ -80,6 +80,8 @@ void imm_draw_circle_fill_3d(uint pos, float x, float y, float radius, int nsegm */ void imm_draw_circle_partial_wire_2d( uint pos, float x, float y, float radius, int nsegments, float start, float sweep); +void imm_draw_circle_partial_wire_3d( + uint pos, float x, float y, float z, float radius, int nsegments, float start, float sweep); /** * Draw a filled arc with the given inner and outer radius. @@ -104,6 +106,15 @@ void imm_draw_disk_partial_fill_2d(uint pos, int nsegments, float start, float sweep); +void imm_draw_disk_partial_fill_3d(uint pos, + float x, + float y, + float z, + float rad_inner, + float rad_outer, + int nsegments, + float start, + float sweep); /** * Draw a lined box. diff --git a/source/blender/gpu/GPU_material.h b/source/blender/gpu/GPU_material.h index 9b63d0feb1e..f38b9681ad7 100644 --- a/source/blender/gpu/GPU_material.h +++ b/source/blender/gpu/GPU_material.h @@ -13,6 +13,7 @@ #include "BLI_sys_types.h" /* for bool */ +#include "GPU_shader.h" /* for GPUShaderCreateInfo */ #include "GPU_texture.h" /* for eGPUSamplerState */ #ifdef __cplusplus @@ -58,8 +59,6 @@ typedef enum eGPUType { GPU_TEX2D = 1002, GPU_TEX2D_ARRAY = 1003, GPU_TEX3D = 1004, - GPU_SHADOW2D = 1005, - GPU_TEXCUBE = 1006, /* GLSL Struct types */ GPU_CLOSURE = 1007, @@ -68,35 +67,30 @@ typedef enum eGPUType { GPU_ATTR = 3001, } eGPUType; -typedef enum eGPUBuiltin { - GPU_VIEW_MATRIX = (1 << 0), - GPU_OBJECT_MATRIX = (1 << 1), - GPU_INVERSE_VIEW_MATRIX = (1 << 2), - GPU_INVERSE_OBJECT_MATRIX = (1 << 3), - GPU_VIEW_POSITION = (1 << 4), - GPU_VIEW_NORMAL = (1 << 5), - GPU_OBJECT_COLOR = (1 << 6), - GPU_AUTO_BUMPSCALE = (1 << 7), - GPU_CAMERA_TEXCO_FACTORS = (1 << 8), - GPU_PARTICLE_SCALAR_PROPS = (1 << 9), - GPU_PARTICLE_LOCATION = (1 << 10), - GPU_PARTICLE_VELOCITY = (1 << 11), - GPU_PARTICLE_ANG_VELOCITY = (1 << 12), - GPU_LOC_TO_VIEW_MATRIX = (1 << 13), - GPU_INVERSE_LOC_TO_VIEW_MATRIX = (1 << 14), - GPU_OBJECT_INFO = (1 << 15), - GPU_BARYCENTRIC_TEXCO = (1 << 16), - GPU_BARYCENTRIC_DIST = (1 << 17), - GPU_WORLD_NORMAL = (1 << 18), -} eGPUBuiltin; - -typedef enum eGPUMatFlag { +typedef enum eGPUMaterialFlag { GPU_MATFLAG_DIFFUSE = (1 << 0), - GPU_MATFLAG_GLOSSY = (1 << 1), - GPU_MATFLAG_REFRACT = (1 << 2), - GPU_MATFLAG_SSS = (1 << 3), - GPU_MATFLAG_BARYCENTRIC = (1 << 4), -} eGPUMatFlag; + GPU_MATFLAG_SUBSURFACE = (1 << 1), + GPU_MATFLAG_GLOSSY = (1 << 2), + GPU_MATFLAG_REFRACT = (1 << 3), + GPU_MATFLAG_EMISSION = (1 << 4), + GPU_MATFLAG_TRANSPARENT = (1 << 5), + GPU_MATFLAG_HOLDOUT = (1 << 6), + GPU_MATFLAG_SHADER_TO_RGBA = (1 << 7), + GPU_MATFLAG_AO = (1 << 8), + + GPU_MATFLAG_OBJECT_INFO = (1 << 10), + GPU_MATFLAG_AOV = (1 << 11), + + GPU_MATFLAG_BARYCENTRIC = (1 << 20), + + /* Tells the render engine the material was just compiled or updated. */ + GPU_MATFLAG_UPDATED = (1 << 29), + + /* HACK(fclem) Tells the environment texture node to not bail out if empty. */ + GPU_MATFLAG_LOOKDEV_HACK = (1 << 30), +} eGPUMaterialFlag; + +ENUM_OPERATORS(eGPUMaterialFlag, GPU_MATFLAG_LOOKDEV_HACK); typedef struct GPUNodeStack { eGPUType type; @@ -110,6 +104,7 @@ typedef struct GPUNodeStack { typedef enum eGPUMaterialStatus { GPU_MAT_FAILED = 0, + GPU_MAT_CREATED, GPU_MAT_QUEUED, GPU_MAT_SUCCESS, } eGPUMaterialStatus; @@ -119,12 +114,19 @@ typedef enum eGPUVolumeDefaultValue { GPU_VOLUME_DEFAULT_1, } eGPUVolumeDefaultValue; -typedef void (*GPUMaterialEvalCallbackFn)(GPUMaterial *mat, - int options, - const char **vert_code, - const char **geom_code, - const char **frag_lib, - const char **defines); +typedef struct GPUCodegenOutput { + char *attr_load; + /* Nodetree functions calls. */ + char *displacement; + char *surface; + char *volume; + char *thickness; + char *material_functions; + + GPUShaderCreateInfo *create_info; +} GPUCodegenOutput; + +typedef void (*GPUCodegenCallbackFn)(void *thunk, GPUMaterial *mat, GPUCodegenOutput *codegen); GPUNodeLink *GPU_constant(const float *num); GPUNodeLink *GPU_uniform(const float *num); @@ -143,7 +145,12 @@ GPUNodeLink *GPU_color_band(GPUMaterial *mat, int size, float *pixels, float *ro GPUNodeLink *GPU_volume_grid(GPUMaterial *mat, const char *name, eGPUVolumeDefaultValue default_value); -GPUNodeLink *GPU_builtin(eGPUBuiltin builtin); +/** + * Create an implementation defined differential calculation of a float function. + * The given function should return a float. + * The result will be a vec2 containing dFdx and dFdy result of that function. + */ +GPUNodeLink *GPU_differentiate_float_function(const char *function_name); bool GPU_link(GPUMaterial *mat, const char *name, ...); bool GPU_stack_link(GPUMaterial *mat, @@ -157,10 +164,26 @@ GPUNodeLink *GPU_uniformbuf_link_out(struct GPUMaterial *mat, struct GPUNodeStack *stack, int index); -void GPU_material_output_link(GPUMaterial *material, GPUNodeLink *link); +void GPU_material_output_surface(GPUMaterial *material, GPUNodeLink *link); +void GPU_material_output_volume(GPUMaterial *material, GPUNodeLink *link); +void GPU_material_output_displacement(GPUMaterial *material, GPUNodeLink *link); +void GPU_material_output_thickness(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]); +/** + * Wrap a part of the material graph into a function. You need then need to call the function by + * using something like #GPU_differentiate_float_function. + * \note This replace the link by a constant to break the link with the main graph. + * \param return_type: sub function return type. Output is cast to this type. + * \param link: link to use as the sub function output. + * \return the name of the generated function. + */ +char *GPU_material_split_sub_function(GPUMaterial *material, + eGPUType return_type, + GPUNodeLink **link); + +bool 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); @@ -180,15 +203,13 @@ GPUMaterial *GPU_material_from_nodetree(struct Scene *scene, struct Material *ma, struct bNodeTree *ntree, struct ListBase *gpumaterials, - const void *engine_type, - int options, - bool is_volume_shader, - const char *vert_code, - const char *geom_code, - const char *frag_lib, - const char *defines, const char *name, - GPUMaterialEvalCallbackFn callback); + uint64_t shader_uuid, + bool is_volume_shader, + bool is_lookdev, + GPUCodegenCallbackFn callback, + void *thunk); + void GPU_material_compile(GPUMaterial *mat); void GPU_material_free(struct ListBase *gpumaterial); @@ -205,6 +226,7 @@ struct Material *GPU_material_get_material(GPUMaterial *material); * Return true if the material compilation has not yet begin or begin. */ eGPUMaterialStatus GPU_material_status(GPUMaterial *mat); +void GPU_material_status_set(GPUMaterial *mat, eGPUMaterialStatus status); struct GPUUniformBuf *GPU_material_uniform_buffer_get(GPUMaterial *material); /** @@ -215,13 +237,15 @@ struct GPUUniformBuf *GPU_material_uniform_buffer_get(GPUMaterial *material); void GPU_material_uniform_buffer_create(GPUMaterial *material, ListBase *inputs); struct GPUUniformBuf *GPU_material_create_sss_profile_ubo(void); +bool GPU_material_is_volume_shader(GPUMaterial *mat); bool GPU_material_has_surface_output(GPUMaterial *mat); bool GPU_material_has_volume_output(GPUMaterial *mat); -bool GPU_material_is_volume_shader(GPUMaterial *mat); - -void GPU_material_flag_set(GPUMaterial *mat, eGPUMatFlag flag); -bool GPU_material_flag_get(GPUMaterial *mat, eGPUMatFlag flag); +void GPU_material_flag_set(GPUMaterial *mat, eGPUMaterialFlag flag); +bool GPU_material_flag_get(const GPUMaterial *mat, eGPUMaterialFlag flag); +eGPUMaterialFlag GPU_material_flag(const GPUMaterial *mat); +bool GPU_material_recalc_flag_get(GPUMaterial *mat); +uint64_t GPU_material_uuid_get(GPUMaterial *mat); void GPU_pass_cache_init(void); void GPU_pass_cache_garbage_collect(void); diff --git a/source/blender/gpu/GPU_shader.h b/source/blender/gpu/GPU_shader.h index c0c25022836..eed7685bf01 100644 --- a/source/blender/gpu/GPU_shader.h +++ b/source/blender/gpu/GPU_shader.h @@ -56,6 +56,7 @@ GPUShader *GPU_shader_create_from_info(const GPUShaderCreateInfo *_info); GPUShader *GPU_shader_create_from_info_name(const char *info_name); const GPUShaderCreateInfo *GPU_shader_create_info_get(const char *info_name); +bool GPU_shader_create_info_check_error(const GPUShaderCreateInfo *_info, char r_error[128]); struct GPU_ShaderCreateFromArray_Params { const char **vert, **geom, **frag, **defs; diff --git a/source/blender/gpu/GPU_uniform_buffer.h b/source/blender/gpu/GPU_uniform_buffer.h index c47be05a646..f78719d1963 100644 --- a/source/blender/gpu/GPU_uniform_buffer.h +++ b/source/blender/gpu/GPU_uniform_buffer.h @@ -42,8 +42,8 @@ void GPU_uniformbuf_bind(GPUUniformBuf *ubo, int slot); void GPU_uniformbuf_unbind(GPUUniformBuf *ubo); void GPU_uniformbuf_unbind_all(void); -#define GPU_UBO_BLOCK_NAME "nodeTree" -#define GPU_ATTRIBUTE_UBO_BLOCK_NAME "uniformAttrs" +#define GPU_UBO_BLOCK_NAME "node_tree" +#define GPU_ATTRIBUTE_UBO_BLOCK_NAME "unf_attrs" #ifdef __cplusplus } diff --git a/source/blender/gpu/intern/gpu_codegen.c b/source/blender/gpu/intern/gpu_codegen.c deleted file mode 100644 index e462308d3ee..00000000000 --- a/source/blender/gpu/intern/gpu_codegen.c +++ /dev/null @@ -1,1123 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later - * Copyright 2005 Blender Foundation. All rights reserved. */ - -/** \file - * \ingroup gpu - * - * Convert material node-trees to GLSL. - */ - -#include "MEM_guardedalloc.h" - -#include "DNA_customdata_types.h" -#include "DNA_image_types.h" - -#include "BLI_blenlib.h" -#include "BLI_dynstr.h" -#include "BLI_ghash.h" -#include "BLI_hash_mm2a.h" -#include "BLI_link_utils.h" -#include "BLI_threads.h" -#include "BLI_utildefines.h" - -#include "PIL_time.h" - -#include "BKE_material.h" - -#include "GPU_capabilities.h" -#include "GPU_material.h" -#include "GPU_shader.h" -#include "GPU_uniform_buffer.h" -#include "GPU_vertex_format.h" - -#include "BLI_sys_types.h" /* for intptr_t support */ - -#include "gpu_codegen.h" -#include "gpu_material_library.h" -#include "gpu_node_graph.h" - -#include <stdarg.h> -#include <string.h> - -extern char datatoc_gpu_shader_codegen_lib_glsl[]; -extern char datatoc_gpu_shader_common_obinfos_lib_glsl[]; - -/* -------------------- GPUPass Cache ------------------ */ -/** - * Internal shader cache: This prevent the shader recompilation / stall when - * using undo/redo AND also allows for GPUPass reuse if the Shader code is the - * same for 2 different Materials. Unused GPUPasses are free by Garbage collection. - */ - -/* Only use one linked-list that contains the GPUPasses grouped by hash. */ -static GPUPass *pass_cache = NULL; -static SpinLock pass_cache_spin; - -static uint32_t gpu_pass_hash(const char *frag_gen, const char *defs, ListBase *attributes) -{ - BLI_HashMurmur2A hm2a; - BLI_hash_mm2a_init(&hm2a, 0); - BLI_hash_mm2a_add(&hm2a, (uchar *)frag_gen, strlen(frag_gen)); - LISTBASE_FOREACH (GPUMaterialAttribute *, attr, attributes) { - BLI_hash_mm2a_add(&hm2a, (uchar *)attr->name, strlen(attr->name)); - } - if (defs) { - BLI_hash_mm2a_add(&hm2a, (uchar *)defs, strlen(defs)); - } - - return BLI_hash_mm2a_end(&hm2a); -} - -/* Search by hash only. Return first pass with the same hash. - * There is hash collision if (pass->next && pass->next->hash == hash) */ -static GPUPass *gpu_pass_cache_lookup(uint32_t hash) -{ - BLI_spin_lock(&pass_cache_spin); - /* Could be optimized with a Lookup table. */ - for (GPUPass *pass = pass_cache; pass; pass = pass->next) { - if (pass->hash == hash) { - BLI_spin_unlock(&pass_cache_spin); - return pass; - } - } - BLI_spin_unlock(&pass_cache_spin); - return NULL; -} - -/* Check all possible passes with the same hash. */ -static GPUPass *gpu_pass_cache_resolve_collision(GPUPass *pass, - const char *vert, - const char *geom, - const char *frag, - const char *defs, - uint32_t hash) -{ - BLI_spin_lock(&pass_cache_spin); - /* Collision, need to `strcmp` the whole shader. */ - for (; pass && (pass->hash == hash); pass = pass->next) { - if ((defs != NULL) && (!STREQ(pass->defines, defs))) { /* Pass */ - } - else if ((geom != NULL) && (!STREQ(pass->geometrycode, geom))) { /* Pass */ - } - else if ((!STREQ(pass->fragmentcode, frag) == 0) && (STREQ(pass->vertexcode, vert))) { - BLI_spin_unlock(&pass_cache_spin); - return pass; - } - } - BLI_spin_unlock(&pass_cache_spin); - return NULL; -} - -/* GLSL code generation */ - -static void codegen_convert_datatype(DynStr *ds, int from, int to, const char *tmp, int id) -{ - char name[1024]; - - BLI_snprintf(name, sizeof(name), "%s%d", tmp, id); - - if (from == to) { - BLI_dynstr_append(ds, name); - } - else if (to == GPU_FLOAT) { - if (from == GPU_VEC4) { - BLI_dynstr_appendf(ds, "dot(%s.rgb, vec3(0.2126, 0.7152, 0.0722))", name); - } - else if (from == GPU_VEC3) { - BLI_dynstr_appendf(ds, "(%s.r + %s.g + %s.b) / 3.0", name, name, name); - } - else if (from == GPU_VEC2) { - BLI_dynstr_appendf(ds, "%s.r", name); - } - } - else if (to == GPU_VEC2) { - if (from == GPU_VEC4) { - BLI_dynstr_appendf(ds, "vec2((%s.r + %s.g + %s.b) / 3.0, %s.a)", name, name, name, name); - } - else if (from == GPU_VEC3) { - BLI_dynstr_appendf(ds, "vec2((%s.r + %s.g + %s.b) / 3.0, 1.0)", name, name, name); - } - else if (from == GPU_FLOAT) { - BLI_dynstr_appendf(ds, "vec2(%s, 1.0)", name); - } - } - else if (to == GPU_VEC3) { - if (from == GPU_VEC4) { - BLI_dynstr_appendf(ds, "%s.rgb", name); - } - else if (from == GPU_VEC2) { - BLI_dynstr_appendf(ds, "vec3(%s.r, %s.r, %s.r)", name, name, name); - } - else if (from == GPU_FLOAT) { - BLI_dynstr_appendf(ds, "vec3(%s, %s, %s)", name, name, name); - } - } - else if (to == GPU_VEC4) { - if (from == GPU_VEC3) { - BLI_dynstr_appendf(ds, "vec4(%s, 1.0)", name); - } - else if (from == GPU_VEC2) { - BLI_dynstr_appendf(ds, "vec4(%s.r, %s.r, %s.r, %s.g)", name, name, name, name); - } - else if (from == GPU_FLOAT) { - BLI_dynstr_appendf(ds, "vec4(%s, %s, %s, 1.0)", name, name, name); - } - } - else if (to == GPU_CLOSURE) { - if (from == GPU_VEC4) { - BLI_dynstr_appendf(ds, "closure_emission(%s.rgb)", name); - } - else if (from == GPU_VEC3) { - BLI_dynstr_appendf(ds, "closure_emission(%s.rgb)", name); - } - else if (from == GPU_VEC2) { - BLI_dynstr_appendf(ds, "closure_emission(%s.rrr)", name); - } - else if (from == GPU_FLOAT) { - BLI_dynstr_appendf(ds, "closure_emission(vec3(%s, %s, %s))", name, name, name); - } - } - else { - BLI_dynstr_append(ds, name); - } -} - -static void codegen_print_datatype(DynStr *ds, const eGPUType type, float *data) -{ - int i; - - BLI_dynstr_appendf(ds, "%s(", gpu_data_type_to_string(type)); - - for (i = 0; i < type; i++) { - BLI_dynstr_appendf(ds, "%.12f", data[i]); - if (i == type - 1) { - BLI_dynstr_append(ds, ")"); - } - else { - BLI_dynstr_append(ds, ", "); - } - } -} - -static const char *gpu_builtin_name(eGPUBuiltin builtin) -{ - if (builtin == GPU_VIEW_MATRIX) { - return "unfviewmat"; - } - if (builtin == GPU_OBJECT_MATRIX) { - return "unfobmat"; - } - if (builtin == GPU_INVERSE_VIEW_MATRIX) { - return "unfinvviewmat"; - } - if (builtin == GPU_INVERSE_OBJECT_MATRIX) { - return "unfinvobmat"; - } - if (builtin == GPU_LOC_TO_VIEW_MATRIX) { - return "unflocaltoviewmat"; - } - if (builtin == GPU_INVERSE_LOC_TO_VIEW_MATRIX) { - return "unfinvlocaltoviewmat"; - } - if (builtin == GPU_VIEW_POSITION) { - return "varposition"; - } - if (builtin == GPU_WORLD_NORMAL) { - return "varwnormal"; - } - if (builtin == GPU_VIEW_NORMAL) { - return "varnormal"; - } - if (builtin == GPU_OBJECT_COLOR) { - return "unfobjectcolor"; - } - if (builtin == GPU_AUTO_BUMPSCALE) { - return "unfobautobumpscale"; - } - if (builtin == GPU_CAMERA_TEXCO_FACTORS) { - return "unfcameratexfactors"; - } - if (builtin == GPU_PARTICLE_SCALAR_PROPS) { - return "unfparticlescalarprops"; - } - if (builtin == GPU_PARTICLE_LOCATION) { - return "unfparticleco"; - } - if (builtin == GPU_PARTICLE_VELOCITY) { - return "unfparticlevel"; - } - if (builtin == GPU_PARTICLE_ANG_VELOCITY) { - return "unfparticleangvel"; - } - if (builtin == GPU_OBJECT_INFO) { - return "unfobjectinfo"; - } - if (builtin == GPU_BARYCENTRIC_TEXCO) { - return "unfbarycentrictex"; - } - if (builtin == GPU_BARYCENTRIC_DIST) { - return "unfbarycentricdist"; - } - return ""; -} - -static void codegen_set_unique_ids(GPUNodeGraph *graph) -{ - int id = 1; - - LISTBASE_FOREACH (GPUNode *, node, &graph->nodes) { - LISTBASE_FOREACH (GPUInput *, input, &node->inputs) { - /* set id for unique names of uniform variables */ - input->id = id++; - } - - LISTBASE_FOREACH (GPUOutput *, output, &node->outputs) { - /* set id for unique names of tmp variables storing output */ - output->id = id++; - } - } -} - -/** - * It will create an UBO for GPUMaterial if there is any GPU_DYNAMIC_UBO. - */ -static int codegen_process_uniforms_functions(GPUMaterial *material, - DynStr *ds, - GPUNodeGraph *graph) -{ - const char *name; - int builtins = 0; - ListBase ubo_inputs = {NULL, NULL}; - - /* Textures */ - LISTBASE_FOREACH (GPUMaterialTexture *, tex, &graph->textures) { - if (tex->colorband) { - BLI_dynstr_appendf(ds, "uniform sampler1DArray %s;\n", tex->sampler_name); - } - else if (tex->tiled_mapping_name[0]) { - BLI_dynstr_appendf(ds, "uniform sampler2DArray %s;\n", tex->sampler_name); - BLI_dynstr_appendf(ds, "uniform sampler1DArray %s;\n", tex->tiled_mapping_name); - } - else { - BLI_dynstr_appendf(ds, "uniform sampler2D %s;\n", tex->sampler_name); - } - } - - /* Volume Grids */ - LISTBASE_FOREACH (GPUMaterialVolumeGrid *, grid, &graph->volume_grids) { - BLI_dynstr_appendf(ds, "uniform sampler3D %s;\n", grid->sampler_name); - BLI_dynstr_appendf(ds, "uniform mat4 %s = mat4(0.0);\n", grid->transform_name); - } - - /* Print other uniforms */ - - LISTBASE_FOREACH (GPUNode *, node, &graph->nodes) { - LISTBASE_FOREACH (GPUInput *, input, &node->inputs) { - if (input->source == GPU_SOURCE_BUILTIN) { - /* only define each builtin uniform/varying once */ - if (!(builtins & input->builtin)) { - builtins |= input->builtin; - name = gpu_builtin_name(input->builtin); - - if (BLI_str_startswith(name, "unf")) { - BLI_dynstr_appendf(ds, "uniform %s %s;\n", gpu_data_type_to_string(input->type), name); - } - else { - BLI_dynstr_appendf(ds, "in %s %s;\n", gpu_data_type_to_string(input->type), name); - } - } - } - else if (input->source == GPU_SOURCE_STRUCT) { - /* Add other struct here if needed. */ - BLI_dynstr_appendf(ds, "Closure strct%d = CLOSURE_DEFAULT;\n", input->id); - } - else if (input->source == GPU_SOURCE_UNIFORM) { - if (!input->link) { - /* We handle the UBOuniforms separately. */ - BLI_addtail(&ubo_inputs, BLI_genericNodeN(input)); - } - } - else if (input->source == GPU_SOURCE_CONSTANT) { - BLI_dynstr_appendf( - ds, "const %s cons%d = ", gpu_data_type_to_string(input->type), input->id); - codegen_print_datatype(ds, input->type, input->vec); - BLI_dynstr_append(ds, ";\n"); - } - } - } - - /* Handle the UBO block separately. */ - if ((material != NULL) && !BLI_listbase_is_empty(&ubo_inputs)) { - GPU_material_uniform_buffer_create(material, &ubo_inputs); - - /* Inputs are sorted */ - BLI_dynstr_appendf(ds, "\nlayout (std140) uniform %s {\n", GPU_UBO_BLOCK_NAME); - - LISTBASE_FOREACH (LinkData *, link, &ubo_inputs) { - GPUInput *input = (GPUInput *)(link->data); - BLI_dynstr_appendf(ds, " %s unf%d;\n", gpu_data_type_to_string(input->type), input->id); - } - BLI_dynstr_append(ds, "};\n"); - BLI_freelistN(&ubo_inputs); - } - - /* Generate the uniform attribute UBO if necessary. */ - if (!BLI_listbase_is_empty(&graph->uniform_attrs.list)) { - BLI_dynstr_append(ds, "\nstruct UniformAttributes {\n"); - LISTBASE_FOREACH (GPUUniformAttr *, attr, &graph->uniform_attrs.list) { - BLI_dynstr_appendf(ds, " vec4 attr%d;\n", attr->id); - } - BLI_dynstr_append(ds, "};\n"); - BLI_dynstr_appendf(ds, "layout (std140) uniform %s {\n", GPU_ATTRIBUTE_UBO_BLOCK_NAME); - BLI_dynstr_append(ds, " UniformAttributes uniform_attrs[DRW_RESOURCE_CHUNK_LEN];\n"); - BLI_dynstr_append(ds, "};\n"); - BLI_dynstr_append(ds, "#define GET_UNIFORM_ATTR(name) (uniform_attrs[resource_id].name)\n"); - } - - BLI_dynstr_append(ds, "\n"); - - return builtins; -} - -static void codegen_declare_tmps(DynStr *ds, GPUNodeGraph *graph) -{ - LISTBASE_FOREACH (GPUNode *, node, &graph->nodes) { - /* declare temporary variables for node output storage */ - LISTBASE_FOREACH (GPUOutput *, output, &node->outputs) { - if (output->type == GPU_CLOSURE) { - BLI_dynstr_appendf(ds, " Closure tmp%d;\n", output->id); - } - else { - BLI_dynstr_appendf(ds, " %s tmp%d;\n", gpu_data_type_to_string(output->type), output->id); - } - } - } - BLI_dynstr_append(ds, "\n"); -} - -static void codegen_call_functions(DynStr *ds, GPUNodeGraph *graph) -{ - LISTBASE_FOREACH (GPUNode *, node, &graph->nodes) { - BLI_dynstr_appendf(ds, " %s(", node->name); - - LISTBASE_FOREACH (GPUInput *, input, &node->inputs) { - if (input->source == GPU_SOURCE_TEX) { - BLI_dynstr_append(ds, input->texture->sampler_name); - } - else if (input->source == GPU_SOURCE_TEX_TILED_MAPPING) { - BLI_dynstr_append(ds, input->texture->tiled_mapping_name); - } - else if (input->source == GPU_SOURCE_VOLUME_GRID) { - BLI_dynstr_append(ds, input->volume_grid->sampler_name); - } - else if (input->source == GPU_SOURCE_VOLUME_GRID_TRANSFORM) { - BLI_dynstr_append(ds, input->volume_grid->transform_name); - } - else if (input->source == GPU_SOURCE_OUTPUT) { - codegen_convert_datatype( - ds, input->link->output->type, input->type, "tmp", input->link->output->id); - } - else if (input->source == GPU_SOURCE_BUILTIN) { - /* TODO(fclem): get rid of that. */ - if (input->builtin == GPU_INVERSE_VIEW_MATRIX) { - BLI_dynstr_append(ds, "viewinv"); - } - else if (input->builtin == GPU_VIEW_MATRIX) { - BLI_dynstr_append(ds, "viewmat"); - } - else if (input->builtin == GPU_CAMERA_TEXCO_FACTORS) { - BLI_dynstr_append(ds, "camtexfac"); - } - else if (input->builtin == GPU_LOC_TO_VIEW_MATRIX) { - BLI_dynstr_append(ds, "localtoviewmat"); - } - else if (input->builtin == GPU_INVERSE_LOC_TO_VIEW_MATRIX) { - BLI_dynstr_append(ds, "invlocaltoviewmat"); - } - else if (input->builtin == GPU_BARYCENTRIC_DIST) { - BLI_dynstr_append(ds, "barycentricDist"); - } - else if (input->builtin == GPU_BARYCENTRIC_TEXCO) { - BLI_dynstr_append(ds, "barytexco"); - } - else if (input->builtin == GPU_OBJECT_MATRIX) { - BLI_dynstr_append(ds, "objmat"); - } - else if (input->builtin == GPU_OBJECT_INFO) { - BLI_dynstr_append(ds, "ObjectInfo"); - } - else if (input->builtin == GPU_OBJECT_COLOR) { - BLI_dynstr_append(ds, "ObjectColor"); - } - else if (input->builtin == GPU_INVERSE_OBJECT_MATRIX) { - BLI_dynstr_append(ds, "objinv"); - } - else if (input->builtin == GPU_VIEW_POSITION) { - BLI_dynstr_append(ds, "viewposition"); - } - else if (input->builtin == GPU_VIEW_NORMAL) { - BLI_dynstr_append(ds, "facingnormal"); - } - else if (input->builtin == GPU_WORLD_NORMAL) { - BLI_dynstr_append(ds, "facingwnormal"); - } - else { - BLI_dynstr_append(ds, gpu_builtin_name(input->builtin)); - } - } - else if (input->source == GPU_SOURCE_STRUCT) { - BLI_dynstr_appendf(ds, "strct%d", input->id); - } - else if (input->source == GPU_SOURCE_UNIFORM) { - BLI_dynstr_appendf(ds, "unf%d", input->id); - } - else if (input->source == GPU_SOURCE_CONSTANT) { - BLI_dynstr_appendf(ds, "cons%d", input->id); - } - else if (input->source == GPU_SOURCE_ATTR) { - codegen_convert_datatype(ds, input->attr->gputype, input->type, "var", input->attr->id); - } - else if (input->source == GPU_SOURCE_UNIFORM_ATTR) { - BLI_dynstr_appendf(ds, "GET_UNIFORM_ATTR(attr%d)", input->uniform_attr->id); - } - - BLI_dynstr_append(ds, ", "); - } - - LISTBASE_FOREACH (GPUOutput *, output, &node->outputs) { - BLI_dynstr_appendf(ds, "tmp%d", output->id); - if (output->next) { - BLI_dynstr_append(ds, ", "); - } - } - - BLI_dynstr_append(ds, ");\n"); - } -} - -static void codegen_final_output(DynStr *ds, GPUOutput *finaloutput) -{ - BLI_dynstr_appendf(ds, "return tmp%d;\n", finaloutput->id); -} - -static char *code_generate_fragment(GPUMaterial *material, - GPUNodeGraph *graph, - const char *interface_str) -{ - DynStr *ds = BLI_dynstr_new(); - char *code; - int builtins; - - codegen_set_unique_ids(graph); - - /* Attributes, Shader stage interface. */ - if (interface_str) { - BLI_dynstr_appendf(ds, "in codegenInterface {%s};\n\n", interface_str); - } - - builtins = codegen_process_uniforms_functions(material, ds, graph); - - if (builtins & (GPU_OBJECT_INFO | GPU_OBJECT_COLOR)) { - BLI_dynstr_append(ds, datatoc_gpu_shader_common_obinfos_lib_glsl); - } - - if (builtins & GPU_BARYCENTRIC_TEXCO) { - BLI_dynstr_append(ds, datatoc_gpu_shader_codegen_lib_glsl); - } - - BLI_dynstr_append(ds, "Closure nodetree_exec(void)\n{\n"); - - if (builtins & GPU_BARYCENTRIC_TEXCO) { - BLI_dynstr_append(ds, " vec2 barytexco = barycentric_resolve(barycentricTexCo);\n"); - } - /* TODO(fclem): get rid of that. */ - if (builtins & GPU_VIEW_MATRIX) { - BLI_dynstr_append(ds, " #define viewmat ViewMatrix\n"); - } - if (builtins & GPU_CAMERA_TEXCO_FACTORS) { - BLI_dynstr_append(ds, " #define camtexfac CameraTexCoFactors\n"); - } - if (builtins & GPU_OBJECT_MATRIX) { - BLI_dynstr_append(ds, " #define objmat ModelMatrix\n"); - } - if (builtins & GPU_INVERSE_OBJECT_MATRIX) { - BLI_dynstr_append(ds, " #define objinv ModelMatrixInverse\n"); - } - if (builtins & GPU_INVERSE_VIEW_MATRIX) { - BLI_dynstr_append(ds, " #define viewinv ViewMatrixInverse\n"); - } - if (builtins & GPU_LOC_TO_VIEW_MATRIX) { - BLI_dynstr_append(ds, " #define localtoviewmat (ViewMatrix * ModelMatrix)\n"); - } - if (builtins & GPU_INVERSE_LOC_TO_VIEW_MATRIX) { - BLI_dynstr_append(ds, - " #define invlocaltoviewmat (ModelMatrixInverse * ViewMatrixInverse)\n"); - } - if (builtins & GPU_VIEW_NORMAL) { - BLI_dynstr_append(ds, "#ifdef HAIR_SHADER\n"); - BLI_dynstr_append(ds, " vec3 n;\n"); - BLI_dynstr_append(ds, " world_normals_get(n);\n"); - BLI_dynstr_append(ds, " vec3 facingnormal = transform_direction(ViewMatrix, n);\n"); - BLI_dynstr_append(ds, "#else\n"); - BLI_dynstr_append(ds, " vec3 facingnormal = gl_FrontFacing ? viewNormal: -viewNormal;\n"); - BLI_dynstr_append(ds, "#endif\n"); - } - if (builtins & GPU_WORLD_NORMAL) { - BLI_dynstr_append(ds, " vec3 facingwnormal;\n"); - if (builtins & GPU_VIEW_NORMAL) { - BLI_dynstr_append(ds, "#ifdef HAIR_SHADER\n"); - BLI_dynstr_append(ds, " facingwnormal = n;\n"); - BLI_dynstr_append(ds, "#else\n"); - BLI_dynstr_append(ds, " world_normals_get(facingwnormal);\n"); - BLI_dynstr_append(ds, "#endif\n"); - } - else { - BLI_dynstr_append(ds, " world_normals_get(facingwnormal);\n"); - } - } - if (builtins & GPU_VIEW_POSITION) { - BLI_dynstr_append(ds, " #define viewposition viewPosition\n"); - } - - codegen_declare_tmps(ds, graph); - codegen_call_functions(ds, graph); - - BLI_dynstr_append(ds, " #ifndef VOLUMETRICS\n"); - BLI_dynstr_append(ds, " if (renderPassAOV) {\n"); - BLI_dynstr_append(ds, " switch (render_pass_aov_hash()) {\n"); - GSet *aovhashes_added = BLI_gset_int_new(__func__); - LISTBASE_FOREACH (GPUNodeGraphOutputLink *, aovlink, &graph->outlink_aovs) { - void *aov_key = POINTER_FROM_INT(aovlink->hash); - if (BLI_gset_haskey(aovhashes_added, aov_key)) { - continue; - } - BLI_dynstr_appendf(ds, " case %d: {\n ", aovlink->hash); - codegen_final_output(ds, aovlink->outlink->output); - BLI_dynstr_append(ds, " }\n"); - BLI_gset_add(aovhashes_added, aov_key); - } - BLI_gset_free(aovhashes_added, NULL); - BLI_dynstr_append(ds, " default: {\n"); - BLI_dynstr_append(ds, " Closure no_aov = CLOSURE_DEFAULT;\n"); - BLI_dynstr_append(ds, " no_aov.holdout = 1.0;\n"); - BLI_dynstr_append(ds, " return no_aov;\n"); - BLI_dynstr_append(ds, " }\n"); - BLI_dynstr_append(ds, " }\n"); - BLI_dynstr_append(ds, " } else {\n"); - BLI_dynstr_append(ds, " #else /* VOLUMETRICS */\n"); - BLI_dynstr_append(ds, " {\n"); - BLI_dynstr_append(ds, " #endif /* VOLUMETRICS */\n "); - codegen_final_output(ds, graph->outlink->output); - BLI_dynstr_append(ds, " }\n"); - - BLI_dynstr_append(ds, "}\n"); - - /* create shader */ - code = BLI_dynstr_get_cstring(ds); - BLI_dynstr_free(ds); - -#if 0 - if (G.debug & G_DEBUG) { - printf("%s\n", code); - } -#endif - - return code; -} - -static const char *attr_prefix_get(CustomDataType type) -{ - switch (type) { - case CD_ORCO: - return "orco"; - case CD_MTFACE: - return "u"; - case CD_TANGENT: - return "t"; - case CD_MCOL: - case CD_MLOOPCOL: - return "c"; - case CD_PROP_COLOR: - 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 ""; - } -} - -/* We talk about shader stage interface, not to be mistaken with GPUShaderInterface. */ -static char *code_generate_interface(GPUNodeGraph *graph, int builtins) -{ - if (BLI_listbase_is_empty(&graph->attributes) && - (builtins & (GPU_BARYCENTRIC_DIST | GPU_BARYCENTRIC_TEXCO)) == 0) { - return NULL; - } - - DynStr *ds = BLI_dynstr_new(); - - BLI_dynstr_append(ds, "\n"); - - LISTBASE_FOREACH (GPUMaterialAttribute *, attr, &graph->attributes) { - 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"); - } - if (builtins & GPU_BARYCENTRIC_DIST) { - BLI_dynstr_append(ds, "vec3 barycentricDist;\n"); - } - - char *code = BLI_dynstr_get_cstring(ds); - - BLI_dynstr_free(ds); - - return code; -} - -static char *code_generate_vertex(GPUNodeGraph *graph, - const char *interface_str, - const char *vert_code, - int builtins) -{ - DynStr *ds = BLI_dynstr_new(); - - BLI_dynstr_append(ds, datatoc_gpu_shader_codegen_lib_glsl); - - /* Inputs */ - LISTBASE_FOREACH (GPUMaterialAttribute *, attr, &graph->attributes) { - const char *type_str = gpu_data_type_to_string(attr->gputype); - const char *prefix = attr_prefix_get(attr->type); - /* XXX FIXME: see notes in mesh_render_data_create() */ - /* NOTE: Replicate changes to mesh_render_data_create() in draw_cache_impl_mesh.c */ - if (attr->type == CD_ORCO) { - /* OPTI: orco is computed from local positions, but only if no modifier is present. */ - 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); - } - else { - char attr_safe_name[GPU_MAX_SAFE_ATTR_NAME]; - GPU_vertformat_safe_attr_name(attr->name, attr_safe_name, GPU_MAX_SAFE_ATTR_NAME); - BLI_dynstr_appendf(ds, "DEFINE_ATTR(%s, %s%s);\n", type_str, prefix, attr_safe_name); - BLI_dynstr_appendf(ds, "#define att%d %s%s\n", attr->id, prefix, attr_safe_name); - } - } - - /* Outputs interface */ - if (interface_str) { - BLI_dynstr_appendf(ds, "out codegenInterface {%s};\n\n", interface_str); - } - - /* Prototype. Needed for hair functions. */ - BLI_dynstr_append(ds, "void pass_attr(vec3 position, mat3 normalmat, mat4 modelmatinv);\n"); - BLI_dynstr_append(ds, "#define USE_ATTR\n\n"); - - BLI_dynstr_append(ds, vert_code); - BLI_dynstr_append(ds, "\n\n"); - - BLI_dynstr_append(ds, "void pass_attr(vec3 position, mat3 normalmat, mat4 modelmatinv) {\n"); - - /* GPU_BARYCENTRIC_TEXCO cannot be computed based on gl_VertexID - * for MESH_SHADER because of indexed drawing. In this case a - * geometry shader is needed. */ - if (builtins & GPU_BARYCENTRIC_TEXCO) { - BLI_dynstr_appendf(ds, " barycentricTexCo = barycentric_get();\n"); - } - if (builtins & GPU_BARYCENTRIC_DIST) { - BLI_dynstr_appendf(ds, " barycentricDist = vec3(0);\n"); - } - - LISTBASE_FOREACH (GPUMaterialAttribute *, attr, &graph->attributes) { - if (attr->type == CD_TANGENT) { /* silly exception */ - BLI_dynstr_appendf(ds, " var%d = tangent_get(att%d, normalmat);\n", attr->id, attr->id); - } - else if (attr->type == CD_ORCO) { - 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); - } - } - - BLI_dynstr_append(ds, "}\n"); - - char *code = BLI_dynstr_get_cstring(ds); - - BLI_dynstr_free(ds); - -#if 0 - if (G.debug & G_DEBUG) { - printf("%s\n", code); - } -#endif - - return code; -} - -static char *code_generate_geometry(GPUNodeGraph *graph, - const char *interface_str, - const char *geom_code, - int builtins) -{ - if (!geom_code) { - return NULL; - } - - DynStr *ds = BLI_dynstr_new(); - - /* Attributes, Shader interface; */ - if (interface_str) { - BLI_dynstr_appendf(ds, "in codegenInterface {%s} dataAttrIn[];\n\n", interface_str); - BLI_dynstr_appendf(ds, "out codegenInterface {%s} dataAttrOut;\n\n", interface_str); - } - - BLI_dynstr_append(ds, datatoc_gpu_shader_codegen_lib_glsl); - - if (builtins & GPU_BARYCENTRIC_DIST) { - /* geom_code should do something with this, but may not. */ - BLI_dynstr_append(ds, "#define DO_BARYCENTRIC_DISTANCES\n"); - } - - /* Generate varying assignments. */ - BLI_dynstr_append(ds, "#define USE_ATTR\n"); - /* This needs to be a define. Some drivers don't like variable vert index inside dataAttrIn. */ - BLI_dynstr_append(ds, "#define pass_attr(vert) {\\\n"); - - if (builtins & GPU_BARYCENTRIC_TEXCO) { - BLI_dynstr_append(ds, "dataAttrOut.barycentricTexCo = calc_barycentric_co(vert);\\\n"); - } - - LISTBASE_FOREACH (GPUMaterialAttribute *, attr, &graph->attributes) { - /* TODO: let shader choose what to do depending on what the attribute is. */ - BLI_dynstr_appendf(ds, "dataAttrOut.var%d = dataAttrIn[vert].var%d;\\\n", attr->id, attr->id); - } - BLI_dynstr_append(ds, "}\n\n"); - - BLI_dynstr_append(ds, geom_code); - - char *code = BLI_dynstr_get_cstring(ds); - BLI_dynstr_free(ds); - - return code; -} - -GPUShader *GPU_pass_shader_get(GPUPass *pass) -{ - return pass->shader; -} - -/* Pass create/free */ - -static bool gpu_pass_is_valid(GPUPass *pass) -{ - /* Shader is not null if compilation is successful. */ - return (pass->compiled == false || pass->shader != NULL); -} - -GPUPass *GPU_generate_pass(GPUMaterial *material, - GPUNodeGraph *graph, - const char *vert_code, - const char *geom_code, - const char *frag_lib, - const char *defines) -{ - /* Prune the unused nodes and extract attributes before compiling so the - * generated VBOs are ready to accept the future shader. */ - gpu_node_graph_prune_unused(graph); - gpu_node_graph_finalize_uniform_attrs(graph); - - int builtins = 0; - LISTBASE_FOREACH (GPUNode *, node, &graph->nodes) { - LISTBASE_FOREACH (GPUInput *, input, &node->inputs) { - if (input->source == GPU_SOURCE_BUILTIN) { - builtins |= input->builtin; - } - } - } - /* generate code */ - char *interface_str = code_generate_interface(graph, builtins); - char *fragmentgen = code_generate_fragment(material, graph, interface_str); - - /* Cache lookup: Reuse shaders already compiled */ - uint32_t hash = gpu_pass_hash(fragmentgen, defines, &graph->attributes); - GPUPass *pass_hash = gpu_pass_cache_lookup(hash); - - if (pass_hash && (pass_hash->next == NULL || pass_hash->next->hash != hash)) { - /* No collision, just return the pass. */ - MEM_SAFE_FREE(interface_str); - MEM_freeN(fragmentgen); - if (!gpu_pass_is_valid(pass_hash)) { - /* Shader has already been created but failed to compile. */ - return NULL; - } - pass_hash->refcount += 1; - return pass_hash; - } - - /* Either the shader is not compiled or there is a hash collision... - * continue generating the shader strings. */ - GSet *used_libraries = gpu_material_used_libraries(material); - char *tmp = gpu_material_library_generate_code(used_libraries, frag_lib); - - char *geometrycode = code_generate_geometry(graph, interface_str, geom_code, builtins); - char *vertexcode = code_generate_vertex(graph, interface_str, vert_code, builtins); - char *fragmentcode = BLI_strdupcat(tmp, fragmentgen); - - MEM_SAFE_FREE(interface_str); - MEM_freeN(fragmentgen); - MEM_freeN(tmp); - - GPUPass *pass = NULL; - if (pass_hash) { - /* Cache lookup: Reuse shaders already compiled */ - pass = gpu_pass_cache_resolve_collision( - pass_hash, vertexcode, geometrycode, fragmentcode, defines, hash); - } - - if (pass) { - MEM_SAFE_FREE(vertexcode); - MEM_SAFE_FREE(fragmentcode); - MEM_SAFE_FREE(geometrycode); - - /* Cache hit. Reuse the same GPUPass and GPUShader. */ - if (!gpu_pass_is_valid(pass)) { - /* Shader has already been created but failed to compile. */ - return NULL; - } - - pass->refcount += 1; - } - else { - /* We still create a pass even if shader compilation - * fails to avoid trying to compile again and again. */ - pass = MEM_callocN(sizeof(GPUPass), "GPUPass"); - pass->shader = NULL; - pass->refcount = 1; - pass->hash = hash; - pass->vertexcode = vertexcode; - pass->fragmentcode = fragmentcode; - pass->geometrycode = geometrycode; - pass->defines = (defines) ? BLI_strdup(defines) : NULL; - pass->compiled = false; - - BLI_spin_lock(&pass_cache_spin); - if (pass_hash != NULL) { - /* Add after the first pass having the same hash. */ - pass->next = pass_hash->next; - pass_hash->next = pass; - } - else { - /* No other pass have same hash, just prepend to the list. */ - BLI_LINKS_PREPEND(pass_cache, pass); - } - BLI_spin_unlock(&pass_cache_spin); - } - - return pass; -} - -static int count_active_texture_sampler(GPUShader *shader, const char *source) -{ - const char *code = source; - - /* Remember this is per stage. */ - GSet *sampler_ids = BLI_gset_int_new(__func__); - int num_samplers = 0; - - while ((code = strstr(code, "uniform "))) { - /* Move past "uniform". */ - code += 7; - /* Skip following spaces. */ - while (*code == ' ') { - code++; - } - /* Skip "i" from potential isamplers. */ - if (*code == 'i') { - code++; - } - /* Skip following spaces. */ - if (BLI_str_startswith(code, "sampler")) { - /* Move past "uniform". */ - code += 7; - /* Skip sampler type suffix. */ - while (!ELEM(*code, ' ', '\0')) { - code++; - } - /* Skip following spaces. */ - while (*code == ' ') { - code++; - } - - if (*code != '\0') { - char sampler_name[64]; - code = gpu_str_skip_token(code, sampler_name, sizeof(sampler_name)); - int id = GPU_shader_get_uniform(shader, sampler_name); - - if (id == -1) { - continue; - } - /* Catch duplicates. */ - if (BLI_gset_add(sampler_ids, POINTER_FROM_INT(id))) { - num_samplers++; - } - } - } - } - - BLI_gset_free(sampler_ids, NULL); - - return num_samplers; -} - -static bool gpu_pass_shader_validate(GPUPass *pass, GPUShader *shader) -{ - if (shader == NULL) { - return false; - } - - /* NOTE: The only drawback of this method is that it will count a sampler - * used in the fragment shader and only declared (but not used) in the vertex - * shader as used by both. But this corner case is not happening for now. */ - int vert_samplers_len = count_active_texture_sampler(shader, pass->vertexcode); - int frag_samplers_len = count_active_texture_sampler(shader, pass->fragmentcode); - - int total_samplers_len = vert_samplers_len + frag_samplers_len; - - /* Validate against opengl limit. */ - if ((frag_samplers_len > GPU_max_textures_frag()) || - (vert_samplers_len > GPU_max_textures_vert())) { - return false; - } - - if (pass->geometrycode) { - int geom_samplers_len = count_active_texture_sampler(shader, pass->geometrycode); - total_samplers_len += geom_samplers_len; - if (geom_samplers_len > GPU_max_textures_geom()) { - return false; - } - } - - return (total_samplers_len <= GPU_max_textures()); -} - -bool GPU_pass_compile(GPUPass *pass, const char *shname) -{ - bool success = true; - if (!pass->compiled) { - GPUShader *shader = GPU_shader_create( - pass->vertexcode, pass->fragmentcode, pass->geometrycode, NULL, pass->defines, shname); - - /* NOTE: Some drivers / gpu allows more active samplers than the opengl limit. - * We need to make sure to count active samplers to avoid undefined behavior. */ - if (!gpu_pass_shader_validate(pass, shader)) { - success = false; - if (shader != NULL) { - fprintf(stderr, "GPUShader: error: too many samplers in shader.\n"); - GPU_shader_free(shader); - shader = NULL; - } - } - pass->shader = shader; - pass->compiled = true; - } - - return success; -} - -void GPU_pass_release(GPUPass *pass) -{ - BLI_assert(pass->refcount > 0); - pass->refcount--; -} - -static void gpu_pass_free(GPUPass *pass) -{ - BLI_assert(pass->refcount == 0); - if (pass->shader) { - GPU_shader_free(pass->shader); - } - MEM_SAFE_FREE(pass->fragmentcode); - MEM_SAFE_FREE(pass->geometrycode); - MEM_SAFE_FREE(pass->vertexcode); - MEM_SAFE_FREE(pass->defines); - MEM_freeN(pass); -} - -void GPU_pass_cache_garbage_collect(void) -{ - static int lasttime = 0; - const int shadercollectrate = 60; /* hardcoded for now. */ - int ctime = (int)PIL_check_seconds_timer(); - - if (ctime < shadercollectrate + lasttime) { - return; - } - - lasttime = ctime; - - BLI_spin_lock(&pass_cache_spin); - GPUPass *next, **prev_pass = &pass_cache; - for (GPUPass *pass = pass_cache; pass; pass = next) { - next = pass->next; - if (pass->refcount == 0) { - /* Remove from list */ - *prev_pass = next; - gpu_pass_free(pass); - } - else { - prev_pass = &pass->next; - } - } - BLI_spin_unlock(&pass_cache_spin); -} - -void GPU_pass_cache_init(void) -{ - BLI_spin_init(&pass_cache_spin); -} - -void GPU_pass_cache_free(void) -{ - BLI_spin_lock(&pass_cache_spin); - while (pass_cache) { - GPUPass *next = pass_cache->next; - gpu_pass_free(pass_cache); - pass_cache = next; - } - BLI_spin_unlock(&pass_cache_spin); - - BLI_spin_end(&pass_cache_spin); -} - -/* Module */ - -void gpu_codegen_init(void) -{ -} - -void gpu_codegen_exit(void) -{ - BKE_material_defaults_free_gpu(); - GPU_shader_free_builtin_shaders(); -} diff --git a/source/blender/gpu/intern/gpu_codegen.cc b/source/blender/gpu/intern/gpu_codegen.cc new file mode 100644 index 00000000000..8963fa45c96 --- /dev/null +++ b/source/blender/gpu/intern/gpu_codegen.cc @@ -0,0 +1,825 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2005 Blender Foundation. */ + +/** \file + * \ingroup gpu + * + * Convert material node-trees to GLSL. + */ + +#include "MEM_guardedalloc.h" + +#include "DNA_customdata_types.h" +#include "DNA_image_types.h" + +#include "BLI_blenlib.h" +#include "BLI_dynstr.h" +#include "BLI_ghash.h" +#include "BLI_hash_mm2a.h" +#include "BLI_link_utils.h" +#include "BLI_threads.h" +#include "BLI_utildefines.h" + +#include "PIL_time.h" + +#include "BKE_material.h" +#include "BKE_world.h" + +#include "GPU_capabilities.h" +#include "GPU_material.h" +#include "GPU_shader.h" +#include "GPU_uniform_buffer.h" +#include "GPU_vertex_format.h" + +#include "BLI_sys_types.h" /* for intptr_t support */ + +#include "gpu_codegen.h" +#include "gpu_material_library.h" +#include "gpu_node_graph.h" +#include "gpu_shader_create_info.hh" +#include "gpu_shader_dependency_private.h" + +#include <stdarg.h> +#include <string.h> + +#include <sstream> +#include <string> + +using namespace blender::gpu::shader; + +struct GPUCodegenCreateInfo : ShaderCreateInfo { + struct NameBuffer { + char attr_names[16][GPU_MAX_SAFE_ATTR_NAME + 1]; + char var_names[16][8]; + }; + + /** Optional generated interface. */ + StageInterfaceInfo *interface_generated = nullptr; + /** Optional name buffer containing names referenced by StringRefNull. */ + NameBuffer *name_buffer = nullptr; + + GPUCodegenCreateInfo(const char *name) : ShaderCreateInfo(name){}; + ~GPUCodegenCreateInfo() + { + delete interface_generated; + MEM_delete(name_buffer); + }; +}; + +struct GPUPass { + struct GPUPass *next; + + GPUShader *shader; + GPUCodegenCreateInfo *create_info = nullptr; + /** Orphaned GPUPasses gets freed by the garbage collector. */ + uint refcount; + /** Identity hash generated from all GLSL code. */ + uint32_t hash; + /** Did we already tried to compile the attached GPUShader. */ + bool compiled; +}; + +/* -------------------------------------------------------------------- */ +/** \name GPUPass Cache + * + * Internal shader cache: This prevent the shader recompilation / stall when + * using undo/redo AND also allows for GPUPass reuse if the Shader code is the + * same for 2 different Materials. Unused GPUPasses are free by Garbage collection. + */ + +/* Only use one linklist that contains the GPUPasses grouped by hash. */ +static GPUPass *pass_cache = nullptr; +static SpinLock pass_cache_spin; + +/* Search by hash only. Return first pass with the same hash. + * There is hash collision if (pass->next && pass->next->hash == hash) */ +static GPUPass *gpu_pass_cache_lookup(uint32_t hash) +{ + BLI_spin_lock(&pass_cache_spin); + /* Could be optimized with a Lookup table. */ + for (GPUPass *pass = pass_cache; pass; pass = pass->next) { + if (pass->hash == hash) { + BLI_spin_unlock(&pass_cache_spin); + return pass; + } + } + BLI_spin_unlock(&pass_cache_spin); + return nullptr; +} + +static void gpu_pass_cache_insert_after(GPUPass *node, GPUPass *pass) +{ + BLI_spin_lock(&pass_cache_spin); + if (node != nullptr) { + /* Add after the first pass having the same hash. */ + pass->next = node->next; + node->next = pass; + } + else { + /* No other pass have same hash, just prepend to the list. */ + BLI_LINKS_PREPEND(pass_cache, pass); + } + BLI_spin_unlock(&pass_cache_spin); +} + +/* Check all possible passes with the same hash. */ +static GPUPass *gpu_pass_cache_resolve_collision(GPUPass *pass, + GPUShaderCreateInfo *info, + uint32_t hash) +{ + BLI_spin_lock(&pass_cache_spin); + for (; pass && (pass->hash == hash); pass = pass->next) { + if (*reinterpret_cast<ShaderCreateInfo *>(info) == + *reinterpret_cast<ShaderCreateInfo *>(pass->create_info)) { + BLI_spin_unlock(&pass_cache_spin); + return pass; + } + } + BLI_spin_unlock(&pass_cache_spin); + return nullptr; +} + +static bool gpu_pass_is_valid(GPUPass *pass) +{ + /* Shader is not null if compilation is successful. */ + return (pass->compiled == false || pass->shader != nullptr); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Type > string conversion + * \{ */ + +static std::ostream &operator<<(std::ostream &stream, const GPUInput *input) +{ + switch (input->source) { + case GPU_SOURCE_FUNCTION_CALL: + case GPU_SOURCE_OUTPUT: + return stream << "tmp" << input->id; + case GPU_SOURCE_CONSTANT: + return stream << "cons" << input->id; + case GPU_SOURCE_UNIFORM: + return stream << "node_tree.u" << input->id; + case GPU_SOURCE_ATTR: + return stream << "var_attrs.v" << input->attr->id; + case GPU_SOURCE_UNIFORM_ATTR: + return stream << "unf_attrs[resource_id].attr" << input->uniform_attr->id; + case GPU_SOURCE_STRUCT: + return stream << "strct" << input->id; + case GPU_SOURCE_TEX: + return stream << input->texture->sampler_name; + case GPU_SOURCE_TEX_TILED_MAPPING: + return stream << input->texture->tiled_mapping_name; + case GPU_SOURCE_VOLUME_GRID: + return stream << input->volume_grid->sampler_name; + case GPU_SOURCE_VOLUME_GRID_TRANSFORM: + return stream << input->volume_grid->transform_name; + default: + BLI_assert(0); + return stream; + } +} + +static std::ostream &operator<<(std::ostream &stream, const GPUOutput *output) +{ + return stream << "tmp" << output->id; +} + +/* Trick type to change overload and keep a somewhat nice syntax. */ +struct GPUConstant : public GPUInput { +}; + +/* Print data constructor (i.e: vec2(1.0f, 1.0f)). */ +static std::ostream &operator<<(std::ostream &stream, const GPUConstant *input) +{ + stream << input->type << "("; + for (int i = 0; i < input->type; i++) { + char formated_float[32]; + /* Print with the maximum precision for single precision float using scientific notation. + * See https://stackoverflow.com/questions/16839658/#answer-21162120 */ + SNPRINTF(formated_float, "%.9g", input->vec[i]); + stream << formated_float; + if (i < input->type - 1) { + stream << ", "; + } + } + stream << ")"; + return stream; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name GLSL code generation + * \{ */ + +class GPUCodegen { + public: + GPUMaterial &mat; + GPUNodeGraph &graph; + GPUCodegenOutput output = {}; + GPUCodegenCreateInfo *create_info = nullptr; + + private: + uint32_t hash_ = 0; + BLI_HashMurmur2A hm2a_; + ListBase ubo_inputs_ = {nullptr, nullptr}; + + public: + GPUCodegen(GPUMaterial *mat_, GPUNodeGraph *graph_) : mat(*mat_), graph(*graph_) + { + BLI_hash_mm2a_init(&hm2a_, GPU_material_uuid_get(&mat)); + BLI_hash_mm2a_add_int(&hm2a_, GPU_material_flag(&mat)); + create_info = new GPUCodegenCreateInfo("codegen"); + output.create_info = reinterpret_cast<GPUShaderCreateInfo *>( + static_cast<ShaderCreateInfo *>(create_info)); + + if (GPU_material_flag_get(mat_, GPU_MATFLAG_OBJECT_INFO)) { + create_info->additional_info("draw_object_infos"); + } + } + + ~GPUCodegen() + { + MEM_SAFE_FREE(output.attr_load); + MEM_SAFE_FREE(output.surface); + MEM_SAFE_FREE(output.volume); + MEM_SAFE_FREE(output.thickness); + MEM_SAFE_FREE(output.displacement); + MEM_SAFE_FREE(output.material_functions); + delete create_info; + BLI_freelistN(&ubo_inputs_); + }; + + void generate_graphs(); + void generate_uniform_buffer(); + void generate_attribs(); + void generate_resources(); + void generate_library(); + + uint32_t hash_get() const + { + return hash_; + } + + private: + void set_unique_ids(); + + void node_serialize(std::stringstream &eval_ss, const GPUNode *node); + char *graph_serialize(eGPUNodeTag tree_tag, GPUNodeLink *output_link); + + static char *extract_c_str(std::stringstream &stream) + { + auto string = stream.str(); + return BLI_strdup(string.c_str()); + } +}; + +static char attr_prefix_get(CustomDataType type) +{ + switch (type) { + case CD_MTFACE: + return 'u'; + case CD_TANGENT: + return 't'; + case CD_MCOL: + case CD_MLOOPCOL: + return 'c'; + case CD_PROP_COLOR: + return 'c'; + case CD_AUTO_FROM_NAME: + return 'a'; + case CD_HAIRLENGTH: + return 'l'; + default: + BLI_assert_msg(0, "GPUVertAttr Prefix type not found : This should not happen!"); + return '\0'; + } +} + +void GPUCodegen::generate_attribs() +{ + if (BLI_listbase_is_empty(&graph.attributes)) { + output.attr_load = nullptr; + return; + } + + GPUCodegenCreateInfo &info = *create_info; + + info.name_buffer = MEM_new<GPUCodegenCreateInfo::NameBuffer>("info.name_buffer"); + info.interface_generated = new StageInterfaceInfo("codegen_iface", "var_attrs"); + StageInterfaceInfo &iface = *info.interface_generated; + info.vertex_out(iface); + + /* Input declaration, loading / assignment to interface and geometry shader passthrough. */ + std::stringstream decl_ss, iface_ss, load_ss; + + int slot = 15; + LISTBASE_FOREACH (GPUMaterialAttribute *, attr, &graph.attributes) { + + /* NOTE: Replicate changes to mesh_render_data_create() in draw_cache_impl_mesh.c */ + if (attr->type == CD_ORCO) { + /* OPTI: orco is computed from local positions, but only if no modifier is present. */ + STRNCPY(info.name_buffer->attr_names[slot], "orco"); + } + else { + char *name = info.name_buffer->attr_names[slot]; + name[0] = attr_prefix_get(static_cast<CustomDataType>(attr->type)); + name[1] = '\0'; + if (attr->name[0] != '\0') { + /* XXX FIXME: see notes in mesh_render_data_create() */ + GPU_vertformat_safe_attr_name(attr->name, &name[1], GPU_MAX_SAFE_ATTR_NAME); + } + } + SNPRINTF(info.name_buffer->var_names[slot], "v%d", attr->id); + + blender::StringRefNull attr_name = info.name_buffer->attr_names[slot]; + blender::StringRefNull var_name = info.name_buffer->var_names[slot]; + + eGPUType input_type, iface_type; + + load_ss << "var_attrs." << var_name; + switch (attr->type) { + case CD_ORCO: + /* Need vec4 to detect usage of default attribute. */ + input_type = GPU_VEC4; + iface_type = GPU_VEC3; + load_ss << " = attr_load_orco(" << attr_name << ");\n"; + break; + case CD_HAIRLENGTH: + iface_type = input_type = GPU_FLOAT; + load_ss << " = attr_load_" << input_type << "(" << attr_name << ");\n"; + break; + case CD_TANGENT: + iface_type = input_type = GPU_VEC4; + load_ss << " = attr_load_tangent(" << attr_name << ");\n"; + break; + case CD_MTFACE: + iface_type = input_type = GPU_VEC3; + load_ss << " = attr_load_uv(" << attr_name << ");\n"; + break; + case CD_MCOL: + iface_type = input_type = GPU_VEC4; + load_ss << " = attr_load_color(" << attr_name << ");\n"; + break; + default: + iface_type = input_type = GPU_VEC4; + load_ss << " = attr_load_" << input_type << "(" << attr_name << ");\n"; + break; + } + + info.vertex_in(slot--, to_type(input_type), attr_name); + iface.smooth(to_type(iface_type), var_name); + } + + output.attr_load = extract_c_str(load_ss); +} + +void GPUCodegen::generate_resources() +{ + GPUCodegenCreateInfo &info = *create_info; + + std::stringstream ss; + + /* Textures. */ + LISTBASE_FOREACH (GPUMaterialTexture *, tex, &graph.textures) { + if (tex->colorband) { + info.sampler(0, ImageType::FLOAT_1D_ARRAY, tex->sampler_name, Frequency::BATCH); + } + else if (tex->tiled_mapping_name[0] != '\0') { + info.sampler(0, ImageType::FLOAT_2D_ARRAY, tex->sampler_name, Frequency::BATCH); + info.sampler(0, ImageType::FLOAT_1D_ARRAY, tex->tiled_mapping_name, Frequency::BATCH); + } + else { + info.sampler(0, ImageType::FLOAT_2D, tex->sampler_name, Frequency::BATCH); + } + } + /* Volume Grids. */ + LISTBASE_FOREACH (GPUMaterialVolumeGrid *, grid, &graph.volume_grids) { + info.sampler(0, ImageType::FLOAT_3D, grid->sampler_name, Frequency::BATCH); + /* TODO(@fclem): Global uniform. To put in an UBO. */ + info.push_constant(Type::MAT4, grid->transform_name); + } + + if (!BLI_listbase_is_empty(&ubo_inputs_)) { + /* NOTE: generate_uniform_buffer() should have sorted the inputs before this. */ + ss << "struct NodeTree {\n"; + LISTBASE_FOREACH (LinkData *, link, &ubo_inputs_) { + GPUInput *input = (GPUInput *)(link->data); + ss << input->type << " u" << input->id << ";\n"; + } + ss << "};\n\n"; + + info.uniform_buf(0, "NodeTree", GPU_UBO_BLOCK_NAME, Frequency::BATCH); + } + + if (!BLI_listbase_is_empty(&graph.uniform_attrs.list)) { + ss << "struct UniformAttrs {\n"; + LISTBASE_FOREACH (GPUUniformAttr *, attr, &graph.uniform_attrs.list) { + ss << "vec4 attr" << attr->id << ";\n"; + } + ss << "};\n\n"; + + /* TODO(fclem): Use the macro for length. Currently not working for EEVEE. */ + /* DRW_RESOURCE_CHUNK_LEN = 512 */ + info.uniform_buf(0, "UniformAttrs", GPU_ATTRIBUTE_UBO_BLOCK_NAME "[512]", Frequency::BATCH); + } + + info.typedef_source_generated = ss.str(); +} + +void GPUCodegen::generate_library() +{ + GPUCodegenCreateInfo &info = *create_info; + + void *value; + GSetIterState pop_state = {}; + while (BLI_gset_pop(graph.used_libraries, &pop_state, &value)) { + auto deps = gpu_shader_dependency_get_resolved_source((const char *)value); + info.dependencies_generated.extend_non_duplicates(deps); + } +} + +void GPUCodegen::node_serialize(std::stringstream &eval_ss, const GPUNode *node) +{ + /* Declare constants. */ + LISTBASE_FOREACH (GPUInput *, input, &node->inputs) { + switch (input->source) { + case GPU_SOURCE_FUNCTION_CALL: + eval_ss << input->type << " " << input << "; " << input->function_call << input << ");\n"; + break; + case GPU_SOURCE_STRUCT: + eval_ss << input->type << " " << input << " = CLOSURE_DEFAULT;\n"; + break; + case GPU_SOURCE_CONSTANT: + eval_ss << input->type << " " << input << " = " << (GPUConstant *)input << ";\n"; + break; + default: + break; + } + } + /* Declare temporary variables for node output storage. */ + LISTBASE_FOREACH (GPUOutput *, output, &node->outputs) { + eval_ss << output->type << " " << output << ";\n"; + } + + /* Function call. */ + eval_ss << node->name << "("; + /* Input arguments. */ + LISTBASE_FOREACH (GPUInput *, input, &node->inputs) { + switch (input->source) { + case GPU_SOURCE_OUTPUT: + case GPU_SOURCE_ATTR: { + /* These inputs can have non matching types. Do conversion. */ + eGPUType to = input->type; + eGPUType from = (input->source == GPU_SOURCE_ATTR) ? input->attr->gputype : + input->link->output->type; + if (from != to) { + /* Use defines declared inside codegen_lib (i.e: vec4_from_float). */ + eval_ss << to << "_from_" << from << "("; + } + + if (input->source == GPU_SOURCE_ATTR) { + eval_ss << input; + } + else { + eval_ss << input->link->output; + } + + if (from != to) { + eval_ss << ")"; + } + break; + } + default: + eval_ss << input; + break; + } + eval_ss << ", "; + } + /* Output arguments. */ + LISTBASE_FOREACH (GPUOutput *, output, &node->outputs) { + eval_ss << output; + if (output->next) { + eval_ss << ", "; + } + } + eval_ss << ");\n\n"; +} + +char *GPUCodegen::graph_serialize(eGPUNodeTag tree_tag, GPUNodeLink *output_link) +{ + if (output_link == nullptr) { + return nullptr; + } + + std::stringstream eval_ss; + /* NOTE: The node order is already top to bottom (or left to right in node editor) + * because of the evaluation order inside ntreeExecGPUNodes(). */ + LISTBASE_FOREACH (GPUNode *, node, &graph.nodes) { + if ((node->tag & tree_tag) == 0) { + continue; + } + node_serialize(eval_ss, node); + } + eval_ss << "return " << output_link->output << ";\n"; + + char *eval_c_str = extract_c_str(eval_ss); + BLI_hash_mm2a_add(&hm2a_, (uchar *)eval_c_str, eval_ss.str().size()); + return eval_c_str; +} + +void GPUCodegen::generate_uniform_buffer() +{ + /* Extract uniform inputs. */ + LISTBASE_FOREACH (GPUNode *, node, &graph.nodes) { + LISTBASE_FOREACH (GPUInput *, input, &node->inputs) { + if (input->source == GPU_SOURCE_UNIFORM && !input->link) { + /* We handle the UBO uniforms separately. */ + BLI_addtail(&ubo_inputs_, BLI_genericNodeN(input)); + } + } + } + if (!BLI_listbase_is_empty(&ubo_inputs_)) { + /* This sorts the inputs based on size. */ + GPU_material_uniform_buffer_create(&mat, &ubo_inputs_); + } +} + +/* Sets id for unique names for all inputs, resources and temp variables. */ +void GPUCodegen::set_unique_ids() +{ + int id = 1; + LISTBASE_FOREACH (GPUNode *, node, &graph.nodes) { + LISTBASE_FOREACH (GPUInput *, input, &node->inputs) { + input->id = id++; + } + LISTBASE_FOREACH (GPUOutput *, output, &node->outputs) { + output->id = id++; + } + } +} + +void GPUCodegen::generate_graphs() +{ + set_unique_ids(); + + output.surface = graph_serialize(GPU_NODE_TAG_SURFACE | GPU_NODE_TAG_AOV, graph.outlink_surface); + output.volume = graph_serialize(GPU_NODE_TAG_VOLUME, graph.outlink_volume); + output.displacement = graph_serialize(GPU_NODE_TAG_DISPLACEMENT, graph.outlink_displacement); + output.thickness = graph_serialize(GPU_NODE_TAG_THICKNESS, graph.outlink_thickness); + + if (!BLI_listbase_is_empty(&graph.material_functions)) { + std::stringstream eval_ss; + eval_ss << "\n/* Generated Functions */\n\n"; + LISTBASE_FOREACH (GPUNodeGraphFunctionLink *, func_link, &graph.material_functions) { + char *fn = graph_serialize(GPU_NODE_TAG_FUNCTION, func_link->outlink); + eval_ss << "float " << func_link->name << "() {\n" << fn << "}\n\n"; + MEM_SAFE_FREE(fn); + } + output.material_functions = extract_c_str(eval_ss); + } + + LISTBASE_FOREACH (GPUMaterialAttribute *, attr, &graph.attributes) { + BLI_hash_mm2a_add(&hm2a_, (uchar *)attr->name, strlen(attr->name)); + } + + hash_ = BLI_hash_mm2a_end(&hm2a_); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name GPUPass + * \{ */ + +GPUPass *GPU_generate_pass(GPUMaterial *material, + GPUNodeGraph *graph, + GPUCodegenCallbackFn finalize_source_cb, + void *thunk) +{ + /* Prune the unused nodes and extract attributes before compiling so the + * generated VBOs are ready to accept the future shader. */ + gpu_node_graph_prune_unused(graph); + gpu_node_graph_finalize_uniform_attrs(graph); + + GPUCodegen codegen(material, graph); + codegen.generate_graphs(); + codegen.generate_uniform_buffer(); + + /* Cache lookup: Reuse shaders already compiled. */ + GPUPass *pass_hash = gpu_pass_cache_lookup(codegen.hash_get()); + + /* FIXME(fclem): This is broken. Since we only check for the hash and not the full source + * there is no way to have a collision currently. Some advocated to only use a bigger hash. */ + if (pass_hash && (pass_hash->next == nullptr || pass_hash->next->hash != codegen.hash_get())) { + if (!gpu_pass_is_valid(pass_hash)) { + /* Shader has already been created but failed to compile. */ + return nullptr; + } + /* No collision, just return the pass. */ + pass_hash->refcount += 1; + return pass_hash; + } + + /* Either the shader is not compiled or there is a hash collision... + * continue generating the shader strings. */ + codegen.generate_attribs(); + codegen.generate_resources(); + codegen.generate_library(); + + /* Make engine add its own code and implement the generated functions. */ + finalize_source_cb(thunk, material, &codegen.output); + + GPUPass *pass = nullptr; + if (pass_hash) { + /* Cache lookup: Reuse shaders already compiled. */ + pass = gpu_pass_cache_resolve_collision( + pass_hash, codegen.output.create_info, codegen.hash_get()); + } + + if (pass) { + /* Cache hit. Reuse the same GPUPass and GPUShader. */ + if (!gpu_pass_is_valid(pass)) { + /* Shader has already been created but failed to compile. */ + return nullptr; + } + pass->refcount += 1; + } + else { + /* We still create a pass even if shader compilation + * fails to avoid trying to compile again and again. */ + pass = (GPUPass *)MEM_callocN(sizeof(GPUPass), "GPUPass"); + pass->shader = nullptr; + pass->refcount = 1; + pass->create_info = codegen.create_info; + pass->hash = codegen.hash_get(); + pass->compiled = false; + + codegen.create_info = nullptr; + + gpu_pass_cache_insert_after(pass_hash, pass); + } + return pass; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Compilation + * \{ */ + +static int count_active_texture_sampler(GPUPass *pass, GPUShader *shader) +{ + int num_samplers = 0; + + for (const ShaderCreateInfo::Resource &res : pass->create_info->pass_resources_) { + if (res.bind_type == ShaderCreateInfo::Resource::BindType::SAMPLER) { + if (GPU_shader_get_uniform(shader, res.sampler.name.c_str()) != -1) { + num_samplers += 1; + } + } + } + + return num_samplers; +} + +static bool gpu_pass_shader_validate(GPUPass *pass, GPUShader *shader) +{ + if (shader == nullptr) { + return false; + } + + /* NOTE: The only drawback of this method is that it will count a sampler + * used in the fragment shader and only declared (but not used) in the vertex + * shader as used by both. But this corner case is not happening for now. */ + int active_samplers_len = count_active_texture_sampler(pass, shader); + + /* Validate against opengl limit. */ + if ((active_samplers_len > GPU_max_textures_frag()) || + (active_samplers_len > GPU_max_textures_vert())) { + return false; + } + + if (pass->create_info->geometry_source_.is_empty() == false) { + if (active_samplers_len > GPU_max_textures_geom()) { + return false; + } + } + + return (active_samplers_len * 3 <= GPU_max_textures()); +} + +bool GPU_pass_compile(GPUPass *pass, const char *shname) +{ + bool success = true; + if (!pass->compiled) { + GPUShaderCreateInfo *info = reinterpret_cast<GPUShaderCreateInfo *>( + static_cast<ShaderCreateInfo *>(pass->create_info)); + + pass->create_info->name_ = shname; + + GPUShader *shader = GPU_shader_create_from_info(info); + + /* NOTE: Some drivers / gpu allows more active samplers than the opengl limit. + * We need to make sure to count active samplers to avoid undefined behavior. */ + if (!gpu_pass_shader_validate(pass, shader)) { + success = false; + if (shader != nullptr) { + fprintf(stderr, "GPUShader: error: too many samplers in shader.\n"); + GPU_shader_free(shader); + shader = nullptr; + } + } + pass->shader = shader; + pass->compiled = true; + } + return success; +} + +GPUShader *GPU_pass_shader_get(GPUPass *pass) +{ + return pass->shader; +} + +void GPU_pass_release(GPUPass *pass) +{ + BLI_assert(pass->refcount > 0); + pass->refcount--; +} + +static void gpu_pass_free(GPUPass *pass) +{ + BLI_assert(pass->refcount == 0); + if (pass->shader) { + GPU_shader_free(pass->shader); + } + delete pass->create_info; + MEM_freeN(pass); +} + +void GPU_pass_cache_garbage_collect(void) +{ + static int lasttime = 0; + const int shadercollectrate = 60; /* hardcoded for now. */ + int ctime = (int)PIL_check_seconds_timer(); + + if (ctime < shadercollectrate + lasttime) { + return; + } + + lasttime = ctime; + + BLI_spin_lock(&pass_cache_spin); + GPUPass *next, **prev_pass = &pass_cache; + for (GPUPass *pass = pass_cache; pass; pass = next) { + next = pass->next; + if (pass->refcount == 0) { + /* Remove from list */ + *prev_pass = next; + gpu_pass_free(pass); + } + else { + prev_pass = &pass->next; + } + } + BLI_spin_unlock(&pass_cache_spin); +} + +void GPU_pass_cache_init(void) +{ + BLI_spin_init(&pass_cache_spin); +} + +void GPU_pass_cache_free(void) +{ + BLI_spin_lock(&pass_cache_spin); + while (pass_cache) { + GPUPass *next = pass_cache->next; + gpu_pass_free(pass_cache); + pass_cache = next; + } + BLI_spin_unlock(&pass_cache_spin); + + BLI_spin_end(&pass_cache_spin); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Module + * \{ */ + +void gpu_codegen_init(void) +{ +} + +void gpu_codegen_exit(void) +{ + // BKE_world_defaults_free_gpu(); + BKE_material_defaults_free_gpu(); + GPU_shader_free_builtin_shaders(); +} + +/** \} */
\ No newline at end of file diff --git a/source/blender/gpu/intern/gpu_codegen.h b/source/blender/gpu/intern/gpu_codegen.h index 0e99476d3ea..95a672c0400 100644 --- a/source/blender/gpu/intern/gpu_codegen.h +++ b/source/blender/gpu/intern/gpu_codegen.h @@ -9,36 +9,24 @@ #pragma once +#include "GPU_material.h" +#include "GPU_shader.h" + #ifdef __cplusplus extern "C" { #endif -struct GPUMaterial; struct GPUNodeGraph; -struct GPUShader; - -typedef struct GPUPass { - struct GPUPass *next; - struct GPUShader *shader; - char *fragmentcode; - char *geometrycode; - char *vertexcode; - char *defines; - uint refcount; /* Orphaned GPUPasses gets freed by the garbage collector. */ - uint32_t hash; /* Identity hash generated from all GLSL code. */ - bool compiled; /* Did we already tried to compile the attached GPUShader. */ -} GPUPass; +typedef struct GPUPass GPUPass; /* Pass */ -GPUPass *GPU_generate_pass(struct GPUMaterial *material, +GPUPass *GPU_generate_pass(GPUMaterial *material, struct GPUNodeGraph *graph, - const char *vert_code, - const char *geom_code, - const char *frag_lib, - const char *defines); -struct GPUShader *GPU_pass_shader_get(GPUPass *pass); + GPUCodegenCallbackFn finalize_source_cb, + void *thunk); +GPUShader *GPU_pass_shader_get(GPUPass *pass); bool GPU_pass_compile(GPUPass *pass, const char *shname); void GPU_pass_release(GPUPass *pass); diff --git a/source/blender/gpu/intern/gpu_immediate_util.c b/source/blender/gpu/intern/gpu_immediate_util.c index cf8837ab26e..67035853594 100644 --- a/source/blender/gpu/intern/gpu_immediate_util.c +++ b/source/blender/gpu/intern/gpu_immediate_util.c @@ -142,12 +142,27 @@ static void imm_draw_circle(GPUPrimType prim_type, float radius_y, int nsegments) { - immBegin(prim_type, nsegments); - for (int i = 0; i < nsegments; i++) { - const float angle = (float)(2 * M_PI) * ((float)i / (float)nsegments); - immVertex2f(shdr_pos, x + (radius_x * cosf(angle)), y + (radius_y * sinf(angle))); + if (prim_type == GPU_PRIM_LINE_LOOP) { + /* Note(Metal/AMD): For small primitives, line list more efficient than line strip.. */ + immBegin(GPU_PRIM_LINES, nsegments * 2); + + immVertex2f(shdr_pos, x + (radius_x * cosf(0.0f)), y + (radius_y * sinf(0.0f))); + for (int i = 1; i < nsegments; i++) { + const float angle = (float)(2 * M_PI) * ((float)i / (float)nsegments); + immVertex2f(shdr_pos, x + (radius_x * cosf(angle)), y + (radius_y * sinf(angle))); + immVertex2f(shdr_pos, x + (radius_x * cosf(angle)), y + (radius_y * sinf(angle))); + } + immVertex2f(shdr_pos, x + (radius_x * cosf(0.0f)), y + (radius_y * sinf(0.0f))); + immEnd(); + } + else { + immBegin(prim_type, nsegments); + for (int i = 0; i < nsegments; i++) { + const float angle = (float)(2 * M_PI) * ((float)i / (float)nsegments); + immVertex2f(shdr_pos, x + (radius_x * cosf(angle)), y + (radius_y * sinf(angle))); + } + immEnd(); } - immEnd(); } void imm_draw_circle_wire_2d(uint shdr_pos, float x, float y, float radius, int nsegments) @@ -194,12 +209,42 @@ static void imm_draw_circle_partial(GPUPrimType prim_type, immEnd(); } +static void imm_draw_circle_partial_3d(GPUPrimType prim_type, + uint pos, + float x, + float y, + float z, + float rad, + int nsegments, + float start, + float sweep) +{ + /* shift & reverse angle, increase 'nsegments' to match gluPartialDisk */ + const float angle_start = -(DEG2RADF(start)) + (float)(M_PI / 2); + const float angle_end = -(DEG2RADF(sweep) - angle_start); + nsegments += 1; + immBegin(prim_type, nsegments); + for (int i = 0; i < nsegments; i++) { + const float angle = interpf(angle_start, angle_end, ((float)i / (float)(nsegments - 1))); + const float angle_sin = sinf(angle); + const float angle_cos = cosf(angle); + immVertex3f(pos, x + rad * angle_cos, y + rad * angle_sin, z); + } + immEnd(); +} + void imm_draw_circle_partial_wire_2d( uint pos, float x, float y, float radius, int nsegments, float start, float sweep) { imm_draw_circle_partial(GPU_PRIM_LINE_STRIP, pos, x, y, radius, nsegments, start, sweep); } +void imm_draw_circle_partial_wire_3d( + uint pos, float x, float y, float z, float rad, int nsegments, float start, float sweep) +{ + imm_draw_circle_partial_3d(GPU_PRIM_LINE_STRIP, pos, x, y, z, rad, nsegments, start, sweep); +} + static void imm_draw_disk_partial(GPUPrimType prim_type, uint pos, float x, @@ -229,6 +274,36 @@ static void imm_draw_disk_partial(GPUPrimType prim_type, immEnd(); } +static void imm_draw_disk_partial_3d(GPUPrimType prim_type, + uint pos, + float x, + float y, + float z, + float rad_inner, + float rad_outer, + int nsegments, + float start, + float sweep) +{ + /* to avoid artifacts */ + const float max_angle = 3 * 360; + CLAMP(sweep, -max_angle, max_angle); + + /* shift & reverse angle, increase 'nsegments' to match gluPartialDisk */ + const float angle_start = -(DEG2RADF(start)) + (float)M_PI_2; + const float angle_end = -(DEG2RADF(sweep) - angle_start); + nsegments += 1; + immBegin(prim_type, nsegments * 2); + for (int i = 0; i < nsegments; i++) { + const float angle = interpf(angle_start, angle_end, ((float)i / (float)(nsegments - 1))); + const float angle_sin = sinf(angle); + const float angle_cos = cosf(angle); + immVertex3f(pos, x + rad_inner * angle_cos, y + rad_inner * angle_sin, z); + immVertex3f(pos, x + rad_outer * angle_cos, y + rad_outer * angle_sin, z); + } + immEnd(); +} + void imm_draw_disk_partial_fill_2d(uint pos, float x, float y, @@ -241,16 +316,44 @@ void imm_draw_disk_partial_fill_2d(uint pos, imm_draw_disk_partial( GPU_PRIM_TRI_STRIP, pos, x, y, rad_inner, rad_outer, nsegments, start, sweep); } +void imm_draw_disk_partial_fill_3d(uint pos, + float x, + float y, + float z, + float rad_inner, + float rad_outer, + int nsegments, + float start, + float sweep) +{ + imm_draw_disk_partial_3d( + GPU_PRIM_TRI_STRIP, pos, x, y, z, rad_inner, rad_outer, nsegments, start, sweep); +} static void imm_draw_circle_3D( GPUPrimType prim_type, uint pos, float x, float y, float radius, int nsegments) { - immBegin(prim_type, nsegments); - for (int i = 0; i < nsegments; i++) { - float angle = (float)(2 * M_PI) * ((float)i / (float)nsegments); - immVertex3f(pos, x + radius * cosf(angle), y + radius * sinf(angle), 0.0f); + if (prim_type == GPU_PRIM_LINE_LOOP) { + /* Note(Metal/AMD): For small primitives, line list more efficient than line strip. */ + immBegin(GPU_PRIM_LINES, nsegments * 2); + + immVertex3f(pos, x + radius * cosf(0.0f), y + radius * sinf(0.0f), 0.0f); + for (int i = 1; i < nsegments; i++) { + float angle = (float)(2 * M_PI) * ((float)i / (float)nsegments); + immVertex3f(pos, x + radius * cosf(angle), y + radius * sinf(angle), 0.0f); + immVertex3f(pos, x + radius * cosf(angle), y + radius * sinf(angle), 0.0f); + } + immVertex3f(pos, x + radius * cosf(0.0f), y + radius * sinf(0.0f), 0.0f); + immEnd(); + } + else { + immBegin(prim_type, nsegments); + for (int i = 0; i < nsegments; i++) { + float angle = (float)(2 * M_PI) * ((float)i / (float)nsegments); + immVertex3f(pos, x + radius * cosf(angle), y + radius * sinf(angle), 0.0f); + } + immEnd(); } - immEnd(); } void imm_draw_circle_wire_3d(uint pos, float x, float y, float radius, int nsegments) @@ -270,22 +373,38 @@ void imm_draw_circle_fill_3d(uint pos, float x, float y, float radius, int nsegm void imm_draw_box_wire_2d(uint pos, float x1, float y1, float x2, float y2) { - immBegin(GPU_PRIM_LINE_LOOP, 4); + /* Note(Metal/AMD): For small primitives, line list more efficient than line-strip. */ + immBegin(GPU_PRIM_LINES, 8); immVertex2f(pos, x1, y1); immVertex2f(pos, x1, y2); + + immVertex2f(pos, x1, y2); immVertex2f(pos, x2, y2); + + immVertex2f(pos, x2, y2); + immVertex2f(pos, x2, y1); + immVertex2f(pos, x2, y1); + immVertex2f(pos, x1, y1); immEnd(); } void imm_draw_box_wire_3d(uint pos, float x1, float y1, float x2, float y2) { /* use this version when GPUVertFormat has a vec3 position */ - immBegin(GPU_PRIM_LINE_LOOP, 4); + /* Note(Metal/AMD): For small primitives, line list more efficient than line-strip. */ + immBegin(GPU_PRIM_LINES, 8); immVertex3f(pos, x1, y1, 0.0f); immVertex3f(pos, x1, y2, 0.0f); + + immVertex3f(pos, x1, y2, 0.0f); + immVertex3f(pos, x2, y2, 0.0f); + immVertex3f(pos, x2, y2, 0.0f); immVertex3f(pos, x2, y1, 0.0f); + + immVertex3f(pos, x2, y1, 0.0f); + immVertex3f(pos, x1, y1, 0.0f); immEnd(); } diff --git a/source/blender/gpu/intern/gpu_init_exit.c b/source/blender/gpu/intern/gpu_init_exit.c index 7ccb0f89dc3..e97c9e9c829 100644 --- a/source/blender/gpu/intern/gpu_init_exit.c +++ b/source/blender/gpu/intern/gpu_init_exit.c @@ -39,7 +39,6 @@ void GPU_init(void) gpu_shader_create_info_init(); gpu_codegen_init(); - gpu_material_library_init(); gpu_batch_init(); @@ -56,7 +55,6 @@ void GPU_exit(void) gpu_batch_exit(); - gpu_material_library_exit(); gpu_codegen_exit(); gpu_shader_dependency_exit(); diff --git a/source/blender/gpu/intern/gpu_material.c b/source/blender/gpu/intern/gpu_material.c index 49732240125..711a3943a25 100644 --- a/source/blender/gpu/intern/gpu_material.c +++ b/source/blender/gpu/intern/gpu_material.c @@ -16,7 +16,6 @@ #include "DNA_scene_types.h" #include "DNA_world_types.h" -#include "BLI_ghash.h" #include "BLI_listbase.h" #include "BLI_math.h" #include "BLI_string.h" @@ -27,6 +26,7 @@ #include "BKE_material.h" #include "BKE_node.h" #include "BKE_scene.h" +#include "BKE_world.h" #include "NOD_shader.h" @@ -49,56 +49,50 @@ typedef struct GPUColorBandBuilder { } GPUColorBandBuilder; struct GPUMaterial { - Scene *scene; /* DEPRECATED was only useful for lights. */ - Material *ma; - + /* Contains GPUShader and source code for deferred compilation. + * Can be shared between similar material (i.e: sharing same nodetree topology). */ + GPUPass *pass; + /** UBOs for this material parameters. */ + GPUUniformBuf *ubo; + /** Compilation status. Do not use if shader is not GPU_MAT_SUCCESS. */ eGPUMaterialStatus status; - - const void *engine_type; /* attached engine type */ - int options; /* to identify shader variations (shadow, probe, world background...) */ - bool is_volume_shader; /* is volumetric shader */ - - /* Nodes */ + /** Some flags about the nodetree & the needed resources. */ + eGPUMaterialFlag flag; + /* Identify shader variations (shadow, probe, world background...). + * Should be unique even across render engines. */ + uint64_t uuid; + /* Number of generated function. */ + int generated_function_len; + /** Object type for attribute fetching. */ + bool is_volume_shader; + + /** DEPRECATED Currently only used for deferred compilation. */ + Scene *scene; + /** Source material, might be null. */ + Material *ma; + /** 1D Texture array containing all color bands. */ + GPUTexture *coba_tex; + /** Builder for coba_tex. */ + GPUColorBandBuilder *coba_builder; + /* Low level node graph(s). Also contains resources needed by the material. */ GPUNodeGraph graph; - /* for binding the material */ - GPUPass *pass; - - /* XXX: Should be in Material. But it depends on the output node - * used and since the output selection is different for GPUMaterial... - */ - bool has_volume_output; + /** DEPRECATED: To remove. */ bool has_surface_output; - - /* Only used by Eevee to know which BSDF are used. */ - eGPUMatFlag flag; - - /* Used by 2.8 pipeline */ - GPUUniformBuf *ubo; /* UBOs for shader uniforms. */ - - /* Eevee SSS */ + bool has_volume_output; + /** DEPRECATED: To remove. */ GPUUniformBuf *sss_profile; /* UBO containing SSS profile. */ GPUTexture *sss_tex_profile; /* Texture containing SSS profile. */ - float sss_enabled; + bool sss_enabled; float sss_radii[3]; int sss_samples; bool sss_dirty; - GPUTexture *coba_tex; /* 1D Texture array containing all color bands. */ - GPUColorBandBuilder *coba_builder; - - GSet *used_libraries; - #ifndef NDEBUG char name[64]; #endif }; -enum { - GPU_USE_SURFACE_OUTPUT = (1 << 0), - GPU_USE_VOLUME_OUTPUT = (1 << 1), -}; - /* Functions */ GPUTexture **gpu_material_ramp_texture_row_set(GPUMaterial *mat, @@ -159,17 +153,15 @@ static void gpu_material_free_single(GPUMaterial *material) if (material->ubo != NULL) { GPU_uniformbuf_free(material->ubo); } - if (material->sss_tex_profile != NULL) { - GPU_texture_free(material->sss_tex_profile); + if (material->coba_tex != NULL) { + GPU_texture_free(material->coba_tex); } if (material->sss_profile != NULL) { GPU_uniformbuf_free(material->sss_profile); } - if (material->coba_tex != NULL) { - GPU_texture_free(material->coba_tex); + if (material->sss_tex_profile != NULL) { + GPU_texture_free(material->sss_tex_profile); } - - BLI_gset_free(material->used_libraries, NULL); } void GPU_material_free(ListBase *gpumaterial) @@ -217,17 +209,40 @@ void GPU_material_uniform_buffer_create(GPUMaterial *material, ListBase *inputs) material->ubo = GPU_uniformbuf_create_from_list(inputs, name); } +ListBase GPU_material_attributes(GPUMaterial *material) +{ + return material->graph.attributes; +} + +ListBase GPU_material_textures(GPUMaterial *material) +{ + return material->graph.textures; +} + +ListBase GPU_material_volume_grids(GPUMaterial *material) +{ + return material->graph.volume_grids; +} + +GPUUniformAttrList *GPU_material_uniform_attributes(GPUMaterial *material) +{ + GPUUniformAttrList *attrs = &material->graph.uniform_attrs; + return attrs->count > 0 ? attrs : NULL; +} + +#if 1 /* End of life code. */ /* Eevee Subsurface scattering. */ /* Based on Separable SSS. by Jorge Jimenez and Diego Gutierrez */ -#define SSS_SAMPLES 65 -#define SSS_EXPONENT 2.0f /* Importance sampling exponent */ +# define SSS_SAMPLES 65 +# define SSS_EXPONENT 2.0f /* Importance sampling exponent */ typedef struct GPUSssKernelData { float kernel[SSS_SAMPLES][4]; float param[3], max_radius; + float avg_inv_radius; int samples; - int pad[3]; + int pad[2]; } GPUSssKernelData; BLI_STATIC_ASSERT_ALIGN(GPUSssKernelData, 16) @@ -243,8 +258,8 @@ static void sss_calculate_offsets(GPUSssKernelData *kd, int count, float exponen } } -#define BURLEY_TRUNCATE 16.0f -#define BURLEY_TRUNCATE_CDF 0.9963790093708328f // cdf(BURLEY_TRUNCATE) +# define BURLEY_TRUNCATE 16.0f +# define BURLEY_TRUNCATE_CDF 0.9963790093708328f // cdf(BURLEY_TRUNCATE) static float burley_profile(float r, float d) { float exp_r_3_d = expf(-r / (3.0f * d)); @@ -259,7 +274,7 @@ static float eval_profile(float r, float param) } /* Resolution for each sample of the precomputed kernel profile */ -#define INTEGRAL_RESOLUTION 32 +# define INTEGRAL_RESOLUTION 32 static float eval_integral(float x0, float x1, float param) { const float range = x1 - x0; @@ -274,7 +289,7 @@ static float eval_integral(float x0, float x1, float param) return integral; } -#undef INTEGRAL_RESOLUTION +# undef INTEGRAL_RESOLUTION static void compute_sss_kernel(GPUSssKernelData *kd, const float radii[3], int sample_len) { @@ -284,6 +299,8 @@ static void compute_sss_kernel(GPUSssKernelData *kd, const float radii[3], int s rad[1] = MAX2(radii[1], 1e-15f); rad[2] = MAX2(radii[2], 1e-15f); + kd->avg_inv_radius = 3.0f / (rad[0] + rad[1] + rad[2]); + /* Christensen-Burley fitting */ float l[3], d[3]; @@ -358,7 +375,7 @@ static void compute_sss_kernel(GPUSssKernelData *kd, const float radii[3], int s kd->samples = sample_len; } -#define INTEGRAL_RESOLUTION 512 +# define INTEGRAL_RESOLUTION 512 static void compute_sss_translucence_kernel(const GPUSssKernelData *kd, int resolution, float **output) @@ -417,10 +434,14 @@ static void compute_sss_translucence_kernel(const GPUSssKernelData *kd, mul_v3_fl(texels[resolution - 3], 0.5f); mul_v3_fl(texels[resolution - 4], 0.75f); } -#undef INTEGRAL_RESOLUTION +# undef INTEGRAL_RESOLUTION -void GPU_material_sss_profile_create(GPUMaterial *material, float radii[3]) +bool GPU_material_sss_profile_create(GPUMaterial *material, float radii[3]) { + /* Enable only once. */ + if (material->sss_enabled) { + return false; + } copy_v3_v3(material->sss_radii, radii); material->sss_dirty = true; material->sss_enabled = true; @@ -429,6 +450,7 @@ void GPU_material_sss_profile_create(GPUMaterial *material, float radii[3]) if (material->sss_profile == NULL) { material->sss_profile = GPU_uniformbuf_create(sizeof(GPUSssKernelData)); } + return true; } struct GPUUniformBuf *GPU_material_sss_profile_get(GPUMaterial *material, @@ -475,34 +497,37 @@ struct GPUUniformBuf *GPU_material_create_sss_profile_ubo(void) return GPU_uniformbuf_create(sizeof(GPUSssKernelData)); } -#undef SSS_EXPONENT -#undef SSS_SAMPLES - -ListBase GPU_material_attributes(GPUMaterial *material) -{ - return material->graph.attributes; -} +# undef SSS_EXPONENT +# undef SSS_SAMPLES +#endif -ListBase GPU_material_textures(GPUMaterial *material) +void GPU_material_output_surface(GPUMaterial *material, GPUNodeLink *link) { - return material->graph.textures; + if (!material->graph.outlink_surface) { + material->graph.outlink_surface = link; + material->has_surface_output = true; + } } -ListBase GPU_material_volume_grids(GPUMaterial *material) +void GPU_material_output_volume(GPUMaterial *material, GPUNodeLink *link) { - return material->graph.volume_grids; + if (!material->graph.outlink_volume) { + material->graph.outlink_volume = link; + material->has_volume_output = true; + } } -GPUUniformAttrList *GPU_material_uniform_attributes(GPUMaterial *material) +void GPU_material_output_displacement(GPUMaterial *material, GPUNodeLink *link) { - GPUUniformAttrList *attrs = &material->graph.uniform_attrs; - return attrs->count > 0 ? attrs : NULL; + if (!material->graph.outlink_displacement) { + material->graph.outlink_displacement = link; + } } -void GPU_material_output_link(GPUMaterial *material, GPUNodeLink *link) +void GPU_material_output_thickness(GPUMaterial *material, GPUNodeLink *link) { - if (!material->graph.outlink) { - material->graph.outlink = link; + if (!material->graph.outlink_thickness) { + material->graph.outlink_thickness = link; } } @@ -514,23 +539,71 @@ void GPU_material_add_output_link_aov(GPUMaterial *material, GPUNodeLink *link, BLI_addtail(&material->graph.outlink_aovs, aov_link); } +char *GPU_material_split_sub_function(GPUMaterial *material, + eGPUType return_type, + GPUNodeLink **link) +{ + /* Force cast to return type. */ + switch (return_type) { + case GPU_FLOAT: + GPU_link(material, "set_value", *link, link); + break; + case GPU_VEC3: + GPU_link(material, "set_rgb", *link, link); + break; + case GPU_VEC4: + GPU_link(material, "set_rgba", *link, link); + break; + default: + BLI_assert(0); + break; + } + + GPUNodeGraphFunctionLink *func_link = MEM_callocN(sizeof(GPUNodeGraphFunctionLink), __func__); + func_link->outlink = *link; + SNPRINTF(func_link->name, "ntree_fn%d", material->generated_function_len++); + BLI_addtail(&material->graph.material_functions, func_link); + + /* Set value to break the link with the main graph. */ + switch (return_type) { + case GPU_FLOAT: + GPU_link(material, "set_value_one", link); + break; + case GPU_VEC3: + GPU_link(material, "set_rgb_one", link); + break; + case GPU_VEC4: + GPU_link(material, "set_rgba_one", link); + break; + default: + BLI_assert(0); + break; + } + return func_link->name; +} + GPUNodeGraph *gpu_material_node_graph(GPUMaterial *material) { return &material->graph; } -GSet *gpu_material_used_libraries(GPUMaterial *material) +eGPUMaterialStatus GPU_material_status(GPUMaterial *mat) { - return material->used_libraries; + return mat->status; } -eGPUMaterialStatus GPU_material_status(GPUMaterial *mat) +void GPU_material_status_set(GPUMaterial *mat, eGPUMaterialStatus status) { - return mat->status; + mat->status = status; } /* Code generation */ +bool GPU_material_is_volume_shader(GPUMaterial *mat) +{ + return mat->is_volume_shader; +} + bool GPU_material_has_surface_output(GPUMaterial *mat) { return mat->has_surface_output; @@ -541,100 +614,79 @@ bool GPU_material_has_volume_output(GPUMaterial *mat) return mat->has_volume_output; } -bool GPU_material_is_volume_shader(GPUMaterial *mat) +void GPU_material_flag_set(GPUMaterial *mat, eGPUMaterialFlag flag) { - return mat->is_volume_shader; + mat->flag |= flag; } -void GPU_material_flag_set(GPUMaterial *mat, eGPUMatFlag flag) +bool GPU_material_flag_get(const GPUMaterial *mat, eGPUMaterialFlag flag) { - mat->flag |= flag; + return (mat->flag & flag) != 0; } -bool GPU_material_flag_get(GPUMaterial *mat, eGPUMatFlag flag) +eGPUMaterialFlag GPU_material_flag(const GPUMaterial *mat) { - return (mat->flag & flag) != 0; + return mat->flag; } -GPUMaterial *GPU_material_from_nodetree_find(ListBase *gpumaterials, - const void *engine_type, - int options) +/* Note: Consumes the flags. */ +bool GPU_material_recalc_flag_get(GPUMaterial *mat) { - LISTBASE_FOREACH (LinkData *, link, gpumaterials) { - GPUMaterial *current_material = (GPUMaterial *)link->data; - if (current_material->engine_type == engine_type && current_material->options == options) { - return current_material; - } - } + bool updated = (mat->flag & GPU_MATFLAG_UPDATED) != 0; + mat->flag &= ~GPU_MATFLAG_UPDATED; + return updated; +} - return NULL; +uint64_t GPU_material_uuid_get(GPUMaterial *mat) +{ + return mat->uuid; } GPUMaterial *GPU_material_from_nodetree(Scene *scene, - struct Material *ma, - struct bNodeTree *ntree, + Material *ma, + bNodeTree *ntree, ListBase *gpumaterials, - const void *engine_type, - const int options, - const bool is_volume_shader, - const char *vert_code, - const char *geom_code, - const char *frag_lib, - const char *defines, const char *name, - GPUMaterialEvalCallbackFn callback) + uint64_t shader_uuid, + bool is_volume_shader, + bool is_lookdev, + GPUCodegenCallbackFn callback, + void *thunk) { - LinkData *link; - bool has_volume_output, has_surface_output; - - /* Caller must re-use materials. */ - BLI_assert(GPU_material_from_nodetree_find(gpumaterials, engine_type, options) == NULL); - - /* HACK: Eevee assume this to create #GHash keys. */ - BLI_assert(sizeof(GPUPass) > 16); + /* Search if this material is not already compiled. */ + LISTBASE_FOREACH (LinkData *, link, gpumaterials) { + GPUMaterial *mat = (GPUMaterial *)link->data; + if (mat->uuid == shader_uuid) { + return mat; + } + } - /* allocate material */ GPUMaterial *mat = MEM_callocN(sizeof(GPUMaterial), "GPUMaterial"); mat->ma = ma; mat->scene = scene; - mat->engine_type = engine_type; - mat->options = options; + mat->uuid = shader_uuid; + mat->flag = GPU_MATFLAG_UPDATED; mat->is_volume_shader = is_volume_shader; + mat->graph.used_libraries = BLI_gset_new( + BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "GPUNodeGraph.used_libraries"); #ifndef NDEBUG BLI_snprintf(mat->name, sizeof(mat->name), "%s", name); #else UNUSED_VARS(name); #endif + if (is_lookdev) { + mat->flag |= GPU_MATFLAG_LOOKDEV_HACK; + } - mat->used_libraries = BLI_gset_new( - BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "GPUMaterial.used_libraries"); - - /* localize tree to create links for reroute and mute */ + /* Localize tree to create links for reroute and mute. */ bNodeTree *localtree = ntreeLocalize(ntree); - ntreeGPUMaterialNodes(localtree, mat, &has_surface_output, &has_volume_output); + ntreeGPUMaterialNodes(localtree, mat); gpu_material_ramp_texture_build(mat); - mat->has_surface_output = has_surface_output; - mat->has_volume_output = has_volume_output; - - if (mat->graph.outlink) { - if (callback) { - callback(mat, options, &vert_code, &geom_code, &frag_lib, &defines); - } - /* HACK: this is only for eevee. We add the define here after the nodetree evaluation. */ - if (GPU_material_flag_get(mat, GPU_MATFLAG_SSS)) { - defines = BLI_string_joinN(defines, - "#ifndef USE_ALPHA_BLEND\n" - "# define USE_SSS\n" - "#endif\n"); - } + { /* Create source code and search pass cache for an already compiled version. */ - mat->pass = GPU_generate_pass(mat, &mat->graph, vert_code, geom_code, frag_lib, defines); - - if (GPU_material_flag_get(mat, GPU_MATFLAG_SSS)) { - MEM_freeN((char *)defines); - } + mat->pass = GPU_generate_pass(mat, &mat->graph, callback, thunk); if (mat->pass == NULL) { /* We had a cache hit and the shader has already failed to compile. */ @@ -649,26 +701,20 @@ GPUMaterial *GPU_material_from_nodetree(Scene *scene, gpu_node_graph_free_nodes(&mat->graph); } else { - mat->status = GPU_MAT_QUEUED; + mat->status = GPU_MAT_CREATED; } } } - else { - mat->status = GPU_MAT_FAILED; - gpu_node_graph_free(&mat->graph); - } - /* Only free after GPU_pass_shader_get where GPUUniformBuf - * read data from the local tree. */ + /* Only free after GPU_pass_shader_get where GPUUniformBuf read data from the local tree. */ ntreeFreeLocalTree(localtree); BLI_assert(!localtree->id.py_instance); /* Or call #BKE_libblock_free_data_py. */ MEM_freeN(localtree); - /* note that even if building the shader fails in some way, we still keep + /* Note that even if building the shader fails in some way, we still keep * it to avoid trying to compile again and again, and simply do not use - * the actual shader on drawing */ - - link = MEM_callocN(sizeof(LinkData), "GPUMaterialLink"); + * the actual shader on drawing. */ + LinkData *link = MEM_callocN(sizeof(LinkData), "GPUMaterialLink"); link->data = mat; BLI_addtail(gpumaterials, link); @@ -690,6 +736,8 @@ void GPU_material_compile(GPUMaterial *mat) success = GPU_pass_compile(mat->pass, __func__); #endif + mat->flag |= GPU_MATFLAG_UPDATED; + if (success) { GPUShader *sh = GPU_pass_shader_get(mat->pass); if (sh != NULL) { @@ -715,5 +763,6 @@ void GPU_materials_free(Main *bmain) GPU_material_free(&wo->gpumaterial); } + // BKE_world_defaults_free_gpu(); BKE_material_defaults_free_gpu(); } diff --git a/source/blender/gpu/intern/gpu_material_library.c b/source/blender/gpu/intern/gpu_material_library.c deleted file mode 100644 index ca5c3d4bb45..00000000000 --- a/source/blender/gpu/intern/gpu_material_library.c +++ /dev/null @@ -1,904 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later - * Copyright 2005 Blender Foundation. All rights reserved. */ - -/** \file - * \ingroup gpu - * - * GPU material library parsing and code generation. - */ - -#include <stdio.h> -#include <string.h> - -#include "MEM_guardedalloc.h" - -#include "BLI_dynstr.h" -#include "BLI_ghash.h" -#include "BLI_string.h" -#include "BLI_utildefines.h" - -#include "gpu_material_library.h" - -/* List of all gpu_shader_material_*.glsl files used by GLSL materials. These - * will be parsed to make all functions in them available to use for GPU_link(). - * - * If a file uses functions from another file, it must be added to the list of - * dependencies, and be placed after that file in the list. */ - -extern char datatoc_gpu_shader_material_add_shader_glsl[]; -extern char datatoc_gpu_shader_material_ambient_occlusion_glsl[]; -extern char datatoc_gpu_shader_material_anisotropic_glsl[]; -extern char datatoc_gpu_shader_material_attribute_glsl[]; -extern char datatoc_gpu_shader_material_background_glsl[]; -extern char datatoc_gpu_shader_material_bevel_glsl[]; -extern char datatoc_gpu_shader_material_wavelength_glsl[]; -extern char datatoc_gpu_shader_material_blackbody_glsl[]; -extern char datatoc_gpu_shader_material_bright_contrast_glsl[]; -extern char datatoc_gpu_shader_material_bump_glsl[]; -extern char datatoc_gpu_shader_material_camera_glsl[]; -extern char datatoc_gpu_shader_material_clamp_glsl[]; -extern char datatoc_gpu_shader_material_color_ramp_glsl[]; -extern char datatoc_gpu_shader_material_color_util_glsl[]; -extern char datatoc_gpu_shader_material_combine_hsv_glsl[]; -extern char datatoc_gpu_shader_material_combine_rgb_glsl[]; -extern char datatoc_gpu_shader_material_combine_xyz_glsl[]; -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[]; -extern char datatoc_gpu_shader_material_geometry_glsl[]; -extern char datatoc_gpu_shader_material_glass_glsl[]; -extern char datatoc_gpu_shader_material_glossy_glsl[]; -extern char datatoc_gpu_shader_material_hair_info_glsl[]; -extern char datatoc_gpu_shader_material_hash_glsl[]; -extern char datatoc_gpu_shader_material_holdout_glsl[]; -extern char datatoc_gpu_shader_material_hue_sat_val_glsl[]; -extern char datatoc_gpu_shader_material_invert_glsl[]; -extern char datatoc_gpu_shader_material_layer_weight_glsl[]; -extern char datatoc_gpu_shader_material_light_falloff_glsl[]; -extern char datatoc_gpu_shader_material_light_path_glsl[]; -extern char datatoc_gpu_shader_material_mapping_glsl[]; -extern char datatoc_gpu_shader_material_map_range_glsl[]; -extern char datatoc_gpu_shader_material_math_glsl[]; -extern char datatoc_gpu_shader_material_math_util_glsl[]; -extern char datatoc_gpu_shader_material_mix_rgb_glsl[]; -extern char datatoc_gpu_shader_material_mix_shader_glsl[]; -extern char datatoc_gpu_shader_material_noise_glsl[]; -extern char datatoc_gpu_shader_material_normal_glsl[]; -extern char datatoc_gpu_shader_material_normal_map_glsl[]; -extern char datatoc_gpu_shader_material_object_info_glsl[]; -extern char datatoc_gpu_shader_material_output_aov_glsl[]; -extern char datatoc_gpu_shader_material_output_material_glsl[]; -extern char datatoc_gpu_shader_material_output_world_glsl[]; -extern char datatoc_gpu_shader_material_particle_info_glsl[]; -extern char datatoc_gpu_shader_material_point_info_glsl[]; -extern char datatoc_gpu_shader_material_principled_glsl[]; -extern char datatoc_gpu_shader_material_refraction_glsl[]; -extern char datatoc_gpu_shader_material_rgb_curves_glsl[]; -extern char datatoc_gpu_shader_material_rgb_to_bw_glsl[]; -extern char datatoc_gpu_shader_material_separate_hsv_glsl[]; -extern char datatoc_gpu_shader_material_separate_rgb_glsl[]; -extern char datatoc_gpu_shader_material_separate_xyz_glsl[]; -extern char datatoc_gpu_shader_material_set_glsl[]; -extern char datatoc_gpu_shader_material_shader_to_rgba_glsl[]; -extern char datatoc_gpu_shader_material_squeeze_glsl[]; -extern char datatoc_gpu_shader_material_subsurface_scattering_glsl[]; -extern char datatoc_gpu_shader_material_tangent_glsl[]; -extern char datatoc_gpu_shader_material_tex_brick_glsl[]; -extern char datatoc_gpu_shader_material_tex_checker_glsl[]; -extern char datatoc_gpu_shader_material_tex_environment_glsl[]; -extern char datatoc_gpu_shader_material_tex_gradient_glsl[]; -extern char datatoc_gpu_shader_material_tex_image_glsl[]; -extern char datatoc_gpu_shader_material_tex_magic_glsl[]; -extern char datatoc_gpu_shader_material_tex_musgrave_glsl[]; -extern char datatoc_gpu_shader_material_tex_noise_glsl[]; -extern char datatoc_gpu_shader_material_tex_sky_glsl[]; -extern char datatoc_gpu_shader_material_texture_coordinates_glsl[]; -extern char datatoc_gpu_shader_material_tex_voronoi_glsl[]; -extern char datatoc_gpu_shader_material_tex_wave_glsl[]; -extern char datatoc_gpu_shader_material_tex_white_noise_glsl[]; -extern char datatoc_gpu_shader_material_toon_glsl[]; -extern char datatoc_gpu_shader_material_translucent_glsl[]; -extern char datatoc_gpu_shader_material_transparent_glsl[]; -extern char datatoc_gpu_shader_material_uv_map_glsl[]; -extern char datatoc_gpu_shader_material_vector_curves_glsl[]; -extern char datatoc_gpu_shader_material_vector_displacement_glsl[]; -extern char datatoc_gpu_shader_material_vector_math_glsl[]; -extern char datatoc_gpu_shader_material_vector_rotate_glsl[]; -extern char datatoc_gpu_shader_material_velvet_glsl[]; -extern char datatoc_gpu_shader_material_vertex_color_glsl[]; -extern char datatoc_gpu_shader_material_volume_absorption_glsl[]; -extern char datatoc_gpu_shader_material_volume_info_glsl[]; -extern char datatoc_gpu_shader_material_volume_principled_glsl[]; -extern char datatoc_gpu_shader_material_volume_scatter_glsl[]; -extern char datatoc_gpu_shader_material_wireframe_glsl[]; -extern char datatoc_gpu_shader_material_world_normals_glsl[]; - -static GPUMaterialLibrary gpu_shader_material_math_util_library = { - .code = datatoc_gpu_shader_material_math_util_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_color_util_library = { - .code = datatoc_gpu_shader_material_color_util_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_hash_library = { - .code = datatoc_gpu_shader_material_hash_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_noise_library = { - .code = datatoc_gpu_shader_material_noise_glsl, - .dependencies = {&gpu_shader_material_hash_library, NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_fractal_noise_library = { - .code = datatoc_gpu_shader_material_fractal_noise_glsl, - .dependencies = {&gpu_shader_material_noise_library, NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_add_shader_library = { - .code = datatoc_gpu_shader_material_add_shader_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_ambient_occlusion_library = { - .code = datatoc_gpu_shader_material_ambient_occlusion_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_glossy_library = { - .code = datatoc_gpu_shader_material_glossy_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_anisotropic_library = { - .code = datatoc_gpu_shader_material_anisotropic_glsl, - .dependencies = {&gpu_shader_material_glossy_library, NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_attribute_library = { - .code = datatoc_gpu_shader_material_attribute_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_background_library = { - .code = datatoc_gpu_shader_material_background_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_bevel_library = { - .code = datatoc_gpu_shader_material_bevel_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_wavelength_library = { - .code = datatoc_gpu_shader_material_wavelength_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_blackbody_library = { - .code = datatoc_gpu_shader_material_blackbody_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_bright_contrast_library = { - .code = datatoc_gpu_shader_material_bright_contrast_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_bump_library = { - .code = datatoc_gpu_shader_material_bump_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_camera_library = { - .code = datatoc_gpu_shader_material_camera_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_clamp_library = { - .code = datatoc_gpu_shader_material_clamp_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_color_ramp_library = { - .code = datatoc_gpu_shader_material_color_ramp_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_combine_hsv_library = { - .code = datatoc_gpu_shader_material_combine_hsv_glsl, - .dependencies = {&gpu_shader_material_color_util_library, NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_combine_rgb_library = { - .code = datatoc_gpu_shader_material_combine_rgb_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_combine_xyz_library = { - .code = datatoc_gpu_shader_material_combine_xyz_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_diffuse_library = { - .code = datatoc_gpu_shader_material_diffuse_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_displacement_library = { - .code = datatoc_gpu_shader_material_displacement_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_eevee_specular_library = { - .code = datatoc_gpu_shader_material_eevee_specular_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_emission_library = { - .code = datatoc_gpu_shader_material_emission_glsl, - .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}, -}; - -static GPUMaterialLibrary gpu_shader_material_gamma_library = { - .code = datatoc_gpu_shader_material_gamma_glsl, - .dependencies = {&gpu_shader_material_math_util_library, NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_tangent_library = { - .code = datatoc_gpu_shader_material_tangent_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_geometry_library = { - .code = datatoc_gpu_shader_material_geometry_glsl, - .dependencies = {&gpu_shader_material_tangent_library, NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_glass_library = { - .code = datatoc_gpu_shader_material_glass_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_hair_info_library = { - .code = datatoc_gpu_shader_material_hair_info_glsl, - .dependencies = {&gpu_shader_material_hash_library, NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_holdout_library = { - .code = datatoc_gpu_shader_material_holdout_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_hue_sat_val_library = { - .code = datatoc_gpu_shader_material_hue_sat_val_glsl, - .dependencies = {&gpu_shader_material_color_util_library, NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_invert_library = { - .code = datatoc_gpu_shader_material_invert_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_layer_weight_library = { - .code = datatoc_gpu_shader_material_layer_weight_glsl, - .dependencies = {&gpu_shader_material_fresnel_library, NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_light_falloff_library = { - .code = datatoc_gpu_shader_material_light_falloff_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_light_path_library = { - .code = datatoc_gpu_shader_material_light_path_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_mapping_library = { - .code = datatoc_gpu_shader_material_mapping_glsl, - .dependencies = {&gpu_shader_material_math_util_library, NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_map_range_library = { - .code = datatoc_gpu_shader_material_map_range_glsl, - .dependencies = {&gpu_shader_material_math_util_library, NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_math_library = { - .code = datatoc_gpu_shader_material_math_glsl, - .dependencies = {&gpu_shader_material_math_util_library, NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_mix_rgb_library = { - .code = datatoc_gpu_shader_material_mix_rgb_glsl, - .dependencies = {&gpu_shader_material_color_util_library, NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_mix_shader_library = { - .code = datatoc_gpu_shader_material_mix_shader_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_normal_library = { - .code = datatoc_gpu_shader_material_normal_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_normal_map_library = { - .code = datatoc_gpu_shader_material_normal_map_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_object_info_library = { - .code = datatoc_gpu_shader_material_object_info_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_output_aov_library = { - .code = datatoc_gpu_shader_material_output_aov_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_output_material_library = { - .code = datatoc_gpu_shader_material_output_material_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_output_world_library = { - .code = datatoc_gpu_shader_material_output_world_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_particle_info_library = { - .code = datatoc_gpu_shader_material_particle_info_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_point_info_library = { - .code = datatoc_gpu_shader_material_point_info_glsl, - .dependencies = {&gpu_shader_material_hash_library, NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_principled_library = { - .code = datatoc_gpu_shader_material_principled_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_refraction_library = { - .code = datatoc_gpu_shader_material_refraction_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_rgb_curves_library = { - .code = datatoc_gpu_shader_material_rgb_curves_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_rgb_to_bw_library = { - .code = datatoc_gpu_shader_material_rgb_to_bw_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_separate_hsv_library = { - .code = datatoc_gpu_shader_material_separate_hsv_glsl, - .dependencies = {&gpu_shader_material_color_util_library, NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_separate_rgb_library = { - .code = datatoc_gpu_shader_material_separate_rgb_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_separate_xyz_library = { - .code = datatoc_gpu_shader_material_separate_xyz_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_set_library = { - .code = datatoc_gpu_shader_material_set_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_shader_to_rgba_library = { - .code = datatoc_gpu_shader_material_shader_to_rgba_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_squeeze_library = { - .code = datatoc_gpu_shader_material_squeeze_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_subsurface_scattering_library = { - .code = datatoc_gpu_shader_material_subsurface_scattering_glsl, - .dependencies = {&gpu_shader_material_diffuse_library, NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_tex_brick_library = { - .code = datatoc_gpu_shader_material_tex_brick_glsl, - .dependencies = {&gpu_shader_material_math_util_library, - &gpu_shader_material_hash_library, - NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_tex_checker_library = { - .code = datatoc_gpu_shader_material_tex_checker_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_tex_environment_library = { - .code = datatoc_gpu_shader_material_tex_environment_glsl, - .dependencies = {&gpu_shader_material_math_util_library, NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_tex_gradient_library = { - .code = datatoc_gpu_shader_material_tex_gradient_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_tex_image_library = { - .code = datatoc_gpu_shader_material_tex_image_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_tex_magic_library = { - .code = datatoc_gpu_shader_material_tex_magic_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_tex_musgrave_library = { - .code = datatoc_gpu_shader_material_tex_musgrave_glsl, - .dependencies = {&gpu_shader_material_noise_library, NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_tex_noise_library = { - .code = datatoc_gpu_shader_material_tex_noise_glsl, - .dependencies = {&gpu_shader_material_fractal_noise_library, NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_tex_sky_library = { - .code = datatoc_gpu_shader_material_tex_sky_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_texture_coordinates_library = { - .code = datatoc_gpu_shader_material_texture_coordinates_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_tex_voronoi_library = { - .code = datatoc_gpu_shader_material_tex_voronoi_glsl, - .dependencies = {&gpu_shader_material_math_util_library, - &gpu_shader_material_hash_library, - NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_tex_wave_library = { - .code = datatoc_gpu_shader_material_tex_wave_glsl, - .dependencies = {&gpu_shader_material_fractal_noise_library, NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_tex_white_noise_library = { - .code = datatoc_gpu_shader_material_tex_white_noise_glsl, - .dependencies = {&gpu_shader_material_hash_library, NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_toon_library = { - .code = datatoc_gpu_shader_material_toon_glsl, - .dependencies = {&gpu_shader_material_diffuse_library, NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_translucent_library = { - .code = datatoc_gpu_shader_material_translucent_glsl, - .dependencies = {&gpu_shader_material_diffuse_library, NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_transparent_library = { - .code = datatoc_gpu_shader_material_transparent_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_uv_map_library = { - .code = datatoc_gpu_shader_material_uv_map_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_vector_curves_library = { - .code = datatoc_gpu_shader_material_vector_curves_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_vector_displacement_library = { - .code = datatoc_gpu_shader_material_vector_displacement_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_vector_math_library = { - .code = datatoc_gpu_shader_material_vector_math_glsl, - .dependencies = {&gpu_shader_material_math_util_library, NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_vector_rotate_library = { - .code = datatoc_gpu_shader_material_vector_rotate_glsl, - .dependencies = {&gpu_shader_material_math_util_library, NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_velvet_library = { - .code = datatoc_gpu_shader_material_velvet_glsl, - .dependencies = {&gpu_shader_material_diffuse_library, NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_vertex_color_library = { - .code = datatoc_gpu_shader_material_vertex_color_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_volume_absorption_library = { - .code = datatoc_gpu_shader_material_volume_absorption_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_volume_info_library = { - .code = datatoc_gpu_shader_material_volume_info_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_volume_principled_library = { - .code = datatoc_gpu_shader_material_volume_principled_glsl, - .dependencies = {&gpu_shader_material_blackbody_library, NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_volume_scatter_library = { - .code = datatoc_gpu_shader_material_volume_scatter_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_wireframe_library = { - .code = datatoc_gpu_shader_material_wireframe_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_world_normals_library = { - .code = datatoc_gpu_shader_material_world_normals_glsl, - .dependencies = {&gpu_shader_material_texture_coordinates_library, NULL}, -}; - -static GPUMaterialLibrary *gpu_material_libraries[] = { - &gpu_shader_material_math_util_library, - &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, - &gpu_shader_material_glossy_library, - &gpu_shader_material_anisotropic_library, - &gpu_shader_material_attribute_library, - &gpu_shader_material_background_library, - &gpu_shader_material_bevel_library, - &gpu_shader_material_wavelength_library, - &gpu_shader_material_blackbody_library, - &gpu_shader_material_bright_contrast_library, - &gpu_shader_material_bump_library, - &gpu_shader_material_camera_library, - &gpu_shader_material_clamp_library, - &gpu_shader_material_color_ramp_library, - &gpu_shader_material_combine_hsv_library, - &gpu_shader_material_combine_rgb_library, - &gpu_shader_material_combine_xyz_library, - &gpu_shader_material_diffuse_library, - &gpu_shader_material_displacement_library, - &gpu_shader_material_eevee_specular_library, - &gpu_shader_material_emission_library, - &gpu_shader_material_fresnel_library, - &gpu_shader_material_gamma_library, - &gpu_shader_material_tangent_library, - &gpu_shader_material_geometry_library, - &gpu_shader_material_glass_library, - &gpu_shader_material_hair_info_library, - &gpu_shader_material_holdout_library, - &gpu_shader_material_hue_sat_val_library, - &gpu_shader_material_invert_library, - &gpu_shader_material_layer_weight_library, - &gpu_shader_material_light_falloff_library, - &gpu_shader_material_light_path_library, - &gpu_shader_material_mapping_library, - &gpu_shader_material_map_range_library, - &gpu_shader_material_math_library, - &gpu_shader_material_mix_rgb_library, - &gpu_shader_material_mix_shader_library, - &gpu_shader_material_normal_library, - &gpu_shader_material_normal_map_library, - &gpu_shader_material_object_info_library, - &gpu_shader_material_output_aov_library, - &gpu_shader_material_output_material_library, - &gpu_shader_material_output_world_library, - &gpu_shader_material_particle_info_library, - &gpu_shader_material_point_info_library, - &gpu_shader_material_principled_library, - &gpu_shader_material_refraction_library, - &gpu_shader_material_rgb_curves_library, - &gpu_shader_material_rgb_to_bw_library, - &gpu_shader_material_separate_hsv_library, - &gpu_shader_material_separate_rgb_library, - &gpu_shader_material_separate_xyz_library, - &gpu_shader_material_set_library, - &gpu_shader_material_shader_to_rgba_library, - &gpu_shader_material_squeeze_library, - &gpu_shader_material_subsurface_scattering_library, - &gpu_shader_material_tex_brick_library, - &gpu_shader_material_tex_checker_library, - &gpu_shader_material_tex_environment_library, - &gpu_shader_material_tex_gradient_library, - &gpu_shader_material_tex_image_library, - &gpu_shader_material_tex_magic_library, - &gpu_shader_material_tex_musgrave_library, - &gpu_shader_material_tex_noise_library, - &gpu_shader_material_tex_sky_library, - &gpu_shader_material_texture_coordinates_library, - &gpu_shader_material_tex_voronoi_library, - &gpu_shader_material_tex_wave_library, - &gpu_shader_material_tex_white_noise_library, - &gpu_shader_material_toon_library, - &gpu_shader_material_translucent_library, - &gpu_shader_material_transparent_library, - &gpu_shader_material_uv_map_library, - &gpu_shader_material_vector_curves_library, - &gpu_shader_material_vector_displacement_library, - &gpu_shader_material_vector_math_library, - &gpu_shader_material_vector_rotate_library, - &gpu_shader_material_velvet_library, - &gpu_shader_material_vertex_color_library, - &gpu_shader_material_volume_absorption_library, - &gpu_shader_material_volume_info_library, - &gpu_shader_material_volume_principled_library, - &gpu_shader_material_volume_scatter_library, - &gpu_shader_material_wireframe_library, - &gpu_shader_material_world_normals_library, - NULL}; - -/* GLSL code parsing for finding function definitions. - * These are stored in a hash for lookup when creating a material. */ - -static GHash *FUNCTION_HASH = NULL; - -const char *gpu_str_skip_token(const char *str, char *token, int max) -{ - int len = 0; - - /* skip a variable/function name */ - while (*str) { - if (ELEM(*str, ' ', '(', ')', ',', ';', '\t', '\n', '\r')) { - break; - } - - if (token && len < max - 1) { - *token = *str; - token++; - len++; - } - str++; - } - - if (token) { - *token = '\0'; - } - - /* skip the next special characters: - * note the missing ')' */ - while (*str) { - if (ELEM(*str, ' ', '(', ',', ';', '\t', '\n', '\r')) { - str++; - } - else { - break; - } - } - - return str; -} - -/* Indices match the eGPUType enum */ -static const char *GPU_DATATYPE_STR[17] = { - "", - "float", - "vec2", - "vec3", - "vec4", - NULL, - NULL, - NULL, - NULL, - "mat3", - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - "mat4", -}; - -const char *gpu_data_type_to_string(const eGPUType type) -{ - return GPU_DATATYPE_STR[type]; -} - -static void gpu_parse_material_library(GHash *hash, GPUMaterialLibrary *library) -{ - GPUFunction *function; - eGPUType type; - GPUFunctionQual qual; - int i; - const char *code = library->code; - - while ((code = strstr(code, "void "))) { - function = MEM_callocN(sizeof(GPUFunction), "GPUFunction"); - function->library = library; - - code = gpu_str_skip_token(code, NULL, 0); - code = gpu_str_skip_token(code, function->name, MAX_FUNCTION_NAME); - - /* get parameters */ - while (*code && *code != ')') { - if (BLI_str_startswith(code, "const ")) { - code = gpu_str_skip_token(code, NULL, 0); - } - - /* test if it's an input or output */ - qual = FUNCTION_QUAL_IN; - if (BLI_str_startswith(code, "out ")) { - qual = FUNCTION_QUAL_OUT; - } - if (BLI_str_startswith(code, "inout ")) { - qual = FUNCTION_QUAL_INOUT; - } - if ((qual != FUNCTION_QUAL_IN) || BLI_str_startswith(code, "in ")) { - code = gpu_str_skip_token(code, NULL, 0); - } - - /* test for type */ - type = GPU_NONE; - for (i = 1; i < ARRAY_SIZE(GPU_DATATYPE_STR); i++) { - if (GPU_DATATYPE_STR[i] && BLI_str_startswith(code, GPU_DATATYPE_STR[i])) { - type = i; - break; - } - } - - if (!type && BLI_str_startswith(code, "samplerCube")) { - type = GPU_TEXCUBE; - } - if (!type && BLI_str_startswith(code, "sampler2DShadow")) { - type = GPU_SHADOW2D; - } - if (!type && BLI_str_startswith(code, "sampler1DArray")) { - type = GPU_TEX1D_ARRAY; - } - if (!type && BLI_str_startswith(code, "sampler2DArray")) { - type = GPU_TEX2D_ARRAY; - } - if (!type && BLI_str_startswith(code, "sampler2D")) { - type = GPU_TEX2D; - } - if (!type && BLI_str_startswith(code, "sampler3D")) { - type = GPU_TEX3D; - } - - if (!type && BLI_str_startswith(code, "Closure")) { - type = GPU_CLOSURE; - } - - if (type) { - /* add parameter */ - code = gpu_str_skip_token(code, NULL, 0); - code = gpu_str_skip_token(code, NULL, 0); - function->paramqual[function->totparam] = qual; - function->paramtype[function->totparam] = type; - function->totparam++; - } - else { - fprintf(stderr, "GPU invalid function parameter in %s.\n", function->name); - break; - } - } - - if (function->name[0] == '\0' || function->totparam == 0) { - fprintf(stderr, "GPU functions parse error.\n"); - MEM_freeN(function); - break; - } - - BLI_ghash_insert(hash, function->name, function); - } -} - -/* Module */ - -void gpu_material_library_init(void) -{ - /* Only parse GLSL shader files once. */ - if (FUNCTION_HASH) { - return; - } - - FUNCTION_HASH = BLI_ghash_str_new("GPU_lookup_function gh"); - for (int i = 0; gpu_material_libraries[i]; i++) { - gpu_parse_material_library(FUNCTION_HASH, gpu_material_libraries[i]); - } -} - -void gpu_material_library_exit(void) -{ - if (FUNCTION_HASH) { - BLI_ghash_free(FUNCTION_HASH, NULL, MEM_freeN); - FUNCTION_HASH = NULL; - } -} - -/* Code Generation */ - -static void gpu_material_use_library_with_dependencies(GSet *used_libraries, - GPUMaterialLibrary *library) -{ - if (BLI_gset_add(used_libraries, library->code)) { - for (int i = 0; library->dependencies[i]; i++) { - gpu_material_use_library_with_dependencies(used_libraries, library->dependencies[i]); - } - } -} - -GPUFunction *gpu_material_library_use_function(GSet *used_libraries, const char *name) -{ - GPUFunction *function = BLI_ghash_lookup(FUNCTION_HASH, (const void *)name); - if (function) { - gpu_material_use_library_with_dependencies(used_libraries, function->library); - } - return function; -} - -char *gpu_material_library_generate_code(GSet *used_libraries, const char *frag_lib) -{ - DynStr *ds = BLI_dynstr_new(); - - if (frag_lib) { - BLI_dynstr_append(ds, frag_lib); - } - - /* Always include those because they may be needed by the execution function. */ - gpu_material_use_library_with_dependencies(used_libraries, - &gpu_shader_material_world_normals_library); - - /* Add library code in order, for dependencies. */ - for (int i = 0; gpu_material_libraries[i]; i++) { - GPUMaterialLibrary *library = gpu_material_libraries[i]; - if (BLI_gset_haskey(used_libraries, library->code)) { - BLI_dynstr_append(ds, library->code); - } - } - - char *result = BLI_dynstr_get_cstring(ds); - BLI_dynstr_free(ds); - - return result; -} diff --git a/source/blender/gpu/intern/gpu_material_library.h b/source/blender/gpu/intern/gpu_material_library.h index 96e1a265c72..ffc2228a49b 100644 --- a/source/blender/gpu/intern/gpu_material_library.h +++ b/source/blender/gpu/intern/gpu_material_library.h @@ -10,16 +10,15 @@ #include "GPU_material.h" +#ifdef __cplusplus +extern "C" { +#endif + #define MAX_FUNCTION_NAME 64 #define MAX_PARAMETER 36 struct GSet; -typedef struct GPUMaterialLibrary { - char *code; - struct GPUMaterialLibrary *dependencies[8]; -} GPUMaterialLibrary; - typedef enum { FUNCTION_QUAL_IN, FUNCTION_QUAL_OUT, @@ -31,20 +30,12 @@ typedef struct GPUFunction { eGPUType paramtype[MAX_PARAMETER]; GPUFunctionQual paramqual[MAX_PARAMETER]; int totparam; - GPUMaterialLibrary *library; + /* TOOD(@fclem): Clean that void pointer. */ + void *source; /* GPUSource */ } GPUFunction; -/* Module */ - -void gpu_material_library_init(void); -void gpu_material_library_exit(void); - -/* Code Generation */ - GPUFunction *gpu_material_library_use_function(struct GSet *used_libraries, const char *name); -char *gpu_material_library_generate_code(struct GSet *used_libraries, const char *frag_lib); - -/* Code Parsing */ -const char *gpu_str_skip_token(const char *str, char *token, int max); -const char *gpu_data_type_to_string(eGPUType type); +#ifdef __cplusplus +} +#endif diff --git a/source/blender/gpu/intern/gpu_node_graph.c b/source/blender/gpu/intern/gpu_node_graph.c index 57e415a8183..d9143a12d5b 100644 --- a/source/blender/gpu/intern/gpu_node_graph.c +++ b/source/blender/gpu/intern/gpu_node_graph.c @@ -87,10 +87,6 @@ static void gpu_node_input_link(GPUNode *node, GPUNodeLink *link, const eGPUType input->type = type; switch (link->link_type) { - case GPU_NODE_LINK_BUILTIN: - input->source = GPU_SOURCE_BUILTIN; - input->builtin = link->builtin; - break; case GPU_NODE_LINK_OUTPUT: input->source = GPU_SOURCE_OUTPUT; input->link = link; @@ -132,6 +128,11 @@ static void gpu_node_input_link(GPUNode *node, GPUNodeLink *link, const eGPUType case GPU_NODE_LINK_UNIFORM: input->source = GPU_SOURCE_UNIFORM; break; + case GPU_NODE_LINK_DIFFERENTIATE_FLOAT_FN: + input->source = GPU_SOURCE_FUNCTION_CALL; + /* NOTE(@fclem): End of function call is the return variable set during codegen. */ + SNPRINTF(input->function_call, "dF_branch(%s(), ", link->function_name); + break; default: break; } @@ -478,6 +479,11 @@ GPUNodeLink *GPU_attribute(GPUMaterial *mat, const CustomDataType type, const ch GPUNodeGraph *graph = gpu_material_node_graph(mat); GPUMaterialAttribute *attr = gpu_node_graph_add_attribute(graph, type, name); + if (type == CD_ORCO) { + /* OPTI: orco might be computed from local positions and needs object infos. */ + GPU_material_flag_set(mat, GPU_MATFLAG_OBJECT_INFO); + } + /* Dummy fallback if out of slots. */ if (attr == NULL) { static const float zero_data[GPU_MAX_CONSTANT_DATA] = {0.0f}; @@ -523,6 +529,14 @@ GPUNodeLink *GPU_uniform(const float *num) return link; } +GPUNodeLink *GPU_differentiate_float_function(const char *function_name) +{ + GPUNodeLink *link = gpu_node_link_create(); + link->link_type = GPU_NODE_LINK_DIFFERENTIATE_FLOAT_FN; + link->function_name = function_name; + return link; +} + GPUNodeLink *GPU_image(GPUMaterial *mat, Image *ima, ImageUser *iuser, @@ -588,41 +602,35 @@ GPUNodeLink *GPU_volume_grid(GPUMaterial *mat, transform_link->volume_grid = link->volume_grid; transform_link->volume_grid->users++; + GPUNodeLink *cos_link = GPU_attribute(mat, CD_ORCO, ""); + /* Two special cases, where we adjust the output values of smoke grids to * bring the into standard range without having to modify the grid values. */ if (STREQ(name, "color")) { - GPU_link(mat, "node_attribute_volume_color", link, transform_link, &link); + GPU_link(mat, "node_attribute_volume_color", link, transform_link, cos_link, &link); } else if (STREQ(name, "temperature")) { - GPU_link(mat, "node_attribute_volume_temperature", link, transform_link, &link); + GPU_link(mat, "node_attribute_volume_temperature", link, transform_link, cos_link, &link); } else { - GPU_link(mat, "node_attribute_volume", link, transform_link, &link); + GPU_link(mat, "node_attribute_volume", link, transform_link, cos_link, &link); } return link; } -GPUNodeLink *GPU_builtin(eGPUBuiltin builtin) -{ - GPUNodeLink *link = gpu_node_link_create(); - link->link_type = GPU_NODE_LINK_BUILTIN; - link->builtin = builtin; - return link; -} - /* Creating Nodes */ bool GPU_link(GPUMaterial *mat, const char *name, ...) { - GSet *used_libraries = gpu_material_used_libraries(mat); + GPUNodeGraph *graph = gpu_material_node_graph(mat); GPUNode *node; GPUFunction *function; GPUNodeLink *link, **linkptr; va_list params; int i; - function = gpu_material_library_use_function(used_libraries, name); + function = gpu_material_library_use_function(graph->used_libraries, name); if (!function) { fprintf(stderr, "GPU failed to find function %s\n", name); return false; @@ -643,27 +651,25 @@ bool GPU_link(GPUMaterial *mat, const char *name, ...) } va_end(params); - GPUNodeGraph *graph = gpu_material_node_graph(mat); BLI_addtail(&graph->nodes, node); return true; } -bool GPU_stack_link(GPUMaterial *material, - bNode *bnode, - const char *name, - GPUNodeStack *in, - GPUNodeStack *out, - ...) +static bool gpu_stack_link_v(GPUMaterial *material, + bNode *bnode, + const char *name, + GPUNodeStack *in, + GPUNodeStack *out, + va_list params) { - GSet *used_libraries = gpu_material_used_libraries(material); + GPUNodeGraph *graph = gpu_material_node_graph(material); GPUNode *node; GPUFunction *function; GPUNodeLink *link, **linkptr; - va_list params; int i, totin, totout; - function = gpu_material_library_use_function(used_libraries, name); + function = gpu_material_library_use_function(graph->used_libraries, name); if (!function) { fprintf(stderr, "GPU failed to find function %s\n", name); return false; @@ -691,7 +697,6 @@ bool GPU_stack_link(GPUMaterial *material, } } - va_start(params, out); for (i = 0; i < function->totparam; i++) { if (function->paramqual[i] != FUNCTION_QUAL_IN) { if (totout == 0) { @@ -717,14 +722,27 @@ bool GPU_stack_link(GPUMaterial *material, } } } - va_end(params); - GPUNodeGraph *graph = gpu_material_node_graph(material); BLI_addtail(&graph->nodes, node); return true; } +bool GPU_stack_link(GPUMaterial *material, + bNode *bnode, + const char *name, + GPUNodeStack *in, + GPUNodeStack *out, + ...) +{ + va_list params; + va_start(params, out); + bool valid = gpu_stack_link_v(material, bnode, name, in, out, params); + va_end(params); + + return valid; +} + GPUNodeLink *GPU_uniformbuf_link_out(GPUMaterial *mat, bNode *node, GPUNodeStack *stack, @@ -786,12 +804,16 @@ void gpu_node_graph_free_nodes(GPUNodeGraph *graph) gpu_node_free(node); } - graph->outlink = NULL; + graph->outlink_surface = NULL; + graph->outlink_volume = NULL; + graph->outlink_displacement = NULL; + graph->outlink_thickness = NULL; } void gpu_node_graph_free(GPUNodeGraph *graph) { BLI_freelistN(&graph->outlink_aovs); + BLI_freelistN(&graph->material_functions); gpu_node_graph_free_nodes(graph); LISTBASE_FOREACH (GPUMaterialVolumeGrid *, grid, &graph->volume_grids) { @@ -801,28 +823,32 @@ void gpu_node_graph_free(GPUNodeGraph *graph) BLI_freelistN(&graph->textures); BLI_freelistN(&graph->attributes); GPU_uniform_attr_list_free(&graph->uniform_attrs); + + if (graph->used_libraries) { + BLI_gset_free(graph->used_libraries, NULL); + graph->used_libraries = NULL; + } } /* Prune Unused Nodes */ -static void gpu_nodes_tag(GPUNodeLink *link) +static void gpu_nodes_tag(GPUNodeLink *link, eGPUNodeTag tag) { GPUNode *node; - GPUInput *input; - if (!link->output) { + if (!link || !link->output) { return; } node = link->output->node; - if (node->tag) { + if (node->tag & tag) { return; } - node->tag = true; - for (input = node->inputs.first; input; input = input->next) { + node->tag |= tag; + LISTBASE_FOREACH (GPUInput *, input, &node->inputs) { if (input->link) { - gpu_nodes_tag(input->link); + gpu_nodes_tag(input->link, tag); } } } @@ -830,18 +856,25 @@ static void gpu_nodes_tag(GPUNodeLink *link) void gpu_node_graph_prune_unused(GPUNodeGraph *graph) { LISTBASE_FOREACH (GPUNode *, node, &graph->nodes) { - node->tag = false; + node->tag = GPU_NODE_TAG_NONE; } - gpu_nodes_tag(graph->outlink); + gpu_nodes_tag(graph->outlink_surface, GPU_NODE_TAG_SURFACE); + gpu_nodes_tag(graph->outlink_volume, GPU_NODE_TAG_VOLUME); + gpu_nodes_tag(graph->outlink_displacement, GPU_NODE_TAG_DISPLACEMENT); + gpu_nodes_tag(graph->outlink_thickness, GPU_NODE_TAG_THICKNESS); + LISTBASE_FOREACH (GPUNodeGraphOutputLink *, aovlink, &graph->outlink_aovs) { - gpu_nodes_tag(aovlink->outlink); + gpu_nodes_tag(aovlink->outlink, GPU_NODE_TAG_AOV); + } + LISTBASE_FOREACH (GPUNodeGraphFunctionLink *, funclink, &graph->material_functions) { + gpu_nodes_tag(funclink->outlink, GPU_NODE_TAG_FUNCTION); } for (GPUNode *node = graph->nodes.first, *next = NULL; node; node = next) { next = node->next; - if (!node->tag) { + if (node->tag == GPU_NODE_TAG_NONE) { BLI_remlink(&graph->nodes, node); gpu_node_free(node); } diff --git a/source/blender/gpu/intern/gpu_node_graph.h b/source/blender/gpu/intern/gpu_node_graph.h index 5856001d548..024119e1c24 100644 --- a/source/blender/gpu/intern/gpu_node_graph.h +++ b/source/blender/gpu/intern/gpu_node_graph.h @@ -12,9 +12,15 @@ #include "DNA_customdata_types.h" #include "DNA_listBase.h" +#include "BLI_ghash.h" + #include "GPU_material.h" #include "GPU_shader.h" +#ifdef __cplusplus +extern "C" { +#endif + struct GPUNode; struct GPUOutput; struct ListBase; @@ -25,19 +31,18 @@ typedef enum eGPUDataSource { GPU_SOURCE_UNIFORM, GPU_SOURCE_ATTR, GPU_SOURCE_UNIFORM_ATTR, - GPU_SOURCE_BUILTIN, GPU_SOURCE_STRUCT, GPU_SOURCE_TEX, GPU_SOURCE_TEX_TILED_MAPPING, GPU_SOURCE_VOLUME_GRID, GPU_SOURCE_VOLUME_GRID_TRANSFORM, + GPU_SOURCE_FUNCTION_CALL, } eGPUDataSource; typedef enum { GPU_NODE_LINK_NONE = 0, GPU_NODE_LINK_ATTR, GPU_NODE_LINK_UNIFORM_ATTR, - GPU_NODE_LINK_BUILTIN, GPU_NODE_LINK_COLORBAND, GPU_NODE_LINK_CONSTANT, GPU_NODE_LINK_IMAGE, @@ -47,15 +52,28 @@ typedef enum { GPU_NODE_LINK_VOLUME_GRID_TRANSFORM, GPU_NODE_LINK_OUTPUT, GPU_NODE_LINK_UNIFORM, + GPU_NODE_LINK_DIFFERENTIATE_FLOAT_FN, } GPUNodeLinkType; +typedef enum { + GPU_NODE_TAG_NONE = 0, + GPU_NODE_TAG_SURFACE = (1 << 0), + GPU_NODE_TAG_VOLUME = (1 << 1), + GPU_NODE_TAG_DISPLACEMENT = (1 << 2), + GPU_NODE_TAG_THICKNESS = (1 << 3), + GPU_NODE_TAG_AOV = (1 << 4), + GPU_NODE_TAG_FUNCTION = (1 << 5), +} eGPUNodeTag; + +ENUM_OPERATORS(eGPUNodeTag, GPU_NODE_TAG_FUNCTION) + struct GPUNode { struct GPUNode *next, *prev; const char *name; /* Internal flag to mark nodes during pruning */ - bool tag; + eGPUNodeTag tag; ListBase inputs; ListBase outputs; @@ -70,8 +88,6 @@ struct GPUNodeLink { union { /* GPU_NODE_LINK_CONSTANT | GPU_NODE_LINK_UNIFORM */ const float *data; - /* GPU_NODE_LINK_BUILTIN */ - eGPUBuiltin builtin; /* GPU_NODE_LINK_COLORBAND */ struct GPUTexture **colorband; /* GPU_NODE_LINK_VOLUME_GRID */ @@ -84,6 +100,8 @@ struct GPUNodeLink { struct GPUUniformAttr *uniform_attr; /* GPU_NODE_LINK_IMAGE_BLENDER */ struct GPUMaterialTexture *texture; + /* GPU_NODE_LINK_DIFFERENTIATE_FLOAT_FN */ + const char *function_name; }; }; @@ -110,8 +128,6 @@ typedef struct GPUInput { union { /* GPU_SOURCE_CONSTANT | GPU_SOURCE_UNIFORM */ float vec[16]; /* vector data */ - /* GPU_SOURCE_BUILTIN */ - eGPUBuiltin builtin; /* builtin uniform */ /* GPU_SOURCE_TEX | GPU_SOURCE_TEX_TILED_MAPPING */ struct GPUMaterialTexture *texture; /* GPU_SOURCE_ATTR */ @@ -120,6 +136,8 @@ typedef struct GPUInput { struct GPUUniformAttr *uniform_attr; /* GPU_SOURCE_VOLUME_GRID | GPU_SOURCE_VOLUME_GRID_TRANSFORM */ struct GPUMaterialVolumeGrid *volume_grid; + /* GPU_SOURCE_FUNCTION_CALL */ + char function_call[64]; }; } GPUInput; @@ -129,14 +147,25 @@ typedef struct GPUNodeGraphOutputLink { GPUNodeLink *outlink; } GPUNodeGraphOutputLink; +typedef struct GPUNodeGraphFunctionLink { + struct GPUNodeGraphFunctionLink *next, *prev; + char name[16]; + GPUNodeLink *outlink; +} GPUNodeGraphFunctionLink; + typedef struct GPUNodeGraph { /* Nodes */ ListBase nodes; - /* Main Output. */ - GPUNodeLink *outlink; + /* Main Outputs. */ + GPUNodeLink *outlink_surface; + GPUNodeLink *outlink_volume; + GPUNodeLink *outlink_displacement; + GPUNodeLink *outlink_thickness; /* List of GPUNodeGraphOutputLink */ ListBase outlink_aovs; + /* List of GPUNodeGraphFunctionLink */ + ListBase material_functions; /* Requested attributes and textures. */ ListBase attributes; @@ -145,6 +174,9 @@ typedef struct GPUNodeGraph { /* The list of uniform attributes. */ GPUUniformAttrList uniform_attrs; + + /** Set of all the GLSL lib code blocks . */ + GSet *used_libraries; } GPUNodeGraph; /* Node Graph */ @@ -171,4 +203,6 @@ struct GPUTexture **gpu_material_ramp_texture_row_set(struct GPUMaterial *mat, float *pixels, float *row); -struct GSet *gpu_material_used_libraries(struct GPUMaterial *material); +#ifdef __cplusplus +} +#endif diff --git a/source/blender/gpu/intern/gpu_shader.cc b/source/blender/gpu/intern/gpu_shader.cc index b434cfbbb0e..fe9aacb95f9 100644 --- a/source/blender/gpu/intern/gpu_shader.cc +++ b/source/blender/gpu/intern/gpu_shader.cc @@ -249,6 +249,19 @@ const GPUShaderCreateInfo *GPU_shader_create_info_get(const char *info_name) return gpu_shader_create_info_get(info_name); } +bool GPU_shader_create_info_check_error(const GPUShaderCreateInfo *_info, char r_error[128]) +{ + using namespace blender::gpu::shader; + const ShaderCreateInfo &info = *reinterpret_cast<const ShaderCreateInfo *>(_info); + std::string error = info.check_error(); + if (error.length() == 0) { + return true; + } + + BLI_strncpy(r_error, error.c_str(), 128); + return false; +} + GPUShader *GPU_shader_create_from_info_name(const char *info_name) { using namespace blender::gpu::shader; @@ -270,28 +283,10 @@ GPUShader *GPU_shader_create_from_info(const GPUShaderCreateInfo *_info) GPU_debug_group_begin(GPU_DEBUG_SHADER_COMPILATION_GROUP); - /* At least a vertex shader and a fragment shader are required, or only a compute shader. */ - if (info.compute_source_.is_empty()) { - if (info.vertex_source_.is_empty()) { - printf("Missing vertex shader in %s.\n", info.name_.c_str()); - } - if (info.fragment_source_.is_empty()) { - printf("Missing fragment shader in %s.\n", info.name_.c_str()); - } - BLI_assert(!info.vertex_source_.is_empty() && !info.fragment_source_.is_empty()); - } - else { - if (!info.vertex_source_.is_empty()) { - printf("Compute shader has vertex_source_ shader attached in %s.\n", info.name_.c_str()); - } - if (!info.geometry_source_.is_empty()) { - printf("Compute shader has geometry_source_ shader attached in %s.\n", info.name_.c_str()); - } - if (!info.fragment_source_.is_empty()) { - printf("Compute shader has fragment_source_ shader attached in %s.\n", info.name_.c_str()); - } - BLI_assert(info.vertex_source_.is_empty() && info.geometry_source_.is_empty() && - info.fragment_source_.is_empty()); + const std::string error = info.check_error(); + if (!error.empty()) { + printf("%s\n", error.c_str()); + BLI_assert(false); } Shader *shader = GPUBackend::get()->shader_alloc(info.name_.c_str()); @@ -299,7 +294,9 @@ GPUShader *GPU_shader_create_from_info(const GPUShaderCreateInfo *_info) std::string defines = shader->defines_declare(info); std::string resources = shader->resources_declare(info); - defines += "#define USE_GPU_SHADER_CREATE_INFO\n"; + if (info.legacy_resource_location_ == false) { + defines += "#define USE_GPU_SHADER_CREATE_INFO\n"; + } Vector<const char *> typedefs; if (!info.typedef_sources_.is_empty() || !info.typedef_source_generated.empty()) { @@ -367,6 +364,7 @@ GPUShader *GPU_shader_create_from_info(const GPUShaderCreateInfo *_info) sources.append(resources.c_str()); sources.append(layout.c_str()); sources.append(interface.c_str()); + sources.append(info.geometry_source_generated.c_str()); sources.extend(code); shader->geometry_shader_from_glsl(sources); diff --git a/source/blender/gpu/intern/gpu_shader_builder_stubs.cc b/source/blender/gpu/intern/gpu_shader_builder_stubs.cc index 95f9b031fe8..515f65adb73 100644 --- a/source/blender/gpu/intern/gpu_shader_builder_stubs.cc +++ b/source/blender/gpu/intern/gpu_shader_builder_stubs.cc @@ -202,10 +202,7 @@ int BKE_subdiv_ccg_grid_to_face_index(const SubdivCCG *UNUSED(subdiv_ccg), /* -------------------------------------------------------------------- */ /** \name Stubs of BKE_node.h * \{ */ -void ntreeGPUMaterialNodes(struct bNodeTree *UNUSED(localtree), - struct GPUMaterial *UNUSED(mat), - bool *UNUSED(has_surface_output), - bool *UNUSED(has_volume_output)) +void ntreeGPUMaterialNodes(struct bNodeTree *UNUSED(localtree), struct GPUMaterial *UNUSED(mat)) { BLI_assert_unreachable(); } diff --git a/source/blender/gpu/intern/gpu_shader_create_info.cc b/source/blender/gpu/intern/gpu_shader_create_info.cc index 0dd82d4ea44..6e82201b424 100644 --- a/source/blender/gpu/intern/gpu_shader_create_info.cc +++ b/source/blender/gpu/intern/gpu_shader_create_info.cc @@ -136,6 +136,34 @@ void ShaderCreateInfo::finalize() } } +std::string ShaderCreateInfo::check_error() const +{ + std::string error; + + /* At least a vertex shader and a fragment shader are required, or only a compute shader. */ + if (this->compute_source_.is_empty()) { + if (this->vertex_source_.is_empty()) { + error += "Missing vertex shader in " + this->name_ + ".\n"; + } + if (this->fragment_source_.is_empty()) { + error += "Missing fragment shader in " + this->name_ + ".\n"; + } + } + else { + if (!this->vertex_source_.is_empty()) { + error += "Compute shader has vertex_source_ shader attached in" + this->name_ + ".\n"; + } + if (!this->geometry_source_.is_empty()) { + error += "Compute shader has geometry_source_ shader attached in" + this->name_ + ".\n"; + } + if (!this->fragment_source_.is_empty()) { + error += "Compute shader has fragment_source_ shader attached in" + this->name_ + ".\n"; + } + } + + return error; +} + void ShaderCreateInfo::validate(const ShaderCreateInfo &other_info) { if (!auto_resource_location_) { diff --git a/source/blender/gpu/intern/gpu_shader_create_info.hh b/source/blender/gpu/intern/gpu_shader_create_info.hh index 51008993353..3ab96d0d84a 100644 --- a/source/blender/gpu/intern/gpu_shader_create_info.hh +++ b/source/blender/gpu/intern/gpu_shader_create_info.hh @@ -284,6 +284,8 @@ struct ShaderCreateInfo { bool auto_resource_location_ = false; /** If true, force depth and stencil tests to always happen before fragment shader invocation. */ bool early_fragment_test_ = false; + /** If true, force the use of the GL shader introspection for resource location. */ + bool legacy_resource_location_ = false; /** Allow optimization when fragment shader writes to `gl_FragDepth`. */ DepthWrite depth_write_ = DepthWrite::ANY; /** @@ -296,6 +298,7 @@ struct ShaderCreateInfo { /** Manually set generated code. */ std::string vertex_source_generated = ""; std::string fragment_source_generated = ""; + std::string geometry_source_generated = ""; std::string typedef_source_generated = ""; /** Manually set generated dependencies. */ Vector<const char *, 0> dependencies_generated; @@ -721,6 +724,12 @@ struct ShaderCreateInfo { return *(Self *)this; } + Self &legacy_resource_location(bool value) + { + legacy_resource_location_ = value; + return *(Self *)this; + } + /** \} */ /* -------------------------------------------------------------------- */ @@ -787,6 +796,8 @@ struct ShaderCreateInfo { /* WARNING: Recursive. */ void finalize(); + std::string check_error() const; + /** Error detection that some backend compilers do not complain about. */ void validate(const ShaderCreateInfo &other_info); diff --git a/source/blender/gpu/intern/gpu_shader_dependency.cc b/source/blender/gpu/intern/gpu_shader_dependency.cc index 2c6845334d3..460b6d32967 100644 --- a/source/blender/gpu/intern/gpu_shader_dependency.cc +++ b/source/blender/gpu/intern/gpu_shader_dependency.cc @@ -10,11 +10,14 @@ #include <iomanip> #include <iostream> +#include <sstream> +#include "BLI_ghash.h" #include "BLI_map.hh" #include "BLI_set.hh" #include "BLI_string_ref.hh" +#include "gpu_material_library.h" #include "gpu_shader_create_info.hh" #include "gpu_shader_dependency_private.h" @@ -31,6 +34,7 @@ extern "C" { namespace blender::gpu { using GPUSourceDictionnary = Map<StringRef, struct GPUSource *>; +using GPUFunctionDictionnary = Map<StringRef, struct GPUFunction *>; struct GPUSource { StringRefNull fullpath; @@ -41,7 +45,10 @@ struct GPUSource { shader::BuiltinBits builtins = (shader::BuiltinBits)0; std::string processed_source; - GPUSource(const char *path, const char *file, const char *datatoc) + GPUSource(const char *path, + const char *file, + const char *datatoc, + GPUFunctionDictionnary *g_functions) : fullpath(path), filename(file), source(datatoc) { /* Scan for builtins. */ @@ -92,16 +99,20 @@ struct GPUSource { if (filename.endswith(".h") || filename.endswith(".hh")) { enum_preprocess(); } + + if (is_from_material_library()) { + material_functions_parse(g_functions); + } }; - bool is_in_comment(const StringRef &input, int64_t offset) + static bool is_in_comment(const StringRef &input, int64_t offset) { return (input.rfind("/*", offset) > input.rfind("*/", offset)) || (input.rfind("//", offset) > input.rfind("\n", offset)); } template<bool check_whole_word = true, bool reversed = false, typename T> - int64_t find_str(const StringRef &input, const T keyword, int64_t offset = 0) + static int64_t find_str(const StringRef &input, const T keyword, int64_t offset = 0) { while (true) { if constexpr (reversed) { @@ -114,7 +125,7 @@ struct GPUSource { if constexpr (check_whole_word) { /* Fix false positive if something has "enum" as suffix. */ char previous_char = input[offset - 1]; - if (!(ELEM(previous_char, '\n', '\t', ' ', ':'))) { + if (!(ELEM(previous_char, '\n', '\t', ' ', ':', '(', ','))) { offset += (reversed) ? -1 : 1; continue; } @@ -129,9 +140,13 @@ struct GPUSource { } } +#define find_keyword find_str<true, false> +#define rfind_keyword find_str<true, true> +#define find_token find_str<false, false> +#define rfind_token find_str<false, true> + void print_error(const StringRef &input, int64_t offset, const StringRef message) { - std::cout << " error: " << message << "\n"; StringRef sub = input.substr(0, offset); int64_t line_number = std::count(sub.begin(), sub.end(), '\n') + 1; int64_t line_end = input.find("\n", offset); @@ -152,6 +167,12 @@ struct GPUSource { std::cout << "^\n"; } +#define CHECK(test_value, str, ofs, msg) \ + if ((test_value) == -1) { \ + print_error(str, ofs, msg); \ + continue; \ + } + /** * Transform C,C++ enum declaration into GLSL compatible defines and constants: * @@ -193,16 +214,6 @@ struct GPUSource { int64_t last_pos = 0; const bool is_cpp = filename.endswith(".hh"); -#define find_keyword find_str<true, false> -#define find_token find_str<false, false> -#define rfind_token find_str<false, true> -#define CHECK(test_value, str, ofs, msg) \ - if ((test_value) == -1) { \ - print_error(str, ofs, msg); \ - cursor++; \ - continue; \ - } - while (true) { cursor = find_keyword(input, "enum ", cursor + 1); if (cursor == -1) { @@ -268,10 +279,6 @@ struct GPUSource { return; } -#undef find_keyword -#undef find_token -#undef rfind_token - if (last_pos != 0) { output += input.substr(last_pos); } @@ -279,37 +286,237 @@ struct GPUSource { source = processed_source.c_str(); }; + void material_functions_parse(GPUFunctionDictionnary *g_functions) + { + const StringRefNull input = source; + + const char whitespace_chars[] = " \r\n\t"; + + auto function_parse = [&](const StringRef input, + int64_t &cursor, + StringRef &out_return_type, + StringRef &out_name, + StringRef &out_args) -> bool { + cursor = find_keyword(input, "void ", cursor + 1); + if (cursor == -1) { + return false; + } + int64_t arg_start = find_token(input, '(', cursor); + if (arg_start == -1) { + return false; + } + int64_t arg_end = find_token(input, ')', arg_start); + if (arg_end == -1) { + return false; + } + int64_t body_start = find_token(input, '{', arg_end); + int64_t next_semicolon = find_token(input, ';', arg_end); + if (body_start != -1 && next_semicolon != -1 && body_start > next_semicolon) { + /* Assert no prototypes but could also just skip them. */ + BLI_assert_msg(false, "No prototypes allowed in node GLSL libraries."); + } + int64_t name_start = input.find_first_not_of(whitespace_chars, input.find(' ', cursor)); + if (name_start == -1) { + return false; + } + int64_t name_end = input.find_last_not_of(whitespace_chars, arg_start); + if (name_end == -1) { + return false; + } + /* Only support void type for now. */ + out_return_type = "void"; + out_name = input.substr(name_start, name_end - name_start); + out_args = input.substr(arg_start + 1, arg_end - (arg_start + 1)); + return true; + }; + + auto keyword_parse = [&](const StringRef str, int64_t &cursor) -> StringRef { + int64_t keyword_start = str.find_first_not_of(whitespace_chars, cursor); + if (keyword_start == -1) { + /* No keyword found. */ + return str.substr(0, 0); + } + int64_t keyword_end = str.find_first_of(whitespace_chars, keyword_start); + if (keyword_end == -1) { + /* Last keyword. */ + keyword_end = str.size(); + } + cursor = keyword_end + 1; + return str.substr(keyword_start, keyword_end - keyword_start); + }; + + auto arg_parse = [&](const StringRef str, + int64_t &cursor, + StringRef &out_qualifier, + StringRef &out_type, + StringRef &out_name) -> bool { + int64_t arg_start = cursor + 1; + if (arg_start >= str.size()) { + return false; + } + cursor = find_token(str, ',', arg_start); + if (cursor == -1) { + /* Last argument. */ + cursor = str.size(); + } + const StringRef arg = str.substr(arg_start, cursor - arg_start); + + int64_t keyword_cursor = 0; + out_qualifier = keyword_parse(arg, keyword_cursor); + out_type = keyword_parse(arg, keyword_cursor); + out_name = keyword_parse(arg, keyword_cursor); + if (out_name.is_empty()) { + /* No qualifier case. */ + out_name = out_type; + out_type = out_qualifier; + out_qualifier = arg.substr(0, 0); + } + return true; + }; + + int64_t cursor = -1; + StringRef func_return_type, func_name, func_args; + while (function_parse(input, cursor, func_return_type, func_name, func_args)) { + GPUFunction *func = MEM_new<GPUFunction>(__func__); + func_name.copy(func->name, sizeof(func->name)); + func->source = reinterpret_cast<void *>(this); + + bool insert = g_functions->add(func->name, func); + + /* NOTE: We allow overloading non void function, but only if the function comes from the + * same file. Otherwise the dependency system breaks. */ + if (!insert) { + GPUSource *other_source = reinterpret_cast<GPUSource *>( + g_functions->lookup(func_name)->source); + if (other_source != this) { + print_error(input, + source.find(func_name), + "Function redefinition or overload in two different files ..."); + print_error( + input, other_source->source.find(func_name), "... previous definition was here"); + } + else { + /* Non-void function overload. */ + MEM_delete(func); + } + continue; + } + + if (func_return_type != "void") { + continue; + } + + func->totparam = 0; + int64_t args_cursor = -1; + StringRef arg_qualifier, arg_type, arg_name; + while (arg_parse(func_args, args_cursor, arg_qualifier, arg_type, arg_name)) { + + if (func->totparam >= ARRAY_SIZE(func->paramtype)) { + print_error(input, source.find(func_name), "Too much parameter in function"); + break; + } + + auto parse_qualifier = [](StringRef qualifier) -> GPUFunctionQual { + if (qualifier == "out") { + return FUNCTION_QUAL_OUT; + } + if (qualifier == "inout") { + return FUNCTION_QUAL_INOUT; + } + return FUNCTION_QUAL_IN; + }; + + auto parse_type = [](StringRef type) -> eGPUType { + if (type == "float") { + return GPU_FLOAT; + } + if (type == "vec2") { + return GPU_VEC2; + } + if (type == "vec3") { + return GPU_VEC3; + } + if (type == "vec4") { + return GPU_VEC4; + } + if (type == "mat3") { + return GPU_MAT3; + } + if (type == "mat4") { + return GPU_MAT4; + } + if (type == "sampler1DArray") { + return GPU_TEX1D_ARRAY; + } + if (type == "sampler2DArray") { + return GPU_TEX2D_ARRAY; + } + if (type == "sampler2D") { + return GPU_TEX2D; + } + if (type == "sampler3D") { + return GPU_TEX3D; + } + if (type == "Closure") { + return GPU_CLOSURE; + } + return GPU_NONE; + }; + + func->paramqual[func->totparam] = parse_qualifier(arg_qualifier); + func->paramtype[func->totparam] = parse_type(arg_type); + + if (func->paramtype[func->totparam] == GPU_NONE) { + std::string err = "Unknown parameter type \"" + arg_type + "\""; + int64_t err_ofs = source.find(func_name); + err_ofs = find_keyword(source, arg_name, err_ofs); + err_ofs = rfind_keyword(source, arg_type, err_ofs); + print_error(input, err_ofs, err); + } + + func->totparam++; + } + } + } + +#undef find_keyword +#undef rfind_keyword +#undef find_token +#undef rfind_token + /* Return 1 one error. */ - int init_dependencies(const GPUSourceDictionnary &dict) + int init_dependencies(const GPUSourceDictionnary &dict, + const GPUFunctionDictionnary &g_functions) { - if (dependencies_init) { + if (this->dependencies_init) { return 0; } - dependencies_init = true; - int64_t pos = 0; + this->dependencies_init = true; + int64_t pos = -1; + while (true) { - pos = source.find("pragma BLENDER_REQUIRE(", pos); - if (pos == -1) { - return 0; - } - int64_t start = source.find('(', pos) + 1; - int64_t end = source.find(')', pos); - if (end == -1) { - /* TODO Use clog. */ - std::cout << "Error: " << filename << " : Malformed BLENDER_REQUIRE: Missing \")\"." - << std::endl; - return 1; - } - StringRef dependency_name = source.substr(start, end - start); - GPUSource *dependency_source = dict.lookup_default(dependency_name, nullptr); - if (dependency_source == nullptr) { - /* TODO Use clog. */ - std::cout << "Error: " << filename << " : Dependency not found \"" << dependency_name - << "\"." << std::endl; - return 1; + GPUSource *dependency_source = nullptr; + + { + pos = source.find("pragma BLENDER_REQUIRE(", pos + 1); + if (pos == -1) { + return 0; + } + int64_t start = source.find('(', pos) + 1; + int64_t end = source.find(')', pos); + if (end == -1) { + print_error(source, start, "Malformed BLENDER_REQUIRE: Missing \")\" token"); + return 1; + } + StringRef dependency_name = source.substr(start, end - start); + dependency_source = dict.lookup_default(dependency_name, nullptr); + if (dependency_source == nullptr) { + print_error(source, start, "Dependency not found"); + return 1; + } } /* Recursive. */ - int result = dependency_source->init_dependencies(dict); + int result = dependency_source->init_dependencies(dict, g_functions); if (result != 0) { return 1; } @@ -318,8 +525,8 @@ struct GPUSource { dependencies.append_non_duplicates(dep); } dependencies.append_non_duplicates(dependency_source); - pos++; - }; + } + return 0; } /* Returns the final string with all includes done. */ @@ -339,6 +546,11 @@ struct GPUSource { } return out_builtins; } + + bool is_from_material_library() const + { + return filename.startswith("gpu_shader_material_") && filename.endswith(".glsl"); + } }; } // namespace blender::gpu @@ -346,13 +558,15 @@ struct GPUSource { using namespace blender::gpu; static GPUSourceDictionnary *g_sources = nullptr; +static GPUFunctionDictionnary *g_functions = nullptr; void gpu_shader_dependency_init() { g_sources = new GPUSourceDictionnary(); + g_functions = new GPUFunctionDictionnary(); #define SHADER_SOURCE(datatoc, filename, filepath) \ - g_sources->add_new(filename, new GPUSource(filepath, filename, datatoc)); + g_sources->add_new(filename, new GPUSource(filepath, filename, datatoc, g_functions)); #include "glsl_draw_source_list.h" #include "glsl_gpu_source_list.h" #ifdef WITH_OCIO @@ -362,7 +576,7 @@ void gpu_shader_dependency_init() int errors = 0; for (auto *value : g_sources->values()) { - errors += value->init_dependencies(*g_sources); + errors += value->init_dependencies(*g_sources, *g_functions); } BLI_assert_msg(errors == 0, "Dependency errors detected: Aborting"); UNUSED_VARS_NDEBUG(errors); @@ -373,7 +587,20 @@ void gpu_shader_dependency_exit() for (auto *value : g_sources->values()) { delete value; } + for (auto *value : g_functions->values()) { + MEM_delete(value); + } delete g_sources; + delete g_functions; +} + +GPUFunction *gpu_material_library_use_function(GSet *used_libraries, const char *name) +{ + GPUFunction *function = g_functions->lookup_default(name, nullptr); + BLI_assert_msg(function != nullptr, "Requested function not in the function library"); + GPUSource *source = reinterpret_cast<GPUSource *>(function->source); + BLI_gset_add(used_libraries, const_cast<char *>(source->filename.c_str())); + return function; } namespace blender::gpu::shader { diff --git a/source/blender/gpu/opengl/gl_shader.cc b/source/blender/gpu/opengl/gl_shader.cc index 5938444ce49..9768318f3b6 100644 --- a/source/blender/gpu/opengl/gl_shader.cc +++ b/source/blender/gpu/opengl/gl_shader.cc @@ -572,10 +572,13 @@ std::string GLShader::fragment_interface_declare(const ShaderCreateInfo &info) c } if (bool(info.builtins_ & BuiltinBits::BARYCENTRIC_COORD)) { if (!GLContext::native_barycentric_support) { + ss << "flat in vec4 gpu_pos[3];\n"; ss << "smooth in vec3 gpu_BaryCoord;\n"; ss << "noperspective in vec3 gpu_BaryCoordNoPersp;\n"; + ss << "#define gpu_position_at_vertex(v) gpu_pos[v]\n"; } else if (GLEW_AMD_shader_explicit_vertex_parameter) { + std::cout << "native" << std::endl; /* NOTE(fclem): This won't work with geometry shader. Hopefully, we don't need geometry * shader workaround if this extension/feature is detected. */ ss << "\n/* Stable Barycentric Coordinates. */\n"; @@ -591,6 +594,12 @@ std::string GLShader::fragment_interface_declare(const ShaderCreateInfo &info) c ss << " if (interpolateAtVertexAMD(gpu_pos, 2) == gpu_pos_flat) { return bary.yzx; }\n"; ss << " return bary.xyz;\n"; ss << "}\n"; + ss << "\n"; + ss << "vec4 gpu_position_at_vertex(int v) {\n"; + ss << " if (interpolateAtVertexAMD(gpu_pos, 0) == gpu_pos_flat) { v = (v + 2) % 3; }\n"; + ss << " if (interpolateAtVertexAMD(gpu_pos, 2) == gpu_pos_flat) { v = (v + 1) % 3; }\n"; + ss << " return interpolateAtVertexAMD(gpu_pos, v);\n"; + ss << "}\n"; pre_main += " gpu_BaryCoord = stable_bary_(gl_BaryCoordSmoothAMD);\n"; pre_main += " gpu_BaryCoordNoPersp = stable_bary_(gl_BaryCoordNoPerspAMD);\n"; @@ -730,6 +739,7 @@ std::string GLShader::workaround_geometry_shader_source_create( ss << "in int gpu_Layer[];\n"; } if (do_barycentric_workaround) { + ss << "flat out vec4 gpu_pos[3];\n"; ss << "smooth out vec3 gpu_BaryCoord;\n"; ss << "noperspective out vec3 gpu_BaryCoordNoPersp;\n"; } @@ -740,6 +750,11 @@ std::string GLShader::workaround_geometry_shader_source_create( if (do_layer_workaround) { ss << " gl_Layer = gpu_Layer[0];\n"; } + if (do_barycentric_workaround) { + ss << " gpu_pos[0] = gl_in[0].gl_Position;\n"; + ss << " gpu_pos[1] = gl_in[1].gl_Position;\n"; + ss << " gpu_pos[2] = gl_in[2].gl_Position;\n"; + } for (auto i : IndexRange(3)) { for (StageInterfaceInfo *iface : info_modified.vertex_out_interfaces_) { for (auto &inout : iface->inouts) { @@ -977,7 +992,7 @@ bool GLShader::finalize(const shader::ShaderCreateInfo *info) return false; } - if (info != nullptr) { + if (info != nullptr && info->legacy_resource_location_ == false) { interface = new GLShaderInterface(shader_program_, *info); } else { diff --git a/source/blender/gpu/opengl/gl_texture.cc b/source/blender/gpu/opengl/gl_texture.cc index b78e30e8b4c..14f84273925 100644 --- a/source/blender/gpu/opengl/gl_texture.cc +++ b/source/blender/gpu/opengl/gl_texture.cc @@ -72,7 +72,7 @@ bool GLTexture::init_internal() GLenum internal_format = to_gl_internal_format(format_); const bool is_cubemap = bool(type_ == GPU_TEXTURE_CUBE); const bool is_layered = bool(type_ & GPU_TEXTURE_ARRAY); - const bool is_compressed = bool(format_flag_ & GPU_TEXTURE_ARRAY); + const bool is_compressed = bool(format_flag_ & GPU_FORMAT_COMPRESSED); const int dimensions = (is_cubemap) ? 2 : this->dimensions_count(); GLenum gl_format = to_gl_data_format(format_); GLenum gl_type = to_gl(to_data_format(format_)); diff --git a/source/blender/gpu/shaders/gpu_shader_codegen_lib.glsl b/source/blender/gpu/shaders/gpu_shader_codegen_lib.glsl index 193a4190cbf..5c97eada77d 100644 --- a/source/blender/gpu/shaders/gpu_shader_codegen_lib.glsl +++ b/source/blender/gpu/shaders/gpu_shader_codegen_lib.glsl @@ -95,3 +95,176 @@ vec4 tangent_get(vec4 attr, mat3 normalmat) } #endif + +/* Assumes GPU_VEC4 is color data. So converting to luminance like cycles. */ +#define float_from_vec4(v) dot(v.rgb, vec3(0.2126, 0.7152, 0.0722)) +#define float_from_vec3(v) avg(v.rgb) +#define float_from_vec2(v) v.r + +#define vec2_from_vec4(v) vec2(avg(v.rgb), v.a) +#define vec2_from_vec3(v) vec2(avg(v.rgb), 1.0) +#define vec2_from_float(v) vec2(v) + +#define vec3_from_vec4(v) v.rgb +#define vec3_from_vec2(v) v.rrr +#define vec3_from_float(v) vec3(v) + +#define vec4_from_vec3(v) vec4(v, 1.0) +#define vec4_from_vec2(v) v.rrrg +#define vec4_from_float(v) vec4(vec3(v), 1.0) + +/* TODO: Move to shader_shared. */ +#define RAY_TYPE_CAMERA 0 +#define RAY_TYPE_SHADOW 1 +#define RAY_TYPE_DIFFUSE 2 +#define RAY_TYPE_GLOSSY 3 + +#ifdef GPU_FRAGMENT_SHADER +# define FrontFacing gl_FrontFacing +#else +# define FrontFacing true +#endif + +struct ClosureDiffuse { + float weight; + vec3 color; + vec3 N; + vec3 sss_radius; + uint sss_id; +}; + +struct ClosureTranslucent { + float weight; + vec3 color; + vec3 N; +}; + +struct ClosureReflection { + float weight; + vec3 color; + vec3 N; + float roughness; +}; + +struct ClosureRefraction { + float weight; + vec3 color; + vec3 N; + float roughness; + float ior; +}; + +struct ClosureHair { + float weight; + vec3 color; + float offset; + vec2 roughness; + vec3 T; +}; + +struct ClosureVolumeScatter { + float weight; + vec3 scattering; + float anisotropy; +}; + +struct ClosureVolumeAbsorption { + float weight; + vec3 absorption; +}; + +struct ClosureEmission { + float weight; + vec3 emission; +}; + +struct ClosureTransparency { + float weight; + vec3 transmittance; + float holdout; +}; + +struct GlobalData { + /** World position. */ + vec3 P; + /** Surface Normal. */ + vec3 N; + /** Geometric Normal. */ + vec3 Ng; + /** Surface default Tangent. */ + vec3 T; + /** Barycentric coordinates. */ + vec2 barycentric_coords; + vec3 barycentric_dists; + /** Ray properties (approximation). */ + int ray_type; + float ray_depth; + float ray_length; + /** Hair time along hair length. 0 at base 1 at tip. */ + float hair_time; + /** Hair time along width of the hair. */ + float hair_time_width; + /** Hair thickness in world space. */ + float hair_thickness; + /** Index of the strand for per strand effects. */ + int hair_strand_id; + /** Is hair. */ + bool is_strand; +}; + +GlobalData g_data; + +#ifndef GPU_FRAGMENT_SHADER +/* Stubs. */ +vec3 dF_impl(vec3 v) +{ + return vec3(0.0); +} + +void dF_branch(float fn, out vec2 result) +{ + result = vec2(0.0); +} + +#elif 0 /* TODO(@fclem): User Option? */ +/* Fast derivatives */ +vec3 dF_impl(vec3 v) +{ + return vec3(0.0); +} + +void dF_branch(float fn, out vec2 result) +{ + result.x = DFDX_SIGN * dFdx(fn); + result.y = DFDY_SIGN * dFdy(fn); +} + +#else +/* Precise derivatives */ +int g_derivative_flag = 0; + +vec3 dF_impl(vec3 v) +{ + if (g_derivative_flag > 0) { + return DFDX_SIGN * dFdx(v); + } + else if (g_derivative_flag < 0) { + return DFDY_SIGN * dFdy(v); + } + return vec3(0.0); +} + +# define dF_branch(fn, result) \ + if (true) { \ + g_derivative_flag = 1; \ + result.x = (fn); \ + g_derivative_flag = -1; \ + result.y = (fn); \ + g_derivative_flag = 0; \ + result -= vec2((fn)); \ + } + +#endif + +/* TODO(fclem): Remove. */ +#define CODEGEN_LIB diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_ambient_occlusion.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_ambient_occlusion.glsl index 12921a31b23..4ba1f6f7368 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_ambient_occlusion.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_ambient_occlusion.glsl @@ -1,4 +1,4 @@ -#ifndef VOLUMETRICS + void node_ambient_occlusion(vec4 color, float dist, vec3 normal, @@ -7,20 +7,6 @@ void node_ambient_occlusion(vec4 color, out vec4 result_color, out float result_ao) { - vec3 bent_normal; - vec4 rand = texelfetch_noise_tex(gl_FragCoord.xy); - OcclusionData data = occlusion_search(viewPosition, maxzBuffer, dist, inverted, sample_count); - - vec3 V = cameraVec(worldPosition); - vec3 N = normalize(normal); - vec3 Ng = safe_normalize(cross(dFdx(worldPosition), dFdy(worldPosition))); - - float unused_error; - vec3 unused; - occlusion_eval(data, V, N, Ng, inverted, result_ao, unused_error, unused); + result_ao = ambient_occlusion_eval(normal, dist, inverted, sample_count); result_color = result_ao * color; } -#else -/* Stub ambient occlusion because it is not compatible with volumetrics. */ -# define node_ambient_occlusion(a, b, c, d, e, f) (e = vec4(0); f = 0.0) -#endif diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_anisotropic.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_anisotropic.glsl index ec49cc86761..77de9e096a6 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_anisotropic.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_anisotropic.glsl @@ -1,17 +1,27 @@ -#ifndef VOLUMETRICS + void node_bsdf_anisotropic(vec4 color, float roughness, float anisotropy, float rotation, vec3 N, vec3 T, - const float use_multiscatter, - const float ssr_id, + float weight, + const float do_multiscatter, out Closure result) { - node_bsdf_glossy(color, roughness, N, use_multiscatter, ssr_id, result); + N = safe_normalize(N); + vec3 V = cameraVec(g_data.P); + float NV = dot(N, V); + + vec2 split_sum = brdf_lut(NV, roughness); + + ClosureReflection reflection_data; + reflection_data.weight = weight; + reflection_data.color = (do_multiscatter != 0.0) ? + F_brdf_multi_scatter(color.rgb, color.rgb, split_sum) : + F_brdf_single_scatter(color.rgb, color.rgb, split_sum); + reflection_data.N = N; + reflection_data.roughness = roughness; + + result = closure_eval(reflection_data); } -#else -/* Stub anisotropic because it is not compatible with volumetrics. */ -# define node_bsdf_anisotropic(a, b, c, d, e, f, g, h, result) (result = CLOSURE_DEFAULT) -#endif diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_background.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_background.glsl index 69ef4dcb7c7..2460bd63b38 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_background.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_background.glsl @@ -1,11 +1,9 @@ -void node_background(vec4 color, float strength, out Closure result) + +void node_background(vec4 color, float strength, float weight, out Closure result) { -#ifndef VOLUMETRICS - color *= strength; - result = CLOSURE_DEFAULT; - result.radiance = color.rgb; - result.transmittance = vec3(0.0); -#else - result = CLOSURE_DEFAULT; -#endif + ClosureEmission emission_data; + emission_data.weight = weight; + emission_data.emission = color.rgb * strength; + + result = closure_eval(emission_data); } diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_bump.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_bump.glsl index 9f73f654217..e0931128485 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_bump.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_bump.glsl @@ -1,28 +1,19 @@ -void dfdx_v3(vec3 v, out vec3 dy) -{ - dy = v + DFDX_SIGN * dFdx(v); -} -void dfdy_v3(vec3 v, out vec3 dy) +void differentiate_texco(vec3 v, out vec3 df) { - dy = v + DFDY_SIGN * dFdy(v); + /* Implementation defined. */ + df = v + dF_impl(v); } -void node_bump(float strength, - float dist, - float height, - float height_dx, - float height_dy, - vec3 N, - vec3 surf_pos, - float invert, - out vec3 result) +void node_bump( + float strength, float dist, float height, vec3 N, vec2 dHd, float invert, out vec3 result) { - N = mat3(ViewMatrix) * normalize(N); - dist *= gl_FrontFacing ? invert : -invert; + N = normalize(N); + dist *= FrontFacing ? invert : -invert; - vec3 dPdx = dFdx(surf_pos); - vec3 dPdy = dFdy(surf_pos); +#ifdef GPU_FRAGMENT_SHADER + vec3 dPdx = dFdx(g_data.P); + vec3 dPdy = dFdy(g_data.P); /* Get surface tangents from normal. */ vec3 Rx = cross(dPdy, N); @@ -31,14 +22,13 @@ void node_bump(float strength, /* Compute surface gradient and determinant. */ float det = dot(dPdx, Rx); - float dHdx = height_dx - height; - float dHdy = height_dy - height; - vec3 surfgrad = dHdx * Rx + dHdy * Ry; + vec3 surfgrad = dHd.x * Rx + dHd.y * Ry; strength = max(strength, 0.0); result = normalize(abs(det) * N - dist * sign(det) * surfgrad); result = normalize(mix(N, result, strength)); - - result = mat3(ViewMatrixInverse) * result; +#else + result = N; +#endif } diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_camera.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_camera.glsl index 03e61e9f472..ff84a0a334c 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_camera.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_camera.glsl @@ -1,6 +1,6 @@ -void camera(vec3 co, out vec3 outview, out float outdepth, out float outdist) +void camera(out vec3 outview, out float outdepth, out float outdist) { - outdepth = abs(co.z); - outdist = length(co); - outview = normalize(co); + outdepth = abs(transform_point(ViewMatrix, g_data.P).z); + outdist = distance(g_data.P, cameraPos); + outview = normalize(g_data.P - cameraPos); } diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_combine_hsv.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_combine_hsv.glsl index 2ce061da3cb..e8f444080b9 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_combine_hsv.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_combine_hsv.glsl @@ -1,3 +1,5 @@ +#pragma BLENDER_REQUIRE(gpu_shader_material_color_util.glsl) + void combine_hsv(float h, float s, float v, out vec4 col) { hsv_to_rgb(vec4(h, s, v, 1.0), col); diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_diffuse.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_diffuse.glsl index ab6024b073d..bbdd86bd664 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_diffuse.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_diffuse.glsl @@ -1,28 +1,11 @@ -#ifndef VOLUMETRICS -CLOSURE_EVAL_FUNCTION_DECLARE_1(node_bsdf_diffuse, Diffuse) - -void node_bsdf_diffuse(vec4 color, float roughness, vec3 N, out Closure result) +void node_bsdf_diffuse(vec4 color, float roughness, vec3 N, float weight, out Closure result) { - CLOSURE_VARS_DECLARE_1(Diffuse); - - in_Diffuse_0.N = N; /* Normalized during eval. */ - in_Diffuse_0.albedo = color.rgb; - - CLOSURE_EVAL_FUNCTION_1(node_bsdf_diffuse, Diffuse); - - result = CLOSURE_DEFAULT; + ClosureDiffuse diffuse_data; + diffuse_data.weight = weight; + diffuse_data.color = color.rgb; + diffuse_data.N = N; + diffuse_data.sss_id = 0u; - out_Diffuse_0.radiance = render_pass_diffuse_mask(vec3(1.0), out_Diffuse_0.radiance); - out_Diffuse_0.radiance *= color.rgb; - - result.radiance = out_Diffuse_0.radiance; - - /* TODO(@fclem): Try to not use this. */ - closure_load_ssr_data(vec3(0.0), 0.0, in_Diffuse_0.N, -1.0, result); + result = closure_eval(diffuse_data); } - -#else -/* Stub diffuse because it is not compatible with volumetrics. */ -# define node_bsdf_diffuse(a, b, c, d) (d = CLOSURE_DEFAULT) -#endif diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_displacement.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_displacement.glsl index 0838b5c8b71..cdcdbe50917 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_displacement.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_displacement.glsl @@ -1,9 +1,9 @@ -void node_displacement_object( - float height, float midlevel, float scale, vec3 N, mat4 obmat, out vec3 result) +void node_displacement_object(float height, float midlevel, float scale, vec3 N, out vec3 result) { - N = (vec4(N, 0.0) * obmat).xyz; + N = transform_direction(ModelMatrix, N); result = (height - midlevel) * scale * normalize(N); - result = (obmat * vec4(result, 0.0)).xyz; + /* Apply object scale and orientation. */ + result = transform_direction(ModelMatrix, result); } void node_displacement_world(float height, float midlevel, float scale, vec3 N, out vec3 result) diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_eevee_specular.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_eevee_specular.glsl index 0941482df45..c81880184e3 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_eevee_specular.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_eevee_specular.glsl @@ -1,80 +1,69 @@ -#ifndef VOLUMETRICS - -CLOSURE_EVAL_FUNCTION_DECLARE_3(node_eevee_specular, Diffuse, Glossy, Glossy) void node_eevee_specular(vec4 diffuse, vec4 specular, float roughness, vec4 emissive, float transp, - vec3 normal, + vec3 N, float clearcoat, float clearcoat_roughness, - vec3 clearcoat_normal, + vec3 CN, float occlusion, - float ssr_id, + float weight, + const float use_clearcoat, out Closure result) { - CLOSURE_VARS_DECLARE_3(Diffuse, Glossy, Glossy); - - in_common.occlusion = occlusion; - - in_Diffuse_0.N = normal; /* Normalized during eval. */ - in_Diffuse_0.albedo = diffuse.rgb; + N = safe_normalize(N); + CN = safe_normalize(CN); + vec3 V = cameraVec(g_data.P); - in_Glossy_1.N = normal; /* Normalized during eval. */ - in_Glossy_1.roughness = roughness; + ClosureEmission emission_data; + emission_data.weight = weight; + emission_data.emission = emissive.rgb; - in_Glossy_2.N = clearcoat_normal; /* Normalized during eval. */ - in_Glossy_2.roughness = clearcoat_roughness; + ClosureTransparency transparency_data; + transparency_data.weight = weight; + transparency_data.transmittance = vec3(transp); + transparency_data.holdout = 0.0; - CLOSURE_EVAL_FUNCTION_3(node_eevee_specular, Diffuse, Glossy, Glossy); + float alpha = (1.0 - transp) * weight; - result = CLOSURE_DEFAULT; + ClosureDiffuse diffuse_data; + diffuse_data.weight = alpha; + diffuse_data.color = diffuse.rgb; + diffuse_data.N = N; + diffuse_data.sss_id = 0u; - vec3 V = cameraVec(worldPosition); - - { - /* Diffuse. */ - out_Diffuse_0.radiance = render_pass_diffuse_mask(vec3(1), out_Diffuse_0.radiance); - out_Diffuse_0.radiance *= in_Diffuse_0.albedo; - result.radiance += out_Diffuse_0.radiance; - } - { - /* Glossy. */ - float NV = dot(in_Glossy_1.N, V); - vec2 split_sum = brdf_lut(NV, in_Glossy_1.roughness); + ClosureReflection reflection_data; + reflection_data.weight = alpha; + if (true) { + float NV = dot(N, V); + vec2 split_sum = brdf_lut(NV, roughness); vec3 brdf = F_brdf_single_scatter(specular.rgb, vec3(1.0), split_sum); - out_Glossy_1.radiance = closure_mask_ssr_radiance(out_Glossy_1.radiance, ssr_id); - out_Glossy_1.radiance *= brdf; - out_Glossy_1.radiance = render_pass_glossy_mask(specular.rgb, out_Glossy_1.radiance); - closure_load_ssr_data( - out_Glossy_1.radiance, in_Glossy_1.roughness, in_Glossy_1.N, ssr_id, result); + reflection_data.color = specular.rgb * brdf; + reflection_data.N = N; + reflection_data.roughness = roughness; } - { - /* Clearcoat. */ - float NV = dot(in_Glossy_2.N, V); - vec2 split_sum = brdf_lut(NV, in_Glossy_2.roughness); + + ClosureReflection clearcoat_data; + clearcoat_data.weight = alpha * clearcoat * 0.25; + if (true) { + float NV = dot(CN, V); + vec2 split_sum = brdf_lut(NV, clearcoat_roughness); vec3 brdf = F_brdf_single_scatter(vec3(0.04), vec3(1.0), split_sum); - out_Glossy_2.radiance *= brdf * clearcoat * 0.25; - out_Glossy_2.radiance = render_pass_glossy_mask(vec3(1), out_Glossy_2.radiance); - result.radiance += out_Glossy_2.radiance; - } - { - /* Emission. */ - vec3 out_emission_radiance = render_pass_emission_mask(emissive.rgb); - result.radiance += out_emission_radiance; + clearcoat_data.color = brdf; + clearcoat_data.N = CN; + clearcoat_data.roughness = clearcoat_roughness; } - float alpha = 1.0 - transp; - result.transmittance = vec3(transp); - result.radiance *= alpha; - result.ssr_data.rgb *= alpha; + if (use_clearcoat != 0.0f) { + result = closure_eval(diffuse_data, reflection_data, clearcoat_data); + } + else { + result = closure_eval(diffuse_data, reflection_data); + } + result = closure_add(result, closure_eval(emission_data)); + result = closure_add(result, closure_eval(transparency_data)); } - -#else -/* Stub specular because it is not compatible with volumetrics. */ -# define node_eevee_specular(a, b, c, d, e, f, g, h, i, j, k, result) (result = CLOSURE_DEFAULT) -#endif diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_emission.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_emission.glsl index f2de7c2da39..32484491abd 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_emission.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_emission.glsl @@ -1,10 +1,9 @@ -void node_emission(vec4 color, float strength, vec3 vN, out Closure result) + +void node_emission(vec4 color, float strength, float weight, out Closure result) { - result = CLOSURE_DEFAULT; -#ifndef VOLUMETRICS - result.radiance = render_pass_emission_mask(color.rgb) * strength; - result.ssr_normal = normal_encode(vN, viewCameraVec(viewPosition)); -#else - result.emission = color.rgb * strength; -#endif + ClosureEmission emission_data; + emission_data.weight = weight; + emission_data.emission = color.rgb * strength; + + result = closure_eval(emission_data); } 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 95f2be4bd44..7f502f74c0c 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 @@ -1,3 +1,6 @@ +#pragma BLENDER_REQUIRE(gpu_shader_material_hash.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_material_noise.glsl) + /* The fractal_noise functions are all exactly the same except for the input type. */ float fractal_noise(float p, float octaves, float roughness) { diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_fresnel.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_fresnel.glsl index 7a4d28f2dd6..9fb98d598ab 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_fresnel.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_fresnel.glsl @@ -26,12 +26,11 @@ float fresnel_dielectric(vec3 Incoming, vec3 Normal, float eta) return fresnel_dielectric_cos(dot(Incoming, Normal), eta); } -void node_fresnel(float ior, vec3 N, vec3 I, out float result) +void node_fresnel(float ior, vec3 N, out float result) { N = normalize(N); - /* handle perspective/orthographic */ - vec3 I_view = (ProjectionMatrix[3][3] == 0.0) ? normalize(I) : vec3(0.0, 0.0, -1.0); + vec3 V = cameraVec(g_data.P); float eta = max(ior, 0.00001); - result = fresnel_dielectric(I_view, N, (gl_FrontFacing) ? eta : 1.0 / eta); + result = fresnel_dielectric(V, N, (FrontFacing) ? eta : 1.0 / eta); } diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_gamma.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_gamma.glsl index 5733992f8dd..29fb09ceebd 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_gamma.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_gamma.glsl @@ -1,3 +1,5 @@ +#pragma BLENDER_REQUIRE(gpu_shader_material_math_util.glsl) + void node_gamma(vec4 col, float gamma, out vec4 outcol) { outcol = col; diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_geometry.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_geometry.glsl index a14ff5021bf..5e86a4577ee 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_geometry.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_geometry.glsl @@ -1,9 +1,6 @@ -void node_geometry(vec3 I, - vec3 N, - vec3 orco, - mat4 objmat, - mat4 toworld, - vec2 barycentric, +#pragma BLENDER_REQUIRE(gpu_shader_material_tangent.glsl) + +void node_geometry(vec3 orco, out vec3 position, out vec3 normal, out vec3 tangent, @@ -15,39 +12,21 @@ void node_geometry(vec3 I, out float random_per_island) { /* handle perspective/orthographic */ - vec3 I_view = (ProjectionMatrix[3][3] == 0.0) ? normalize(I) : vec3(0.0, 0.0, -1.0); - incoming = -(toworld * vec4(I_view, 0.0)).xyz; - -#if defined(WORLD_BACKGROUND) || defined(PROBE_CAPTURE) - position = -incoming; - true_normal = normal = incoming; - tangent = parametric = vec3(0.0); - vec3(0.0); - backfacing = 0.0; - pointiness = 0.0; -#else - - position = worldPosition; -# ifndef VOLUMETRICS - normal = normalize(N); - vec3 B = dFdx(worldPosition); - vec3 T = dFdy(worldPosition); - true_normal = normalize(cross(B, T)); -# else - normal = (toworld * vec4(N, 0.0)).xyz; - true_normal = normal; -# endif + incoming = coordinate_incoming(g_data.P); + position = g_data.P; + normal = g_data.N; + true_normal = g_data.Ng; -# ifdef HAIR_SHADER - tangent = -hairTangent; -# else - tangent_orco_z(orco, orco); - node_tangent(N, orco, objmat, tangent); -# endif + if (g_data.is_strand) { + tangent = g_data.T; + } + else { + tangent_orco_z(orco, orco); + node_tangent(orco, tangent); + } - parametric = vec3(barycentric, 0.0); - backfacing = (gl_FrontFacing) ? 0.0 : 1.0; + parametric = vec3(g_data.barycentric_coords, 0.0); + backfacing = (FrontFacing) ? 0.0 : 1.0; pointiness = 0.5; random_per_island = 0.0; -#endif } diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_glass.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_glass.glsl index aa0a8873596..36c71c27837 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_glass.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_glass.glsl @@ -1,57 +1,33 @@ -#ifndef VOLUMETRICS - -CLOSURE_EVAL_FUNCTION_DECLARE_2(node_bsdf_glass, Glossy, Refraction) void node_bsdf_glass(vec4 color, float roughness, float ior, vec3 N, - const float do_multiscatter, - const float ssr_id, + float weight, + float do_multiscatter, out Closure result) { - CLOSURE_VARS_DECLARE_2(Glossy, Refraction); - - in_Glossy_0.N = N; /* Normalized during eval. */ - in_Glossy_0.roughness = roughness; - - in_Refraction_1.N = N; /* Normalized during eval. */ - in_Refraction_1.roughness = roughness; - in_Refraction_1.ior = ior; + N = safe_normalize(N); + vec3 V = cameraVec(g_data.P); + float NV = dot(N, V); - CLOSURE_EVAL_FUNCTION_2(node_bsdf_glass, Glossy, Refraction); + vec2 split_sum = btdf_lut(NV, roughness, ior); - result = CLOSURE_DEFAULT; + float fresnel = (do_multiscatter != 0.0) ? split_sum.y : F_eta(ior, NV); + float btdf = (do_multiscatter != 0.0) ? 1.0 : split_sum.x; - float NV = dot(in_Refraction_1.N, cameraVec(worldPosition)); + ClosureReflection reflection_data; + reflection_data.weight = fresnel * weight; + reflection_data.color = color.rgb; + reflection_data.N = N; + reflection_data.roughness = roughness; - float fresnel = (do_multiscatter != 0.0) ? - btdf_lut(NV, in_Refraction_1.roughness, in_Refraction_1.ior).y : - F_eta(in_Refraction_1.ior, NV); + ClosureRefraction refraction_data; + refraction_data.weight = (1.0 - fresnel) * weight; + refraction_data.color = color.rgb * btdf; + refraction_data.N = N; + refraction_data.roughness = roughness; + refraction_data.ior = ior; - vec2 split_sum = brdf_lut(NV, in_Glossy_0.roughness); - vec3 brdf = (do_multiscatter != 0.0) ? F_brdf_multi_scatter(vec3(1.0), vec3(1.0), split_sum) : - F_brdf_single_scatter(vec3(1.0), vec3(1.0), split_sum); - - out_Glossy_0.radiance = closure_mask_ssr_radiance(out_Glossy_0.radiance, ssr_id); - out_Glossy_0.radiance *= brdf; - out_Glossy_0.radiance = render_pass_glossy_mask(vec3(1.0), out_Glossy_0.radiance); - out_Glossy_0.radiance *= color.rgb * fresnel; - closure_load_ssr_data( - out_Glossy_0.radiance, in_Glossy_0.roughness, in_Glossy_0.N, ssr_id, result); - - float btdf = (do_multiscatter != 0.0) ? - 1.0 : - btdf_lut(NV, in_Refraction_1.roughness, in_Refraction_1.ior).x; - out_Refraction_1.radiance *= btdf; - out_Refraction_1.radiance = render_pass_glossy_mask(vec3(1.0), out_Refraction_1.radiance); - out_Refraction_1.radiance *= color.rgb * (1.0 - fresnel); - /* Simulate 2nd absorption event. */ - out_Refraction_1.radiance *= (refractionDepth > 0.0) ? color.rgb : vec3(1.0); - result.radiance += out_Refraction_1.radiance; + result = closure_eval(reflection_data, refraction_data); } - -#else -/* Stub glass because it is not compatible with volumetrics. */ -# define node_bsdf_glass(a, b, c, d, e, f, result) (result = CLOSURE_DEFAULT) -#endif diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_glossy.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_glossy.glsl index fa83bfb6c7a..2e48ddd1c5e 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_glossy.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_glossy.glsl @@ -1,33 +1,20 @@ -#ifndef VOLUMETRICS - -CLOSURE_EVAL_FUNCTION_DECLARE_1(node_bsdf_glossy, Glossy) void node_bsdf_glossy( - vec4 color, float roughness, vec3 N, float use_multiscatter, float ssr_id, out Closure result) + vec4 color, float roughness, vec3 N, float weight, float do_multiscatter, out Closure result) { - bool do_ssr = (ssrToggle && int(ssr_id) == outputSsrId); - - CLOSURE_VARS_DECLARE_1(Glossy); + N = safe_normalize(N); + vec3 V = cameraVec(g_data.P); + float NV = dot(N, V); - in_Glossy_0.N = N; /* Normalized during eval. */ - in_Glossy_0.roughness = roughness; + vec2 split_sum = brdf_lut(NV, roughness); - CLOSURE_EVAL_FUNCTION_1(node_bsdf_glossy, Glossy); + ClosureReflection reflection_data; + reflection_data.weight = weight; + reflection_data.color = (do_multiscatter != 0.0) ? + F_brdf_multi_scatter(color.rgb, color.rgb, split_sum) : + F_brdf_single_scatter(color.rgb, color.rgb, split_sum); + reflection_data.N = N; + reflection_data.roughness = roughness; - result = CLOSURE_DEFAULT; - - vec2 split_sum = brdf_lut(dot(in_Glossy_0.N, cameraVec(worldPosition)), in_Glossy_0.roughness); - vec3 brdf = (use_multiscatter != 0.0) ? F_brdf_multi_scatter(vec3(1.0), vec3(1.0), split_sum) : - F_brdf_single_scatter(vec3(1.0), vec3(1.0), split_sum); - out_Glossy_0.radiance = closure_mask_ssr_radiance(out_Glossy_0.radiance, ssr_id); - out_Glossy_0.radiance *= brdf; - out_Glossy_0.radiance = render_pass_glossy_mask(vec3(1.0), out_Glossy_0.radiance); - out_Glossy_0.radiance *= color.rgb; - closure_load_ssr_data( - out_Glossy_0.radiance, in_Glossy_0.roughness, in_Glossy_0.N, ssr_id, result); + result = closure_eval(reflection_data); } - -#else -/* Stub glossy because it is not compatible with volumetrics. */ -# define node_bsdf_glossy(a, b, c, d, e, result) (result = CLOSURE_DEFAULT) -#endif diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_hair.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_hair.glsl new file mode 100644 index 00000000000..7bf8795495a --- /dev/null +++ b/source/blender/gpu/shaders/material/gpu_shader_material_hair.glsl @@ -0,0 +1,46 @@ + +void node_bsdf_hair(vec4 color, + float offset, + float roughness_u, + float roughness_v, + vec3 T, + float weight, + out Closure result) +{ + ClosureHair hair_data; + hair_data.weight = weight; + hair_data.color = color.rgb; + hair_data.offset = offset; + hair_data.roughness = vec2(roughness_u, roughness_v); + hair_data.T = T; + + result = closure_eval(hair_data); +} + +void node_bsdf_hair_principled(vec4 color, + float melanin, + float melanin_redness, + vec4 tint, + vec3 absorption_coefficient, + float roughness, + float radial_roughness, + float coat, + float ior, + float offset, + float random_color, + float random_roughness, + float random, + float weight, + out Closure result) +{ + /* Placeholder closure. + * Some computation will have to happen here just like the Principled BSDF. */ + ClosureHair hair_data; + hair_data.weight = weight; + hair_data.color = color.rgb; + hair_data.offset = offset; + hair_data.roughness = vec2(0.0); + hair_data.T = g_data.T; + + result = closure_eval(hair_data); +} 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 59f0377869b..2885bf4e082 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 @@ -1,25 +1,19 @@ +#pragma BLENDER_REQUIRE(gpu_shader_material_hash.glsl) + void node_hair_info(float hair_length, out float is_strand, out float intercept, - out float length, + out float out_length, out float thickness, out vec3 tangent, out float random) { - length = hair_length; -#ifdef HAIR_SHADER - is_strand = 1.0; - intercept = hairTime; - thickness = hairThickness; - tangent = normalize(worldNormal); - random = wang_hash_noise( - uint(hairStrandID)); /* TODO: could be precomputed per strand instead. */ -#else - is_strand = 0.0; - intercept = 0.0; - thickness = 0.0; - tangent = vec3(1.0); - random = 0.0; -#endif + is_strand = float(g_data.is_strand); + intercept = g_data.hair_time; + thickness = g_data.hair_thickness; + out_length = hair_length; + tangent = g_data.T; + /* TODO: could be precomputed per strand instead. */ + random = wang_hash_noise(uint(g_data.hair_strand_id)); } diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_hash.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_hash.glsl index cb798047791..32d61f06a65 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_hash.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_hash.glsl @@ -209,10 +209,12 @@ vec3 hash_vec4_to_vec3(vec4 k) float integer_noise(int n) { - int nn; - n = (n + 1013) & 0x7fffffff; - n = (n >> 13) ^ n; - nn = (n * (n * n * 60493 + 19990303) + 1376312589) & 0x7fffffff; + /* Integer bit-shifts for these calculations can cause precision problems on macOS. + * Using uint resolves these issues. */ + uint nn; + nn = (uint(n) + 1013u) & 0x7fffffffu; + nn = (nn >> 13u) ^ nn; + nn = (uint(nn * (nn * nn * 60493u + 19990303u)) + 1376312589u) & 0x7fffffffu; return 0.5 * (float(nn) / 1073741824.0); } diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_holdout.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_holdout.glsl index 50ce2bf2ab8..d022c1ced59 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_holdout.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_holdout.glsl @@ -1,8 +1,10 @@ -void node_holdout(out Closure result) + +void node_holdout(float weight, out Closure result) { - result = CLOSURE_DEFAULT; -#ifndef VOLUMETRICS - result.holdout = 1.0; - result.flag = CLOSURE_HOLDOUT_FLAG; -#endif + ClosureTransparency transparency_data; + transparency_data.weight = weight; + transparency_data.transmittance = vec3(0.0); + transparency_data.holdout = 1.0; + + result = closure_eval(transparency_data); } diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_hue_sat_val.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_hue_sat_val.glsl index 64ac73ecdf3..30b808508e9 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_hue_sat_val.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_hue_sat_val.glsl @@ -1,3 +1,5 @@ +#pragma BLENDER_REQUIRE(gpu_shader_material_color_util.glsl) + void hue_sat(float hue, float sat, float value, float fac, vec4 col, out vec4 outcol) { vec4 hsv; diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_layer_weight.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_layer_weight.glsl index 588d295bcc4..2b61343a200 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_layer_weight.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_layer_weight.glsl @@ -1,15 +1,17 @@ -void node_layer_weight(float blend, vec3 N, vec3 I, out float fresnel, out float facing) +#pragma BLENDER_REQUIRE(gpu_shader_material_fresnel.glsl) + +void node_layer_weight(float blend, vec3 N, out float fresnel, out float facing) { N = normalize(N); /* fresnel */ float eta = max(1.0 - blend, 0.00001); - vec3 I_view = (ProjectionMatrix[3][3] == 0.0) ? normalize(I) : vec3(0.0, 0.0, -1.0); + vec3 V = cameraVec(g_data.P); - fresnel = fresnel_dielectric(I_view, N, (gl_FrontFacing) ? 1.0 / eta : eta); + fresnel = fresnel_dielectric(V, N, (FrontFacing) ? 1.0 / eta : eta); /* facing */ - facing = abs(dot(I_view, N)); + facing = abs(dot(V, N)); if (blend != 0.5) { blend = clamp(blend, 0.0, 0.99999); blend = (blend < 0.5) ? 2.0 * blend : 0.5 / (1.0 - blend); diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_light_path.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_light_path.glsl index 50c87e3f105..628a3d5e0e5 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_light_path.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_light_path.glsl @@ -13,19 +13,19 @@ void node_light_path(out float is_camera_ray, out float transmission_depth) { /* Supported. */ - is_camera_ray = (rayType == EEVEE_RAY_CAMERA) ? 1.0 : 0.0; - is_shadow_ray = (rayType == EEVEE_RAY_SHADOW) ? 1.0 : 0.0; - is_diffuse_ray = (rayType == EEVEE_RAY_DIFFUSE) ? 1.0 : 0.0; - is_glossy_ray = (rayType == EEVEE_RAY_GLOSSY) ? 1.0 : 0.0; + is_camera_ray = float(g_data.ray_type == RAY_TYPE_CAMERA); + is_shadow_ray = float(g_data.ray_type == RAY_TYPE_SHADOW); + is_diffuse_ray = float(g_data.ray_type == RAY_TYPE_DIFFUSE); + is_glossy_ray = float(g_data.ray_type == RAY_TYPE_GLOSSY); /* Kind of supported. */ is_singular_ray = is_glossy_ray; is_reflection_ray = is_glossy_ray; is_transmission_ray = is_glossy_ray; - ray_depth = rayDepth; - diffuse_depth = (is_diffuse_ray == 1.0) ? rayDepth : 0.0; - glossy_depth = (is_glossy_ray == 1.0) ? rayDepth : 0.0; + ray_depth = g_data.ray_depth; + diffuse_depth = (is_diffuse_ray == 1.0) ? g_data.ray_depth : 0.0; + glossy_depth = (is_glossy_ray == 1.0) ? g_data.ray_depth : 0.0; transmission_depth = (is_transmission_ray == 1.0) ? glossy_depth : 0.0; + ray_length = g_data.ray_length; /* Not supported. */ - ray_length = 1.0; transparent_depth = 0.0; } diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_map_range.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_map_range.glsl index 1def3abec26..119ee3c0eaa 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_map_range.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_map_range.glsl @@ -1,3 +1,5 @@ +#pragma BLENDER_REQUIRE(gpu_shader_material_math_util.glsl) + float smootherstep(float edge0, float edge1, float x) { x = clamp(safe_divide((x - edge0), (edge1 - edge0)), 0.0, 1.0); diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_mapping.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_mapping.glsl index 07f152439fe..312c57231c5 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_mapping.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_mapping.glsl @@ -1,3 +1,5 @@ +#pragma BLENDER_REQUIRE(gpu_shader_material_math_util.glsl) + void mapping_mat4( vec3 vec, vec4 m0, vec4 m1, vec4 m2, vec4 m3, vec3 minvec, vec3 maxvec, out vec3 outvec) { diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_math.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_math.glsl index f200d666e28..0948ce2c9fa 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_math.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_math.glsl @@ -1,3 +1,5 @@ +#pragma BLENDER_REQUIRE(gpu_shader_material_math_util.glsl) + void math_add(float a, float b, float c, out float result) { result = a + b; diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_math_util.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_math_util.glsl index 2a98d9fadd0..91a8996939a 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_math_util.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_math_util.glsl @@ -140,17 +140,74 @@ mat3 euler_to_mat3(vec3 euler) return mat; } -void direction_transform_m4v3(vec3 vin, mat4 mat, out vec3 vout) +void normal_transform_object_to_world(vec3 vin, out vec3 vout) { - vout = (mat * vec4(vin, 0.0)).xyz; + vout = normal_object_to_world(vin); } -void normal_transform_transposed_m4v3(vec3 vin, mat4 mat, out vec3 vout) +void normal_transform_world_to_object(vec3 vin, out vec3 vout) { - vout = transpose(mat3(mat)) * vin; + vout = normal_world_to_object(vin); } -void point_transform_m4v3(vec3 vin, mat4 mat, out vec3 vout) +void direction_transform_object_to_world(vec3 vin, out vec3 vout) { - vout = (mat * vec4(vin, 1.0)).xyz; + vout = transform_direction(ModelMatrix, vin); +} + +void direction_transform_object_to_view(vec3 vin, out vec3 vout) +{ + vout = transform_direction(ModelMatrix, vin); + vout = transform_direction(ViewMatrix, vout); +} + +void direction_transform_view_to_world(vec3 vin, out vec3 vout) +{ + vout = transform_direction(ViewMatrixInverse, vin); +} + +void direction_transform_view_to_object(vec3 vin, out vec3 vout) +{ + vout = transform_direction(ViewMatrixInverse, vin); + vout = transform_direction(ModelMatrixInverse, vout); +} + +void direction_transform_world_to_view(vec3 vin, out vec3 vout) +{ + vout = transform_direction(ViewMatrix, vin); +} + +void direction_transform_world_to_object(vec3 vin, out vec3 vout) +{ + vout = transform_direction(ModelMatrixInverse, vin); +} + +void point_transform_object_to_world(vec3 vin, out vec3 vout) +{ + vout = point_object_to_world(vin); +} + +void point_transform_object_to_view(vec3 vin, out vec3 vout) +{ + vout = point_object_to_view(vin); +} + +void point_transform_view_to_world(vec3 vin, out vec3 vout) +{ + vout = point_view_to_world(vin); +} + +void point_transform_view_to_object(vec3 vin, out vec3 vout) +{ + vout = point_view_to_object(vin); +} + +void point_transform_world_to_view(vec3 vin, out vec3 vout) +{ + vout = point_world_to_view(vin); +} + +void point_transform_world_to_object(vec3 vin, out vec3 vout) +{ + vout = point_world_to_object(vin); } diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_mix_rgb.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_mix_rgb.glsl index e089aec1d92..157a6a27c15 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_mix_rgb.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_mix_rgb.glsl @@ -1,3 +1,5 @@ +#pragma BLENDER_REQUIRE(gpu_shader_material_color_util.glsl) + void mix_blend(float fac, vec4 col1, vec4 col2, out vec4 outcol) { fac = clamp(fac, 0.0, 1.0); diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_noise.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_noise.glsl index cc65b1eb57c..c84f34a834c 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_noise.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_noise.glsl @@ -1,3 +1,5 @@ +#pragma BLENDER_REQUIRE(gpu_shader_material_hash.glsl) + /* clang-format off */ #define FLOORFRAC(x, x_int, x_fract) { float x_floor = floor(x); x_int = int(x_floor); x_fract = x - x_floor; } /* clang-format on */ diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_normal_map.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_normal_map.glsl index 2b4a0204d97..e219e2b9bbe 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_normal_map.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_normal_map.glsl @@ -1,13 +1,13 @@ -void node_normal_map(vec4 info, vec4 tangent, vec3 normal, vec3 texnormal, out vec3 outnormal) +void node_normal_map(vec4 tangent, vec3 texnormal, out vec3 outnormal) { if (all(equal(tangent, vec4(0.0, 0.0, 0.0, 1.0)))) { - outnormal = normal; + outnormal = g_data.N; return; } - tangent *= (gl_FrontFacing ? 1.0 : -1.0); - vec3 B = tangent.w * cross(normal, tangent.xyz) * sign(info.w); + tangent *= (FrontFacing ? 1.0 : -1.0); + vec3 B = tangent.w * cross(g_data.N, tangent.xyz) * sign(ObjectInfo.w); - outnormal = texnormal.x * tangent.xyz + texnormal.y * B + texnormal.z * normal; + outnormal = texnormal.x * tangent.xyz + texnormal.y * B + texnormal.z * g_data.N; outnormal = normalize(outnormal); } @@ -21,7 +21,7 @@ void color_to_blender_normal_new_shading(vec3 color, out vec3 normal) normal = vec3(2.0, -2.0, -2.0) * color - vec3(1.0); } -void node_normal_map_mix(float strength, vec3 newnormal, vec3 oldnormal, out vec3 outnormal) +void node_normal_map_mix(float strength, vec3 newnormal, out vec3 outnormal) { - outnormal = normalize(mix(oldnormal, newnormal, max(strength, 0.0))); + outnormal = normalize(mix(g_data.N, newnormal, max(strength, 0.0))); } diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_object_info.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_object_info.glsl index 607cf119b36..2dd2993ceb0 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_object_info.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_object_info.glsl @@ -1,7 +1,4 @@ -void node_object_info(mat4 obmat, - vec4 obcolor, - vec4 info, - float mat_index, +void node_object_info(float mat_index, out vec3 location, out vec4 color, out float alpha, @@ -9,10 +6,11 @@ void node_object_info(mat4 obmat, out float material_index, out float random) { - location = obmat[3].xyz; - color = obcolor; - alpha = obcolor.w; - object_index = info.x; + location = ModelMatrix[3].xyz; + color = ObjectColor; + alpha = ObjectColor.a; + object_index = ObjectInfo.x; + /* TODO(fclem): Put that inside the Material UBO. */ material_index = mat_index; - random = info.z; + random = ObjectInfo.z; } diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_output_aov.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_output_aov.glsl index 648994739bf..b166c3e4e9f 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_output_aov.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_output_aov.glsl @@ -1,13 +1,5 @@ -void node_output_aov(vec4 color, float value, out Closure result) +void node_output_aov(vec4 color, float value, float hash, out Closure dummy) { - result = CLOSURE_DEFAULT; -#ifndef VOLUMETRICS - if (render_pass_aov_is_color()) { - result.radiance = color.rgb; - } - else { - result.radiance = vec3(value); - } -#endif + output_aov(color, value, floatBitsToUint(hash)); } diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_output_material.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_output_material.glsl index 14271f9d107..2c24f50264c 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_output_material.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_output_material.glsl @@ -1,20 +1,20 @@ -void node_output_material(Closure surface, - Closure volume, - vec3 displacement, - float alpha_threshold, - float shadow_threshold, - out Closure result) + +void node_output_material_surface(Closure surface, out Closure out_surface) { -#ifdef VOLUMETRICS - result = volume; -#else - result = surface; -# if defined(USE_ALPHA_HASH) - /* Alpha clip emulation. */ - if ((rayType != EEVEE_RAY_SHADOW) ? (alpha_threshold >= 0.0) : (shadow_threshold >= 0.0)) { - float alpha = saturate(1.0 - avg(result.transmittance)); - result.transmittance = vec3(step(alpha, max(alpha_threshold, shadow_threshold))); - } -# endif -#endif + out_surface = surface; +} + +void node_output_material_volume(Closure volume, out Closure out_volume) +{ + out_volume = volume; +} + +void node_output_material_displacement(vec3 displacement, out vec3 out_displacement) +{ + out_displacement = displacement; +} + +void node_output_material_thickness(float thickness, out float out_thickness) +{ + out_thickness = thickness; } diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_output_world.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_output_world.glsl index 5eb853a4c1a..37c34a4f0d7 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_output_world.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_output_world.glsl @@ -1,14 +1,10 @@ -uniform float backgroundAlpha; -void node_output_world(Closure surface, Closure volume, out Closure result) +void node_output_world_surface(Closure surface, out Closure out_surface) { -#ifndef VOLUMETRICS - float alpha = renderPassEnvironment ? 1.0 : backgroundAlpha; - result = CLOSURE_DEFAULT; - result.radiance = surface.radiance * alpha; - result.transmittance = vec3(0.0); - result.holdout = (1.0 - alpha); -#else - result = volume; -#endif /* VOLUMETRICS */ + out_surface = surface; +} + +void node_output_world_volume(Closure volume, out Closure out_volume) +{ + out_volume = volume; } diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_particle_info.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_particle_info.glsl index bdd60c20a81..5602345ea4a 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_particle_info.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_particle_info.glsl @@ -1,8 +1,4 @@ -void particle_info(vec4 sprops, - vec4 loc, - vec3 vel, - vec3 avel, - out float index, +void particle_info(out float index, out float random, out float age, out float life_time, @@ -11,13 +7,14 @@ void particle_info(vec4 sprops, out vec3 velocity, out vec3 angular_velocity) { - index = sprops.x; - random = loc.w; - age = sprops.y; - life_time = sprops.z; - size = sprops.w; + /* Unsupported for now. */ + index = 0.0; + random = 0.0; + age = 0.0; + life_time = 0.0; + size = 0.0; - location = loc.xyz; - velocity = vel; - angular_velocity = avel; + location = vec3(0.0); + velocity = vec3(0.0); + angular_velocity = vec3(0.0); } diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_point_info.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_point_info.glsl index d717ac97b28..1b1fed9502e 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_point_info.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_point_info.glsl @@ -1,3 +1,5 @@ +#pragma BLENDER_REQUIRE(gpu_shader_material_hash.glsl) + void node_point_info(out vec3 position, out float radius, out float random) { 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 c97fc090fe2..033dc05c57d 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_principled.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_principled.glsl @@ -1,4 +1,4 @@ -#ifndef VOLUMETRICS + vec3 tint_from_color(vec3 color) { float lum = dot(color, vec3(0.3, 0.6, 0.1)); /* luminance approx. */ @@ -13,8 +13,6 @@ float principled_sheen(float NV) return sheen; } -CLOSURE_EVAL_FUNCTION_DECLARE_4(node_bsdf_principled, Diffuse, Glossy, Glossy, Refraction) - void node_bsdf_principled(vec4 base_color, float subsurface, vec3 subsurface_radius, @@ -40,169 +38,137 @@ void node_bsdf_principled(vec4 base_color, vec3 N, vec3 CN, vec3 T, + float weight, const float do_diffuse, const float do_clearcoat, const float do_refraction, const float do_multiscatter, - float ssr_id, - float sss_id, - vec3 sss_scale, + float do_sss, out Closure result) { /* Match cycles. */ - metallic = saturate(metallic); - transmission = saturate(transmission); + metallic = clamp(metallic, 0.0, 1.0); + transmission = clamp(transmission, 0.0, 1.0) * (1.0 - metallic); float diffuse_weight = (1.0 - transmission) * (1.0 - metallic); - transmission *= (1.0 - metallic); float specular_weight = (1.0 - transmission); - clearcoat = max(clearcoat, 0.0); + float clearcoat_weight = max(clearcoat, 0.0) * 0.25; transmission_roughness = 1.0 - (1.0 - roughness) * (1.0 - transmission_roughness); specular = max(0.0, specular); - CLOSURE_VARS_DECLARE_4(Diffuse, Glossy, Glossy, Refraction); - - in_Diffuse_0.N = N; /* Normalized during eval. */ - in_Diffuse_0.albedo = mix(base_color.rgb, subsurface_color.rgb, subsurface); - - in_Glossy_1.N = N; /* Normalized during eval. */ - in_Glossy_1.roughness = roughness; - - in_Glossy_2.N = CN; /* Normalized during eval. */ - in_Glossy_2.roughness = clearcoat_roughness; - - in_Refraction_3.N = N; /* Normalized during eval. */ - in_Refraction_3.roughness = do_multiscatter != 0.0 ? roughness : transmission_roughness; - in_Refraction_3.ior = ior; + N = safe_normalize(N); + CN = safe_normalize(CN); + vec3 V = cameraVec(g_data.P); + float NV = dot(N, V); - CLOSURE_EVAL_FUNCTION_4(node_bsdf_principled, Diffuse, Glossy, Glossy, Refraction); + float fresnel = (do_multiscatter != 0.0) ? btdf_lut(NV, roughness, ior).y : F_eta(ior, NV); + float glass_reflection_weight = fresnel * transmission; + float glass_transmission_weight = (1.0 - fresnel) * transmission; - result = CLOSURE_DEFAULT; + vec3 base_color_tint = tint_from_color(base_color.rgb); - /* This will tag the whole eval for optimisation. */ - if (do_diffuse == 0.0) { - out_Diffuse_0.radiance = vec3(0); - } - if (do_clearcoat == 0.0) { - out_Glossy_2.radiance = vec3(0); - } - if (do_refraction == 0.0) { - out_Refraction_3.radiance = vec3(0); + vec2 split_sum = brdf_lut(NV, roughness); + + ClosureTransparency transparency_data; + transparency_data.weight = weight; + transparency_data.transmittance = vec3(1.0 - alpha); + transparency_data.holdout = 0.0; + + weight *= alpha; + + ClosureEmission emission_data; + emission_data.weight = weight; + emission_data.emission = emission.rgb * emission_strength; + + /* Diffuse. */ + ClosureDiffuse diffuse_data; + diffuse_data.weight = diffuse_weight * weight; + diffuse_data.color = mix(base_color.rgb, subsurface_color.rgb, subsurface); + /* Sheen Coarse approximation: We reuse the diffuse radiance and just scale it. */ + vec3 sheen_color = mix(vec3(1.0), base_color_tint, sheen_tint); + diffuse_data.color += sheen * sheen_color * principled_sheen(NV); + diffuse_data.N = N; + diffuse_data.sss_radius = subsurface_radius * subsurface; + diffuse_data.sss_id = uint(do_sss); + + /* NOTE(@fclem): We need to blend the reflection color but also need to avoid applying the + * weights so we compule the ratio. */ + float reflection_weight = specular_weight + glass_reflection_weight; + float reflection_weight_inv = safe_rcp(reflection_weight); + specular_weight *= reflection_weight_inv; + glass_reflection_weight *= reflection_weight_inv; + + /* Reflection. */ + ClosureReflection reflection_data; + reflection_data.weight = reflection_weight * weight; + reflection_data.N = N; + reflection_data.roughness = roughness; + if (true) { + vec3 dielectric_f0_color = mix(vec3(1.0), base_color_tint, specular_tint); + vec3 metallic_f0_color = base_color.rgb; + vec3 f0 = mix((0.08 * specular) * dielectric_f0_color, metallic_f0_color, metallic); + /* Cycles does this blending using the microfacet fresnel factor. However, our fresnel + * is already baked inside the split sum LUT. We approximate by changing the f90 color + * directly in a non linear fashion. */ + vec3 f90 = mix(f0, vec3(1.0), fast_sqrt(specular)); + + vec3 reflection_brdf = (do_multiscatter != 0.0) ? F_brdf_multi_scatter(f0, f90, split_sum) : + F_brdf_single_scatter(f0, f90, split_sum); + reflection_data.color = reflection_brdf * specular_weight; } + if (true) { + /* Poor approximation since we baked the LUT using a fixed IOR. */ + vec3 f0 = mix(vec3(1.0), base_color.rgb, specular_tint); + vec3 f90 = vec3(1.0); - vec3 V = cameraVec(worldPosition); + vec3 glass_brdf = (do_multiscatter != 0.0) ? F_brdf_multi_scatter(f0, f90, split_sum) : + F_brdf_single_scatter(f0, f90, split_sum); - /* Glossy_1 will always be evaluated. */ - float NV = dot(in_Glossy_1.N, V); - - vec3 base_color_tint = tint_from_color(base_color.rgb); + /* Avoid 3 glossy evaluation. Use the same closure for glass reflection. */ + reflection_data.color += glass_brdf * glass_reflection_weight; + } - float fresnel = (do_multiscatter != 0.0) ? - btdf_lut(NV, in_Glossy_1.roughness, in_Refraction_3.ior).y : - F_eta(in_Refraction_3.ior, NV); - - { - /* Glossy reflections. - * Separate Glass reflections and main specular reflections to match Cycles renderpasses. */ - out_Glossy_1.radiance = closure_mask_ssr_radiance(out_Glossy_1.radiance, ssr_id); - - vec2 split_sum = brdf_lut(NV, roughness); - - vec3 glossy_radiance_final = vec3(0.0); - if (transmission > 1e-5) { - /* Glass Reflection: Reuse radiance from Glossy1. */ - vec3 out_glass_refl_radiance = out_Glossy_1.radiance; - - /* Poor approximation since we baked the LUT using a fixed IOR. */ - vec3 f0 = mix(vec3(1.0), base_color.rgb, specular_tint); - vec3 f90 = vec3(1); - - vec3 brdf = (do_multiscatter != 0.0) ? F_brdf_multi_scatter(f0, f90, split_sum) : - F_brdf_single_scatter(f0, f90, split_sum); - - out_glass_refl_radiance *= brdf; - out_glass_refl_radiance = render_pass_glossy_mask(vec3(1), out_glass_refl_radiance); - out_glass_refl_radiance *= fresnel * transmission; - glossy_radiance_final += out_glass_refl_radiance; - } - if (specular_weight > 1e-5) { - vec3 dielectric_f0_color = mix(vec3(1.0), base_color_tint, specular_tint); - vec3 metallic_f0_color = base_color.rgb; - vec3 f0 = mix((0.08 * specular) * dielectric_f0_color, metallic_f0_color, metallic); - /* Cycles does this blending using the microfacet fresnel factor. However, our fresnel - * is already baked inside the split sum LUT. We approximate using by modifying the - * changing the f90 color directly in a non linear fashion. */ - vec3 f90 = mix(f0, vec3(1), fast_sqrt(specular)); - - vec3 brdf = (do_multiscatter != 0.0) ? F_brdf_multi_scatter(f0, f90, split_sum) : - F_brdf_single_scatter(f0, f90, split_sum); - - out_Glossy_1.radiance *= brdf; - out_Glossy_1.radiance = render_pass_glossy_mask(vec3(1), out_Glossy_1.radiance); - out_Glossy_1.radiance *= specular_weight; - glossy_radiance_final += out_Glossy_1.radiance; - } - - closure_load_ssr_data( - glossy_radiance_final, in_Glossy_1.roughness, in_Glossy_1.N, ssr_id, result); + ClosureReflection clearcoat_data; + clearcoat_data.weight = clearcoat_weight * weight; + clearcoat_data.N = CN; + clearcoat_data.roughness = clearcoat_roughness; + if (true) { + float NV = dot(clearcoat_data.N, V); + vec2 split_sum = brdf_lut(NV, clearcoat_data.roughness); + vec3 brdf = F_brdf_single_scatter(vec3(0.04), vec3(1.0), split_sum); + clearcoat_data.color = brdf; } - if (diffuse_weight > 1e-5) { - /* Mask over all diffuse radiance. */ - out_Diffuse_0.radiance *= diffuse_weight; + /* Refraction. */ + ClosureRefraction refraction_data; + refraction_data.weight = glass_transmission_weight * weight; + float btdf = (do_multiscatter != 0.0) ? 1.0 : btdf_lut(NV, roughness, ior).x; - /* Sheen Coarse approximation: We reuse the diffuse radiance and just scale it. */ - vec3 sheen_color = mix(vec3(1), base_color_tint, sheen_tint); - vec3 out_sheen_radiance = out_Diffuse_0.radiance * principled_sheen(NV); - out_sheen_radiance = render_pass_diffuse_mask(vec3(1), out_sheen_radiance); - out_sheen_radiance *= sheen * sheen_color; - result.radiance += out_sheen_radiance; + refraction_data.color = base_color.rgb * btdf; + refraction_data.N = N; + refraction_data.roughness = do_multiscatter != 0.0 ? roughness : + max(roughness, transmission_roughness); + refraction_data.ior = ior; - /* Diffuse / Subsurface. */ - float scale = avg(sss_scale) * subsurface; - closure_load_sss_data(scale, out_Diffuse_0.radiance, in_Diffuse_0.albedo, int(sss_id), result); + if (do_diffuse == 0.0 && do_refraction == 0.0 && do_clearcoat != 0.0) { + /* Metallic & Clearcoat case. */ + result = closure_eval(reflection_data, clearcoat_data); } - - if (transmission > 1e-5) { - float btdf = (do_multiscatter != 0.0) ? - 1.0 : - btdf_lut(NV, in_Refraction_3.roughness, in_Refraction_3.ior).x; - /* TODO(@fclem): This could be going to a transmission render pass instead. */ - out_Refraction_3.radiance *= btdf; - out_Refraction_3.radiance = render_pass_glossy_mask(vec3(1), out_Refraction_3.radiance); - out_Refraction_3.radiance *= base_color.rgb; - /* Simulate 2nd transmission event. */ - out_Refraction_3.radiance *= (refractionDepth > 0.0) ? base_color.rgb : vec3(1); - out_Refraction_3.radiance *= (1.0 - fresnel) * transmission; - result.radiance += out_Refraction_3.radiance; + else if (do_diffuse == 0.0 && do_refraction == 0.0 && do_clearcoat == 0.0) { + /* Metallic case. */ + result = closure_eval(reflection_data); } - - if (clearcoat > 1e-5) { - float NV = dot(in_Glossy_2.N, V); - vec2 split_sum = brdf_lut(NV, in_Glossy_2.roughness); - vec3 brdf = F_brdf_single_scatter(vec3(0.04), vec3(1.0), split_sum); - - out_Glossy_2.radiance *= brdf * clearcoat * 0.25; - out_Glossy_2.radiance = render_pass_glossy_mask(vec3(1), out_Glossy_2.radiance); - result.radiance += out_Glossy_2.radiance; + else if (do_diffuse != 0.0 && do_refraction == 0.0 && do_clearcoat == 0.0) { + /* Dielectric case. */ + result = closure_eval(diffuse_data, reflection_data); } - - { - vec3 out_emission_radiance = render_pass_emission_mask(emission.rgb); - out_emission_radiance *= emission_strength; - result.radiance += out_emission_radiance; + else if (do_diffuse == 0.0 && do_refraction != 0.0 && do_clearcoat == 0.0) { + /* Glass case. */ + result = closure_eval(reflection_data, refraction_data); } - - result.transmittance = vec3(1.0 - alpha); - result.radiance *= alpha; - result.ssr_data.rgb *= alpha; -# ifdef USE_SSS - result.sss_albedo *= alpha; -# endif + else { + /* Un-optimized case. */ + result = closure_eval(diffuse_data, reflection_data, clearcoat_data, refraction_data); + } + result = closure_add(result, closure_eval(emission_data)); + result = closure_add(result, closure_eval(transparency_data)); } - -#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, ee, ff, result) (result = CLOSURE_DEFAULT) -/* clang-format on */ -#endif diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_refraction.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_refraction.glsl index 8a42a131f43..871fa00b3d4 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_refraction.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_refraction.glsl @@ -1,32 +1,15 @@ -#ifndef VOLUMETRICS -CLOSURE_EVAL_FUNCTION_DECLARE_1(node_bsdf_refraction, Refraction) - -void node_bsdf_refraction(vec4 color, float roughness, float ior, vec3 N, out Closure result) +void node_bsdf_refraction( + vec4 color, float roughness, float ior, vec3 N, float weight, out Closure result) { - CLOSURE_VARS_DECLARE_1(Refraction); - - in_Refraction_0.N = N; /* Normalized during eval. */ - in_Refraction_0.roughness = roughness; - in_Refraction_0.ior = ior; - - CLOSURE_EVAL_FUNCTION_1(node_bsdf_refraction, Refraction); + N = safe_normalize(N); - result = CLOSURE_DEFAULT; + ClosureRefraction refraction_data; + refraction_data.weight = weight; + refraction_data.color = color.rgb; + refraction_data.N = N; + refraction_data.roughness = roughness; + refraction_data.ior = ior; - out_Refraction_0.radiance = render_pass_glossy_mask(vec3(1.0), out_Refraction_0.radiance); - out_Refraction_0.radiance *= color.rgb; - /* Simulate 2nd absorption event. */ - out_Refraction_0.radiance *= (refractionDepth > 0.0) ? color.rgb : vec3(1.0); - - result.radiance = out_Refraction_0.radiance; - - /* TODO(@fclem): Try to not use this. */ - result.ssr_normal = normal_encode(mat3(ViewMatrix) * in_Refraction_0.N, - viewCameraVec(viewPosition)); + result = closure_eval(refraction_data); } - -#else -/* Stub refraction because it is not compatible with volumetrics. */ -# define node_bsdf_refraction(a, b, c, d, e) (e = CLOSURE_DEFAULT) -#endif diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_separate_hsv.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_separate_hsv.glsl index fb64e424c6c..180e0fd1940 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_separate_hsv.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_separate_hsv.glsl @@ -1,3 +1,5 @@ +#pragma BLENDER_REQUIRE(gpu_shader_material_color_util.glsl) + void separate_hsv(vec4 col, out float h, out float s, out float v) { vec4 hsv; 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 f0f2f79c60e..d791f067821 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,29 +1,6 @@ -#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)) { - CLOSURE_VARS_DECLARE_1(Glossy); - - vec3 vN = normal_decode(cl.ssr_normal, viewCameraVec(viewPosition)); - vec3 N = transform_direction(ViewMatrixInverse, vN); - - 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)); - outcol = vec4((spec_accum.rgb * cl.ssr_data.rgb) + cl.radiance, 1.0); - -# ifdef USE_SSS - outcol.rgb += cl.sss_irradiance.rgb * cl.sss_albedo; -# endif + outcol = closure_to_rgba(cl); + outalpha = outcol.a; } -#endif /* VOLUMETRICS */ 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 20b634aa801..c560dd01c4f 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 @@ -1,6 +1,3 @@ -#ifndef VOLUMETRICS - -CLOSURE_EVAL_FUNCTION_DECLARE_1(node_subsurface_scattering, Diffuse) void node_subsurface_scattering(vec4 color, float scale, @@ -8,25 +5,18 @@ void node_subsurface_scattering(vec4 color, float ior, float anisotropy, vec3 N, - float sss_id, + float weight, + float do_sss, out Closure result) { - CLOSURE_VARS_DECLARE_1(Diffuse); - - in_Diffuse_0.N = N; /* Normalized during eval. */ - in_Diffuse_0.albedo = color.rgb; - - CLOSURE_EVAL_FUNCTION_1(node_subsurface_scattering, Diffuse); + N = safe_normalize(N); - result = CLOSURE_DEFAULT; + ClosureDiffuse diffuse_data; + diffuse_data.weight = weight; + diffuse_data.color = color.rgb; + diffuse_data.N = N; + diffuse_data.sss_radius = radius * scale; + diffuse_data.sss_id = uint(do_sss); - 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); + result = closure_eval(diffuse_data); } - -#else -/* Stub subsurface scattering because it is not compatible with volumetrics. */ -# define node_subsurface_scattering(a, b, c, d, e, f, g, h) (h = CLOSURE_DEFAULT) -#endif diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_tangent.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_tangent.glsl index ff2dbc7ead3..4e4bf759ec9 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_tangent.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_tangent.glsl @@ -18,8 +18,8 @@ void node_tangentmap(vec4 attr_tangent, out vec3 tangent) tangent = normalize(attr_tangent.xyz); } -void node_tangent(vec3 N, vec3 orco, mat4 objmat, out vec3 T) +void node_tangent(vec3 orco, out vec3 T) { - T = (objmat * vec4(orco, 0.0)).xyz; - T = cross(N, normalize(cross(T, N))); + T = transform_direction(ModelMatrix, orco); + T = cross(g_data.N, normalize(cross(T, g_data.N))); } diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_tex_brick.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_tex_brick.glsl index e252e5ba726..edc2fa32177 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_tex_brick.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_tex_brick.glsl @@ -1,3 +1,6 @@ +#pragma BLENDER_REQUIRE(gpu_shader_material_math_util.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_material_hash.glsl) + vec2 calc_brick_texture(vec3 p, float mortar_size, float mortar_smooth, diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_tex_environment.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_tex_environment.glsl index 434e07e7b86..89091316823 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_tex_environment.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_tex_environment.glsl @@ -1,19 +1,5 @@ -void node_tex_environment_texco(vec3 viewvec, out vec3 worldvec) -{ -#ifdef MESH_SHADER - worldvec = worldPosition; -#else - vec4 v = (ProjectionMatrix[3][3] == 0.0) ? vec4(viewvec, 1.0) : vec4(0.0, 0.0, 1.0, 1.0); - vec4 co_homogeneous = (ProjectionMatrixInverse * v); +#pragma BLENDER_REQUIRE(gpu_shader_material_math_util.glsl) - vec3 co = co_homogeneous.xyz / co_homogeneous.w; -# if defined(WORLD_BACKGROUND) || defined(PROBE_CAPTURE) - worldvec = mat3(ViewMatrixInverse) * co; -# else - worldvec = mat3(ModelMatrixInverse) * (mat3(ViewMatrixInverse) * co); -# endif -#endif -} void node_tex_environment_equirectangular(vec3 co, out vec3 uv) { 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 586385b7e86..1552a2facc3 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 @@ -1,3 +1,6 @@ +#pragma BLENDER_REQUIRE(gpu_shader_material_hash.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_material_noise.glsl) + /* 1D Musgrave fBm * * H: fractal increment parameter diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_tex_noise.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_tex_noise.glsl index 5745f11ede4..c90b2211dcf 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_tex_noise.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_tex_noise.glsl @@ -1,3 +1,7 @@ +#pragma BLENDER_REQUIRE(gpu_shader_material_hash.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_material_noise.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_material_fractal_noise.glsl) + /* The following offset functions generate random offsets to be added to texture * coordinates to act as a seed since the noise functions don't have seed values. * A seed value is needed for generating distortion textures and color outputs. diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_tex_voronoi.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_tex_voronoi.glsl index c8219848e29..dd12b602edf 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_tex_voronoi.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_tex_voronoi.glsl @@ -1,3 +1,6 @@ +#pragma BLENDER_REQUIRE(gpu_shader_material_hash.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_material_math_util.glsl) + /* * Original code is under the MIT License, Copyright (c) 2013 Inigo Quilez. * diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_tex_wave.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_tex_wave.glsl index 070f42a5e30..eed98232d0b 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_tex_wave.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_tex_wave.glsl @@ -1,3 +1,7 @@ +#pragma BLENDER_REQUIRE(gpu_shader_material_hash.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_material_noise.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_material_fractal_noise.glsl) + float calc_wave(vec3 p, float distortion, float detail, diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_tex_white_noise.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_tex_white_noise.glsl index b11d13a0413..030b58f0736 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_tex_white_noise.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_tex_white_noise.glsl @@ -1,3 +1,5 @@ +#pragma BLENDER_REQUIRE(gpu_shader_material_hash.glsl) + /* White Noise */ void node_white_noise_1d(vec3 vector, float w, out float value, out vec4 color) diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_texture_coordinates.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_texture_coordinates.glsl index a8ef9687b0a..a3666164cf7 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_texture_coordinates.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_texture_coordinates.glsl @@ -1,38 +1,5 @@ -vec3 mtex_2d_mapping(vec3 vec) -{ - return vec3(vec.xy * 0.5 + vec2(0.5), vec.z); -} - -void generated_from_orco(vec3 orco, out vec3 generated) -{ -#ifdef VOLUMETRICS -# ifdef MESH_SHADER - generated = volumeObjectLocalCoord; -# else - generated = worldPosition; -# endif -#else - generated = orco; -#endif -} -void generated_texco(vec3 I, vec3 attr_orco, out vec3 generated) -{ - vec4 v = (ProjectionMatrix[3][3] == 0.0) ? vec4(I, 1.0) : vec4(0.0, 0.0, 1.0, 1.0); - vec4 co_homogeneous = (ProjectionMatrixInverse * v); - vec4 co = vec4(co_homogeneous.xyz / co_homogeneous.w, 0.0); - co.xyz = normalize(co.xyz); -#if defined(WORLD_BACKGROUND) || defined(PROBE_CAPTURE) - generated = (ViewMatrixInverse * co).xyz; -#else - generated_from_orco(attr_orco, generated); -#endif -} - -void node_tex_coord(vec3 I, - vec3 wN, - mat4 obmatinv, - vec4 camerafac, +void node_tex_coord(mat4 obmatinv, vec3 attr_orco, vec3 attr_uv, out vec3 generated, @@ -44,49 +11,10 @@ void node_tex_coord(vec3 I, out vec3 reflection) { generated = attr_orco; - normal = normalize(normal_world_to_object(wN)); + normal = normal_world_to_object(g_data.N); uv = attr_uv; - object = (obmatinv * (ViewMatrixInverse * vec4(I, 1.0))).xyz; - camera = vec3(I.xy, -I.z); - vec4 projvec = ProjectionMatrix * vec4(I, 1.0); - window = vec3(mtex_2d_mapping(projvec.xyz / projvec.w).xy * camerafac.xy + camerafac.zw, 0.0); - reflection = -reflect(cameraVec(worldPosition), normalize(wN)); -} - -void node_tex_coord_background(vec3 I, - vec3 N, - mat4 obmatinv, - vec4 camerafac, - vec3 attr_orco, - vec3 attr_uv, - out vec3 generated, - out vec3 normal, - out vec3 uv, - out vec3 object, - out vec3 camera, - out vec3 window, - out vec3 reflection) -{ - vec4 v = (ProjectionMatrix[3][3] == 0.0) ? vec4(I, 1.0) : vec4(0.0, 0.0, 1.0, 1.0); - vec4 co_homogeneous = (ProjectionMatrixInverse * v); - - vec4 co = vec4(co_homogeneous.xyz / co_homogeneous.w, 0.0); - - co = normalize(co); - - vec3 coords = (ViewMatrixInverse * co).xyz; - - generated = coords; - normal = -coords; - uv = vec3(attr_uv.xy, 0.0); - object = (obmatinv * vec4(coords, 1.0)).xyz; - - camera = vec3(co.xy, -co.z); - window = vec3(mtex_2d_mapping(I).xy * camerafac.xy + camerafac.zw, 0.0); - - reflection = -coords; + object = transform_point((obmatinv[3][3] == 0.0) ? ModelMatrixInverse : obmatinv, g_data.P); + camera = coordinate_camera(g_data.P); + window = coordinate_screen(g_data.P); + reflection = coordinate_reflect(g_data.P, g_data.N); } - -#if defined(WORLD_BACKGROUND) || (defined(PROBE_CAPTURE) && !defined(MESH_SHADER)) -# define node_tex_coord node_tex_coord_background -#endif diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_toon.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_toon.glsl index bbfc99ccc73..ae7d4fc5631 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_toon.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_toon.glsl @@ -1,9 +1,15 @@ -#ifndef VOLUMETRICS -void node_bsdf_toon(vec4 color, float size, float tsmooth, vec3 N, out Closure result) + +void node_bsdf_toon( + vec4 color, float size, float tsmooth, vec3 N, float weight, out Closure result) { - node_bsdf_diffuse(color, 0.0, N, result); + N = safe_normalize(N); + + /* Fallback to diffuse. */ + ClosureDiffuse diffuse_data; + diffuse_data.weight = weight; + diffuse_data.color = color.rgb; + diffuse_data.N = N; + diffuse_data.sss_id = 0u; + + result = closure_eval(diffuse_data); } -#else -/* Stub toon because it is not compatible with volumetrics. */ -# define node_bsdf_toon(a, b, c, d, e) (e = CLOSURE_DEFAULT) -#endif diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_translucent.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_translucent.glsl index 80bd3941b22..0cc162f42f5 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_translucent.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_translucent.glsl @@ -1,21 +1,12 @@ -#ifndef VOLUMETRICS -CLOSURE_EVAL_FUNCTION_DECLARE_1(node_bsdf_translucent, Translucent) - -void node_bsdf_translucent(vec4 color, vec3 N, out Closure result) +void node_bsdf_translucent(vec4 color, vec3 N, float weight, out Closure result) { - CLOSURE_VARS_DECLARE_1(Translucent); - - in_Translucent_0.N = -N; /* Normalized during eval. */ + N = safe_normalize(N); - CLOSURE_EVAL_FUNCTION_1(node_bsdf_translucent, Translucent); + ClosureTranslucent translucent_data; + translucent_data.weight = weight; + translucent_data.color = color.rgb; + translucent_data.N = -N; - result = CLOSURE_DEFAULT; - closure_load_ssr_data(vec3(0.0), 0.0, -in_Translucent_0.N, -1.0, result); - result.radiance = render_pass_diffuse_mask(color.rgb, out_Translucent_0.radiance * color.rgb); + result = closure_eval(translucent_data); } - -#else -/* Stub translucent because it is not compatible with volumetrics. */ -# define node_bsdf_translucent(a, b, c) (c = CLOSURE_DEFAULT) -#endif diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_transparent.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_transparent.glsl index 9040f62bd3f..c650f10b6e4 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_transparent.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_transparent.glsl @@ -1,11 +1,10 @@ -#ifndef VOLUMETRICS -void node_bsdf_transparent(vec4 color, out Closure result) + +void node_bsdf_transparent(vec4 color, float weight, out Closure result) { - result = CLOSURE_DEFAULT; - result.radiance = vec3(0.0); - result.transmittance = abs(color.rgb); + ClosureTransparency transparency_data; + transparency_data.weight = weight; + transparency_data.transmittance = color.rgb; + transparency_data.holdout = 0.0; + + result = closure_eval(transparency_data); } -#else -/* Stub transparent because it is not compatible with volumetrics. */ -# define node_bsdf_transparent(a, b) (b = CLOSURE_DEFAULT) -#endif diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_vector_displacement.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_vector_displacement.glsl index 4b5ed172081..0ff074bc04f 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_vector_displacement.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_vector_displacement.glsl @@ -1,30 +1,22 @@ -void node_vector_displacement_tangent(vec4 vector, - float midlevel, - float scale, - vec4 tangent, - vec3 normal, - mat4 obmat, - mat4 viewmat, - out vec3 result) +void node_vector_displacement_tangent( + vec4 vector, float midlevel, float scale, vec4 T, out vec3 result) { - /* TODO(fclem): this is broken. revisit latter. */ - vec3 N_object = normalize(((vec4(normal, 0.0) * viewmat) * obmat).xyz); - vec3 T_object = normalize(((vec4(tangent.xyz, 0.0) * viewmat) * obmat).xyz); - vec3 B_object = tangent.w * normalize(cross(N_object, T_object)); + vec3 oN = normalize(normal_world_to_object(g_data.N)); + vec3 oT = normalize(normal_world_to_object(T.xyz)); + vec3 oB = T.w * normalize(cross(oN, oT)); - vec3 offset = (vector.xyz - vec3(midlevel)) * scale; - result = offset.x * T_object + offset.y * N_object + offset.z * B_object; - result = (obmat * vec4(result, 0.0)).xyz; + result = (vector.xyz - midlevel) * scale; + result = result.x * oT + result.y * oN + result.z * oB; + result = transform_point(ModelMatrix, result); } -void node_vector_displacement_object( - vec4 vector, float midlevel, float scale, mat4 obmat, out vec3 result) +void node_vector_displacement_object(vec4 vector, float midlevel, float scale, out vec3 result) { - result = (vector.xyz - vec3(midlevel)) * scale; - result = (obmat * vec4(result, 0.0)).xyz; + result = (vector.xyz - midlevel) * scale; + result = transform_point(ModelMatrix, result); } void node_vector_displacement_world(vec4 vector, float midlevel, float scale, out vec3 result) { - result = (vector.xyz - vec3(midlevel)) * scale; + result = (vector.xyz - midlevel) * scale; } diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_vector_math.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_vector_math.glsl index 4ad5d4232de..8f6bf17f195 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_vector_math.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_vector_math.glsl @@ -1,3 +1,5 @@ +#pragma BLENDER_REQUIRE(gpu_shader_material_math_util.glsl) + void vector_math_add(vec3 a, vec3 b, vec3 c, float scale, out vec3 outVector, out float outValue) { outVector = a + b; diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_vector_rotate.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_vector_rotate.glsl index 41ad16cce0b..ff0fb1c0418 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_vector_rotate.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_vector_rotate.glsl @@ -1,3 +1,5 @@ +#pragma BLENDER_REQUIRE(gpu_shader_material_math_util.glsl) + vec3 rotate_around_axis(vec3 p, vec3 axis, float angle) { float costheta = cos(angle); diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_velvet.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_velvet.glsl index 989f18b881a..97726bfe66f 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_velvet.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_velvet.glsl @@ -1,9 +1,14 @@ -#ifndef VOLUMETRICS -void node_bsdf_velvet(vec4 color, float sigma, vec3 N, out Closure result) + +void node_bsdf_velvet(vec4 color, float roughness, vec3 N, float weight, out Closure result) { - node_bsdf_diffuse(color, 0.0, N, result); + N = safe_normalize(N); + + /* Fallback to diffuse. */ + ClosureDiffuse diffuse_data; + diffuse_data.weight = weight; + diffuse_data.color = color.rgb; + diffuse_data.N = N; + diffuse_data.sss_id = 0u; + + result = closure_eval(diffuse_data); } -#else -/* Stub velvet because it is not compatible with volumetrics. */ -# define node_bsdf_velvet(a, b, c, d) (d = CLOSURE_DEFAULT) -#endif diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_volume_absorption.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_volume_absorption.glsl index e6c0880cd07..8fd2e179187 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_volume_absorption.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_volume_absorption.glsl @@ -1,8 +1,9 @@ -void node_volume_absorption(vec4 color, float density, out Closure result) + +void node_volume_absorption(vec4 color, float density, float weight, out Closure result) { -#ifdef VOLUMETRICS - result = Closure((1.0 - color.rgb) * density, vec3(0.0), vec3(0.0), 0.0); -#else - result = CLOSURE_DEFAULT; -#endif + ClosureVolumeAbsorption volume_absorption_data; + volume_absorption_data.weight = weight; + volume_absorption_data.absorption = (1.0 - color.rgb) * density; + + result = closure_eval(volume_absorption_data); } diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_volume_info.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_volume_info.glsl index e6d7b9d3721..464cf5227b4 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_volume_info.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_volume_info.glsl @@ -4,14 +4,8 @@ uniform vec3 volumeColor = vec3(1.0); uniform vec2 volumeTemperature = vec2(0.0); /* Generic volume attribute. */ -void node_attribute_volume(sampler3D tex, mat4 transform, out vec3 outvec) +void node_attribute_volume(sampler3D tex, mat4 transform, vec3 cos, out vec3 outvec) { -#if defined(MESH_SHADER) && defined(VOLUMETRICS) - vec3 cos = volumeObjectLocalCoord; -#else - vec3 cos = vec3(0.0); -#endif - /* Optional per-grid transform. */ if (transform[3][3] != 0.0) { cos = (transform * vec4(cos, 1.0)).xyz; @@ -21,14 +15,8 @@ void node_attribute_volume(sampler3D tex, mat4 transform, out vec3 outvec) } /* Special color attribute for smoke. */ -void node_attribute_volume_color(sampler3D tex, mat4 transform, out vec3 outvec) +void node_attribute_volume_color(sampler3D tex, mat4 transform, vec3 cos, out vec3 outvec) { -#if defined(MESH_SHADER) && defined(VOLUMETRICS) - vec3 cos = volumeObjectLocalCoord; -#else - vec3 cos = vec3(0.0); -#endif - /* Optional per-grid transform. */ if (transform[3][3] != 0.0) { cos = (transform * vec4(cos, 1.0)).xyz; @@ -44,14 +32,8 @@ void node_attribute_volume_color(sampler3D tex, mat4 transform, out vec3 outvec) } /* Special temperature attribute for smoke. */ -void node_attribute_volume_temperature(sampler3D tex, mat4 transform, out float outf) +void node_attribute_volume_temperature(sampler3D tex, mat4 transform, vec3 cos, out float outf) { -#if defined(MESH_SHADER) && defined(VOLUMETRICS) - vec3 cos = volumeObjectLocalCoord; -#else - vec3 cos = vec3(0.0); -#endif - /* Optional per-grid transform. */ if (transform[3][3] != 0.0) { cos = (transform * vec4(cos, 1.0)).xyz; diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_volume_principled.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_volume_principled.glsl index 884d5415c51..1127c34b3ac 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_volume_principled.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_volume_principled.glsl @@ -1,3 +1,5 @@ +#pragma BLENDER_REQUIRE(gpu_shader_material_blackbody.glsl) + void node_volume_principled(vec4 color, float density, float anisotropy, @@ -7,6 +9,7 @@ void node_volume_principled(vec4 color, float blackbody_intensity, vec4 blackbody_tint, float temperature, + float weight, float density_attribute, vec4 color_attribute, float temperature_attribute, @@ -14,7 +17,6 @@ void node_volume_principled(vec4 color, float layer, out Closure result) { -#ifdef VOLUMETRICS vec3 absorption_coeff = vec3(0.0); vec3 scatter_coeff = vec3(0.0); vec3 emission_coeff = vec3(0.0); @@ -60,8 +62,18 @@ void node_volume_principled(vec4 color, } } - result = Closure(absorption_coeff, scatter_coeff, emission_coeff, anisotropy); -#else - result = CLOSURE_DEFAULT; -#endif + ClosureVolumeScatter volume_scatter_data; + volume_scatter_data.weight = weight; + volume_scatter_data.scattering = scatter_coeff; + volume_scatter_data.anisotropy = anisotropy; + + ClosureVolumeAbsorption volume_absorption_data; + volume_absorption_data.weight = weight; + volume_absorption_data.absorption = absorption_coeff; + + ClosureEmission emission_data; + emission_data.weight = weight; + emission_data.emission = emission_coeff; + + result = closure_eval(volume_scatter_data, volume_absorption_data, emission_data); } diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_volume_scatter.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_volume_scatter.glsl index 02c54658be5..f01ead3618f 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_volume_scatter.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_volume_scatter.glsl @@ -1,8 +1,11 @@ -void node_volume_scatter(vec4 color, float density, float anisotropy, out Closure result) + +void node_volume_scatter( + vec4 color, float density, float anisotropy, float weight, out Closure result) { -#ifdef VOLUMETRICS - result = Closure(vec3(0.0), color.rgb * density, vec3(0.0), anisotropy); -#else - result = CLOSURE_DEFAULT; -#endif + ClosureVolumeScatter volume_scatter_data; + volume_scatter_data.weight = weight; + volume_scatter_data.scattering = color.rgb * density; + volume_scatter_data.anisotropy = anisotropy; + + result = closure_eval(volume_scatter_data); } diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_wireframe.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_wireframe.glsl index e2789e046e1..0c02dab3ae4 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_wireframe.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_wireframe.glsl @@ -1,20 +1,21 @@ -#ifndef VOLUMETRICS -void node_wireframe(float size, vec2 barycentric, vec3 barycentric_dist, out float fac) + +void node_wireframe(float size, out float fac) { - vec3 barys = barycentric.xyy; - barys.z = 1.0 - barycentric.x - barycentric.y; + vec3 barys = g_data.barycentric_coords.xyy; + barys.z = 1.0 - barys.x - barys.y; size *= 0.5; - vec3 s = step(-size, -barys * barycentric_dist); + vec3 s = step(-size, -barys * g_data.barycentric_dists); fac = max(s.x, max(s.y, s.z)); } -void node_wireframe_screenspace(float size, vec2 barycentric, out float fac) +void node_wireframe_screenspace(float size, out float fac) { - vec3 barys = barycentric.xyy; - barys.z = 1.0 - barycentric.x - barycentric.y; + vec3 barys = g_data.barycentric_coords.xyy; + barys.z = 1.0 - barys.x - barys.y; +#ifdef GPU_FRAGMENT_SHADER size *= (1.0 / 3.0); vec3 dx = dFdx(barys); vec3 dy = dFdy(barys); @@ -23,9 +24,7 @@ void node_wireframe_screenspace(float size, vec2 barycentric, out float fac) vec3 s = step(-deltas * size, -barys); fac = max(s.x, max(s.y, s.z)); -} #else -/* Stub wireframe because it is not compatible with volumetrics. */ -# define node_wireframe(a, b, c, d) (d = 0.0) -# define node_wireframe_screenspace(a, b, c) (c = 0.0) + fac = 1.0; #endif +} diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_world_normals.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_world_normals.glsl index 40e46bc250c..5a0aeb2f932 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_world_normals.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_world_normals.glsl @@ -1,25 +1,5 @@ -/* TODO: clean this `ifdef` mess. */ + void world_normals_get(out vec3 N) { -#ifndef VOLUMETRICS -# ifdef HAIR_SHADER - vec3 B = normalize(cross(worldNormal, hairTangent)); - float cos_theta; - if (hairThicknessRes == 1) { - vec4 rand = texelfetch_noise_tex(gl_FragCoord.xy); - /* Random cosine normal distribution on the hair surface. */ - cos_theta = rand.x * 2.0 - 1.0; - } - else { - /* Shade as a cylinder. */ - cos_theta = hairThickTime / hairThickness; - } - float sin_theta = sqrt(max(0.0, 1.0 - cos_theta * cos_theta)); - N = normalize(worldNormal * sin_theta + B * cos_theta); -# else - N = gl_FrontFacing ? worldNormal : -worldNormal; -# endif -#else - generated_from_orco(vec3(0.0), N); -#endif + N = g_data.N; } diff --git a/source/blender/imbuf/IMB_colormanagement.h b/source/blender/imbuf/IMB_colormanagement.h index 7cf2c02e657..97030d44047 100644 --- a/source/blender/imbuf/IMB_colormanagement.h +++ b/source/blender/imbuf/IMB_colormanagement.h @@ -256,10 +256,6 @@ void IMB_colormanagement_display_settings_from_ctx( struct ColorManagedViewSettings **r_view_settings, struct ColorManagedDisplaySettings **r_display_settings); -const char *IMB_colormanagement_get_display_colorspace_name( - const struct ColorManagedViewSettings *view_settings, - const struct ColorManagedDisplaySettings *display_settings); - /** * Acquire display buffer for given image buffer using specified view and display settings. */ diff --git a/source/blender/imbuf/intern/colormanagement.c b/source/blender/imbuf/intern/colormanagement.c index 193fda01816..53aa74edc61 100644 --- a/source/blender/imbuf/intern/colormanagement.c +++ b/source/blender/imbuf/intern/colormanagement.c @@ -794,9 +794,8 @@ void IMB_colormanagement_display_settings_from_ctx( } } -const char *IMB_colormanagement_get_display_colorspace_name( - const ColorManagedViewSettings *view_settings, - const ColorManagedDisplaySettings *display_settings) +static const char *get_display_colorspace_name(const ColorManagedViewSettings *view_settings, + const ColorManagedDisplaySettings *display_settings) { OCIO_ConstConfigRcPtr *config = OCIO_getCurrentConfig(); @@ -815,8 +814,7 @@ static ColorSpace *display_transform_get_colorspace( const ColorManagedViewSettings *view_settings, const ColorManagedDisplaySettings *display_settings) { - const char *colorspace_name = IMB_colormanagement_get_display_colorspace_name(view_settings, - display_settings); + const char *colorspace_name = get_display_colorspace_name(view_settings, display_settings); if (colorspace_name) { return colormanage_colorspace_get_named(colorspace_name); @@ -837,8 +835,14 @@ static OCIO_ConstCPUProcessorRcPtr *create_display_buffer_processor(const char * const float scale = (exposure == 0.0f) ? 1.0f : powf(2.0f, exposure); const float exponent = (gamma == 1.0f) ? 1.0f : 1.0f / max_ff(FLT_EPSILON, gamma); - OCIO_ConstProcessorRcPtr *processor = OCIO_createDisplayProcessor( - config, from_colorspace, view_transform, display, (use_look) ? look : "", scale, exponent); + OCIO_ConstProcessorRcPtr *processor = OCIO_createDisplayProcessor(config, + from_colorspace, + view_transform, + display, + (use_look) ? look : "", + scale, + exponent, + false); OCIO_configRelease(config); @@ -923,10 +927,8 @@ static OCIO_ConstCPUProcessorRcPtr *display_from_scene_linear_processor( OCIO_ConstProcessorRcPtr *processor = NULL; if (view_name && config) { - const char *view_colorspace = OCIO_configGetDisplayColorSpaceName( - config, display->name, view_name); - processor = OCIO_configGetProcessorWithNames( - config, global_role_scene_linear, view_colorspace); + processor = OCIO_createDisplayProcessor( + config, global_role_scene_linear, view_name, display->name, NULL, 1.0f, 1.0f, false); OCIO_configRelease(config); } @@ -955,10 +957,8 @@ static OCIO_ConstCPUProcessorRcPtr *display_to_scene_linear_processor(ColorManag OCIO_ConstProcessorRcPtr *processor = NULL; if (view_name && config) { - const char *view_colorspace = OCIO_configGetDisplayColorSpaceName( - config, display->name, view_name); - processor = OCIO_configGetProcessorWithNames( - config, view_colorspace, global_role_scene_linear); + processor = OCIO_createDisplayProcessor( + config, global_role_scene_linear, view_name, display->name, NULL, 1.0f, 1.0f, true); OCIO_configRelease(config); } @@ -1730,8 +1730,7 @@ static bool is_ibuf_rect_in_display_space(ImBuf *ibuf, if ((view_settings->flag & COLORMANAGE_VIEW_USE_CURVES) == 0 && view_settings->exposure == 0.0f && view_settings->gamma == 1.0f) { const char *from_colorspace = ibuf->rect_colorspace->name; - const char *to_colorspace = IMB_colormanagement_get_display_colorspace_name(view_settings, - display_settings); + const char *to_colorspace = get_display_colorspace_name(view_settings, display_settings); ColorManagedLook *look_descr = colormanage_look_get_named(view_settings->look); if (look_descr != NULL && !STREQ(look_descr->process_space, "")) { return false; diff --git a/source/blender/io/alembic/ABC_alembic.h b/source/blender/io/alembic/ABC_alembic.h index d7d3adefc33..ded3258ff18 100644 --- a/source/blender/io/alembic/ABC_alembic.h +++ b/source/blender/io/alembic/ABC_alembic.h @@ -99,15 +99,19 @@ void ABC_get_transform(struct CacheReader *reader, double time, float scale); +typedef struct ABCReadParams { + double time; + int read_flags; + const char *velocity_name; + float velocity_scale; +} ABCReadParams; + /* Either modifies existing_mesh in-place or constructs a new mesh. */ struct Mesh *ABC_read_mesh(struct CacheReader *reader, struct Object *ob, struct Mesh *existing_mesh, - double time, - const char **err_str, - int read_flags, - const char *velocity_name, - float velocity_scale); + const ABCReadParams *params, + const char **err_str); bool ABC_mesh_topology_changed(struct CacheReader *reader, struct Object *ob, diff --git a/source/blender/io/alembic/intern/alembic_capi.cc b/source/blender/io/alembic/intern/alembic_capi.cc index fe459ce4370..0d4e1d04db0 100644 --- a/source/blender/io/alembic/intern/alembic_capi.cc +++ b/source/blender/io/alembic/intern/alembic_capi.cc @@ -785,20 +785,21 @@ static ISampleSelector sample_selector_for_time(chrono_t time) Mesh *ABC_read_mesh(CacheReader *reader, Object *ob, Mesh *existing_mesh, - const double time, - const char **err_str, - const int read_flag, - const char *velocity_name, - const float velocity_scale) + const ABCReadParams *params, + const char **err_str) { AbcObjectReader *abc_reader = get_abc_reader(reader, ob, err_str); if (abc_reader == nullptr) { return nullptr; } - ISampleSelector sample_sel = sample_selector_for_time(time); - return abc_reader->read_mesh( - existing_mesh, sample_sel, read_flag, velocity_name, velocity_scale, err_str); + ISampleSelector sample_sel = sample_selector_for_time(params->time); + return abc_reader->read_mesh(existing_mesh, + sample_sel, + params->read_flags, + params->velocity_name, + params->velocity_scale, + err_str); } bool ABC_mesh_topology_changed(CacheReader *reader, diff --git a/source/blender/io/common/CMakeLists.txt b/source/blender/io/common/CMakeLists.txt index 02bd5b2b81f..b1add38bf01 100644 --- a/source/blender/io/common/CMakeLists.txt +++ b/source/blender/io/common/CMakeLists.txt @@ -7,6 +7,8 @@ set(INC ../../blenlib ../../depsgraph ../../makesdna + ../../../../intern/guardedalloc + ../../../../extern/fast_float ) set(INC_SYS @@ -17,9 +19,11 @@ set(SRC intern/dupli_parent_finder.cc intern/dupli_persistent_id.cc intern/object_identifier.cc + intern/string_utils.cc IO_abstract_hierarchy_iterator.h IO_dupli_persistent_id.hh + IO_string_utils.hh IO_types.h intern/dupli_parent_finder.hh ) @@ -38,6 +42,7 @@ if(WITH_GTESTS) intern/abstract_hierarchy_iterator_test.cc intern/hierarchy_context_order_test.cc intern/object_identifier_test.cc + intern/string_utils_test.cc ) set(TEST_INC ../../blenloader diff --git a/source/blender/io/common/IO_string_utils.hh b/source/blender/io/common/IO_string_utils.hh new file mode 100644 index 00000000000..25f1f01c6ed --- /dev/null +++ b/source/blender/io/common/IO_string_utils.hh @@ -0,0 +1,69 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "BLI_string_ref.hh" + +/* + * Various text parsing utilities commonly used by text-based input formats. + */ + +namespace blender::io { + +/** + * Fetches next line from an input string buffer. + * + * The returned line will not have '\n' characters at the end; + * the `buffer` is modified to contain remaining text without + * the input line. + * + * Note that backslash (\) character is treated as a line + * continuation, similar to OBJ file format or a C preprocessor. + */ +StringRef read_next_line(StringRef &buffer); + +/** + * Drop leading white-space from a StringRef. + * Note that backslash character is considered white-space. + */ +StringRef drop_whitespace(StringRef str); + +/** + * Drop leading non-white-space from a StringRef. + * Note that backslash character is considered white-space. + */ +StringRef drop_non_whitespace(StringRef str); + +/** + * Parse an integer from an input string. + * The parsed result is stored in `dst`. The function skips + * leading white-space unless `skip_space=false`. If the + * number can't be parsed (invalid syntax, out of range), + * `fallback` value is stored instead. + * + * Returns the remainder of the input string after parsing. + */ +StringRef parse_int(StringRef str, int fallback, int &dst, bool skip_space = true); + +/** + * Parse a float from an input string. + * The parsed result is stored in `dst`. The function skips + * leading white-space unless `skip_space=false`. If the + * number can't be parsed (invalid syntax, out of range), + * `fallback` value is stored instead. + * + * Returns the remainder of the input string after parsing. + */ +StringRef parse_float(StringRef str, float fallback, float &dst, bool skip_space = true); + +/** + * Parse a number of white-space separated floats from an input string. + * The parsed `count` numbers are stored in `dst`. If a + * number can't be parsed (invalid syntax, out of range), + * `fallback` value is stored instead. + * + * Returns the remainder of the input string after parsing. + */ +StringRef parse_floats(StringRef str, float fallback, float *dst, int count); + +} // namespace blender::io diff --git a/source/blender/io/common/intern/string_utils.cc b/source/blender/io/common/intern/string_utils.cc new file mode 100644 index 00000000000..01107b0866e --- /dev/null +++ b/source/blender/io/common/intern/string_utils.cc @@ -0,0 +1,99 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "IO_string_utils.hh" + +/* Note: we could use C++17 <charconv> from_chars to parse + * floats, but even if some compilers claim full support, + * their standard libraries are not quite there yet. + * LLVM/libc++ only has a float parser since LLVM 14, + * and gcc/libstdc++ since 11.1. So until at least these are + * the mininum spec, use an external library. */ +#include "fast_float.h" +#include <charconv> + +namespace blender::io { + +StringRef read_next_line(StringRef &buffer) +{ + const char *start = buffer.begin(); + const char *end = buffer.end(); + size_t len = 0; + char prev = 0; + const char *ptr = start; + while (ptr < end) { + char c = *ptr++; + if (c == '\n' && prev != '\\') { + break; + } + prev = c; + ++len; + } + + buffer = StringRef(ptr, end); + return StringRef(start, len); +} + +static bool is_whitespace(char c) +{ + return c <= ' ' || c == '\\'; +} + +StringRef drop_whitespace(StringRef str) +{ + while (!str.is_empty() && is_whitespace(str[0])) { + str = str.drop_prefix(1); + } + return str; +} + +StringRef drop_non_whitespace(StringRef str) +{ + while (!str.is_empty() && !is_whitespace(str[0])) { + str = str.drop_prefix(1); + } + return str; +} + +static StringRef drop_plus(StringRef str) +{ + if (!str.is_empty() && str[0] == '+') { + str = str.drop_prefix(1); + } + return str; +} + +StringRef parse_float(StringRef str, float fallback, float &dst, bool skip_space) +{ + if (skip_space) { + str = drop_whitespace(str); + } + str = drop_plus(str); + fast_float::from_chars_result res = fast_float::from_chars(str.begin(), str.end(), dst); + if (res.ec == std::errc::invalid_argument || res.ec == std::errc::result_out_of_range) { + dst = fallback; + } + return StringRef(res.ptr, str.end()); +} + +StringRef parse_floats(StringRef str, float fallback, float *dst, int count) +{ + for (int i = 0; i < count; ++i) { + str = parse_float(str, fallback, dst[i]); + } + return str; +} + +StringRef parse_int(StringRef str, int fallback, int &dst, bool skip_space) +{ + if (skip_space) { + str = drop_whitespace(str); + } + str = drop_plus(str); + std::from_chars_result res = std::from_chars(str.begin(), str.end(), dst); + if (res.ec == std::errc::invalid_argument || res.ec == std::errc::result_out_of_range) { + dst = fallback; + } + return StringRef(res.ptr, str.end()); +} + +} // namespace blender::io diff --git a/source/blender/io/common/intern/string_utils_test.cc b/source/blender/io/common/intern/string_utils_test.cc new file mode 100644 index 00000000000..a78bd7ab8a3 --- /dev/null +++ b/source/blender/io/common/intern/string_utils_test.cc @@ -0,0 +1,118 @@ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "IO_string_utils.hh" + +#include "testing/testing.h" + +namespace blender::io { + +#define EXPECT_STRREF_EQ(str1, str2) EXPECT_STREQ(str1, std::string(str2).c_str()) + +TEST(string_utils, read_next_line) +{ + std::string str = "abc\n \n\nline with \\\ncontinuation\nCRLF ending:\r\na"; + StringRef s = str; + EXPECT_STRREF_EQ("abc", read_next_line(s)); + EXPECT_STRREF_EQ(" ", read_next_line(s)); + EXPECT_STRREF_EQ("", read_next_line(s)); + EXPECT_STRREF_EQ("line with \\\ncontinuation", read_next_line(s)); + EXPECT_STRREF_EQ("CRLF ending:\r", read_next_line(s)); + EXPECT_STRREF_EQ("a", read_next_line(s)); + EXPECT_TRUE(s.is_empty()); +} + +TEST(string_utils, drop_whitespace) +{ + /* Empty */ + EXPECT_STRREF_EQ("", drop_whitespace("")); + /* Only whitespace */ + EXPECT_STRREF_EQ("", drop_whitespace(" ")); + EXPECT_STRREF_EQ("", drop_whitespace(" ")); + EXPECT_STRREF_EQ("", drop_whitespace(" \t\n\r ")); + /* Drops leading whitespace */ + EXPECT_STRREF_EQ("a", drop_whitespace(" a")); + EXPECT_STRREF_EQ("a b", drop_whitespace(" a b")); + EXPECT_STRREF_EQ("a b ", drop_whitespace(" a b ")); + /* No leading whitespace */ + EXPECT_STRREF_EQ("c", drop_whitespace("c")); + /* Case with backslash, should be treated as whitespace */ + EXPECT_STRREF_EQ("d", drop_whitespace(" \\ d")); +} + +TEST(string_utils, parse_int_valid) +{ + std::string str = "1 -10 \t 1234 1234567890 +7 123a"; + StringRef s = str; + int val; + s = parse_int(s, 0, val); + EXPECT_EQ(1, val); + s = parse_int(s, 0, val); + EXPECT_EQ(-10, val); + s = parse_int(s, 0, val); + EXPECT_EQ(1234, val); + s = parse_int(s, 0, val); + EXPECT_EQ(1234567890, val); + s = parse_int(s, 0, val); + EXPECT_EQ(7, val); + s = parse_int(s, 0, val); + EXPECT_EQ(123, val); + EXPECT_STRREF_EQ("a", s); +} + +TEST(string_utils, parse_int_invalid) +{ + int val; + /* Invalid syntax */ + EXPECT_STRREF_EQ("--123", parse_int("--123", -1, val)); + EXPECT_EQ(val, -1); + EXPECT_STRREF_EQ("foobar", parse_int("foobar", -2, val)); + EXPECT_EQ(val, -2); + /* Out of integer range */ + EXPECT_STRREF_EQ(" a", parse_int("1234567890123 a", -3, val)); + EXPECT_EQ(val, -3); + /* Has leading white-space when we don't expect it */ + EXPECT_STRREF_EQ(" 1", parse_int(" 1", -4, val, false)); + EXPECT_EQ(val, -4); +} + +TEST(string_utils, parse_float_valid) +{ + std::string str = "1 -10 123.5 -17.125 0.1 1e6 50.0e-1"; + StringRef s = str; + float val; + s = parse_float(s, 0, val); + EXPECT_EQ(1.0f, val); + s = parse_float(s, 0, val); + EXPECT_EQ(-10.0f, val); + s = parse_float(s, 0, val); + EXPECT_EQ(123.5f, val); + s = parse_float(s, 0, val); + EXPECT_EQ(-17.125f, val); + s = parse_float(s, 0, val); + EXPECT_EQ(0.1f, val); + s = parse_float(s, 0, val); + EXPECT_EQ(1.0e6f, val); + s = parse_float(s, 0, val); + EXPECT_EQ(5.0f, val); + EXPECT_TRUE(s.is_empty()); +} + +TEST(string_utils, parse_float_invalid) +{ + float val; + /* Invalid syntax */ + EXPECT_STRREF_EQ("_0", parse_float("_0", -1.0f, val)); + EXPECT_EQ(val, -1.0f); + EXPECT_STRREF_EQ("..5", parse_float("..5", -2.0f, val)); + EXPECT_EQ(val, -2.0f); + /* Out of float range. Current float parser (fast_float) + * clamps out of range numbers to +/- infinity, so this + * one gets a +inf instead of fallback -3.0. */ + EXPECT_STRREF_EQ(" a", parse_float("9.0e500 a", -3.0f, val)); + EXPECT_EQ(val, std::numeric_limits<float>::infinity()); + /* Has leading white-space when we don't expect it */ + EXPECT_STRREF_EQ(" 1", parse_float(" 1", -4.0f, val, false)); + EXPECT_EQ(val, -4.0f); +} + +} // namespace blender::io diff --git a/source/blender/io/usd/CMakeLists.txt b/source/blender/io/usd/CMakeLists.txt index 2b5ea39617e..e2e959814fa 100644 --- a/source/blender/io/usd/CMakeLists.txt +++ b/source/blender/io/usd/CMakeLists.txt @@ -16,6 +16,24 @@ add_definitions(-DPXR_STATIC) # USD headers use deprecated TBB headers, silence warning. add_definitions(-DTBB_SUPPRESS_DEPRECATED_MESSAGES=1) +# Check if USD has the imaging headers available, if they are +# add a USD_HAS_IMAGING define so code can dynamically detect this. +# Cleanup of this variable is done at the end of the file since +# test code further down uses it to add imaging tests. +FIND_FILE(USD_IMAGING_HEADERS + NAMES + capsuleAdapter.h + PATHS + ${USD_INCLUDE_DIRS} + PATH_SUFFIXES + pxr/usdImaging/usdImaging/ + NO_DEFAULT_PATH +) + +if(USD_IMAGING_HEADERS) + add_definitions(-DUSD_HAS_IMAGING) +endif() + set(INC . ../common @@ -129,7 +147,13 @@ target_link_libraries(bf_usd INTERFACE ${TBB_LIBRARIES}) if(WITH_GTESTS) set(TEST_SRC tests/usd_stage_creation_test.cc + tests/usd_tests_common.cc + tests/usd_tests_common.h ) + if(USD_IMAGING_HEADERS) + LIST(APPEND TEST_SRC tests/usd_imaging_test.cc) + endif() + set(TEST_INC ) set(TEST_LIB @@ -137,3 +161,7 @@ if(WITH_GTESTS) include(GTestTesting) blender_add_test_lib(bf_io_usd_tests "${TEST_SRC}" "${INC};${TEST_INC}" "${INC_SYS}" "${LIB};${TEST_LIB}") endif() + +# In cmake version 3.21 and up, we can instead use the NO_CACHE option for +# find_file so we don't need to clear it from the cache here. +unset(USD_IMAGING_HEADERS CACHE) diff --git a/source/blender/io/usd/tests/usd_imaging_test.cc b/source/blender/io/usd/tests/usd_imaging_test.cc new file mode 100644 index 00000000000..497319c59bd --- /dev/null +++ b/source/blender/io/usd/tests/usd_imaging_test.cc @@ -0,0 +1,71 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2022 Blender Foundation. All rights reserved. */ +#include "testing/testing.h" + +#include "usd_tests_common.h" + +#include <pxr/usd/usd/stage.h> +#include <pxr/usd/usdGeom/capsule.h> +#include <pxr/usdImaging/usdImaging/capsuleAdapter.h> + +namespace blender::io::usd { + +class USDImagingTest : public testing::Test { +}; + +TEST_F(USDImagingTest, CapsuleAdapterTest) +{ + /* A simple test to exercise the UsdImagingGprimAdapter API to + * ensure the code compiles, links and returns reasonable results. + * We create a capsule shape on an in-memory stage and attempt + * to access the shape's points and topology. */ + + /* We must register USD plugin paths before creating the stage + * to avoid a crash in the USD asset resolver initialization code. */ + if (register_usd_plugins_for_tests().empty()) { + FAIL(); + return; + } + + pxr::UsdStageRefPtr stage = pxr::UsdStage::CreateInMemory(); + + if (!stage) { + FAIL() << "Couldn't create in-memory stage."; + return; + } + + pxr::UsdGeomCapsule capsule = pxr::UsdGeomCapsule::Define(stage, pxr::SdfPath("/Capsule")); + + if (!capsule) { + FAIL() << "Couldn't create UsdGeomCapsule."; + return; + } + + pxr::UsdImagingCapsuleAdapter capsule_adapter; + pxr::VtValue points_value = capsule_adapter.GetMeshPoints(capsule.GetPrim(), + pxr::UsdTimeCode::Default()); + if (!points_value.IsHolding<pxr::VtArray<pxr::GfVec3f>>()) { + FAIL() << "Mesh points value holding unexpected type."; + return; + } + + pxr::VtArray<pxr::GfVec3f> points = points_value.Get<pxr::VtArray<pxr::GfVec3f>>(); + EXPECT_FALSE(points.empty()); + + pxr::VtValue topology_value = capsule_adapter.GetMeshTopology(); + + if (!topology_value.IsHolding<pxr::HdMeshTopology>()) { + FAIL() << "Mesh topology value holding unexpected type."; + return; + } + + pxr::HdMeshTopology topology = topology_value.Get<pxr::HdMeshTopology>(); + + pxr::VtArray<int> vertex_counts = topology.GetFaceVertexCounts(); + EXPECT_FALSE(vertex_counts.empty()); + + pxr::VtArray<int> vertex_indices = topology.GetFaceVertexIndices(); + EXPECT_FALSE(vertex_indices.empty()); +} + +} // namespace blender::io::usd diff --git a/source/blender/io/usd/tests/usd_stage_creation_test.cc b/source/blender/io/usd/tests/usd_stage_creation_test.cc index dbe5f8cd9ce..1aa6f9691ff 100644 --- a/source/blender/io/usd/tests/usd_stage_creation_test.cc +++ b/source/blender/io/usd/tests/usd_stage_creation_test.cc @@ -2,6 +2,8 @@ * Copyright 2019 Blender Foundation. All rights reserved. */ #include "testing/testing.h" +#include "usd_tests_common.h" + #include <pxr/base/plug/registry.h> #include <pxr/usd/usd/stage.h> @@ -19,23 +21,12 @@ class USDStageCreationTest : public testing::Test { TEST_F(USDStageCreationTest, JSONFileLoadingTest) { - const std::string &release_dir = blender::tests::flags_test_release_dir(); - if (release_dir.empty()) { + std::string usd_datafiles_dir = register_usd_plugins_for_tests(); + if (usd_datafiles_dir.empty()) { FAIL(); + return; } - char usd_datafiles_dir[FILE_MAX]; - const size_t path_len = BLI_path_join( - usd_datafiles_dir, FILE_MAX, release_dir.c_str(), "datafiles", "usd", nullptr); - - /* #BLI_path_join removes trailing slashes, but the USD library requires one in order to - * recognize the path as directory. */ - BLI_assert(path_len + 1 < FILE_MAX); - usd_datafiles_dir[path_len] = '/'; - usd_datafiles_dir[path_len + 1] = '\0'; - - pxr::PlugRegistry::GetInstance().RegisterPlugins(usd_datafiles_dir); - /* Simply the ability to create a USD Stage for a specific filename means that the extension * has been recognized by the USD library, and that a USD plugin has been loaded to write such * files. Practically, this is a test to see whether the USD JSON files can be found and diff --git a/source/blender/io/usd/tests/usd_tests_common.cc b/source/blender/io/usd/tests/usd_tests_common.cc new file mode 100644 index 00000000000..9f18a289433 --- /dev/null +++ b/source/blender/io/usd/tests/usd_tests_common.cc @@ -0,0 +1,45 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2022 Blender Foundation. All rights reserved. */ + +#include "usd_tests_common.h" + +#include "testing/testing.h" + +#include <pxr/base/plug/registry.h> + +#include "BLI_path_util.h" +#include "BLI_utildefines.h" + +#include "BKE_appdir.h" + +namespace blender::io::usd { + +std::string register_usd_plugins_for_tests() +{ + static char usd_datafiles_dir[FILE_MAX] = {'\0'}; + static bool plugin_path_registered = false; + if (plugin_path_registered) { + return usd_datafiles_dir; + } + plugin_path_registered = true; + + const std::string &release_dir = blender::tests::flags_test_release_dir(); + if (release_dir.empty()) { + return ""; + } + + const size_t path_len = BLI_path_join( + usd_datafiles_dir, FILE_MAX, release_dir.c_str(), "datafiles", "usd", nullptr); + + /* #BLI_path_join removes trailing slashes, but the USD library requires one in order to + * recognize the path as directory. */ + BLI_assert(path_len + 1 < FILE_MAX); + usd_datafiles_dir[path_len] = '/'; + usd_datafiles_dir[path_len + 1] = '\0'; + + pxr::PlugRegistry::GetInstance().RegisterPlugins(usd_datafiles_dir); + + return usd_datafiles_dir; +} + +} // namespace blender::io::usd diff --git a/source/blender/io/usd/tests/usd_tests_common.h b/source/blender/io/usd/tests/usd_tests_common.h new file mode 100644 index 00000000000..b298a253ddc --- /dev/null +++ b/source/blender/io/usd/tests/usd_tests_common.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2022 Blender Foundation. All rights reserved. */ +#pragma once + +#include <string> + +namespace blender::io::usd { + +/* Calls the function to load the USD plugins from the + * USD data directory under the Blender bin directory + * that was supplied as the --test-release-dir flag to ctest. + * Thus function must be called before instantiating a USD + * stage to avoid errors. The returned string is the path to + * the USD data files directory from which the plugins were + * loaded. If the USD data files directory can't be determined, + * plugin registration is skipped and the empty string is + * returned. */ +std::string register_usd_plugins_for_tests(); + +} // namespace blender::io::usd diff --git a/source/blender/io/wavefront_obj/CMakeLists.txt b/source/blender/io/wavefront_obj/CMakeLists.txt index 9cdd96ee7be..67cec000778 100644 --- a/source/blender/io/wavefront_obj/CMakeLists.txt +++ b/source/blender/io/wavefront_obj/CMakeLists.txt @@ -4,6 +4,7 @@ set(INC . ./exporter ./importer + ../common ../../blenkernel ../../blenlib ../../bmesh @@ -35,7 +36,6 @@ set(SRC importer/obj_import_mtl.cc importer/obj_import_nurbs.cc importer/obj_importer.cc - importer/parser_string_utils.cc IO_wavefront_obj.h exporter/obj_export_file_writer.hh @@ -51,11 +51,11 @@ set(SRC importer/obj_import_nurbs.hh importer/obj_import_objects.hh importer/obj_importer.hh - importer/parser_string_utils.hh ) set(LIB bf_blenkernel + bf_io_common ) if(WITH_TBB) @@ -70,6 +70,7 @@ if(WITH_GTESTS) set(TEST_SRC tests/obj_exporter_tests.cc tests/obj_importer_tests.cc + tests/obj_mtl_parser_tests.cc tests/obj_exporter_tests.hh ) diff --git a/source/blender/io/wavefront_obj/IO_wavefront_obj.h b/source/blender/io/wavefront_obj/IO_wavefront_obj.h index b06ccbf8702..8b71ec750c0 100644 --- a/source/blender/io/wavefront_obj/IO_wavefront_obj.h +++ b/source/blender/io/wavefront_obj/IO_wavefront_obj.h @@ -84,6 +84,7 @@ struct OBJImportParams { float clamp_size; eTransformAxisForward forward_axis; eTransformAxisUp up_axis; + bool validate_meshes; }; /** diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc b/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc index f78ef334d4d..96b342252c4 100644 --- a/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc +++ b/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc @@ -9,6 +9,7 @@ #include "BKE_blender_version.h" +#include "BLI_enumerable_thread_specific.hh" #include "BLI_path_util.h" #include "BLI_task.hh" @@ -308,6 +309,9 @@ void OBJWriter::write_poly_elements(FormatHandler<eFileType::OBJ> &fh, obj_mesh_data.tot_uv_vertices()); const int tot_polygons = obj_mesh_data.tot_polygons(); + const int tot_deform_groups = obj_mesh_data.tot_deform_groups(); + threading::EnumerableThreadSpecific<Vector<float>> group_weights; + obj_parallel_chunked_output(fh, tot_polygons, [&](FormatHandler<eFileType::OBJ> &buf, int idx) { /* Polygon order for writing into the file is not necessarily the same * as order in the mesh; it will be sorted by material indices. Remap current @@ -330,9 +334,12 @@ void OBJWriter::write_poly_elements(FormatHandler<eFileType::OBJ> &fh, /* Write vertex group if different from previous. */ if (export_params_.export_vertex_groups) { + Vector<float> &local_weights = group_weights.local(); + local_weights.resize(tot_deform_groups); const int16_t prev_group = idx == 0 ? NEGATIVE_INIT : - obj_mesh_data.get_poly_deform_group_index(prev_i); - const int16_t group = obj_mesh_data.get_poly_deform_group_index(i); + obj_mesh_data.get_poly_deform_group_index( + prev_i, local_weights); + const int16_t group = obj_mesh_data.get_poly_deform_group_index(i, local_weights); if (group != prev_group) { buf.write<eOBJSyntaxElement::object_group>( group == NOT_FOUND ? DEFORM_GROUP_DISABLED : diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_mesh.cc b/source/blender/io/wavefront_obj/exporter/obj_export_mesh.cc index 8c9b04a5ac3..c2a9e0574eb 100644 --- a/source/blender/io/wavefront_obj/exporter/obj_export_mesh.cc +++ b/source/blender/io/wavefront_obj/exporter/obj_export_mesh.cc @@ -426,6 +426,9 @@ void OBJMesh::store_normal_coords_and_indices() Vector<int> OBJMesh::calc_poly_normal_indices(const int poly_index) const { + if (loop_to_normal_index_.is_empty()) { + return {}; + } const MPoly &mpoly = export_mesh_eval_->mpoly[poly_index]; const int totloop = mpoly.totloop; Vector<int> r_poly_normal_indices(totloop); @@ -436,46 +439,47 @@ Vector<int> OBJMesh::calc_poly_normal_indices(const int poly_index) const return r_poly_normal_indices; } -int16_t OBJMesh::get_poly_deform_group_index(const int poly_index) const +int OBJMesh::tot_deform_groups() const +{ + if (!BKE_object_supports_vertex_groups(&export_object_eval_)) { + return 0; + } + return BKE_object_defgroup_count(&export_object_eval_); +} + +int16_t OBJMesh::get_poly_deform_group_index(const int poly_index, + MutableSpan<float> group_weights) const { BLI_assert(poly_index < export_mesh_eval_->totpoly); - const MPoly &mpoly = export_mesh_eval_->mpoly[poly_index]; - const MLoop *mloop = &export_mesh_eval_->mloop[mpoly.loopstart]; - const Object *obj = &export_object_eval_; - const int tot_deform_groups = BKE_object_defgroup_count(obj); - /* Indices of the vector index into deform groups of an object; values are the] - * number of vertex members in one deform group. */ - Vector<int16_t> deform_group_members(tot_deform_groups, 0); - /* Whether at least one vertex in the polygon belongs to any group. */ - bool found_group = false; - - const MDeformVert *dvert_orig = static_cast<MDeformVert *>( + BLI_assert(group_weights.size() == BKE_object_defgroup_count(&export_object_eval_)); + + const MDeformVert *dvert_layer = static_cast<MDeformVert *>( CustomData_get_layer(&export_mesh_eval_->vdata, CD_MDEFORMVERT)); - if (!dvert_orig) { + if (!dvert_layer) { return NOT_FOUND; } - const MDeformWeight *curr_weight = nullptr; - const MDeformVert *dvert = nullptr; - for (int loop_index = 0; loop_index < mpoly.totloop; loop_index++) { - dvert = &dvert_orig[(mloop + loop_index)->v]; - curr_weight = dvert->dw; - if (curr_weight) { - bDeformGroup *vertex_group = static_cast<bDeformGroup *>( - BLI_findlink(BKE_object_defgroup_list(obj), curr_weight->def_nr)); - if (vertex_group) { - deform_group_members[curr_weight->def_nr] += 1; - found_group = true; + group_weights.fill(0); + bool found_any_group = false; + const MPoly &mpoly = export_mesh_eval_->mpoly[poly_index]; + const MLoop *mloop = &export_mesh_eval_->mloop[mpoly.loopstart]; + for (int loop_i = 0; loop_i < mpoly.totloop; ++loop_i, ++mloop) { + const MDeformVert &dvert = dvert_layer[mloop->v]; + for (int weight_i = 0; weight_i < dvert.totweight; ++weight_i) { + const auto group = dvert.dw[weight_i].def_nr; + if (group < group_weights.size()) { + group_weights[group] += dvert.dw[weight_i].weight; + found_any_group = true; } } } - if (!found_group) { + if (!found_any_group) { return NOT_FOUND; } /* Index of the group with maximum vertices. */ - int16_t max_idx = std::max_element(deform_group_members.begin(), deform_group_members.end()) - - deform_group_members.begin(); + int16_t max_idx = std::max_element(group_weights.begin(), group_weights.end()) - + group_weights.begin(); return max_idx; } diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_mesh.hh b/source/blender/io/wavefront_obj/exporter/obj_export_mesh.hh index 7650a220810..f47ca423dbc 100644 --- a/source/blender/io/wavefront_obj/exporter/obj_export_mesh.hh +++ b/source/blender/io/wavefront_obj/exporter/obj_export_mesh.hh @@ -116,6 +116,7 @@ class OBJMesh : NonCopyable { int tot_uv_vertices() const; int tot_normal_indices() const; int tot_edges() const; + int tot_deform_groups() const; bool is_mirrored_transform() const { return mirrored_transform_; @@ -204,13 +205,15 @@ class OBJMesh : NonCopyable { */ Vector<int> calc_poly_normal_indices(int poly_index) const; /** - * Find the index of the vertex group with the maximum number of vertices in a polygon. - * The index indices into the #Object.defbase. + * Find the most representative vertex group of a polygon. + * + * This adds up vertex group weights, and the group with the largest + * weight sum across the polygon is the one returned. * - * If two or more groups have the same number of vertices (maximum), group name depends on the - * implementation of #std::max_element. + * group_weights is temporary storage to avoid reallocations, it must + * be the size of amount of vertex groups in the object. */ - int16_t get_poly_deform_group_index(int poly_index) const; + int16_t get_poly_deform_group_index(int poly_index, MutableSpan<float> group_weights) const; /** * Find the name of the vertex deform group at the given index. * The index indices into the #Object.defbase. diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_nurbs.cc b/source/blender/io/wavefront_obj/exporter/obj_export_nurbs.cc index 17069c18295..c247048ce13 100644 --- a/source/blender/io/wavefront_obj/exporter/obj_export_nurbs.cc +++ b/source/blender/io/wavefront_obj/exporter/obj_export_nurbs.cc @@ -72,12 +72,12 @@ float3 OBJCurve::vertex_coordinates(const int spline_index, int OBJCurve::total_spline_control_points(const int spline_index) const { const Nurb *const nurb = static_cast<Nurb *>(BLI_findlink(&export_curve_->nurb, spline_index)); - const int r_nurbs_degree = nurb->orderu - 1; + int degree = nurb->type == CU_POLY ? 1 : nurb->orderu - 1; /* Total control points = Number of points in the curve (+ degree of the * curve if it is cyclic). */ int r_tot_control_points = nurb->pntsv * nurb->pntsu; if (nurb->flagu & CU_NURB_CYCLIC) { - r_tot_control_points += r_nurbs_degree; + r_tot_control_points += degree; } return r_tot_control_points; } @@ -85,7 +85,7 @@ int OBJCurve::total_spline_control_points(const int spline_index) const int OBJCurve::get_nurbs_degree(const int spline_index) const { const Nurb *const nurb = static_cast<Nurb *>(BLI_findlink(&export_curve_->nurb, spline_index)); - return nurb->orderu - 1; + return nurb->type == CU_POLY ? 1 : nurb->orderu - 1; } short OBJCurve::get_nurbs_flagu(const int spline_index) const diff --git a/source/blender/io/wavefront_obj/exporter/obj_exporter.cc b/source/blender/io/wavefront_obj/exporter/obj_exporter.cc index 584d8ae4ec0..78b709c884a 100644 --- a/source/blender/io/wavefront_obj/exporter/obj_exporter.cc +++ b/source/blender/io/wavefront_obj/exporter/obj_exporter.cc @@ -68,6 +68,17 @@ static void print_exception_error(const std::system_error &ex) << std::endl; } +static bool is_curve_nurbs_compatible(const Nurb *nurb) +{ + while (nurb) { + if (nurb->type == CU_BEZIER || nurb->pntsv != 1) { + return false; + } + nurb = nurb->next; + } + return true; +} + /** * Filter supported objects from the Scene. * @@ -104,27 +115,13 @@ filter_supported_objects(Depsgraph *depsgraph, const OBJExportParams &export_par } break; } - switch (nurb->type) { - case CU_NURBS: - if (export_params.export_curves_as_nurbs) { - /* Export in parameter form: control points. */ - r_exportable_nurbs.append( - std::make_unique<OBJCurve>(depsgraph, export_params, object)); - } - else { - /* Export in mesh form: edges and vertices. */ - r_exportable_meshes.append( - std::make_unique<OBJMesh>(depsgraph, export_params, object)); - } - break; - case CU_BEZIER: - /* Always export in mesh form: edges and vertices. */ - r_exportable_meshes.append( - std::make_unique<OBJMesh>(depsgraph, export_params, object)); - break; - default: - /* Other curve types are not supported. */ - break; + if (export_params.export_curves_as_nurbs && is_curve_nurbs_compatible(nurb)) { + /* Export in parameter form: control points. */ + r_exportable_nurbs.append(std::make_unique<OBJCurve>(depsgraph, export_params, object)); + } + else { + /* Export in mesh form: edges and vertices. */ + r_exportable_meshes.append(std::make_unique<OBJMesh>(depsgraph, export_params, object)); } break; } diff --git a/source/blender/io/wavefront_obj/importer/obj_import_file_reader.cc b/source/blender/io/wavefront_obj/importer/obj_import_file_reader.cc index d3d4e1ba860..3e722f8a0dd 100644 --- a/source/blender/io/wavefront_obj/importer/obj_import_file_reader.cc +++ b/source/blender/io/wavefront_obj/importer/obj_import_file_reader.cc @@ -8,7 +8,7 @@ #include "BLI_string_ref.hh" #include "BLI_vector.hh" -#include "parser_string_utils.hh" +#include "IO_string_utils.hh" #include "obj_import_file_reader.hh" @@ -34,7 +34,8 @@ static Geometry *create_geometry(Geometry *const prev_geometry, Geometry *g = r_all_geometries.last().get(); g->geom_type_ = new_type; g->geometry_name_ = name.is_empty() ? "New object" : name; - r_offset.set_index_offset(global_vertices.vertices.size()); + g->vertex_start_ = global_vertices.vertices.size(); + r_offset.set_index_offset(g->vertex_start_); return g; }; @@ -66,48 +67,40 @@ static Geometry *create_geometry(Geometry *const prev_geometry, } static void geom_add_vertex(Geometry *geom, - const StringRef rest_line, + const StringRef line, GlobalVertices &r_global_vertices) { - float3 curr_vert; - Vector<StringRef> str_vert_split; - split_by_char(rest_line, ' ', str_vert_split); - copy_string_to_float(str_vert_split, FLT_MAX, {curr_vert, 3}); - r_global_vertices.vertices.append(curr_vert); - geom->vertex_indices_.append(r_global_vertices.vertices.size() - 1); + float3 vert; + parse_floats(line, FLT_MAX, vert, 3); + r_global_vertices.vertices.append(vert); + geom->vertex_count_++; } static void geom_add_vertex_normal(Geometry *geom, - const StringRef rest_line, + const StringRef line, GlobalVertices &r_global_vertices) { - float3 curr_vert_normal; - Vector<StringRef> str_vert_normal_split; - split_by_char(rest_line, ' ', str_vert_normal_split); - copy_string_to_float(str_vert_normal_split, FLT_MAX, {curr_vert_normal, 3}); - r_global_vertices.vertex_normals.append(curr_vert_normal); + float3 normal; + parse_floats(line, FLT_MAX, normal, 3); + r_global_vertices.vertex_normals.append(normal); geom->has_vertex_normals_ = true; } -static void geom_add_uv_vertex(const StringRef rest_line, GlobalVertices &r_global_vertices) +static void geom_add_uv_vertex(const StringRef line, GlobalVertices &r_global_vertices) { - float2 curr_uv_vert; - Vector<StringRef> str_uv_vert_split; - split_by_char(rest_line, ' ', str_uv_vert_split); - copy_string_to_float(str_uv_vert_split, FLT_MAX, {curr_uv_vert, 2}); - r_global_vertices.uv_vertices.append(curr_uv_vert); + float2 uv; + parse_floats(line, FLT_MAX, uv, 2); + r_global_vertices.uv_vertices.append(uv); } static void geom_add_edge(Geometry *geom, - const StringRef rest_line, + StringRef line, const VertexIndexOffset &offsets, GlobalVertices &r_global_vertices) { - int edge_v1 = -1, edge_v2 = -1; - Vector<StringRef> str_edge_split; - split_by_char(rest_line, ' ', str_edge_split); - copy_string_to_int(str_edge_split[0], -1, edge_v1); - copy_string_to_int(str_edge_split[1], -1, edge_v2); + int edge_v1, edge_v2; + line = parse_int(line, -1, edge_v1); + line = parse_int(line, -1, edge_v2); /* Always keep stored indices non-negative and zero-based. */ edge_v1 += edge_v1 < 0 ? r_global_vertices.vertices.size() : -offsets.get_index_offset() - 1; edge_v2 += edge_v2 < 0 ? r_global_vertices.vertices.size() : -offsets.get_index_offset() - 1; @@ -116,78 +109,45 @@ static void geom_add_edge(Geometry *geom, } static void geom_add_polygon(Geometry *geom, - const StringRef rest_line, + StringRef line, const GlobalVertices &global_vertices, const VertexIndexOffset &offsets, - const StringRef state_material_name, - const StringRef state_object_group, - const bool state_shaded_smooth) + const int material_index, + const int group_index, + const bool shaded_smooth) { PolyElem curr_face; - curr_face.shaded_smooth = state_shaded_smooth; - if (!state_material_name.is_empty()) { - curr_face.material_name = state_material_name; - } - if (!state_object_group.is_empty()) { - curr_face.vertex_group = state_object_group; - /* Yes it repeats several times, but another if-check will not reduce steps either. */ + curr_face.shaded_smooth = shaded_smooth; + curr_face.material_index = material_index; + if (group_index >= 0) { + curr_face.vertex_group_index = group_index; geom->use_vertex_groups_ = true; } + const int orig_corners_size = geom->face_corners_.size(); + curr_face.start_index_ = orig_corners_size; + bool face_valid = true; - Vector<StringRef> str_corners_split; - split_by_char(rest_line, ' ', str_corners_split); - for (StringRef str_corner : str_corners_split) { + while (!line.is_empty() && face_valid) { PolyCorner corner; - const size_t n_slash = std::count(str_corner.begin(), str_corner.end(), '/'); bool got_uv = false, got_normal = false; - if (n_slash == 0) { - /* Case: "f v1 v2 v3". */ - copy_string_to_int(str_corner, INT32_MAX, corner.vert_index); - } - else if (n_slash == 1) { - /* Case: "f v1/vt1 v2/vt2 v3/vt3". */ - Vector<StringRef> vert_uv_split; - split_by_char(str_corner, '/', vert_uv_split); - if (vert_uv_split.size() != 1 && vert_uv_split.size() != 2) { - fprintf(stderr, "Invalid face syntax '%s', ignoring\n", std::string(str_corner).c_str()); - face_valid = false; + /* Parse vertex index. */ + line = parse_int(line, INT32_MAX, corner.vert_index, false); + face_valid &= corner.vert_index != INT32_MAX; + if (!line.is_empty() && line[0] == '/') { + /* Parse UV index. */ + line = line.drop_prefix(1); + if (!line.is_empty() && line[0] != '/') { + line = parse_int(line, INT32_MAX, corner.uv_vert_index, false); + got_uv = corner.uv_vert_index != INT32_MAX; } - else { - copy_string_to_int(vert_uv_split[0], INT32_MAX, corner.vert_index); - if (vert_uv_split.size() == 2) { - copy_string_to_int(vert_uv_split[1], INT32_MAX, corner.uv_vert_index); - got_uv = corner.uv_vert_index != INT32_MAX; - } + /* Parse normal index. */ + if (!line.is_empty() && line[0] == '/') { + line = line.drop_prefix(1); + line = parse_int(line, INT32_MAX, corner.vertex_normal_index, false); + got_normal = corner.uv_vert_index != INT32_MAX; } } - else if (n_slash == 2) { - /* Case: "f v1//vn1 v2//vn2 v3//vn3". */ - /* Case: "f v1/vt1/vn1 v2/vt2/vn2 v3/vt3/vn3". */ - Vector<StringRef> vert_uv_normal_split; - split_by_char(str_corner, '/', vert_uv_normal_split); - if (vert_uv_normal_split.size() != 2 && vert_uv_normal_split.size() != 3) { - fprintf(stderr, "Invalid face syntax '%s', ignoring\n", std::string(str_corner).c_str()); - face_valid = false; - } - else { - copy_string_to_int(vert_uv_normal_split[0], INT32_MAX, corner.vert_index); - if (vert_uv_normal_split.size() == 3) { - copy_string_to_int(vert_uv_normal_split[1], INT32_MAX, corner.uv_vert_index); - got_uv = corner.uv_vert_index != INT32_MAX; - copy_string_to_int(vert_uv_normal_split[2], INT32_MAX, corner.vertex_normal_index); - got_normal = corner.vertex_normal_index != INT32_MAX; - } - else { - copy_string_to_int(vert_uv_normal_split[1], INT32_MAX, corner.vertex_normal_index); - got_normal = corner.vertex_normal_index != INT32_MAX; - } - } - } - else { - fprintf(stderr, "Invalid face syntax '%s', ignoring\n", std::string(str_corner).c_str()); - face_valid = false; - } /* Always keep stored indices non-negative and zero-based. */ corner.vert_index += corner.vert_index < 0 ? global_vertices.vertices.size() : -offsets.get_index_offset() - 1; @@ -221,19 +181,28 @@ static void geom_add_polygon(Geometry *geom, face_valid = false; } } - curr_face.face_corners.append(corner); + geom->face_corners_.append(corner); + curr_face.corner_count_++; + + /* Skip whitespace to get to the next face corner. */ + line = drop_whitespace(line); } if (face_valid) { geom->face_elements_.append(curr_face); - geom->total_loops_ += curr_face.face_corners.size(); + geom->total_loops_ += curr_face.corner_count_; + } + else { + /* Remove just-added corners for the invalid face. */ + geom->face_corners_.resize(orig_corners_size); + geom->has_invalid_polys_ = true; } } static Geometry *geom_set_curve_type(Geometry *geom, const StringRef rest_line, const GlobalVertices &global_vertices, - const StringRef state_object_group, + const StringRef group_name, VertexIndexOffset &r_offsets, Vector<std::unique_ptr<Geometry>> &r_all_geometries) { @@ -242,254 +211,411 @@ static Geometry *geom_set_curve_type(Geometry *geom, return geom; } geom = create_geometry( - geom, GEOM_CURVE, state_object_group, global_vertices, r_all_geometries, r_offsets); - geom->nurbs_element_.group_ = state_object_group; + geom, GEOM_CURVE, group_name, global_vertices, r_all_geometries, r_offsets); + geom->nurbs_element_.group_ = group_name; return geom; } -static void geom_set_curve_degree(Geometry *geom, const StringRef rest_line) +static void geom_set_curve_degree(Geometry *geom, const StringRef line) { - copy_string_to_int(rest_line, 3, geom->nurbs_element_.degree); + parse_int(line, 3, geom->nurbs_element_.degree); } static void geom_add_curve_vertex_indices(Geometry *geom, - const StringRef rest_line, + StringRef line, const GlobalVertices &global_vertices) { - Vector<StringRef> str_curv_split; - split_by_char(rest_line, ' ', str_curv_split); - /* Remove "0.0" and "1.0" from the strings. They are hardcoded. */ - str_curv_split.remove(0); - str_curv_split.remove(0); - geom->nurbs_element_.curv_indices.resize(str_curv_split.size()); - copy_string_to_int(str_curv_split, INT32_MAX, geom->nurbs_element_.curv_indices); - for (int &curv_index : geom->nurbs_element_.curv_indices) { + /* Curve lines always have "0.0" and "1.0", skip over them. */ + float dummy[2]; + line = parse_floats(line, 0, dummy, 2); + /* Parse indices. */ + while (!line.is_empty()) { + int index; + line = parse_int(line, INT32_MAX, index); + if (index == INT32_MAX) { + return; + } /* Always keep stored indices non-negative and zero-based. */ - curv_index += curv_index < 0 ? global_vertices.vertices.size() : -1; + index += index < 0 ? global_vertices.vertices.size() : -1; + geom->nurbs_element_.curv_indices.append(index); } } -static void geom_add_curve_parameters(Geometry *geom, const StringRef rest_line) +static void geom_add_curve_parameters(Geometry *geom, StringRef line) { - Vector<StringRef> str_parm_split; - split_by_char(rest_line, ' ', str_parm_split); - if (str_parm_split[0] != "u" && str_parm_split[0] != "v") { - std::cerr << "Surfaces are not supported:'" << str_parm_split[0] << "'" << std::endl; + line = drop_whitespace(line); + if (line.is_empty()) { + std::cerr << "Invalid OBJ curve parm line: '" << line << "'" << std::endl; + return; + } + if (line[0] != 'u') { + std::cerr << "OBJ curve surfaces are not supported: '" << line[0] << "'" << std::endl; return; } - str_parm_split.remove(0); - geom->nurbs_element_.parm.resize(str_parm_split.size()); - copy_string_to_float(str_parm_split, FLT_MAX, geom->nurbs_element_.parm); + line = line.drop_prefix(1); + + while (!line.is_empty()) { + float val; + line = parse_float(line, FLT_MAX, val); + if (val != FLT_MAX) { + geom->nurbs_element_.parm.append(val); + } + else { + std::cerr << "OBJ curve parm line has invalid number" << std::endl; + return; + } + } } -static void geom_update_object_group(const StringRef rest_line, std::string &r_state_object_group) +static void geom_update_group(const StringRef rest_line, std::string &r_group_name) { if (rest_line.find("off") != string::npos || rest_line.find("null") != string::npos || rest_line.find("default") != string::npos) { /* Set group for future elements like faces or curves to empty. */ - r_state_object_group = ""; + r_group_name = ""; return; } - r_state_object_group = rest_line; -} - -static void geom_update_polygon_material(Geometry *geom, - const StringRef rest_line, - std::string &r_state_material_name) -{ - /* Materials may repeat if faces are written without sorting. */ - geom->material_names_.add(string(rest_line)); - r_state_material_name = rest_line; + r_group_name = rest_line; } -static void geom_update_smooth_group(const StringRef rest_line, bool &r_state_shaded_smooth) +static void geom_update_smooth_group(StringRef line, bool &r_state_shaded_smooth) { + line = drop_whitespace(line); /* Some implementations use "0" and "null" too, in addition to "off". */ - if (rest_line != "0" && rest_line.find("off") == StringRef::not_found && - rest_line.find("null") == StringRef::not_found) { - int smooth = 0; - copy_string_to_int(rest_line, 0, smooth); - r_state_shaded_smooth = smooth != 0; - } - else { - /* The OBJ file explicitly set shading to off. */ + if (line == "0" || line.startswith("off") || line.startswith("null")) { r_state_shaded_smooth = false; + return; } + + int smooth = 0; + parse_int(line, 0, smooth); + r_state_shaded_smooth = smooth != 0; } -OBJParser::OBJParser(const OBJImportParams &import_params) : import_params_(import_params) +OBJParser::OBJParser(const OBJImportParams &import_params, size_t read_buffer_size = 64 * 1024) + : import_params_(import_params), read_buffer_size_(read_buffer_size) { - obj_file_.open(import_params_.filepath); - if (!obj_file_.good()) { + obj_file_ = BLI_fopen(import_params_.filepath, "rb"); + if (!obj_file_) { fprintf(stderr, "Cannot read from OBJ file:'%s'.\n", import_params_.filepath); return; } } +OBJParser::~OBJParser() +{ + if (obj_file_) { + fclose(obj_file_); + } +} + void OBJParser::parse(Vector<std::unique_ptr<Geometry>> &r_all_geometries, GlobalVertices &r_global_vertices) { - if (!obj_file_.good()) { + if (!obj_file_) { return; } - string line; - /* Store vertex coordinates that belong to other Geometry instances. */ VertexIndexOffset offsets; - /* Non owning raw pointer to a Geometry. To be updated while creating a new Geometry. */ Geometry *curr_geom = create_geometry( nullptr, GEOM_MESH, "", r_global_vertices, r_all_geometries, offsets); - /* State-setting variables: if set, they remain the same for the remaining + /* State variables: once set, they remain the same for the remaining * elements in the object. */ bool state_shaded_smooth = false; - string state_object_group; + string state_group_name; + int state_group_index = -1; string state_material_name; + int state_material_index = -1; + + /* Read the input file in chunks. We need up to twice the possible chunk size, + * to possibly store remainder of the previous input line that got broken mid-chunk. */ + Array<char> buffer(read_buffer_size_ * 2); + + size_t buffer_offset = 0; + size_t line_number = 0; + while (true) { + /* Read a chunk of input from the file. */ + size_t bytes_read = fread(buffer.data() + buffer_offset, 1, read_buffer_size_, obj_file_); + if (bytes_read == 0 && buffer_offset == 0) { + break; /* No more data to read. */ + } - while (std::getline(obj_file_, line)) { - /* Keep reading new lines if the last character is `\`. */ - /* Another way is to make a getline wrapper and use it in the while condition. */ - read_next_line(obj_file_, line); + /* Ensure buffer ends in a newline. */ + if (bytes_read < read_buffer_size_) { + if (bytes_read == 0 || buffer[buffer_offset + bytes_read - 1] != '\n') { + buffer[buffer_offset + bytes_read] = '\n'; + bytes_read++; + } + } - StringRef line_key, rest_line; - split_line_key_rest(line, line_key, rest_line); - if (line.empty() || rest_line.is_empty()) { - continue; + size_t buffer_end = buffer_offset + bytes_read; + if (buffer_end == 0) { + break; } - switch (line_key_str_to_enum(line_key)) { - case eOBJLineKey::V: { - geom_add_vertex(curr_geom, rest_line, r_global_vertices); - break; + + /* Find last newline. */ + size_t last_nl = buffer_end; + while (last_nl > 0) { + --last_nl; + if (buffer[last_nl] == '\n') { + if (last_nl < 1 || buffer[last_nl - 1] != '\\') { + break; + } } - case eOBJLineKey::VN: { - geom_add_vertex_normal(curr_geom, rest_line, r_global_vertices); - break; + } + if (buffer[last_nl] != '\n') { + /* Whole line did not fit into our read buffer. Warn and exit. */ + fprintf(stderr, + "OBJ file contains a line #%zu that is too long (max. length %zu)\n", + line_number, + read_buffer_size_); + break; + } + ++last_nl; + + /* Parse the buffer (until last newline) that we have so far, + * line by line. */ + StringRef buffer_str{buffer.data(), (int64_t)last_nl}; + while (!buffer_str.is_empty()) { + StringRef line = read_next_line(buffer_str); + ++line_number; + if (line.is_empty()) { + continue; } - case eOBJLineKey::VT: { - geom_add_uv_vertex(rest_line, r_global_vertices); - break; + /* Most common things that start with 'v': vertices, normals, UVs. */ + if (line[0] == 'v') { + if (line.startswith("v ")) { + line = line.drop_prefix(2); + geom_add_vertex(curr_geom, line, r_global_vertices); + } + else if (line.startswith("vn ")) { + line = line.drop_prefix(3); + geom_add_vertex_normal(curr_geom, line, r_global_vertices); + } + else if (line.startswith("vt ")) { + line = line.drop_prefix(3); + geom_add_uv_vertex(line, r_global_vertices); + } } - case eOBJLineKey::F: { + /* Faces. */ + else if (line.startswith("f ")) { + line = line.drop_prefix(2); geom_add_polygon(curr_geom, - rest_line, + line, r_global_vertices, offsets, - state_material_name, - state_material_name, + state_material_index, + state_group_index, /* TODO was wrongly material name! */ state_shaded_smooth); - break; - } - case eOBJLineKey::L: { - geom_add_edge(curr_geom, rest_line, offsets, r_global_vertices); - break; - } - case eOBJLineKey::CSTYPE: { - curr_geom = geom_set_curve_type(curr_geom, - rest_line, - r_global_vertices, - state_object_group, - offsets, - r_all_geometries); - break; - } - case eOBJLineKey::DEG: { - geom_set_curve_degree(curr_geom, rest_line); - break; - } - case eOBJLineKey::CURV: { - geom_add_curve_vertex_indices(curr_geom, rest_line, r_global_vertices); - break; - } - case eOBJLineKey::PARM: { - geom_add_curve_parameters(curr_geom, rest_line); - break; - } - case eOBJLineKey::O: { + } + /* Faces. */ + else if (line.startswith("l ")) { + line = line.drop_prefix(2); + geom_add_edge(curr_geom, line, offsets, r_global_vertices); + } + /* Objects. */ + else if (line.startswith("o ")) { + line = line.drop_prefix(2); state_shaded_smooth = false; - state_object_group = ""; + state_group_name = ""; state_material_name = ""; curr_geom = create_geometry( - curr_geom, GEOM_MESH, rest_line, r_global_vertices, r_all_geometries, offsets); - break; - } - case eOBJLineKey::G: { - geom_update_object_group(rest_line, state_object_group); - break; - } - case eOBJLineKey::S: { - geom_update_smooth_group(rest_line, state_shaded_smooth); - break; - } - case eOBJLineKey::USEMTL: { - geom_update_polygon_material(curr_geom, rest_line, state_material_name); - break; - } - case eOBJLineKey::MTLLIB: { - mtl_libraries_.append(string(rest_line)); - break; - } - case eOBJLineKey::COMMENT: - break; - default: - std::cout << "Element not recognised: '" << line_key << "'" << std::endl; - break; + curr_geom, GEOM_MESH, line, r_global_vertices, r_all_geometries, offsets); + } + /* Groups. */ + else if (line.startswith("g ")) { + line = line.drop_prefix(2); + geom_update_group(line, state_group_name); + int new_index = curr_geom->group_indices_.size(); + state_group_index = curr_geom->group_indices_.lookup_or_add(state_group_name, new_index); + if (new_index == state_group_index) { + curr_geom->group_order_.append(state_group_name); + } + } + /* Smoothing groups. */ + else if (line.startswith("s ")) { + line = line.drop_prefix(2); + geom_update_smooth_group(line, state_shaded_smooth); + } + /* Materials and their libraries. */ + else if (line.startswith("usemtl ")) { + line = line.drop_prefix(7); + state_material_name = line; + int new_mat_index = curr_geom->material_indices_.size(); + state_material_index = curr_geom->material_indices_.lookup_or_add(state_material_name, + new_mat_index); + if (new_mat_index == state_material_index) { + curr_geom->material_order_.append(state_material_name); + } + } + else if (line.startswith("mtllib ")) { + line = line.drop_prefix(7); + mtl_libraries_.append(string(line)); + } + /* Comments. */ + else if (line.startswith("#")) { + /* Nothing to do. */ + } + /* Curve related things. */ + else if (line.startswith("cstype ")) { + line = line.drop_prefix(7); + curr_geom = geom_set_curve_type( + curr_geom, line, r_global_vertices, state_group_name, offsets, r_all_geometries); + } + else if (line.startswith("deg ")) { + line = line.drop_prefix(4); + geom_set_curve_degree(curr_geom, line); + } + else if (line.startswith("curv ")) { + line = line.drop_prefix(5); + geom_add_curve_vertex_indices(curr_geom, line, r_global_vertices); + } + else if (line.startswith("parm ")) { + line = line.drop_prefix(5); + geom_add_curve_parameters(curr_geom, line); + } + else if (line.startswith("end")) { + /* End of curve definition, nothing else to do. */ + } + else { + std::cout << "OBJ element not recognised: '" << line << "'" << std::endl; + } } + + /* We might have a line that was cut in the middle by the previous buffer; + * copy it over for next chunk reading. */ + size_t left_size = buffer_end - last_nl; + memmove(buffer.data(), buffer.data() + last_nl, left_size); + buffer_offset = left_size; } } -/** - * Skip all texture map options and get the filepath from a "map_" line. - */ -static StringRef skip_unsupported_options(StringRef line) +static eMTLSyntaxElement mtl_line_start_to_enum(StringRef &line) { - TextureMapOptions map_options; - StringRef last_option; - int64_t last_option_pos = 0; - - /* Find the last texture map option. */ - for (StringRef option : map_options.all_options()) { - const int64_t pos{line.find(option)}; - /* Equality (>=) takes care of finding an option in the beginning of the line. Avoid messing - * with signed-unsigned int comparison. */ - if (pos != StringRef::not_found && pos >= last_option_pos) { - last_option = option; - last_option_pos = pos; - } + if (line.startswith("map_Kd")) { + line = line.drop_prefix(6); + return eMTLSyntaxElement::map_Kd; } - - if (last_option.is_empty()) { - /* No option found, line is the filepath */ - return line; + if (line.startswith("map_Ks")) { + line = line.drop_prefix(6); + return eMTLSyntaxElement::map_Ks; + } + if (line.startswith("map_Ns")) { + line = line.drop_prefix(6); + return eMTLSyntaxElement::map_Ns; + } + if (line.startswith("map_d")) { + line = line.drop_prefix(5); + return eMTLSyntaxElement::map_d; } + if (line.startswith("refl")) { + line = line.drop_prefix(4); + return eMTLSyntaxElement::map_refl; + } + if (line.startswith("map_refl")) { + line = line.drop_prefix(8); + return eMTLSyntaxElement::map_refl; + } + if (line.startswith("map_Ke")) { + line = line.drop_prefix(6); + return eMTLSyntaxElement::map_Ke; + } + if (line.startswith("bump")) { + line = line.drop_prefix(4); + return eMTLSyntaxElement::map_Bump; + } + if (line.startswith("map_Bump") || line.startswith("map_bump")) { + line = line.drop_prefix(8); + return eMTLSyntaxElement::map_Bump; + } + return eMTLSyntaxElement::string; +} - /* Remove up to start of the last option + size of the last option + space after it. */ - line = line.drop_prefix(last_option_pos + last_option.size() + 1); - for (int i = 0; i < map_options.number_of_args(last_option); i++) { - const int64_t pos_space{line.find_first_of(' ')}; - if (pos_space != StringRef::not_found) { - BLI_assert(pos_space + 1 < line.size()); - line = line.drop_prefix(pos_space + 1); +static const std::pair<const char *, int> unsupported_texture_options[] = { + {"-blendu ", 1}, + {"-blendv ", 1}, + {"-boost ", 1}, + {"-cc ", 1}, + {"-clamp ", 1}, + {"-imfchan ", 1}, + {"-mm ", 2}, + {"-t ", 3}, + {"-texres ", 1}, +}; + +static bool parse_texture_option(StringRef &line, MTLMaterial *material, tex_map_XX &tex_map) +{ + line = drop_whitespace(line); + if (line.startswith("-o ")) { + line = line.drop_prefix(3); + line = parse_floats(line, 0.0f, tex_map.translation, 3); + return true; + } + if (line.startswith("-s ")) { + line = line.drop_prefix(3); + line = parse_floats(line, 1.0f, tex_map.scale, 3); + return true; + } + if (line.startswith("-bm ")) { + line = line.drop_prefix(4); + line = parse_float(line, 0.0f, material->map_Bump_strength); + return true; + } + if (line.startswith("-type ")) { + line = line.drop_prefix(6); + line = drop_whitespace(line); + /* Only sphere is supported. */ + tex_map.projection_type = SHD_PROJ_SPHERE; + if (!line.startswith("sphere")) { + std::cerr << "OBJ import: only sphere MTL projection type is supported: '" << line << "'" + << std::endl; + } + line = drop_non_whitespace(line); + return true; + } + /* Check for unsupported options and skip them. */ + for (const auto &opt : unsupported_texture_options) { + if (line.startswith(opt.first)) { + /* Drop the option name. */ + line = line.drop_known_prefix(opt.first); + /* Drop the arguments. */ + for (int i = 0; i < opt.second; ++i) { + line = drop_whitespace(line); + line = drop_non_whitespace(line); + } + return true; } } - return line; + return false; } -/** - * Fix incoming texture map line keys for variations due to other exporters. - */ -static string fix_bad_map_keys(StringRef map_key) +static void parse_texture_map(StringRef line, MTLMaterial *material, const char *mtl_dir_path) { - string new_map_key(map_key); - if (map_key == "refl") { - new_map_key = "map_refl"; + bool is_map = line.startswith("map_"); + bool is_refl = line.startswith("refl"); + bool is_bump = line.startswith("bump"); + if (!is_map && !is_refl && !is_bump) { + return; + } + eMTLSyntaxElement key = mtl_line_start_to_enum(line); + if (key == eMTLSyntaxElement::string || !material->texture_maps.contains(key)) { + /* No supported texture map found. */ + std::cerr << "OBJ import: MTL texture map type not supported: '" << line << "'" << std::endl; + return; } - if (map_key.find("bump") != StringRef::not_found) { - /* Handles both "bump" and "map_Bump" */ - new_map_key = "map_Bump"; + tex_map_XX &tex_map = material->texture_maps.lookup(key); + tex_map.mtl_dir_path = mtl_dir_path; + + /* Parse texture map options. */ + while (parse_texture_option(line, material, tex_map)) { } - return new_map_key; + + /* What remains is the image path. */ + line = line.trim(); + tex_map.image_path = line; } Span<std::string> OBJParser::mtl_libraries() const @@ -503,125 +629,73 @@ MTLParser::MTLParser(StringRef mtl_library, StringRefNull obj_filepath) BLI_split_dir_part(obj_filepath.data(), obj_file_dir, FILE_MAXDIR); BLI_path_join(mtl_file_path_, FILE_MAX, obj_file_dir, mtl_library.data(), NULL); BLI_split_dir_part(mtl_file_path_, mtl_dir_path_, FILE_MAXDIR); - mtl_file_.open(mtl_file_path_); - if (!mtl_file_.good()) { - fprintf(stderr, "Cannot read from MTL file:'%s'\n", mtl_file_path_); - return; - } } -void MTLParser::parse_and_store(Map<string, std::unique_ptr<MTLMaterial>> &r_mtl_materials) +void MTLParser::parse_and_store(Map<string, std::unique_ptr<MTLMaterial>> &r_materials) { - if (!mtl_file_.good()) { + size_t buffer_len; + void *buffer = BLI_file_read_text_as_mem(mtl_file_path_, 0, &buffer_len); + if (buffer == nullptr) { + fprintf(stderr, "OBJ import: cannot read from MTL file: '%s'\n", mtl_file_path_); return; } - string line; - MTLMaterial *current_mtlmaterial = nullptr; + MTLMaterial *material = nullptr; - while (std::getline(mtl_file_, line)) { - StringRef line_key, rest_line; - split_line_key_rest(line, line_key, rest_line); - if (line.empty() || rest_line.is_empty()) { + StringRef buffer_str{(const char *)buffer, (int64_t)buffer_len}; + while (!buffer_str.is_empty()) { + StringRef line = read_next_line(buffer_str); + if (line.is_empty()) { continue; } - /* Fix lower case/ incomplete texture map identifiers. */ - const string fixed_key = fix_bad_map_keys(line_key); - line_key = fixed_key; - - if (line_key == "newmtl") { - if (r_mtl_materials.remove_as(rest_line)) { - std::cerr << "Duplicate material found:'" << rest_line + if (line.startswith("newmtl ")) { + line = line.drop_prefix(7); + if (r_materials.remove_as(line)) { + std::cerr << "Duplicate material found:'" << line << "', using the last encountered Material definition." << std::endl; } - current_mtlmaterial = - r_mtl_materials.lookup_or_add(string(rest_line), std::make_unique<MTLMaterial>()).get(); - } - else if (line_key == "Ns") { - copy_string_to_float(rest_line, 324.0f, current_mtlmaterial->Ns); - } - else if (line_key == "Ka") { - Vector<StringRef> str_ka_split; - split_by_char(rest_line, ' ', str_ka_split); - copy_string_to_float(str_ka_split, 0.0f, {current_mtlmaterial->Ka, 3}); - } - else if (line_key == "Kd") { - Vector<StringRef> str_kd_split; - split_by_char(rest_line, ' ', str_kd_split); - copy_string_to_float(str_kd_split, 0.8f, {current_mtlmaterial->Kd, 3}); - } - else if (line_key == "Ks") { - Vector<StringRef> str_ks_split; - split_by_char(rest_line, ' ', str_ks_split); - copy_string_to_float(str_ks_split, 0.5f, {current_mtlmaterial->Ks, 3}); - } - else if (line_key == "Ke") { - Vector<StringRef> str_ke_split; - split_by_char(rest_line, ' ', str_ke_split); - copy_string_to_float(str_ke_split, 0.0f, {current_mtlmaterial->Ke, 3}); - } - else if (line_key == "Ni") { - copy_string_to_float(rest_line, 1.45f, current_mtlmaterial->Ni); - } - else if (line_key == "d") { - copy_string_to_float(rest_line, 1.0f, current_mtlmaterial->d); + material = r_materials.lookup_or_add(string(line), std::make_unique<MTLMaterial>()).get(); } - else if (line_key == "illum") { - copy_string_to_int(rest_line, 2, current_mtlmaterial->illum); - } - - /* Parse image textures. */ - else if (line_key.find("map_") != StringRef::not_found) { - /* TODO(@howardt): fix this. */ - eMTLSyntaxElement line_key_enum = mtl_line_key_str_to_enum(line_key); - if (line_key_enum == eMTLSyntaxElement::string || - !current_mtlmaterial->texture_maps.contains_as(line_key_enum)) { - /* No supported texture map found. */ - std::cerr << "Texture map type not supported:'" << line_key << "'" << std::endl; - continue; + else if (material != nullptr) { + if (line.startswith("Ns ")) { + line = line.drop_prefix(3); + parse_float(line, 324.0f, material->Ns); } - tex_map_XX &tex_map = current_mtlmaterial->texture_maps.lookup(line_key_enum); - Vector<StringRef> str_map_xx_split; - split_by_char(rest_line, ' ', str_map_xx_split); - - /* TODO(@ankitm): use `skip_unsupported_options` for parsing these options too? */ - const int64_t pos_o{str_map_xx_split.first_index_of_try("-o")}; - if (pos_o != -1 && pos_o + 3 < str_map_xx_split.size()) { - copy_string_to_float({str_map_xx_split[pos_o + 1], - str_map_xx_split[pos_o + 2], - str_map_xx_split[pos_o + 3]}, - 0.0f, - {tex_map.translation, 3}); - } - const int64_t pos_s{str_map_xx_split.first_index_of_try("-s")}; - if (pos_s != -1 && pos_s + 3 < str_map_xx_split.size()) { - copy_string_to_float({str_map_xx_split[pos_s + 1], - str_map_xx_split[pos_s + 2], - str_map_xx_split[pos_s + 3]}, - 1.0f, - {tex_map.scale, 3}); - } - /* Only specific to Normal Map node. */ - const int64_t pos_bm{str_map_xx_split.first_index_of_try("-bm")}; - if (pos_bm != -1 && pos_bm + 1 < str_map_xx_split.size()) { - copy_string_to_float( - str_map_xx_split[pos_bm + 1], 0.0f, current_mtlmaterial->map_Bump_strength); - } - const int64_t pos_projection{str_map_xx_split.first_index_of_try("-type")}; - if (pos_projection != -1 && pos_projection + 1 < str_map_xx_split.size()) { - /* Only Sphere is supported, so whatever the type is, set it to Sphere. */ - tex_map.projection_type = SHD_PROJ_SPHERE; - if (str_map_xx_split[pos_projection + 1] != "sphere") { - std::cerr << "Using projection type 'sphere', not:'" - << str_map_xx_split[pos_projection + 1] << "'." << std::endl; - } + else if (line.startswith("Ka ")) { + line = line.drop_prefix(3); + parse_floats(line, 0.0f, material->Ka, 3); + } + else if (line.startswith("Kd ")) { + line = line.drop_prefix(3); + parse_floats(line, 0.8f, material->Kd, 3); + } + else if (line.startswith("Ks ")) { + line = line.drop_prefix(3); + parse_floats(line, 0.5f, material->Ks, 3); + } + else if (line.startswith("Ke ")) { + line = line.drop_prefix(3); + parse_floats(line, 0.0f, material->Ke, 3); + } + else if (line.startswith("Ni ")) { + line = line.drop_prefix(3); + parse_float(line, 1.45f, material->Ni); + } + else if (line.startswith("d ")) { + line = line.drop_prefix(2); + parse_float(line, 1.0f, material->d); + } + else if (line.startswith("illum ")) { + line = line.drop_prefix(6); + parse_int(line, 2, material->illum); + } + else { + parse_texture_map(line, material, mtl_dir_path_); } - - /* Skip all unsupported options and arguments. */ - tex_map.image_path = string(skip_unsupported_options(rest_line)); - tex_map.mtl_dir_path = mtl_dir_path_; } } + + MEM_freeN(buffer); } } // namespace blender::io::obj diff --git a/source/blender/io/wavefront_obj/importer/obj_import_file_reader.hh b/source/blender/io/wavefront_obj/importer/obj_import_file_reader.hh index 24d026d75e5..8093417fcda 100644 --- a/source/blender/io/wavefront_obj/importer/obj_import_file_reader.hh +++ b/source/blender/io/wavefront_obj/importer/obj_import_file_reader.hh @@ -18,14 +18,16 @@ namespace blender::io::obj { class OBJParser { private: const OBJImportParams &import_params_; - blender::fstream obj_file_; + FILE *obj_file_; Vector<std::string> mtl_libraries_; + size_t read_buffer_size_; public: /** * Open OBJ file at the path given in import parameters. */ - OBJParser(const OBJImportParams &import_params); + OBJParser(const OBJImportParams &import_params, size_t read_buffer_size); + ~OBJParser(); /** * Read the OBJ file line by line and create OBJ Geometry instances. Also store all the vertex @@ -39,111 +41,6 @@ class OBJParser { Span<std::string> mtl_libraries() const; }; -enum class eOBJLineKey { - V, - VN, - VT, - F, - L, - CSTYPE, - DEG, - CURV, - PARM, - O, - G, - S, - USEMTL, - MTLLIB, - COMMENT -}; - -constexpr eOBJLineKey line_key_str_to_enum(const std::string_view key_str) -{ - if (key_str == "v" || key_str == "V") { - return eOBJLineKey::V; - } - if (key_str == "vn" || key_str == "VN") { - return eOBJLineKey::VN; - } - if (key_str == "vt" || key_str == "VT") { - return eOBJLineKey::VT; - } - if (key_str == "f" || key_str == "F") { - return eOBJLineKey::F; - } - if (key_str == "l" || key_str == "L") { - return eOBJLineKey::L; - } - if (key_str == "cstype" || key_str == "CSTYPE") { - return eOBJLineKey::CSTYPE; - } - if (key_str == "deg" || key_str == "DEG") { - return eOBJLineKey::DEG; - } - if (key_str == "curv" || key_str == "CURV") { - return eOBJLineKey::CURV; - } - if (key_str == "parm" || key_str == "PARM") { - return eOBJLineKey::PARM; - } - if (key_str == "o" || key_str == "O") { - return eOBJLineKey::O; - } - if (key_str == "g" || key_str == "G") { - return eOBJLineKey::G; - } - if (key_str == "s" || key_str == "S") { - return eOBJLineKey::S; - } - if (key_str == "usemtl" || key_str == "USEMTL") { - return eOBJLineKey::USEMTL; - } - if (key_str == "mtllib" || key_str == "MTLLIB") { - return eOBJLineKey::MTLLIB; - } - if (key_str == "#") { - return eOBJLineKey::COMMENT; - } - return eOBJLineKey::COMMENT; -} - -/** - * All texture map options with number of arguments they accept. - */ -class TextureMapOptions { - private: - Map<const std::string, int> tex_map_options; - - public: - TextureMapOptions() - { - tex_map_options.add_new("-blendu", 1); - tex_map_options.add_new("-blendv", 1); - tex_map_options.add_new("-boost", 1); - tex_map_options.add_new("-mm", 2); - tex_map_options.add_new("-o", 3); - tex_map_options.add_new("-s", 3); - tex_map_options.add_new("-t", 3); - tex_map_options.add_new("-texres", 1); - tex_map_options.add_new("-clamp", 1); - tex_map_options.add_new("-bm", 1); - tex_map_options.add_new("-imfchan", 1); - } - - /** - * All valid option strings. - */ - Map<const std::string, int>::KeyIterator all_options() const - { - return tex_map_options.keys(); - } - - int number_of_args(StringRef option) const - { - return tex_map_options.lookup_as(std::string(option)); - } -}; - class MTLParser { private: char mtl_file_path_[FILE_MAX]; @@ -151,7 +48,6 @@ class MTLParser { * Directory in which the MTL file is found. */ char mtl_dir_path_[FILE_MAX]; - blender::fstream mtl_file_; public: /** @@ -162,6 +58,6 @@ class MTLParser { /** * Read MTL file(s) and add MTLMaterial instances to the given Map reference. */ - void parse_and_store(Map<std::string, std::unique_ptr<MTLMaterial>> &r_mtl_materials); + void parse_and_store(Map<std::string, std::unique_ptr<MTLMaterial>> &r_materials); }; } // namespace blender::io::obj diff --git a/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc b/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc index 55b2873a3de..01a2d63927e 100644 --- a/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc +++ b/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc @@ -18,6 +18,7 @@ #include "BLI_math_vector.h" #include "BLI_set.hh" +#include "IO_wavefront_obj.h" #include "importer_mesh_utils.hh" #include "obj_import_mesh.hh" @@ -35,7 +36,7 @@ Object *MeshFromGeometry::create_mesh( } fixup_invalid_faces(); - const int64_t tot_verts_object{mesh_geometry_.vertex_indices_.size()}; + const int64_t tot_verts_object{mesh_geometry_.vertex_count_}; /* Total explicitly imported edges, not the ones belonging the polygons to be created. */ const int64_t tot_edges{mesh_geometry_.edges_.size()}; const int64_t tot_face_elems{mesh_geometry_.face_elements_.size()}; @@ -52,11 +53,13 @@ Object *MeshFromGeometry::create_mesh( create_normals(mesh); create_materials(bmain, materials, created_materials, obj); - bool verbose_validate = false; + if (import_params.validate_meshes || mesh_geometry_.has_invalid_polys_) { + bool verbose_validate = false; #ifdef DEBUG - verbose_validate = true; + verbose_validate = true; #endif - BKE_mesh_validate(mesh, verbose_validate, false); + BKE_mesh_validate(mesh, verbose_validate, false); + } transform_object(obj, import_params); /* FIXME: after 2.80; `mesh->flag` isn't copied by #BKE_mesh_nomain_to_mesh() */ @@ -73,9 +76,9 @@ void MeshFromGeometry::fixup_invalid_faces() for (int64_t face_idx = 0; face_idx < mesh_geometry_.face_elements_.size(); ++face_idx) { const PolyElem &curr_face = mesh_geometry_.face_elements_[face_idx]; - if (curr_face.face_corners.size() < 3) { + if (curr_face.corner_count_ < 3) { /* Skip and remove faces that have fewer than 3 corners. */ - mesh_geometry_.total_loops_ -= curr_face.face_corners.size(); + mesh_geometry_.total_loops_ -= curr_face.corner_count_; mesh_geometry_.face_elements_.remove_and_reorder(face_idx); continue; } @@ -84,12 +87,14 @@ void MeshFromGeometry::fixup_invalid_faces() * basically whether it has duplicate vertex indices. */ bool valid = true; Set<int, 8> used_verts; - for (const PolyCorner &corner : curr_face.face_corners) { - if (used_verts.contains(corner.vert_index)) { + for (int i = 0; i < curr_face.corner_count_; ++i) { + int corner_idx = curr_face.start_index_ + i; + int vertex_idx = mesh_geometry_.face_corners_[corner_idx].vert_index; + if (used_verts.contains(vertex_idx)) { valid = false; break; } - used_verts.add(corner.vert_index); + used_verts.add(vertex_idx); } if (valid) { continue; @@ -100,20 +105,22 @@ void MeshFromGeometry::fixup_invalid_faces() Vector<int, 8> face_verts; Vector<int, 8> face_uvs; Vector<int, 8> face_normals; - face_verts.reserve(curr_face.face_corners.size()); - face_uvs.reserve(curr_face.face_corners.size()); - face_normals.reserve(curr_face.face_corners.size()); - for (const PolyCorner &corner : curr_face.face_corners) { + face_verts.reserve(curr_face.corner_count_); + face_uvs.reserve(curr_face.corner_count_); + face_normals.reserve(curr_face.corner_count_); + for (int i = 0; i < curr_face.corner_count_; ++i) { + int corner_idx = curr_face.start_index_ + i; + const PolyCorner &corner = mesh_geometry_.face_corners_[corner_idx]; face_verts.append(corner.vert_index); face_normals.append(corner.vertex_normal_index); face_uvs.append(corner.uv_vert_index); } - std::string face_vertex_group = curr_face.vertex_group; - std::string face_material_name = curr_face.material_name; + int face_vertex_group = curr_face.vertex_group_index; + int face_material = curr_face.material_index; bool face_shaded_smooth = curr_face.shaded_smooth; /* Remove the invalid face. */ - mesh_geometry_.total_loops_ -= curr_face.face_corners.size(); + mesh_geometry_.total_loops_ -= curr_face.corner_count_; mesh_geometry_.face_elements_.remove_and_reorder(face_idx); Vector<Vector<int>> new_faces = fixup_invalid_polygon(global_vertices_.vertices, face_verts); @@ -124,13 +131,14 @@ void MeshFromGeometry::fixup_invalid_faces() continue; } PolyElem new_face{}; - new_face.vertex_group = face_vertex_group; - new_face.material_name = face_material_name; + new_face.vertex_group_index = face_vertex_group; + new_face.material_index = face_material; new_face.shaded_smooth = face_shaded_smooth; - new_face.face_corners.reserve(face.size()); + new_face.start_index_ = mesh_geometry_.face_corners_.size(); + new_face.corner_count_ = face.size(); for (int idx : face) { BLI_assert(idx >= 0 && idx < face_verts.size()); - new_face.face_corners.append({face_verts[idx], face_uvs[idx], face_normals[idx]}); + mesh_geometry_.face_corners_.append({face_verts[idx], face_uvs[idx], face_normals[idx]}); } mesh_geometry_.face_elements_.append(new_face); mesh_geometry_.total_loops_ += face.size(); @@ -140,13 +148,14 @@ void MeshFromGeometry::fixup_invalid_faces() void MeshFromGeometry::create_vertices(Mesh *mesh) { - const int64_t tot_verts_object{mesh_geometry_.vertex_indices_.size()}; + const int tot_verts_object{mesh_geometry_.vertex_count_}; for (int i = 0; i < tot_verts_object; ++i) { - if (mesh_geometry_.vertex_indices_[i] < global_vertices_.vertices.size()) { - copy_v3_v3(mesh->mvert[i].co, global_vertices_.vertices[mesh_geometry_.vertex_indices_[i]]); + int vi = mesh_geometry_.vertex_start_ + i; + if (vi < global_vertices_.vertices.size()) { + copy_v3_v3(mesh->mvert[i].co, global_vertices_.vertices[vi]); } else { - std::cerr << "Vertex index:" << mesh_geometry_.vertex_indices_[i] + std::cerr << "Vertex index:" << vi << " larger than total vertices:" << global_vertices_.vertices.size() << " ." << std::endl; } @@ -158,7 +167,7 @@ void MeshFromGeometry::create_polys_loops(Object *obj, Mesh *mesh) /* Will not be used if vertex groups are not imported. */ mesh->dvert = nullptr; float weight = 0.0f; - const int64_t total_verts = mesh_geometry_.vertex_indices_.size(); + const int64_t total_verts = mesh_geometry_.vertex_count_; if (total_verts && mesh_geometry_.use_vertex_groups_) { mesh->dvert = static_cast<MDeformVert *>( CustomData_add_layer(&mesh->vdata, CD_MDEFORMVERT, CD_CALLOC, nullptr, total_verts)); @@ -168,34 +177,32 @@ void MeshFromGeometry::create_polys_loops(Object *obj, Mesh *mesh) UNUSED_VARS(weight); } - /* Do not remove elements from the VectorSet since order of insertion is required. - * StringRef is fine since per-face deform group name outlives the VectorSet. */ - VectorSet<StringRef> group_names; const int64_t tot_face_elems{mesh->totpoly}; int tot_loop_idx = 0; for (int poly_idx = 0; poly_idx < tot_face_elems; ++poly_idx) { const PolyElem &curr_face = mesh_geometry_.face_elements_[poly_idx]; - if (curr_face.face_corners.size() < 3) { + if (curr_face.corner_count_ < 3) { /* Don't add single vertex face, or edges. */ std::cerr << "Face with less than 3 vertices found, skipping." << std::endl; continue; } MPoly &mpoly = mesh->mpoly[poly_idx]; - mpoly.totloop = curr_face.face_corners.size(); + mpoly.totloop = curr_face.corner_count_; mpoly.loopstart = tot_loop_idx; if (curr_face.shaded_smooth) { mpoly.flag |= ME_SMOOTH; } - mpoly.mat_nr = mesh_geometry_.material_names_.index_of_try(curr_face.material_name); + mpoly.mat_nr = curr_face.material_index; /* Importing obj files without any materials would result in negative indices, which is not * supported. */ if (mpoly.mat_nr < 0) { mpoly.mat_nr = 0; } - for (const PolyCorner &curr_corner : curr_face.face_corners) { + for (int idx = 0; idx < curr_face.corner_count_; ++idx) { + const PolyCorner &curr_corner = mesh_geometry_.face_corners_[curr_face.start_index_ + idx]; MLoop &mloop = mesh->mloop[tot_loop_idx]; tot_loop_idx++; mloop.v = curr_corner.vert_index; @@ -212,23 +219,17 @@ void MeshFromGeometry::create_polys_loops(Object *obj, Mesh *mesh) MEM_callocN(sizeof(MDeformWeight), "OBJ Import Deform Weight")); } /* Every vertex in a face is assigned the same deform group. */ - int64_t pos_name{group_names.index_of_try(curr_face.vertex_group)}; - if (pos_name == -1) { - group_names.add_new(curr_face.vertex_group); - pos_name = group_names.size() - 1; - } - BLI_assert(pos_name >= 0); + int group_idx = curr_face.vertex_group_index; /* Deform group number (def_nr) must behave like an index into the names' list. */ - *(def_vert.dw) = {static_cast<unsigned int>(pos_name), weight}; + *(def_vert.dw) = {static_cast<unsigned int>(group_idx), weight}; } } if (!mesh->dvert) { return; } - /* Add deform group(s) to the object's defbase. */ - for (StringRef name : group_names) { - /* Adding groups in this order assumes that def_nr is an index into the names' list. */ + /* Add deform group names. */ + for (const std::string &name : mesh_geometry_.group_order_) { BKE_object_defgroup_add_name(obj, name.data()); } } @@ -236,7 +237,7 @@ void MeshFromGeometry::create_polys_loops(Object *obj, Mesh *mesh) void MeshFromGeometry::create_edges(Mesh *mesh) { const int64_t tot_edges{mesh_geometry_.edges_.size()}; - const int64_t total_verts{mesh_geometry_.vertex_indices_.size()}; + const int64_t total_verts{mesh_geometry_.vertex_count_}; UNUSED_VARS_NDEBUG(total_verts); for (int i = 0; i < tot_edges; ++i) { const MEdge &src_edge = mesh_geometry_.edges_[i]; @@ -263,7 +264,8 @@ void MeshFromGeometry::create_uv_verts(Mesh *mesh) int tot_loop_idx = 0; for (const PolyElem &curr_face : mesh_geometry_.face_elements_) { - for (const PolyCorner &curr_corner : curr_face.face_corners) { + for (int idx = 0; idx < curr_face.corner_count_; ++idx) { + const PolyCorner &curr_corner = mesh_geometry_.face_corners_[curr_face.start_index_ + idx]; if (curr_corner.uv_vert_index >= 0 && curr_corner.uv_vert_index < global_vertices_.uv_vertices.size()) { const float2 &mluv_src = global_vertices_.uv_vertices[curr_corner.uv_vert_index]; @@ -317,7 +319,7 @@ void MeshFromGeometry::create_materials( Map<std::string, Material *> &created_materials, Object *obj) { - for (const std::string &name : mesh_geometry_.material_names_) { + for (const std::string &name : mesh_geometry_.material_order_) { Material *mat = get_or_create_material(bmain, name, materials, created_materials); if (mat == nullptr) { continue; @@ -340,7 +342,8 @@ void MeshFromGeometry::create_normals(Mesh *mesh) MEM_malloc_arrayN(mesh_geometry_.total_loops_, sizeof(float[3]), __func__)); int tot_loop_idx = 0; for (const PolyElem &curr_face : mesh_geometry_.face_elements_) { - for (const PolyCorner &curr_corner : curr_face.face_corners) { + for (int idx = 0; idx < curr_face.corner_count_; ++idx) { + const PolyCorner &curr_corner = mesh_geometry_.face_corners_[curr_face.start_index_ + idx]; int n_index = curr_corner.vertex_normal_index; float3 normal(0, 0, 0); if (n_index >= 0) { diff --git a/source/blender/io/wavefront_obj/importer/obj_import_mesh.hh b/source/blender/io/wavefront_obj/importer/obj_import_mesh.hh index 2a838215421..7cc7ed25ad1 100644 --- a/source/blender/io/wavefront_obj/importer/obj_import_mesh.hh +++ b/source/blender/io/wavefront_obj/importer/obj_import_mesh.hh @@ -45,11 +45,8 @@ class MeshFromGeometry : NonMovable, NonCopyable { void fixup_invalid_faces(); void create_vertices(Mesh *mesh); /** - * Create polygons for the Mesh, set smooth shading flag, deform group name, - * assigned material also. - * - * It must receive all polygons to be added to the mesh. - * Remove holes from polygons before * calling this. + * Create polygons for the Mesh, set smooth shading flags, deform group names, + * Materials. */ void create_polys_loops(Object *obj, Mesh *mesh); /** diff --git a/source/blender/io/wavefront_obj/importer/obj_import_mtl.cc b/source/blender/io/wavefront_obj/importer/obj_import_mtl.cc index 88a8c07e325..f2a8941e8a7 100644 --- a/source/blender/io/wavefront_obj/importer/obj_import_mtl.cc +++ b/source/blender/io/wavefront_obj/importer/obj_import_mtl.cc @@ -13,12 +13,13 @@ #include "DNA_material_types.h" #include "DNA_node_types.h" +#include "IO_string_utils.hh" + #include "NOD_shader.h" /* TODO: move eMTLSyntaxElement out of following file into a more neutral place */ #include "obj_export_io.hh" #include "obj_import_mtl.hh" -#include "parser_string_utils.hh" namespace blender::io::obj { @@ -96,13 +97,16 @@ static bool load_texture_image(Main *bmain, const tex_map_XX &tex_map, bNode *r_ return true; } /* Try removing quotes. */ - std::string no_quote_path{replace_all_occurences(tex_path, "\"", "")}; + std::string no_quote_path{tex_path}; + auto end_pos = std::remove(no_quote_path.begin(), no_quote_path.end(), '"'); + no_quote_path.erase(end_pos, no_quote_path.end()); if (no_quote_path != tex_path && load_texture_image_at_path(bmain, tex_map, r_node, no_quote_path)) { return true; } /* Try replacing underscores with spaces. */ - std::string no_underscore_path{replace_all_occurences(no_quote_path, "_", " ")}; + std::string no_underscore_path{no_quote_path}; + std::replace(no_underscore_path.begin(), no_underscore_path.end(), '_', ' '); if (no_underscore_path != no_quote_path && no_underscore_path != tex_path && load_texture_image_at_path(bmain, tex_map, r_node, no_underscore_path)) { return true; diff --git a/source/blender/io/wavefront_obj/importer/obj_import_mtl.hh b/source/blender/io/wavefront_obj/importer/obj_import_mtl.hh index 4b7827b2035..74bc9f21bc4 100644 --- a/source/blender/io/wavefront_obj/importer/obj_import_mtl.hh +++ b/source/blender/io/wavefront_obj/importer/obj_import_mtl.hh @@ -90,29 +90,4 @@ class ShaderNodetreeWrap { void add_image_textures(Main *bmain, Material *mat); }; -constexpr eMTLSyntaxElement mtl_line_key_str_to_enum(const std::string_view key_str) -{ - if (key_str == "map_Kd") { - return eMTLSyntaxElement::map_Kd; - } - if (key_str == "map_Ks") { - return eMTLSyntaxElement::map_Ks; - } - if (key_str == "map_Ns") { - return eMTLSyntaxElement::map_Ns; - } - if (key_str == "map_d") { - return eMTLSyntaxElement::map_d; - } - if (key_str == "refl" || key_str == "map_refl") { - return eMTLSyntaxElement::map_refl; - } - if (key_str == "map_Ke") { - return eMTLSyntaxElement::map_Ke; - } - if (key_str == "map_Bump" || key_str == "bump") { - return eMTLSyntaxElement::map_Bump; - } - return eMTLSyntaxElement::string; -} } // namespace blender::io::obj diff --git a/source/blender/io/wavefront_obj/importer/obj_import_objects.hh b/source/blender/io/wavefront_obj/importer/obj_import_objects.hh index c6ce7d3c434..b67ba46af03 100644 --- a/source/blender/io/wavefront_obj/importer/obj_import_objects.hh +++ b/source/blender/io/wavefront_obj/importer/obj_import_objects.hh @@ -8,6 +8,7 @@ #include "BKE_lib_id.h" +#include "BLI_map.hh" #include "BLI_math_vec_types.hh" #include "BLI_vector.hh" #include "BLI_vector_set.hh" @@ -61,10 +62,11 @@ struct PolyCorner { }; struct PolyElem { - std::string vertex_group; - std::string material_name; + int vertex_group_index = -1; + int material_index = -1; bool shaded_smooth = false; - Vector<PolyCorner> face_corners; + int start_index_ = 0; + int corner_count_ = 0; }; /** @@ -93,15 +95,20 @@ enum eGeometryType { struct Geometry { eGeometryType geom_type_ = GEOM_MESH; std::string geometry_name_; - VectorSet<std::string> material_names_; - /** - * Indices in the vector range from zero to total vertices in a geometry. - * Values range from zero to total coordinates in the global list. - */ - Vector<int> vertex_indices_; + Map<std::string, int> group_indices_; + Vector<std::string> group_order_; + Map<std::string, int> material_indices_; + Vector<std::string> material_order_; + + int vertex_start_ = 0; + int vertex_count_ = 0; /** Edges written in the file in addition to (or even without polygon) elements. */ Vector<MEdge> edges_; + + Vector<PolyCorner> face_corners_; Vector<PolyElem> face_elements_; + + bool has_invalid_polys_ = false; bool has_vertex_normals_ = false; bool use_vertex_groups_ = false; NurbsElement nurbs_element_; diff --git a/source/blender/io/wavefront_obj/importer/obj_importer.cc b/source/blender/io/wavefront_obj/importer/obj_importer.cc index 631ddcc5cf4..c21d2d9583c 100644 --- a/source/blender/io/wavefront_obj/importer/obj_importer.cc +++ b/source/blender/io/wavefront_obj/importer/obj_importer.cc @@ -42,6 +42,12 @@ static void geometry_to_blender_objects( BKE_view_layer_base_deselect_all(view_layer); LayerCollection *lc = BKE_layer_collection_get_active(view_layer); + /* Don't do collection syncs for each object, will do once after the loop. */ + BKE_layer_collection_resync_forbid(); + + /* Create all the objects. */ + Vector<Object *> objects; + objects.reserve(all_geometries.size()); for (const std::unique_ptr<Geometry> &geometry : all_geometries) { Object *obj = nullptr; if (geometry->geom_type_ == GEOM_MESH) { @@ -54,17 +60,25 @@ static void geometry_to_blender_objects( } if (obj != nullptr) { BKE_collection_object_add(bmain, lc->collection, obj); - Base *base = BKE_view_layer_base_find(view_layer, obj); - /* TODO: is setting active needed? */ - BKE_view_layer_base_select_and_set_active(view_layer, base); - - DEG_id_tag_update(&lc->collection->id, ID_RECALC_COPY_ON_WRITE); - DEG_id_tag_update_ex(bmain, - &obj->id, - ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_ANIMATION | - ID_RECALC_BASE_FLAGS); + objects.append(obj); } } + + /* Sync the collection after all objects are created. */ + BKE_layer_collection_resync_allow(); + BKE_main_collection_sync(bmain); + + /* After collection sync, select objects in the view layer and do DEG updates. */ + for (Object *obj : objects) { + Base *base = BKE_view_layer_base_find(view_layer, obj); + BKE_view_layer_base_select_and_set_active(view_layer, base); + + DEG_id_tag_update(&lc->collection->id, ID_RECALC_COPY_ON_WRITE); + int flags = ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_ANIMATION | + ID_RECALC_BASE_FLAGS; + DEG_id_tag_update_ex(bmain, &obj->id, flags); + } + DEG_id_tag_update(&scene->id, ID_RECALC_BASE_FLAGS); DEG_relations_tag_update(bmain); } @@ -81,7 +95,8 @@ void importer_main(bContext *C, const OBJImportParams &import_params) void importer_main(Main *bmain, Scene *scene, ViewLayer *view_layer, - const OBJImportParams &import_params) + const OBJImportParams &import_params, + size_t read_buffer_size) { /* List of Geometry instances to be parsed from OBJ file. */ Vector<std::unique_ptr<Geometry>> all_geometries; @@ -91,7 +106,7 @@ void importer_main(Main *bmain, Map<std::string, std::unique_ptr<MTLMaterial>> materials; Map<std::string, Material *> created_materials; - OBJParser obj_parser{import_params}; + OBJParser obj_parser{import_params, read_buffer_size}; obj_parser.parse(all_geometries, global_vertices); for (StringRef mtl_library : obj_parser.mtl_libraries()) { diff --git a/source/blender/io/wavefront_obj/importer/obj_importer.hh b/source/blender/io/wavefront_obj/importer/obj_importer.hh index fd83117ebc6..35f401d7cb0 100644 --- a/source/blender/io/wavefront_obj/importer/obj_importer.hh +++ b/source/blender/io/wavefront_obj/importer/obj_importer.hh @@ -17,6 +17,7 @@ void importer_main(bContext *C, const OBJImportParams &import_params); void importer_main(Main *bmain, Scene *scene, ViewLayer *view_layer, - const OBJImportParams &import_params); + const OBJImportParams &import_params, + size_t read_buffer_size = 64 * 1024); } // namespace blender::io::obj diff --git a/source/blender/io/wavefront_obj/importer/parser_string_utils.cc b/source/blender/io/wavefront_obj/importer/parser_string_utils.cc deleted file mode 100644 index 6671a86f5ee..00000000000 --- a/source/blender/io/wavefront_obj/importer/parser_string_utils.cc +++ /dev/null @@ -1,174 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -#include <fstream> -#include <iostream> -#include <sstream> - -#include "BLI_math_vec_types.hh" -#include "BLI_span.hh" -#include "BLI_string_ref.hh" -#include "BLI_vector.hh" - -#include "parser_string_utils.hh" - -/* Note: these OBJ parser helper functions are planned to get fairly large - * changes "soon", so don't read too much into current implementation... */ - -namespace blender::io::obj { -using std::string; - -void read_next_line(std::fstream &file, string &r_line) -{ - std::string new_line; - while (file.good() && !r_line.empty() && r_line.back() == '\\') { - new_line.clear(); - const bool ok = static_cast<bool>(std::getline(file, new_line)); - /* Remove the last backslash character. */ - r_line.pop_back(); - r_line.append(new_line); - if (!ok || new_line.empty()) { - return; - } - } -} - -void split_line_key_rest(const StringRef line, StringRef &r_line_key, StringRef &r_rest_line) -{ - if (line.is_empty()) { - return; - } - - const int64_t pos_split{line.find_first_of(' ')}; - if (pos_split == StringRef::not_found) { - /* Use the first character if no space is found in the line. It's usually a comment like: - * #This is a comment. */ - r_line_key = line.substr(0, 1); - } - else { - r_line_key = line.substr(0, pos_split); - } - - /* Eat the delimiter also using "+ 1". */ - r_rest_line = line.drop_prefix(r_line_key.size() + 1); - if (r_rest_line.is_empty()) { - return; - } - - /* Remove any leading spaces, trailing spaces & \r character, if any. */ - const int64_t leading_space{r_rest_line.find_first_not_of(' ')}; - if (leading_space != StringRef::not_found) { - r_rest_line = r_rest_line.drop_prefix(leading_space); - } - - /* Another way is to do a test run before the actual parsing to find the newline - * character and use it in the getline. */ - const int64_t carriage_return{r_rest_line.find_first_of('\r')}; - if (carriage_return != StringRef::not_found) { - r_rest_line = r_rest_line.substr(0, carriage_return + 1); - } - - const int64_t trailing_space{r_rest_line.find_last_not_of(' ')}; - if (trailing_space != StringRef::not_found) { - /* The position is of a character that is not ' ', so count of characters is position + 1. */ - r_rest_line = r_rest_line.substr(0, trailing_space + 1); - } -} - -void split_by_char(StringRef in_string, const char delimiter, Vector<StringRef> &r_out_list) -{ - r_out_list.clear(); - - while (!in_string.is_empty()) { - const int64_t pos_delim{in_string.find_first_of(delimiter)}; - const int64_t word_len = pos_delim == StringRef::not_found ? in_string.size() : pos_delim; - - StringRef word{in_string.data(), word_len}; - if (!word.is_empty() && !(word == " " && !(word[0] == '\0'))) { - r_out_list.append(word); - } - if (pos_delim == StringRef::not_found) { - return; - } - /* Skip the word already stored. */ - in_string = in_string.drop_prefix(word_len); - /* Skip all delimiters. */ - const int64_t pos_non_delim = in_string.find_first_not_of(delimiter); - if (pos_non_delim == StringRef::not_found) { - return; - } - in_string = in_string.drop_prefix(std::min(pos_non_delim, in_string.size())); - } -} - -void copy_string_to_float(StringRef src, const float fallback_value, float &r_dst) -{ - try { - r_dst = std::stof(string(src)); - } - catch (const std::invalid_argument &inv_arg) { - std::cerr << "Bad conversion to float:'" << inv_arg.what() << "':'" << src << "'" << std::endl; - r_dst = fallback_value; - } - catch (const std::out_of_range &out_of_range) { - std::cerr << "Out of range for float:'" << out_of_range.what() << ":'" << src << "'" - << std::endl; - r_dst = fallback_value; - } -} - -void copy_string_to_float(Span<StringRef> src, - const float fallback_value, - MutableSpan<float> r_dst) -{ - for (int i = 0; i < r_dst.size(); ++i) { - if (i < src.size()) { - copy_string_to_float(src[i], fallback_value, r_dst[i]); - } - else { - r_dst[i] = fallback_value; - } - } -} - -void copy_string_to_int(StringRef src, const int fallback_value, int &r_dst) -{ - try { - r_dst = std::stoi(string(src)); - } - catch (const std::invalid_argument &inv_arg) { - std::cerr << "Bad conversion to int:'" << inv_arg.what() << "':'" << src << "'" << std::endl; - r_dst = fallback_value; - } - catch (const std::out_of_range &out_of_range) { - std::cerr << "Out of range for int:'" << out_of_range.what() << ":'" << src << "'" - << std::endl; - r_dst = fallback_value; - } -} - -void copy_string_to_int(Span<StringRef> src, const int fallback_value, MutableSpan<int> r_dst) -{ - for (int i = 0; i < r_dst.size(); ++i) { - if (i < src.size()) { - copy_string_to_int(src[i], fallback_value, r_dst[i]); - } - else { - r_dst[i] = fallback_value; - } - } -} - -std::string replace_all_occurences(StringRef original, StringRef to_remove, StringRef to_add) -{ - std::string clean{original}; - while (true) { - const std::string::size_type pos = clean.find(to_remove); - if (pos == std::string::npos) { - break; - } - clean.replace(pos, to_add.size(), to_add); - } - return clean; -} - -} // namespace blender::io::obj diff --git a/source/blender/io/wavefront_obj/importer/parser_string_utils.hh b/source/blender/io/wavefront_obj/importer/parser_string_utils.hh deleted file mode 100644 index 62cfbebccf3..00000000000 --- a/source/blender/io/wavefront_obj/importer/parser_string_utils.hh +++ /dev/null @@ -1,54 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -namespace blender::io::obj { - -/* Note: these OBJ parser helper functions are planned to get fairly large - * changes "soon", so don't read too much into current implementation... */ - -/** - * Store multiple lines separated by an escaped newline character: `\\n`. - * Use this before doing any parse operations on the read string. - */ -void read_next_line(std::fstream &file, std::string &r_line); -/** - * Split a line string into the first word (key) and the rest of the line. - * Also remove leading & trailing spaces as well as `\r` carriage return - * character if present. - */ -void split_line_key_rest(StringRef line, StringRef &r_line_key, StringRef &r_rest_line); -/** - * Split the given string by the delimiter and fill the given vector. - * If an intermediate string is empty, or space or null character, it is not appended to the - * vector. - */ -void split_by_char(StringRef in_string, const char delimiter, Vector<StringRef> &r_out_list); -/** - * Convert the given string to float and assign it to the destination value. - * - * If the string cannot be converted to a float, the fallback value is used. - */ -void copy_string_to_float(StringRef src, const float fallback_value, float &r_dst); -/** - * Convert all members of the Span of strings to floats and assign them to the float - * array members. Usually used for values like coordinates. - * - * If a string cannot be converted to a float, the fallback value is used. - */ -void copy_string_to_float(Span<StringRef> src, - const float fallback_value, - MutableSpan<float> r_dst); -/** - * Convert the given string to int and assign it to the destination value. - * - * If the string cannot be converted to an integer, the fallback value is used. - */ -void copy_string_to_int(StringRef src, const int fallback_value, int &r_dst); -/** - * Convert the given strings to ints and fill the destination int buffer. - * - * If a string cannot be converted to an integer, the fallback value is used. - */ -void copy_string_to_int(Span<StringRef> src, const int fallback_value, MutableSpan<int> r_dst); -std::string replace_all_occurences(StringRef original, StringRef to_remove, StringRef to_add); - -} // namespace blender::io::obj diff --git a/source/blender/io/wavefront_obj/tests/obj_exporter_tests.cc b/source/blender/io/wavefront_obj/tests/obj_exporter_tests.cc index 8599b38d55b..f74bfc155dd 100644 --- a/source/blender/io/wavefront_obj/tests/obj_exporter_tests.cc +++ b/source/blender/io/wavefront_obj/tests/obj_exporter_tests.cc @@ -48,7 +48,6 @@ class obj_exporter_test : public BlendfileLoadingBaseTest { }; const std::string all_objects_file = "io_tests/blend_scene/all_objects.blend"; -const std::string all_curve_objects_file = "io_tests/blend_scene/all_curves.blend"; TEST_F(obj_exporter_test, filter_objects_curves_as_mesh) { @@ -58,7 +57,7 @@ TEST_F(obj_exporter_test, filter_objects_curves_as_mesh) return; } auto [objmeshes, objcurves]{filter_supported_objects(depsgraph, _export.params)}; - EXPECT_EQ(objmeshes.size(), 19); + EXPECT_EQ(objmeshes.size(), 20); EXPECT_EQ(objcurves.size(), 0); } @@ -72,7 +71,7 @@ TEST_F(obj_exporter_test, filter_objects_curves_as_nurbs) _export.params.export_curves_as_nurbs = true; auto [objmeshes, objcurves]{filter_supported_objects(depsgraph, _export.params)}; EXPECT_EQ(objmeshes.size(), 18); - EXPECT_EQ(objcurves.size(), 2); + EXPECT_EQ(objcurves.size(), 3); } TEST_F(obj_exporter_test, filter_objects_selected) @@ -111,64 +110,6 @@ TEST(obj_exporter_utils, append_positive_frame_to_filename) EXPECT_EQ_ARRAY(path_with_frame, path_truth, BLI_strlen_utf8(path_truth)); } -TEST_F(obj_exporter_test, curve_nurbs_points) -{ - if (!load_file_and_depsgraph(all_curve_objects_file)) { - ADD_FAILURE(); - return; - } - - OBJExportParamsDefault _export; - _export.params.export_curves_as_nurbs = true; - auto [objmeshes_unused, objcurves]{filter_supported_objects(depsgraph, _export.params)}; - - for (auto &objcurve : objcurves) { - if (all_nurbs_truth.count(objcurve->get_curve_name()) != 1) { - ADD_FAILURE(); - return; - } - const NurbsObject *const nurbs_truth = all_nurbs_truth.at(objcurve->get_curve_name()).get(); - EXPECT_EQ(objcurve->total_splines(), nurbs_truth->total_splines()); - for (int spline_index : IndexRange(objcurve->total_splines())) { - EXPECT_EQ(objcurve->total_spline_vertices(spline_index), - nurbs_truth->total_spline_vertices(spline_index)); - EXPECT_EQ(objcurve->get_nurbs_degree(spline_index), - nurbs_truth->get_nurbs_degree(spline_index)); - EXPECT_EQ(objcurve->total_spline_control_points(spline_index), - nurbs_truth->total_spline_control_points(spline_index)); - } - } -} - -TEST_F(obj_exporter_test, curve_coordinates) -{ - if (!load_file_and_depsgraph(all_curve_objects_file)) { - ADD_FAILURE(); - return; - } - - OBJExportParamsDefault _export; - _export.params.export_curves_as_nurbs = true; - auto [objmeshes_unused, objcurves]{filter_supported_objects(depsgraph, _export.params)}; - - for (auto &objcurve : objcurves) { - if (all_nurbs_truth.count(objcurve->get_curve_name()) != 1) { - ADD_FAILURE(); - return; - } - const NurbsObject *const nurbs_truth = all_nurbs_truth.at(objcurve->get_curve_name()).get(); - EXPECT_EQ(objcurve->total_splines(), nurbs_truth->total_splines()); - for (int spline_index : IndexRange(objcurve->total_splines())) { - for (int vertex_index : IndexRange(objcurve->total_spline_vertices(spline_index))) { - EXPECT_V3_NEAR(objcurve->vertex_coordinates( - spline_index, vertex_index, _export.params.scaling_factor), - nurbs_truth->vertex_coordinates(spline_index, vertex_index), - 0.000001f); - } - } - } -} - static std::unique_ptr<OBJWriter> init_writer(const OBJExportParams ¶ms, const std::string out_filepath) { @@ -467,6 +408,19 @@ TEST_F(obj_exporter_regression_test, cube_normal_edit) _export.params); } +TEST_F(obj_exporter_regression_test, cube_vertex_groups) +{ + OBJExportParamsDefault _export; + _export.params.export_materials = false; + _export.params.export_normals = false; + _export.params.export_uv = false; + _export.params.export_vertex_groups = true; + compare_obj_export_to_golden("io_tests/blend_geometry/cube_vertex_groups.blend", + "io_tests/obj/cube_vertex_groups.obj", + "", + _export.params); +} + TEST_F(obj_exporter_regression_test, cubes_positioned) { OBJExportParamsDefault _export; @@ -504,6 +458,25 @@ TEST_F(obj_exporter_regression_test, suzanne_all_data) _export.params); } +TEST_F(obj_exporter_regression_test, all_curves) +{ + OBJExportParamsDefault _export; + _export.params.export_materials = false; + compare_obj_export_to_golden( + "io_tests/blend_scene/all_curves.blend", "io_tests/obj/all_curves.obj", "", _export.params); +} + +TEST_F(obj_exporter_regression_test, all_curves_as_nurbs) +{ + OBJExportParamsDefault _export; + _export.params.export_materials = false; + _export.params.export_curves_as_nurbs = true; + compare_obj_export_to_golden("io_tests/blend_scene/all_curves.blend", + "io_tests/obj/all_curves_as_nurbs.obj", + "", + _export.params); +} + TEST_F(obj_exporter_regression_test, all_objects) { OBJExportParamsDefault _export; diff --git a/source/blender/io/wavefront_obj/tests/obj_exporter_tests.hh b/source/blender/io/wavefront_obj/tests/obj_exporter_tests.hh index 42660bbbe56..6a821e0b1bf 100644 --- a/source/blender/io/wavefront_obj/tests/obj_exporter_tests.hh +++ b/source/blender/io/wavefront_obj/tests/obj_exporter_tests.hh @@ -1,77 +1,11 @@ /* SPDX-License-Identifier: Apache-2.0 */ -/** - * This file contains default values for several items like - * vertex coordinates, export parameters, MTL values etc. - */ - #pragma once -#include <array> -#include <gtest/gtest.h> -#include <string> -#include <vector> - #include "IO_wavefront_obj.h" namespace blender::io::obj { -using array_float_3 = std::array<float, 3>; - -/** - * This matches #OBJCurve's member functions, except that all the numbers and names are known - * constants. Used to store expected values of NURBS curves objects. - */ -class NurbsObject { - private: - std::string nurbs_name_; - /* The indices in these vectors are spline indices. */ - std::vector<std::vector<array_float_3>> coordinates_; - std::vector<int> degrees_; - std::vector<int> control_points_; - - public: - NurbsObject(const std::string nurbs_name, - const std::vector<std::vector<array_float_3>> coordinates, - const std::vector<int> degrees, - const std::vector<int> control_points) - : nurbs_name_(nurbs_name), - coordinates_(coordinates), - degrees_(degrees), - control_points_(control_points) - { - } - - int total_splines() const - { - return coordinates_.size(); - } - - int total_spline_vertices(const int spline_index) const - { - if (spline_index >= coordinates_.size()) { - ADD_FAILURE(); - return 0; - } - return coordinates_[spline_index].size(); - } - - const float *vertex_coordinates(const int spline_index, const int vertex_index) const - { - return coordinates_[spline_index][vertex_index].data(); - } - - int get_nurbs_degree(const int spline_index) const - { - return degrees_[spline_index]; - } - - int total_spline_control_points(const int spline_index) const - { - return control_points_[spline_index]; - } -}; - struct OBJExportParamsDefault { OBJExportParams params; OBJExportParamsDefault() @@ -103,48 +37,4 @@ struct OBJExportParamsDefault { } }; -const std::vector<std::vector<array_float_3>> coordinates_NurbsCurve{ - {{6.94742, 0.000000, 0.000000}, - {7.44742, 0.000000, -1.000000}, - {9.44742, 0.000000, -1.000000}, - {9.94742, 0.000000, 0.000000}}}; -const std::vector<std::vector<array_float_3>> coordinates_NurbsCircle{ - {{11.463165, 0.000000, 1.000000}, - {10.463165, 0.000000, 1.000000}, - {10.463165, 0.000000, 0.000000}, - {10.463165, 0.000000, -1.000000}, - {11.463165, 0.000000, -1.000000}, - {12.463165, 0.000000, -1.000000}, - {12.463165, 0.000000, 0.000000}, - {12.463165, 0.000000, 1.000000}}}; -const std::vector<std::vector<array_float_3>> coordinates_NurbsPathCurve{ - {{13.690557, 0.000000, 0.000000}, - {14.690557, 0.000000, 0.000000}, - {15.690557, 0.000000, 0.000000}, - {16.690557, 0.000000, 0.000000}, - {17.690557, 0.000000, 0.000000}}, - {{14.192808, 0.000000, 0.000000}, - {14.692808, 0.000000, -1.000000}, - {16.692808, 0.000000, -1.000000}, - {17.192808, 0.000000, 0.000000}}}; - -const std::map<std::string, std::unique_ptr<NurbsObject>> all_nurbs_truth = []() { - std::map<std::string, std::unique_ptr<NurbsObject>> all_nurbs; - all_nurbs.emplace( - "NurbsCurve", - /* Name, coordinates, degrees of splines, control points of splines. */ - std::make_unique<NurbsObject>( - "NurbsCurve", coordinates_NurbsCurve, std::vector<int>{3}, std::vector<int>{4})); - all_nurbs.emplace( - "NurbsCircle", - std::make_unique<NurbsObject>( - "NurbsCircle", coordinates_NurbsCircle, std::vector<int>{3}, std::vector<int>{11})); - /* This is actually an Object containing a NurbsPath and a NurbsCurve spline. */ - all_nurbs.emplace("NurbsPathCurve", - std::make_unique<NurbsObject>("NurbsPathCurve", - coordinates_NurbsPathCurve, - std::vector<int>{3, 3}, - std::vector<int>{5, 4})); - return all_nurbs; -}(); } // namespace blender::io::obj diff --git a/source/blender/io/wavefront_obj/tests/obj_importer_tests.cc b/source/blender/io/wavefront_obj/tests/obj_importer_tests.cc index 9dd9e7c1a37..3d34fb6f9c6 100644 --- a/source/blender/io/wavefront_obj/tests/obj_importer_tests.cc +++ b/source/blender/io/wavefront_obj/tests/obj_importer_tests.cc @@ -60,7 +60,8 @@ class obj_importer_test : public BlendfileLoadingBaseTest { std::string obj_path = blender::tests::flags_test_asset_dir() + "/io_tests/obj/" + path; strncpy(params.filepath, obj_path.c_str(), FILE_MAX - 1); - importer_main(bfile->main, bfile->curscene, bfile->cur_view_layer, params); + const size_t read_buffer_size = 650; + importer_main(bfile->main, bfile->curscene, bfile->cur_view_layer, params, read_buffer_size); depsgraph_create(DAG_EVAL_VIEWPORT); @@ -444,6 +445,7 @@ TEST_F(obj_importer_test, import_all_objects) float3(5, 1, 1), float3(0, 0, 1), float2(0.654526f, 0.579873f)}, + {"OBNurbsCircle.001", OB_MESH, 4, 4, 0, 0, float3(2, -3, 0), float3(3, -2, 0)}, {"OBSurface", OB_MESH, 256, diff --git a/source/blender/io/wavefront_obj/tests/obj_mtl_parser_tests.cc b/source/blender/io/wavefront_obj/tests/obj_mtl_parser_tests.cc new file mode 100644 index 00000000000..176d32a0be4 --- /dev/null +++ b/source/blender/io/wavefront_obj/tests/obj_mtl_parser_tests.cc @@ -0,0 +1,172 @@ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include <gtest/gtest.h> + +#include "testing/testing.h" + +#include "obj_import_file_reader.hh" + +namespace blender::io::obj { + +class obj_mtl_parser_test : public testing::Test { + public: + void check(const char *file, const MTLMaterial *expect, size_t expect_count) + { + std::string obj_dir = blender::tests::flags_test_asset_dir() + "/io_tests/obj/"; + MTLParser parser(file, obj_dir + "dummy.obj"); + Map<std::string, std::unique_ptr<MTLMaterial>> materials; + parser.parse_and_store(materials); + + for (int i = 0; i < expect_count; ++i) { + const MTLMaterial &exp = expect[i]; + if (!materials.contains(exp.name)) { + fprintf(stderr, "Material '%s' was expected in parsed result\n", exp.name.c_str()); + ADD_FAILURE(); + continue; + } + const MTLMaterial &got = *materials.lookup(exp.name); + const float tol = 0.0001f; + EXPECT_V3_NEAR(exp.Ka, got.Ka, tol); + EXPECT_V3_NEAR(exp.Kd, got.Kd, tol); + EXPECT_V3_NEAR(exp.Ks, got.Ks, tol); + EXPECT_V3_NEAR(exp.Ke, got.Ke, tol); + EXPECT_NEAR(exp.Ns, got.Ns, tol); + EXPECT_NEAR(exp.Ni, got.Ni, tol); + EXPECT_NEAR(exp.d, got.d, tol); + EXPECT_NEAR(exp.map_Bump_strength, got.map_Bump_strength, tol); + EXPECT_EQ(exp.illum, got.illum); + for (const auto &it : exp.texture_maps.items()) { + const tex_map_XX &exp_tex = it.value; + const tex_map_XX &got_tex = got.texture_maps.lookup(it.key); + EXPECT_STREQ(exp_tex.image_path.c_str(), got_tex.image_path.c_str()); + EXPECT_V3_NEAR(exp_tex.translation, got_tex.translation, tol); + EXPECT_V3_NEAR(exp_tex.scale, got_tex.scale, tol); + EXPECT_EQ(exp_tex.projection_type, got_tex.projection_type); + } + } + EXPECT_EQ(materials.size(), expect_count); + } +}; + +TEST_F(obj_mtl_parser_test, cube) +{ + MTLMaterial mat; + mat.name = "red"; + mat.Ka = {0.2f, 0.2f, 0.2f}; + mat.Kd = {1, 0, 0}; + check("cube.mtl", &mat, 1); +} + +TEST_F(obj_mtl_parser_test, all_objects) +{ + MTLMaterial mat[7]; + for (auto &m : mat) { + m.Ka = {1, 1, 1}; + m.Ks = {0.5f, 0.5f, 0.5f}; + m.Ke = {0, 0, 0}; + m.Ns = 250; + m.Ni = 1; + m.d = 1; + m.illum = 2; + } + mat[0].name = "Blue"; + mat[0].Kd = {0, 0, 1}; + mat[1].name = "BlueDark"; + mat[1].Kd = {0, 0, 0.5f}; + mat[2].name = "Green"; + mat[2].Kd = {0, 1, 0}; + mat[3].name = "GreenDark"; + mat[3].Kd = {0, 0.5f, 0}; + mat[4].name = "Material"; + mat[4].Kd = {0.8f, 0.8f, 0.8f}; + mat[5].name = "Red"; + mat[5].Kd = {1, 0, 0}; + mat[6].name = "RedDark"; + mat[6].Kd = {0.5f, 0, 0}; + check("all_objects.mtl", mat, ARRAY_SIZE(mat)); +} + +TEST_F(obj_mtl_parser_test, materials) +{ + MTLMaterial mat[5]; + mat[0].name = "no_textures_red"; + mat[0].Ka = {0.3f, 0.3f, 0.3f}; + mat[0].Kd = {0.8f, 0.3f, 0.1f}; + mat[0].Ns = 5.624998f; + + mat[1].name = "four_maps"; + mat[1].Ka = {1, 1, 1}; + mat[1].Kd = {0.8f, 0.8f, 0.8f}; + mat[1].Ks = {0.5f, 0.5f, 0.5f}; + mat[1].Ke = {0, 0, 0}; + mat[1].Ns = 1000; + mat[1].Ni = 1.45f; + mat[1].d = 1; + mat[1].illum = 2; + mat[1].map_Bump_strength = 1; + { + tex_map_XX &kd = mat[1].tex_map_of_type(eMTLSyntaxElement::map_Kd); + kd.image_path = "texture.png"; + tex_map_XX &ns = mat[1].tex_map_of_type(eMTLSyntaxElement::map_Ns); + ns.image_path = "sometexture_Roughness.png"; + tex_map_XX &refl = mat[1].tex_map_of_type(eMTLSyntaxElement::map_refl); + refl.image_path = "sometexture_Metallic.png"; + tex_map_XX &bump = mat[1].tex_map_of_type(eMTLSyntaxElement::map_Bump); + bump.image_path = "sometexture_Normal.png"; + } + + mat[2].name = "Clay"; + mat[2].Ka = {1, 1, 1}; + mat[2].Kd = {0.8f, 0.682657f, 0.536371f}; + mat[2].Ks = {0.5f, 0.5f, 0.5f}; + mat[2].Ke = {0, 0, 0}; + mat[2].Ns = 440.924042f; + mat[2].Ni = 1.45f; + mat[2].d = 1; + mat[2].illum = 2; + + mat[3].name = "Hat"; + mat[3].Ka = {1, 1, 1}; + mat[3].Kd = {0.8f, 0.8f, 0.8f}; + mat[3].Ks = {0.5f, 0.5f, 0.5f}; + mat[3].Ns = 800; + mat[3].map_Bump_strength = 0.5f; + { + tex_map_XX &kd = mat[3].tex_map_of_type(eMTLSyntaxElement::map_Kd); + kd.image_path = "someHatTexture_BaseColor.jpg"; + tex_map_XX &ns = mat[3].tex_map_of_type(eMTLSyntaxElement::map_Ns); + ns.image_path = "someHatTexture_Roughness.jpg"; + tex_map_XX &refl = mat[3].tex_map_of_type(eMTLSyntaxElement::map_refl); + refl.image_path = "someHatTexture_Metalness.jpg"; + tex_map_XX &bump = mat[3].tex_map_of_type(eMTLSyntaxElement::map_Bump); + bump.image_path = "someHatTexture_Normal.jpg"; + } + + mat[4].name = "Parser_Test"; + mat[4].Ka = {0.1f, 0.2f, 0.3f}; + mat[4].Kd = {0.4f, 0.5f, 0.6f}; + mat[4].Ks = {0.7f, 0.8f, 0.9f}; + mat[4].illum = 6; + mat[4].Ns = 15.5; + mat[4].Ni = 1.5; + mat[4].d = 0.5; + mat[4].map_Bump_strength = 0.1f; + { + tex_map_XX &kd = mat[4].tex_map_of_type(eMTLSyntaxElement::map_Kd); + kd.image_path = "sometex_d.png"; + tex_map_XX &ns = mat[4].tex_map_of_type(eMTLSyntaxElement::map_Ns); + ns.image_path = "sometex_ns.psd"; + tex_map_XX &refl = mat[4].tex_map_of_type(eMTLSyntaxElement::map_refl); + refl.image_path = "clouds.tiff"; + refl.scale = {1.5f, 2.5f, 3.5f}; + refl.translation = {4.5f, 5.5f, 6.5f}; + refl.projection_type = SHD_PROJ_SPHERE; + tex_map_XX &bump = mat[4].tex_map_of_type(eMTLSyntaxElement::map_Bump); + bump.image_path = "somebump.tga"; + bump.scale = {3, 4, 5}; + } + + check("materials.mtl", mat, ARRAY_SIZE(mat)); +} + +} // namespace blender::io::obj diff --git a/source/blender/makesdna/DNA_anim_types.h b/source/blender/makesdna/DNA_anim_types.h index 5d3c8872d47..99737aa3b67 100644 --- a/source/blender/makesdna/DNA_anim_types.h +++ b/source/blender/makesdna/DNA_anim_types.h @@ -1128,6 +1128,8 @@ typedef enum eAnimData_Flag { ADT_NLA_EDIT_NOMAP = (1 << 3), /** NLA-Strip F-Curves are expanded in UI. */ ADT_NLA_SKEYS_COLLAPSED = (1 << 4), + /* Evaluate tracks above tweaked strip. Only relevant in tweak mode. */ + ADT_NLA_EVAL_UPPER_TRACKS = (1 << 5), /** Drivers expanded in UI. */ ADT_DRIVERS_COLLAPSED = (1 << 10), diff --git a/source/blender/makesdna/DNA_brush_enums.h b/source/blender/makesdna/DNA_brush_enums.h index 96a97648ac9..ee78b610276 100644 --- a/source/blender/makesdna/DNA_brush_enums.h +++ b/source/blender/makesdna/DNA_brush_enums.h @@ -462,7 +462,6 @@ typedef enum eBrushCurvesSculptTool { CURVES_SCULPT_TOOL_SNAKE_HOOK = 2, CURVES_SCULPT_TOOL_ADD = 3, CURVES_SCULPT_TOOL_GROW_SHRINK = 4, - CURVES_SCULPT_TOOL_TEST1 = 5, } eBrushCurvesSculptTool; /** When #BRUSH_ACCUMULATE is used */ diff --git a/source/blender/makesdna/DNA_curve_types.h b/source/blender/makesdna/DNA_curve_types.h index 556e467c4b6..305b913b19e 100644 --- a/source/blender/makesdna/DNA_curve_types.h +++ b/source/blender/makesdna/DNA_curve_types.h @@ -127,6 +127,8 @@ typedef struct BPoint { * also, it should be NURBS (Nurb isn't the singular of Nurbs). */ typedef struct Nurb { + DNA_DEFINE_CXX_METHODS(Nurb) + /** Multiple nurbs per curve object are allowed. */ struct Nurb *next, *prev; short type; @@ -169,6 +171,8 @@ typedef struct TextBox { # # typedef struct EditNurb { + DNA_DEFINE_CXX_METHODS(EditNurb) + /* base of nurbs' list (old Curve->editnurb) */ ListBase nurbs; @@ -187,6 +191,8 @@ typedef struct EditNurb { } EditNurb; typedef struct Curve { + DNA_DEFINE_CXX_METHODS(Curve) + ID id; /** Animation data (must be immediately after id for utilities to use it). */ struct AnimData *adt; diff --git a/source/blender/makesdna/DNA_lattice_types.h b/source/blender/makesdna/DNA_lattice_types.h index b2775aa771a..907f1150ce5 100644 --- a/source/blender/makesdna/DNA_lattice_types.h +++ b/source/blender/makesdna/DNA_lattice_types.h @@ -23,6 +23,8 @@ struct MDeformVert; # # typedef struct EditLatt { + DNA_DEFINE_CXX_METHODS(EditLatt) + struct Lattice *latt; int shapenr; @@ -35,6 +37,8 @@ typedef struct EditLatt { } EditLatt; typedef struct Lattice { + DNA_DEFINE_CXX_METHODS(Lattice) + ID id; struct AnimData *adt; diff --git a/source/blender/makesdna/DNA_light_types.h b/source/blender/makesdna/DNA_light_types.h index e78a381603b..9202d7c2d51 100644 --- a/source/blender/makesdna/DNA_light_types.h +++ b/source/blender/makesdna/DNA_light_types.h @@ -24,6 +24,8 @@ struct Ipo; struct bNodeTree; typedef struct Light { + DNA_DEFINE_CXX_METHODS(Light) + ID id; /** Animation data (must be immediately after id for utilities to use it). */ struct AnimData *adt; diff --git a/source/blender/makesdna/DNA_linestyle_types.h b/source/blender/makesdna/DNA_linestyle_types.h index b8279e7f2ca..0755803ea8f 100644 --- a/source/blender/makesdna/DNA_linestyle_types.h +++ b/source/blender/makesdna/DNA_linestyle_types.h @@ -29,6 +29,8 @@ struct Object; struct bNodeTree; typedef struct LineStyleModifier { + DNA_DEFINE_CXX_METHODS(LineStyleModifier) + struct LineStyleModifier *next, *prev; /** MAX_NAME. */ @@ -92,12 +94,16 @@ typedef struct LineStyleModifier { /* Along Stroke modifiers */ typedef struct LineStyleColorModifier_AlongStroke { + DNA_DEFINE_CXX_METHODS(LineStyleColorModifier_AlongStroke) + struct LineStyleModifier modifier; struct ColorBand *color_ramp; } LineStyleColorModifier_AlongStroke; typedef struct LineStyleAlphaModifier_AlongStroke { + DNA_DEFINE_CXX_METHODS(LineStyleAlphaModifier_AlongStroke) + struct LineStyleModifier modifier; struct CurveMapping *curve; @@ -106,6 +112,8 @@ typedef struct LineStyleAlphaModifier_AlongStroke { } LineStyleAlphaModifier_AlongStroke; typedef struct LineStyleThicknessModifier_AlongStroke { + DNA_DEFINE_CXX_METHODS(LineStyleThicknessModifier_AlongStroke) + struct LineStyleModifier modifier; struct CurveMapping *curve; @@ -117,6 +125,8 @@ typedef struct LineStyleThicknessModifier_AlongStroke { /* Distance from Camera modifiers */ typedef struct LineStyleColorModifier_DistanceFromCamera { + DNA_DEFINE_CXX_METHODS(LineStyleColorModifier_DistanceFromCamera) + struct LineStyleModifier modifier; struct ColorBand *color_ramp; @@ -124,6 +134,8 @@ typedef struct LineStyleColorModifier_DistanceFromCamera { } LineStyleColorModifier_DistanceFromCamera; typedef struct LineStyleAlphaModifier_DistanceFromCamera { + DNA_DEFINE_CXX_METHODS(LineStyleAlphaModifier_DistanceFromCamera) + struct LineStyleModifier modifier; struct CurveMapping *curve; @@ -133,6 +145,8 @@ typedef struct LineStyleAlphaModifier_DistanceFromCamera { } LineStyleAlphaModifier_DistanceFromCamera; typedef struct LineStyleThicknessModifier_DistanceFromCamera { + DNA_DEFINE_CXX_METHODS(LineStyleThicknessModifier_DistanceFromCamera) + struct LineStyleModifier modifier; struct CurveMapping *curve; @@ -145,6 +159,8 @@ typedef struct LineStyleThicknessModifier_DistanceFromCamera { /* Distance from Object modifiers */ typedef struct LineStyleColorModifier_DistanceFromObject { + DNA_DEFINE_CXX_METHODS(LineStyleColorModifier_DistanceFromObject) + struct LineStyleModifier modifier; struct Object *target; @@ -153,6 +169,8 @@ typedef struct LineStyleColorModifier_DistanceFromObject { } LineStyleColorModifier_DistanceFromObject; typedef struct LineStyleAlphaModifier_DistanceFromObject { + DNA_DEFINE_CXX_METHODS(LineStyleAlphaModifier_DistanceFromObject) + struct LineStyleModifier modifier; struct Object *target; @@ -163,6 +181,8 @@ typedef struct LineStyleAlphaModifier_DistanceFromObject { } LineStyleAlphaModifier_DistanceFromObject; typedef struct LineStyleThicknessModifier_DistanceFromObject { + DNA_DEFINE_CXX_METHODS(LineStyleThicknessModifier_DistanceFromObject) + struct LineStyleModifier modifier; struct Object *target; @@ -176,6 +196,8 @@ typedef struct LineStyleThicknessModifier_DistanceFromObject { /* 3D curvature modifiers */ typedef struct LineStyleColorModifier_Curvature_3D { + DNA_DEFINE_CXX_METHODS(LineStyleColorModifier_Curvature_3D) + struct LineStyleModifier modifier; float min_curvature, max_curvature; @@ -184,6 +206,8 @@ typedef struct LineStyleColorModifier_Curvature_3D { } LineStyleColorModifier_Curvature_3D; typedef struct LineStyleAlphaModifier_Curvature_3D { + DNA_DEFINE_CXX_METHODS(LineStyleAlphaModifier_Curvature_3D) + struct LineStyleModifier modifier; struct CurveMapping *curve; @@ -193,6 +217,8 @@ typedef struct LineStyleAlphaModifier_Curvature_3D { } LineStyleAlphaModifier_Curvature_3D; typedef struct LineStyleThicknessModifier_Curvature_3D { + DNA_DEFINE_CXX_METHODS(LineStyleThicknessModifier_Curvature_3D) + struct LineStyleModifier modifier; struct CurveMapping *curve; @@ -205,6 +231,8 @@ typedef struct LineStyleThicknessModifier_Curvature_3D { /* Noise modifiers (for color, alpha and thickness) */ typedef struct LineStyleColorModifier_Noise { + DNA_DEFINE_CXX_METHODS(LineStyleColorModifier_Noise) + struct LineStyleModifier modifier; struct ColorBand *color_ramp; @@ -214,6 +242,8 @@ typedef struct LineStyleColorModifier_Noise { } LineStyleColorModifier_Noise; typedef struct LineStyleAlphaModifier_Noise { + DNA_DEFINE_CXX_METHODS(LineStyleAlphaModifier_Noise) + struct LineStyleModifier modifier; struct CurveMapping *curve; @@ -223,6 +253,8 @@ typedef struct LineStyleAlphaModifier_Noise { } LineStyleAlphaModifier_Noise; typedef struct LineStyleThicknessModifier_Noise { + DNA_DEFINE_CXX_METHODS(LineStyleThicknessModifier_Noise) + struct LineStyleModifier modifier; float period, amplitude; @@ -233,6 +265,8 @@ typedef struct LineStyleThicknessModifier_Noise { /* Crease Angle modifiers */ typedef struct LineStyleColorModifier_CreaseAngle { + DNA_DEFINE_CXX_METHODS(LineStyleColorModifier_CreaseAngle) + struct LineStyleModifier modifier; struct ColorBand *color_ramp; @@ -240,6 +274,8 @@ typedef struct LineStyleColorModifier_CreaseAngle { } LineStyleColorModifier_CreaseAngle; typedef struct LineStyleAlphaModifier_CreaseAngle { + DNA_DEFINE_CXX_METHODS(LineStyleAlphaModifier_CreaseAngle) + struct LineStyleModifier modifier; struct CurveMapping *curve; @@ -249,6 +285,8 @@ typedef struct LineStyleAlphaModifier_CreaseAngle { } LineStyleAlphaModifier_CreaseAngle; typedef struct LineStyleThicknessModifier_CreaseAngle { + DNA_DEFINE_CXX_METHODS(LineStyleThicknessModifier_CreaseAngle) + struct LineStyleModifier modifier; struct CurveMapping *curve; @@ -261,12 +299,16 @@ typedef struct LineStyleThicknessModifier_CreaseAngle { /* Tangent modifiers */ typedef struct LineStyleColorModifier_Tangent { + DNA_DEFINE_CXX_METHODS(LineStyleColorModifier_Tangent) + struct LineStyleModifier modifier; struct ColorBand *color_ramp; } LineStyleColorModifier_Tangent; typedef struct LineStyleAlphaModifier_Tangent { + DNA_DEFINE_CXX_METHODS(LineStyleAlphaModifier_Tangent) + struct LineStyleModifier modifier; struct CurveMapping *curve; @@ -275,6 +317,8 @@ typedef struct LineStyleAlphaModifier_Tangent { } LineStyleAlphaModifier_Tangent; typedef struct LineStyleThicknessModifier_Tangent { + DNA_DEFINE_CXX_METHODS(LineStyleThicknessModifier_Tangent) + struct LineStyleModifier modifier; struct CurveMapping *curve; @@ -303,6 +347,8 @@ typedef struct LineStyleThicknessModifier_Tangent { #define LS_MODIFIER_MATERIAL_LINE_A 15 typedef struct LineStyleColorModifier_Material { + DNA_DEFINE_CXX_METHODS(LineStyleColorModifier_Material) + struct LineStyleModifier modifier; struct ColorBand *color_ramp; @@ -311,6 +357,8 @@ typedef struct LineStyleColorModifier_Material { } LineStyleColorModifier_Material; typedef struct LineStyleAlphaModifier_Material { + DNA_DEFINE_CXX_METHODS(LineStyleAlphaModifier_Material) + struct LineStyleModifier modifier; struct CurveMapping *curve; @@ -319,6 +367,8 @@ typedef struct LineStyleAlphaModifier_Material { } LineStyleAlphaModifier_Material; typedef struct LineStyleThicknessModifier_Material { + DNA_DEFINE_CXX_METHODS(LineStyleThicknessModifier_Material) + struct LineStyleModifier modifier; struct CurveMapping *curve; @@ -330,6 +380,8 @@ typedef struct LineStyleThicknessModifier_Material { /* Geometry modifiers */ typedef struct LineStyleGeometryModifier_Sampling { + DNA_DEFINE_CXX_METHODS(LineStyleGeometryModifier_Sampling) + struct LineStyleModifier modifier; float sampling; @@ -337,6 +389,8 @@ typedef struct LineStyleGeometryModifier_Sampling { } LineStyleGeometryModifier_Sampling; typedef struct LineStyleGeometryModifier_BezierCurve { + DNA_DEFINE_CXX_METHODS(LineStyleGeometryModifier_BezierCurve) + struct LineStyleModifier modifier; float error; @@ -344,6 +398,8 @@ typedef struct LineStyleGeometryModifier_BezierCurve { } LineStyleGeometryModifier_BezierCurve; typedef struct LineStyleGeometryModifier_SinusDisplacement { + DNA_DEFINE_CXX_METHODS(LineStyleGeometryModifier_SinusDisplacement) + struct LineStyleModifier modifier; float wavelength, amplitude, phase; @@ -355,6 +411,8 @@ typedef struct LineStyleGeometryModifier_SinusDisplacement { #define LS_MODIFIER_SPATIAL_NOISE_PURERANDOM 2 typedef struct LineStyleGeometryModifier_SpatialNoise { + DNA_DEFINE_CXX_METHODS(LineStyleGeometryModifier_SpatialNoise) + struct LineStyleModifier modifier; float amplitude, scale; @@ -363,6 +421,8 @@ typedef struct LineStyleGeometryModifier_SpatialNoise { } LineStyleGeometryModifier_SpatialNoise; typedef struct LineStyleGeometryModifier_PerlinNoise1D { + DNA_DEFINE_CXX_METHODS(LineStyleGeometryModifier_PerlinNoise1D) + struct LineStyleModifier modifier; float frequency, amplitude; @@ -374,6 +434,8 @@ typedef struct LineStyleGeometryModifier_PerlinNoise1D { } LineStyleGeometryModifier_PerlinNoise1D; typedef struct LineStyleGeometryModifier_PerlinNoise2D { + DNA_DEFINE_CXX_METHODS(LineStyleGeometryModifier_PerlinNoise2D) + struct LineStyleModifier modifier; float frequency, amplitude; @@ -385,6 +447,8 @@ typedef struct LineStyleGeometryModifier_PerlinNoise2D { } LineStyleGeometryModifier_PerlinNoise2D; typedef struct LineStyleGeometryModifier_BackboneStretcher { + DNA_DEFINE_CXX_METHODS(LineStyleGeometryModifier_BackboneStretcher) + struct LineStyleModifier modifier; float backbone_length; @@ -392,6 +456,8 @@ typedef struct LineStyleGeometryModifier_BackboneStretcher { } LineStyleGeometryModifier_BackboneStretcher; typedef struct LineStyleGeometryModifier_TipRemover { + DNA_DEFINE_CXX_METHODS(LineStyleGeometryModifier_TipRemover) + struct LineStyleModifier modifier; float tip_length; @@ -399,6 +465,8 @@ typedef struct LineStyleGeometryModifier_TipRemover { } LineStyleGeometryModifier_TipRemover; typedef struct LineStyleGeometryModifier_Polygonalization { + DNA_DEFINE_CXX_METHODS(LineStyleGeometryModifier_Polygonalization) + struct LineStyleModifier modifier; float error; @@ -406,6 +474,8 @@ typedef struct LineStyleGeometryModifier_Polygonalization { } LineStyleGeometryModifier_Polygonalization; typedef struct LineStyleGeometryModifier_GuidingLines { + DNA_DEFINE_CXX_METHODS(LineStyleGeometryModifier_GuidingLines) + struct LineStyleModifier modifier; float offset; @@ -418,6 +488,8 @@ typedef struct LineStyleGeometryModifier_GuidingLines { #define LS_MODIFIER_BLUEPRINT_SQUARES 4 typedef struct LineStyleGeometryModifier_Blueprint { + DNA_DEFINE_CXX_METHODS(LineStyleGeometryModifier_Blueprint) + struct LineStyleModifier modifier; int flags; @@ -429,6 +501,8 @@ typedef struct LineStyleGeometryModifier_Blueprint { } LineStyleGeometryModifier_Blueprint; typedef struct LineStyleGeometryModifier_2DOffset { + DNA_DEFINE_CXX_METHODS(LineStyleGeometryModifier_2DOffset) + struct LineStyleModifier modifier; float start, end; @@ -443,6 +517,8 @@ typedef struct LineStyleGeometryModifier_2DOffset { #define LS_MODIFIER_2D_TRANSFORM_PIVOT_ABSOLUTE 5 typedef struct LineStyleGeometryModifier_2DTransform { + DNA_DEFINE_CXX_METHODS(LineStyleGeometryModifier_2DTransform) + struct LineStyleModifier modifier; int pivot; @@ -455,6 +531,8 @@ typedef struct LineStyleGeometryModifier_2DTransform { } LineStyleGeometryModifier_2DTransform; typedef struct LineStyleGeometryModifier_Simplification { + DNA_DEFINE_CXX_METHODS(LineStyleGeometryModifier_Simplification) + struct LineStyleModifier modifier; float tolerance; @@ -464,6 +542,8 @@ typedef struct LineStyleGeometryModifier_Simplification { /* Calligraphic thickness modifier */ typedef struct LineStyleThicknessModifier_Calligraphy { + DNA_DEFINE_CXX_METHODS(LineStyleThicknessModifier_Calligraphy) + struct LineStyleModifier modifier; float min_thickness, max_thickness; @@ -527,6 +607,8 @@ typedef struct LineStyleThicknessModifier_Calligraphy { #define LS_INTEGRATION_LAST 5 typedef struct FreestyleLineStyle { + DNA_DEFINE_CXX_METHODS(FreestyleLineStyle) + ID id; struct AnimData *adt; diff --git a/source/blender/makesdna/DNA_material_types.h b/source/blender/makesdna/DNA_material_types.h index b535d3cdb8a..332317142c7 100644 --- a/source/blender/makesdna/DNA_material_types.h +++ b/source/blender/makesdna/DNA_material_types.h @@ -27,8 +27,12 @@ struct bNodeTree; /* WATCH IT: change type? also make changes in ipo.h */ typedef struct TexPaintSlot { + DNA_DEFINE_CXX_METHODS(TexPaintSlot) + /** Image to be painted on. Mutual exclusive with attribute_name. */ struct Image *ima; + struct ImageUser *image_user; + /** Custom-data index for uv layer, #MAX_NAME. */ char *uvname; /** @@ -43,6 +47,8 @@ typedef struct TexPaintSlot { } TexPaintSlot; typedef struct MaterialGPencilStyle { + DNA_DEFINE_CXX_METHODS(MaterialGPencilStyle) + /** Texture image for strokes. */ struct Image *sima; /** Texture image for filling. */ @@ -155,6 +161,8 @@ typedef enum eMaterialLineArtFlags { } eMaterialLineArtFlags; typedef struct Material { + DNA_DEFINE_CXX_METHODS(Material) + ID id; /** Animation data (must be immediately after id for utilities to use it). */ struct AnimData *adt; diff --git a/source/blender/makesdna/DNA_mesh_defaults.h b/source/blender/makesdna/DNA_mesh_defaults.h index 079bda42009..30fe387b05a 100644 --- a/source/blender/makesdna/DNA_mesh_defaults.h +++ b/source/blender/makesdna/DNA_mesh_defaults.h @@ -22,7 +22,7 @@ .remesh_voxel_adaptivity = 0.0f, \ .face_sets_color_seed = 0, \ .face_sets_color_default = 1, \ - .flag = ME_REMESH_FIX_POLES | ME_REMESH_REPROJECT_VOLUME, \ + .flag = ME_REMESH_REPROJECT_VOLUME | ME_REMESH_REPROJECT_PAINT_MASK | ME_REMESH_REPROJECT_SCULPT_FACE_SETS | ME_REMESH_REPROJECT_VERTEX_COLORS, \ .editflag = ME_EDIT_MIRROR_VERTEX_GROUPS \ } diff --git a/source/blender/makesdna/DNA_mesh_types.h b/source/blender/makesdna/DNA_mesh_types.h index d8a853681fd..0ff9ebb2337 100644 --- a/source/blender/makesdna/DNA_mesh_types.h +++ b/source/blender/makesdna/DNA_mesh_types.h @@ -50,6 +50,8 @@ typedef struct EditMeshData { * #BKE_mesh_runtime_looptri_ensure, #BKE_mesh_runtime_looptri_len. */ struct MLoopTri_Store { + DNA_DEFINE_CXX_METHODS(MLoopTri_Store) + /* WARNING! swapping between array (ready-to-be-used data) and array_wip * (where data is actually computed) * shall always be protected by same lock as one used for looptris computing. */ @@ -60,6 +62,8 @@ struct MLoopTri_Store { /** Runtime data, not saved in files. */ typedef struct Mesh_Runtime { + DNA_DEFINE_CXX_METHODS(Mesh_Runtime) + /* Evaluated mesh for objects which do not have effective modifiers. * This mesh is used as a result of modifier stack evaluation. * Since modifier stack evaluation is threaded on object level we need some synchronization. */ @@ -138,6 +142,8 @@ typedef struct Mesh_Runtime { } Mesh_Runtime; typedef struct Mesh { + DNA_DEFINE_CXX_METHODS(Mesh) + ID id; /** Animation data (must be immediately after id for utilities to use it). */ struct AnimData *adt; @@ -336,6 +342,8 @@ typedef struct Mesh { /* deprecated by MTFace, only here for file reading */ #ifdef DNA_DEPRECATED_ALLOW typedef struct TFace { + DNA_DEFINE_CXX_METHODS(TFace) + /** The faces image for the active UVLayer. */ void *tpage; float uv[4][2]; diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index 5f909ea325b..1b9192c75cf 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -318,23 +318,12 @@ typedef struct bNode { short preview_xsize, preview_ysize; /** Used at runtime when going through the tree. Initialize before use. */ short tmp_flag; - /** Used at runtime to tag derivatives branches. EEVEE only. */ - char branch_tag; + + char _pad0; /** Used at runtime when iterating over node branches. */ char iter_flag; /** - * XXX: eevee only, id of screen space reflection layer, - * needs to be a float to feed GPU_uniform. - */ - float ssr_id; - /** - * XXX: eevee only, id of screen subsurface scatter layer, - * needs to be a float to feed GPU_uniform. - */ - float sss_id; - - /** * Describes the desired interface of the node. This is run-time data only. * The actual interface of the node may deviate from the declaration temporarily. * It's possible to sync the actual state of the node to the desired state. Currently, this is diff --git a/source/blender/makesdna/DNA_particle_types.h b/source/blender/makesdna/DNA_particle_types.h index f3d342e4849..268e1412eef 100644 --- a/source/blender/makesdna/DNA_particle_types.h +++ b/source/blender/makesdna/DNA_particle_types.h @@ -109,7 +109,12 @@ typedef struct ParticleData { /** Die-time is not necessarily time+lifetime as. */ float time, lifetime; - /** Particles can die unnaturally (collision). */ + /** + * Particles can die unnaturally (collision). + * + * \note Particles die on this frame, be sure to add 1 when clamping the lifetime of particles + * to inclusive ranges such as the scenes end frame. See: T68290. + */ float dietime; /** diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index 9cc4d5ed55b..d6c1040110f 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -939,6 +939,7 @@ typedef struct PaintModeSettings { /** Selected image when canvas_source=PAINT_CANVAS_SOURCE_IMAGE. */ Image *canvas_image; + ImageUser image_user; } PaintModeSettings; diff --git a/source/blender/makesdna/DNA_screen_types.h b/source/blender/makesdna/DNA_screen_types.h index c1eee109630..8560f8a454e 100644 --- a/source/blender/makesdna/DNA_screen_types.h +++ b/source/blender/makesdna/DNA_screen_types.h @@ -664,7 +664,7 @@ typedef enum eRegion_Type { * context (surface, mirror view). Does not represent any real region. */ RGN_TYPE_XR = 13, -#define RGN_TYPE_LEN (RGN_TYPE_XR + 1) +#define RGN_TYPE_NUM (RGN_TYPE_XR + 1) } eRegion_Type; /* use for function args */ diff --git a/source/blender/makesdna/DNA_simulation_types.h b/source/blender/makesdna/DNA_simulation_types.h index 4b2df1ddbfb..4bbbe9cd4e8 100644 --- a/source/blender/makesdna/DNA_simulation_types.h +++ b/source/blender/makesdna/DNA_simulation_types.h @@ -14,6 +14,8 @@ extern "C" { #endif typedef struct Simulation { + DNA_DEFINE_CXX_METHODS(Simulation) + ID id; struct AnimData *adt; /* animation data (must be immediately after id) */ diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h index bcf54ee47a0..838213dd2f3 100644 --- a/source/blender/makesdna/DNA_space_types.h +++ b/source/blender/makesdna/DNA_space_types.h @@ -1513,6 +1513,7 @@ typedef enum eSpaceNodeOverlay_Flag { SN_OVERLAY_SHOW_WIRE_COLORS = (1 << 2), SN_OVERLAY_SHOW_TIMINGS = (1 << 3), SN_OVERLAY_SHOW_PATH = (1 << 4), + SN_OVERLAY_SHOW_NAMED_ATTRIBUTES = (1 << 5), } eSpaceNodeOverlay_Flag; typedef struct SpaceNode { @@ -2057,7 +2058,7 @@ typedef enum eSpace_Type { SPACE_STATUSBAR = 22, SPACE_SPREADSHEET = 23 -#define SPACE_TYPE_LAST SPACE_SPREADSHEET +#define SPACE_TYPE_NUM (SPACE_SPREADSHEET + 1) } eSpace_Type; /* use for function args */ diff --git a/source/blender/makesdna/DNA_texture_types.h b/source/blender/makesdna/DNA_texture_types.h index dab3a1b331e..b725939dbab 100644 --- a/source/blender/makesdna/DNA_texture_types.h +++ b/source/blender/makesdna/DNA_texture_types.h @@ -25,6 +25,7 @@ struct PreviewImage; struct Tex; typedef struct MTex { + DNA_DEFINE_CXX_METHODS(MTex) short texco, mapto, maptoneg, blendtype; struct Object *object; @@ -96,6 +97,8 @@ typedef struct ColorBand { } ColorBand; typedef struct PointDensity { + DNA_DEFINE_CXX_METHODS(PointDensity) + short flag; short falloff_type; @@ -143,6 +146,8 @@ typedef struct PointDensity { } PointDensity; typedef struct Tex { + DNA_DEFINE_CXX_METHODS(Tex) + ID id; /** Animation data (must be immediately after id for utilities to use it). */ struct AnimData *adt; diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h index 0f6c32e4ddf..619f4c05875 100644 --- a/source/blender/makesdna/DNA_userdef_types.h +++ b/source/blender/makesdna/DNA_userdef_types.h @@ -649,9 +649,9 @@ typedef struct UserDef_Experimental { char use_extended_asset_browser; char use_override_templates; char use_named_attribute_nodes; - char use_select_nearest_on_first_click; char enable_eevee_next; char use_sculpt_texture_paint; + char _pad0[1]; /** `makesdna` does not allow empty structs. */ } UserDef_Experimental; diff --git a/source/blender/makesdna/DNA_world_types.h b/source/blender/makesdna/DNA_world_types.h index 88f2e0c9407..bd2ba1e3bf7 100644 --- a/source/blender/makesdna/DNA_world_types.h +++ b/source/blender/makesdna/DNA_world_types.h @@ -27,6 +27,8 @@ struct bNodeTree; * World defines general modeling data such as a background fill, * gravity, color model etc. It mixes rendering data and modeling data. */ typedef struct World { + DNA_DEFINE_CXX_METHODS(World) + ID id; /** Animation data (must be immediately after id for utilities to use it). */ struct AnimData *adt; diff --git a/source/blender/makesrna/intern/rna_ID.c b/source/blender/makesrna/intern/rna_ID.c index d3b7e380afa..e2ce1f23a33 100644 --- a/source/blender/makesrna/intern/rna_ID.c +++ b/source/blender/makesrna/intern/rna_ID.c @@ -1457,7 +1457,7 @@ static void rna_def_ID_properties(BlenderRNA *brna) * when we only really want this so RNA_def_struct_name_property() is set to something useful */ prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE); RNA_def_property_flag(prop, PROP_IDPROPERTY); - /*RNA_def_property_clear_flag(prop, PROP_EDITABLE); */ + // RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "Name", "Unique name used in the code and scripting"); RNA_def_struct_name_property(srna, prop); } diff --git a/source/blender/makesrna/intern/rna_armature.c b/source/blender/makesrna/intern/rna_armature.c index 11c4dad15e9..8d022338d7a 100644 --- a/source/blender/makesrna/intern/rna_armature.c +++ b/source/blender/makesrna/intern/rna_armature.c @@ -1383,7 +1383,7 @@ static void rna_def_armature_edit_bones(BlenderRNA *brna, PropertyRNA *cprop) RNA_def_property_pointer_sdna(prop, NULL, "act_edbone"); RNA_def_property_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "Active EditBone", "Armatures active edit bone"); - /*RNA_def_property_update(prop, 0, "rna_Armature_act_editbone_update"); */ + // RNA_def_property_update(prop, 0, "rna_Armature_act_editbone_update"); RNA_def_property_pointer_funcs(prop, NULL, "rna_Armature_act_edit_bone_set", NULL, NULL); /* TODO: redraw. */ diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c index 062c827b9d0..25dd87b1e74 100644 --- a/source/blender/makesrna/intern/rna_brush.c +++ b/source/blender/makesrna/intern/rna_brush.c @@ -249,7 +249,6 @@ const EnumPropertyItem rna_enum_brush_curves_sculpt_tool_items[] = { {CURVES_SCULPT_TOOL_SNAKE_HOOK, "SNAKE_HOOK", ICON_NONE, "Snake Hook", ""}, {CURVES_SCULPT_TOOL_ADD, "ADD", ICON_NONE, "Add", ""}, {CURVES_SCULPT_TOOL_GROW_SHRINK, "GROW_SHRINK", ICON_NONE, "Grow / Shrink", ""}, - {CURVES_SCULPT_TOOL_TEST1, "TEST1", ICON_NONE, "Test 1", ""}, {0, NULL, 0, NULL, NULL}, }; @@ -664,7 +663,7 @@ static void rna_Brush_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerR { Brush *br = (Brush *)ptr->data; WM_main_add_notifier(NC_BRUSH | NA_EDITED, br); - /*WM_main_add_notifier(NC_SPACE|ND_SPACE_VIEW3D, NULL); */ + // WM_main_add_notifier(NC_SPACE | ND_SPACE_VIEW3D, NULL); } static void rna_Brush_material_update(bContext *UNUSED(C), PointerRNA *UNUSED(ptr)) diff --git a/source/blender/makesrna/intern/rna_camera.c b/source/blender/makesrna/intern/rna_camera.c index 794f08d850e..f535cdcee96 100644 --- a/source/blender/makesrna/intern/rna_camera.c +++ b/source/blender/makesrna/intern/rna_camera.c @@ -564,7 +564,8 @@ void RNA_def_camera(BlenderRNA *brna) RNA_def_property_float_sdna(prop, NULL, "lens"); RNA_def_property_range(prop, 1.0f, FLT_MAX); RNA_def_property_ui_range(prop, 1.0f, 5000.0f, 100, 4); - RNA_def_property_ui_text(prop, "Focal Length", "Perspective Camera focal length value in millimeters"); + RNA_def_property_ui_text( + prop, "Focal Length", "Perspective Camera focal length value in millimeters"); RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Camera_update"); prop = RNA_def_property(srna, "sensor_width", PROP_FLOAT, PROP_DISTANCE_CAMERA); diff --git a/source/blender/makesrna/intern/rna_mesh.c b/source/blender/makesrna/intern/rna_mesh.c index 22275df61f9..52af96e355d 100644 --- a/source/blender/makesrna/intern/rna_mesh.c +++ b/source/blender/makesrna/intern/rna_mesh.c @@ -3451,20 +3451,17 @@ static void rna_def_mesh(BlenderRNA *brna) prop = RNA_def_property(srna, "use_remesh_preserve_paint_mask", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", ME_REMESH_REPROJECT_PAINT_MASK); - RNA_def_property_boolean_default(prop, false); RNA_def_property_ui_text(prop, "Preserve Paint Mask", "Keep the current mask on the new mesh"); RNA_def_property_update(prop, 0, "rna_Mesh_update_draw"); prop = RNA_def_property(srna, "use_remesh_preserve_sculpt_face_sets", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", ME_REMESH_REPROJECT_SCULPT_FACE_SETS); - RNA_def_property_boolean_default(prop, false); RNA_def_property_ui_text( prop, "Preserve Face Sets", "Keep the current Face Sets on the new mesh"); RNA_def_property_update(prop, 0, "rna_Mesh_update_draw"); prop = RNA_def_property(srna, "use_remesh_preserve_vertex_colors", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", ME_REMESH_REPROJECT_VERTEX_COLORS); - RNA_def_property_boolean_default(prop, false); RNA_def_property_ui_text( prop, "Preserve Vertex Colors", "Keep the current vertex colors on the new mesh"); RNA_def_property_update(prop, 0, "rna_Mesh_update_draw"); diff --git a/source/blender/makesrna/intern/rna_modifier.c b/source/blender/makesrna/intern/rna_modifier.c index 54031f5a416..16045e6cddf 100644 --- a/source/blender/makesrna/intern/rna_modifier.c +++ b/source/blender/makesrna/intern/rna_modifier.c @@ -5825,7 +5825,7 @@ static void rna_def_modifier_ocean(BlenderRNA *brna) prop = RNA_def_property(srna, "filepath", PROP_STRING, PROP_DIRPATH); RNA_def_property_string_sdna(prop, NULL, "cachepath"); RNA_def_property_ui_text(prop, "Cache Path", "Path to a folder to store external baked images"); - /*RNA_def_property_update(prop, 0, "rna_Modifier_update"); */ + // RNA_def_property_update(prop, 0, "rna_Modifier_update"); /* XXX how to update? */ RNA_define_lib_overridable(false); diff --git a/source/blender/makesrna/intern/rna_object.c b/source/blender/makesrna/intern/rna_object.c index 358f88c8f4d..1fb41bf792f 100644 --- a/source/blender/makesrna/intern/rna_object.c +++ b/source/blender/makesrna/intern/rna_object.c @@ -2572,11 +2572,11 @@ static void rna_def_object_modifiers(BlenderRNA *brna, PropertyRNA *cprop) RNA_def_property_pointer_sdna(prop, NULL, "act_edbone"); RNA_def_property_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "Active EditBone", "Armatures active edit bone"); - /*RNA_def_property_update(prop, 0, "rna_Armature_act_editbone_update"); */ + // RNA_def_property_update(prop, 0, "rna_Armature_act_editbone_update"); RNA_def_property_pointer_funcs(prop, NULL, "rna_Armature_act_edit_bone_set", NULL, NULL); /* TODO: redraw. */ -/* RNA_def_property_collection_active(prop, prop_act); */ + // RNA_def_property_collection_active(prop, prop_act); # endif /* add modifier */ @@ -3387,7 +3387,7 @@ static void rna_def_object(BlenderRNA *brna) prop, "rna_Object_matrix_basis_get", "rna_Object_matrix_basis_set", NULL); RNA_def_property_update(prop, NC_OBJECT | ND_TRANSFORM, "rna_Object_internal_update"); - /*parent_inverse*/ + /* Parent_inverse. */ prop = RNA_def_property(srna, "matrix_parent_inverse", PROP_FLOAT, PROP_MATRIX); RNA_def_property_float_sdna(prop, NULL, "parentinv"); RNA_def_property_multi_array(prop, 2, rna_matrix_dimsize_4x4); diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index d2b8eb203aa..c40a2ed9260 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -7143,6 +7143,13 @@ static void rna_def_space_node_overlay(BlenderRNA *brna) RNA_def_property_boolean_default(prop, true); RNA_def_property_ui_text(prop, "Show Tree Path", "Display breadcrumbs for the editor's context"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_NODE, NULL); + + prop = RNA_def_property(srna, "show_named_attributes", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "overlay.flag", SN_OVERLAY_SHOW_NAMED_ATTRIBUTES); + RNA_def_property_boolean_default(prop, true); + RNA_def_property_ui_text( + prop, "Show Named Attributes", "Show when nodes are using named attributes"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_NODE, NULL); } static void rna_def_space_node(BlenderRNA *brna) diff --git a/source/blender/makesrna/intern/rna_texture.c b/source/blender/makesrna/intern/rna_texture.c index 56d1b48811a..6f7ee966723 100644 --- a/source/blender/makesrna/intern/rna_texture.c +++ b/source/blender/makesrna/intern/rna_texture.c @@ -1556,7 +1556,7 @@ static void rna_def_texture(BlenderRNA *brna) RNA_def_struct_refine_func(srna, "rna_Texture_refine"); prop = RNA_def_property(srna, "type", PROP_ENUM, PROP_NONE); - /*RNA_def_property_clear_flag(prop, PROP_EDITABLE); */ + // RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_enum_sdna(prop, NULL, "type"); RNA_def_property_enum_items(prop, rna_enum_texture_type_items); RNA_def_property_enum_funcs(prop, NULL, "rna_Texture_type_set", NULL); diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index cf622818a3d..8c86e44aebf 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -6440,12 +6440,6 @@ static void rna_def_userdef_experimental(BlenderRNA *brna) "Named Attribute Nodes", "Enable named attribute nodes in the geometry nodes add menu"); - prop = RNA_def_property(srna, "use_select_nearest_on_first_click", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "use_select_nearest_on_first_click", 1); - RNA_def_property_ui_text(prop, - "Object Select Nearest on First Click", - "When enabled, always select the front-most object on the first click"); - prop = RNA_def_property(srna, "enable_eevee_next", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "enable_eevee_next", 1); RNA_def_property_ui_text(prop, "EEVEE Next", "Enable the new EEVEE codebase, requires restart"); diff --git a/source/blender/modifiers/intern/MOD_laplaciansmooth.c b/source/blender/modifiers/intern/MOD_laplaciansmooth.c index 11abe9a4d0a..95283b1cd20 100644 --- a/source/blender/modifiers/intern/MOD_laplaciansmooth.c +++ b/source/blender/modifiers/intern/MOD_laplaciansmooth.c @@ -60,7 +60,7 @@ struct BLaplacianSystem { const MEdge *medges; LinearSolver *context; - /*Data*/ + /* Data. */ float min_area; float vert_centroid[3]; }; diff --git a/source/blender/modifiers/intern/MOD_meshsequencecache.cc b/source/blender/modifiers/intern/MOD_meshsequencecache.cc index 7dbd4c5b260..998fb0a94a3 100644 --- a/source/blender/modifiers/intern/MOD_meshsequencecache.cc +++ b/source/blender/modifiers/intern/MOD_meshsequencecache.cc @@ -226,14 +226,13 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * velocity_scale *= FPS; } - result = ABC_read_mesh(mcmd->reader, - ctx->object, - mesh, - time, - &err_str, - mcmd->read_flag, - mcmd->cache_file->velocity_name, - velocity_scale); + ABCReadParams params = {}; + params.time = time; + params.read_flags = mcmd->read_flag; + params.velocity_name = mcmd->cache_file->velocity_name; + params.velocity_scale = velocity_scale; + + result = ABC_read_mesh(mcmd->reader, ctx->object, mesh, ¶ms, &err_str); # endif break; } diff --git a/source/blender/modifiers/intern/MOD_screw.c b/source/blender/modifiers/intern/MOD_screw.c index e8fb667c572..4ace6404388 100644 --- a/source/blender/modifiers/intern/MOD_screw.c +++ b/source/blender/modifiers/intern/MOD_screw.c @@ -515,7 +515,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * mv_new = mvert_new; if (ob_axis != NULL) { - /*mtx_tx is initialized early on */ + /* `mtx_tx` is initialized early on. */ for (i = 0; i < totvert; i++, mv_new++, mv_orig++, vc++) { vc->co[0] = mv_new->co[0] = mv_orig->co[0]; vc->co[1] = mv_new->co[1] = mv_orig->co[1]; @@ -883,7 +883,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * copy_v3_v3(mv_new->co, mv_new_base->co); /* only need to set these if using non cleared memory */ - /*mv_new->mat_nr = mv_new->flag = 0;*/ + // mv_new->mat_nr = mv_new->flag = 0; if (ob_axis != NULL) { sub_v3_v3(mv_new->co, mtx_tx[3]); diff --git a/source/blender/nodes/NOD_geometry_exec.hh b/source/blender/nodes/NOD_geometry_exec.hh index 96a1904abdd..bac4d0165e9 100644 --- a/source/blender/nodes/NOD_geometry_exec.hh +++ b/source/blender/nodes/NOD_geometry_exec.hh @@ -40,6 +40,7 @@ using fn::FieldInput; using fn::FieldOperation; using fn::GField; using fn::ValueOrField; +using geometry_nodes_eval_log::NamedAttributeUsage; using geometry_nodes_eval_log::NodeWarningType; /** @@ -342,6 +343,8 @@ class GeoNodeExecParams { void set_default_remaining_outputs(); + void used_named_attribute(std::string attribute_name, NamedAttributeUsage usage); + 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 16332be5179..1ad859aa47b 100644 --- a/source/blender/nodes/NOD_geometry_nodes_eval_log.hh +++ b/source/blender/nodes/NOD_geometry_nodes_eval_log.hh @@ -170,6 +170,24 @@ struct ValueOfSockets { destruct_ptr<ValueLog> value; }; +enum class NamedAttributeUsage { + None = 0, + Read = 1 << 0, + Write = 1 << 1, + Remove = 1 << 2, +}; +ENUM_OPERATORS(NamedAttributeUsage, NamedAttributeUsage::Remove); + +struct UsedNamedAttribute { + std::string name; + NamedAttributeUsage usage; +}; + +struct NodeWithUsedNamedAttribute { + DNode node; + UsedNamedAttribute attribute; +}; + class GeoLogger; class ModifierLog; @@ -186,6 +204,7 @@ class LocalGeoLogger { Vector<NodeWithWarning> node_warnings_; Vector<NodeWithExecutionTime> node_exec_times_; Vector<NodeWithDebugMessage> node_debug_messages_; + Vector<NodeWithUsedNamedAttribute> used_named_attributes_; friend ModifierLog; @@ -199,6 +218,7 @@ class LocalGeoLogger { void log_multi_value_socket(DSocket socket, Span<GPointer> values); void log_node_warning(DNode node, NodeWarningType type, std::string message); void log_execution_time(DNode node, std::chrono::microseconds exec_time); + void log_used_named_attribute(DNode node, std::string attribute_name, NamedAttributeUsage usage); /** * Log a message that will be displayed in the node editor next to the node. * This should only be used for debugging purposes and not to display information to users. @@ -278,6 +298,7 @@ class NodeLog { Vector<SocketLog> output_logs_; Vector<NodeWarning, 0> warnings_; Vector<std::string, 0> debug_messages_; + Vector<UsedNamedAttribute, 0> used_named_attributes_; std::chrono::microseconds exec_time_; friend ModifierLog; @@ -307,6 +328,11 @@ class NodeLog { return debug_messages_; } + Span<UsedNamedAttribute> used_named_attributes() const + { + return used_named_attributes_; + } + std::chrono::microseconds execution_time() const { return exec_time_; diff --git a/source/blender/nodes/NOD_node_tree_ref.hh b/source/blender/nodes/NOD_node_tree_ref.hh index 3ed09de8fab..61d1d11d859 100644 --- a/source/blender/nodes/NOD_node_tree_ref.hh +++ b/source/blender/nodes/NOD_node_tree_ref.hh @@ -65,7 +65,6 @@ class SocketRef : NonCopyable, NonMovable { bool is_input_; int id_; int index_; - PointerRNA rna_; Vector<LinkRef *> directly_linked_links_; /* These sockets are linked directly, i.e. with a single link in between. */ @@ -101,7 +100,7 @@ class SocketRef : NonCopyable, NonMovable { const InputSocketRef &as_input() const; const OutputSocketRef &as_output() const; - PointerRNA *rna() const; + PointerRNA rna() const; StringRefNull idname() const; StringRefNull name() const; @@ -152,7 +151,6 @@ class NodeRef : NonCopyable, NonMovable { private: NodeTreeRef *tree_; bNode *bnode_; - PointerRNA rna_; int id_; Vector<InputSocketRef *> inputs_; Vector<OutputSocketRef *> outputs_; @@ -183,7 +181,7 @@ class NodeRef : NonCopyable, NonMovable { bNode *bnode() const; bNodeTree *btree() const; - PointerRNA *rna() const; + PointerRNA rna() const; StringRefNull idname() const; StringRefNull name() const; StringRefNull label() const; @@ -410,11 +408,6 @@ inline const OutputSocketRef &SocketRef::as_output() const return static_cast<const OutputSocketRef &>(*this); } -inline PointerRNA *SocketRef::rna() const -{ - return const_cast<PointerRNA *>(&rna_); -} - inline StringRefNull SocketRef::idname() const { return bsocket_->idname; @@ -571,11 +564,6 @@ inline bNodeTree *NodeRef::btree() const return tree_->btree(); } -inline PointerRNA *NodeRef::rna() const -{ - return const_cast<PointerRNA *>(&rna_); -} - inline StringRefNull NodeRef::idname() const { return bnode_->idname; diff --git a/source/blender/nodes/NOD_shader.h b/source/blender/nodes/NOD_shader.h index f1c08f6a589..4996f12e27d 100644 --- a/source/blender/nodes/NOD_shader.h +++ b/source/blender/nodes/NOD_shader.h @@ -144,10 +144,7 @@ struct bNode *ntreeShaderOutputNode(struct bNodeTree *ntree, int target); /** * This one needs to work on a local tree. */ -void ntreeGPUMaterialNodes(struct bNodeTree *localtree, - struct GPUMaterial *mat, - bool *has_surface_output, - bool *has_volume_output); +void ntreeGPUMaterialNodes(struct bNodeTree *localtree, struct GPUMaterial *mat); #ifdef __cplusplus } 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 e7a8c61290b..903a5e7c1d7 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 @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ -#include "BKE_spline.hh" +#include "BKE_curves.hh" #include "BKE_curve_to_mesh.hh" @@ -27,17 +27,18 @@ static void geometry_set_curve_to_mesh(GeometrySet &geometry_set, const GeometrySet &profile_set, const bool fill_caps) { - const std::unique_ptr<CurveEval> curve = curves_to_curve_eval( - *geometry_set.get_curves_for_read()); + const Curves &curves = *geometry_set.get_curves_for_read(); + const Curves *profile_curves = profile_set.get_curves_for_read(); if (profile_curves == nullptr) { - Mesh *mesh = bke::curve_to_wire_mesh(*curve); + Mesh *mesh = bke::curve_to_wire_mesh(bke::CurvesGeometry::wrap(curves.geometry)); geometry_set.replace_mesh(mesh); } else { - const std::unique_ptr<CurveEval> profile_curve = curves_to_curve_eval(*profile_curves); - Mesh *mesh = bke::curve_to_mesh_sweep(*curve, *profile_curve, fill_caps); + Mesh *mesh = bke::curve_to_mesh_sweep(bke::CurvesGeometry::wrap(curves.geometry), + bke::CurvesGeometry::wrap(profile_curves->geometry), + fill_caps); geometry_set.replace_mesh(mesh); } } diff --git a/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc b/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc index 1e170dd5350..1b26cfe31fe 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc @@ -759,6 +759,7 @@ static void duplicate_edges(GeometrySet &geometry_set, MEdge &new_edge = new_edges[edge_range[i_duplicate]]; new_edge.v1 = vert_range[i_duplicate * 2]; new_edge.v2 = vert_range[i_duplicate * 2] + 1; + new_edge.flag = ME_LOOSEEDGE; } } }); diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_named_attribute.cc b/source/blender/nodes/geometry/nodes/node_geo_input_named_attribute.cc index f6e2be9119c..6cb9ca642ef 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_named_attribute.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_named_attribute.cc @@ -81,11 +81,13 @@ static void node_geo_exec(GeoNodeExecParams params) const std::string name = params.extract_input<std::string>("Name"); - if (!U.experimental.use_named_attribute_nodes) { + if (!U.experimental.use_named_attribute_nodes || name.empty()) { params.set_default_remaining_outputs(); return; } + params.used_named_attribute(name, NamedAttributeUsage::Read); + switch (data_type) { case CD_PROP_FLOAT: params.set_output("Attribute_Float", AttributeFieldInput::Create<float>(std::move(name))); diff --git a/source/blender/nodes/geometry/nodes/node_geo_remove_attribute.cc b/source/blender/nodes/geometry/nodes/node_geo_remove_attribute.cc index 202241affeb..f46b70c91a9 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_remove_attribute.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_remove_attribute.cc @@ -32,6 +32,8 @@ static void node_geo_exec(GeoNodeExecParams params) return; } + params.used_named_attribute(name, NamedAttributeUsage::Remove); + std::atomic<bool> attribute_exists = false; std::atomic<bool> cannot_delete = false; diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_material.cc b/source/blender/nodes/geometry/nodes/node_geo_set_material.cc index c48129acbdc..296b40c6c49 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_material.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_material.cc @@ -5,6 +5,7 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "DNA_curves_types.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" #include "DNA_pointcloud_types.h" @@ -63,6 +64,7 @@ static void node_geo_exec(GeoNodeExecParams params) /* Only add the warnings once, even if there are many unique instances. */ bool point_selection_warning = false; bool volume_selection_warning = false; + bool curves_selection_warning = false; geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { if (geometry_set.has_mesh()) { @@ -89,6 +91,12 @@ static void node_geo_exec(GeoNodeExecParams params) point_selection_warning = true; } } + if (Curves *curves = geometry_set.get_curves_for_write()) { + BKE_id_material_eval_assign(&curves->id, 1, material); + if (selection_field.node().depends_on_input()) { + curves_selection_warning = true; + } + } }); if (volume_selection_warning) { @@ -101,6 +109,11 @@ static void node_geo_exec(GeoNodeExecParams params) NodeWarningType::Info, TIP_("Point clouds only support a single material; selection input can not be a field")); } + if (curves_selection_warning) { + params.error_message_add( + NodeWarningType::Info, + TIP_("Curves only support a single material; selection input can not be a field")); + } params.set_output("Geometry", std::move(geometry_set)); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_store_named_attribute.cc b/source/blender/nodes/geometry/nodes/node_geo_store_named_attribute.cc index 1d1446ce1bd..92614d1a31d 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_store_named_attribute.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_store_named_attribute.cc @@ -127,11 +127,13 @@ static void node_geo_exec(GeoNodeExecParams params) GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); std::string name = params.extract_input<std::string>("Name"); - if (!U.experimental.use_named_attribute_nodes) { + if (!U.experimental.use_named_attribute_nodes || name.empty()) { params.set_output("Geometry", std::move(geometry_set)); return; } + params.used_named_attribute(name, NamedAttributeUsage::Write); + const NodeGeometryStoreNamedAttribute &storage = node_storage(params.node()); const CustomDataType data_type = static_cast<CustomDataType>(storage.data_type); const AttributeDomain domain = static_cast<AttributeDomain>(storage.domain); 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 index bc34a1a6f2c..658de02fdab 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc @@ -186,7 +186,7 @@ static TextLayout get_text_layout(GeoNodeExecParams ¶ms) params.extract_input<float>("Text Box Height"); VFont *vfont = (VFont *)params.node().id; - Curve cu = {{nullptr}}; + Curve cu = dna::shallow_zero_initialize(); cu.type = OB_FONT; /* Set defaults */ cu.resolu = 12; @@ -278,7 +278,7 @@ static Map<int, int> create_curve_instances(GeoNodeExecParams ¶ms, if (handles.contains(layout.char_codes[i])) { continue; } - Curve cu = {{nullptr}}; + Curve cu = dna::shallow_zero_initialize(); cu.type = OB_FONT; cu.resolu = 12; cu.vfont = vfont; diff --git a/source/blender/nodes/intern/geometry_nodes_eval_log.cc b/source/blender/nodes/intern/geometry_nodes_eval_log.cc index 13f38c3352e..378bac894e8 100644 --- a/source/blender/nodes/intern/geometry_nodes_eval_log.cc +++ b/source/blender/nodes/intern/geometry_nodes_eval_log.cc @@ -62,6 +62,13 @@ ModifierLog::ModifierLog(GeoLogger &logger) NodeLog &node_log = this->lookup_or_add_node_log(log_by_tree_context, debug_message.node); node_log.debug_messages_.append(debug_message.message); } + + for (NodeWithUsedNamedAttribute &node_with_attribute_name : + local_logger.used_named_attributes_) { + NodeLog &node_log = this->lookup_or_add_node_log(log_by_tree_context, + node_with_attribute_name.node); + node_log.used_named_attributes_.append(std::move(node_with_attribute_name.attribute)); + } } } @@ -486,6 +493,13 @@ void LocalGeoLogger::log_execution_time(DNode node, std::chrono::microseconds ex node_exec_times_.append({node, exec_time}); } +void LocalGeoLogger::log_used_named_attribute(DNode node, + std::string attribute_name, + NamedAttributeUsage usage) +{ + used_named_attributes_.append({node, {std::move(attribute_name), usage}}); +} + void LocalGeoLogger::log_debug_message(DNode node, std::string message) { node_debug_messages_.append({node, std::move(message)}); diff --git a/source/blender/nodes/intern/node_geometry_exec.cc b/source/blender/nodes/intern/node_geometry_exec.cc index d63a6d11eda..cea3084a418 100644 --- a/source/blender/nodes/intern/node_geometry_exec.cc +++ b/source/blender/nodes/intern/node_geometry_exec.cc @@ -23,6 +23,16 @@ void GeoNodeExecParams::error_message_add(const NodeWarningType type, std::strin local_logger.log_node_warning(provider_->dnode, type, std::move(message)); } +void GeoNodeExecParams::used_named_attribute(std::string attribute_name, + const NamedAttributeUsage usage) +{ + if (provider_->logger == nullptr) { + return; + } + LocalGeoLogger &local_logger = provider_->logger->local(); + local_logger.log_used_named_attribute(provider_->dnode, std::move(attribute_name), usage); +} + void GeoNodeExecParams::check_input_geometry_set(StringRef identifier, const GeometrySet &geometry_set) const { diff --git a/source/blender/nodes/intern/node_tree_ref.cc b/source/blender/nodes/intern/node_tree_ref.cc index 01a31ab852d..64a8690a869 100644 --- a/source/blender/nodes/intern/node_tree_ref.cc +++ b/source/blender/nodes/intern/node_tree_ref.cc @@ -21,7 +21,6 @@ NodeTreeRef::NodeTreeRef(bNodeTree *btree) : btree_(btree) node.tree_ = this; node.bnode_ = bnode; node.id_ = nodes_by_id_.append_and_get_index(&node); - RNA_pointer_create(&btree->id, &RNA_Node, bnode, &node.rna_); LISTBASE_FOREACH (bNodeSocket *, bsocket, &bnode->inputs) { InputSocketRef &socket = *allocator_.construct<InputSocketRef>().release(); @@ -30,7 +29,6 @@ NodeTreeRef::NodeTreeRef(bNodeTree *btree) : btree_(btree) socket.is_input_ = true; socket.bsocket_ = bsocket; socket.id_ = sockets_by_id_.append_and_get_index(&socket); - RNA_pointer_create(&btree->id, &RNA_NodeSocket, bsocket, &socket.rna_); } LISTBASE_FOREACH (bNodeSocket *, bsocket, &bnode->outputs) { @@ -40,7 +38,6 @@ NodeTreeRef::NodeTreeRef(bNodeTree *btree) : btree_(btree) socket.is_input_ = false; socket.bsocket_ = bsocket; socket.id_ = sockets_by_id_.append_and_get_index(&socket); - RNA_pointer_create(&btree->id, &RNA_NodeSocket, bsocket, &socket.rna_); } LISTBASE_FOREACH (bNodeLink *, blink, &bnode->internal_links) { @@ -664,4 +661,18 @@ const NodeTreeRef &get_tree_ref_from_map(NodeTreeRefMap &node_tree_refs, bNodeTr [&]() { return std::make_unique<NodeTreeRef>(&btree); }); } +PointerRNA NodeRef::rna() const +{ + PointerRNA rna; + RNA_pointer_create(&tree_->btree()->id, &RNA_Node, bnode_, &rna); + return rna; +} + +PointerRNA SocketRef::rna() const +{ + PointerRNA rna; + RNA_pointer_create(&this->tree().btree()->id, &RNA_NodeSocket, bsocket_, &rna); + return rna; +} + } // namespace blender::nodes diff --git a/source/blender/nodes/shader/node_shader_tree.cc b/source/blender/nodes/shader/node_shader_tree.cc index 03baebfd28b..d917106807c 100644 --- a/source/blender/nodes/shader/node_shader_tree.cc +++ b/source/blender/nodes/shader/node_shader_tree.cc @@ -16,11 +16,12 @@ #include "DNA_workspace_types.h" #include "DNA_world_types.h" -#include "BLI_alloca.h" +#include "BLI_array.hh" #include "BLI_linklist.h" #include "BLI_listbase.h" #include "BLI_threads.h" #include "BLI_utildefines.h" +#include "BLI_vector.hh" #include "BLT_translation.h" @@ -47,11 +48,8 @@ #include "node_shader_util.hh" #include "node_util.h" -struct nTreeTags { - float ssr_id, sss_id; -}; - -static void ntree_shader_tag_nodes(bNodeTree *ntree, bNode *output_node, nTreeTags *tags); +using blender::Array; +using blender::Vector; static bool shader_tree_poll(const bContext *C, bNodeTreeType *UNUSED(treetype)) { @@ -250,6 +248,18 @@ static bNodeSocket *ntree_shader_node_find_output(bNode *node, const char *ident return ntree_shader_node_find_socket(&node->outputs, identifier); } +/* Find input socket at a specific position. */ +static bNodeSocket *ntree_shader_node_input_get(bNode *node, int n) +{ + return reinterpret_cast<bNodeSocket *>(BLI_findlink(&node->inputs, n)); +} + +/* Find output socket at a specific position. */ +static bNodeSocket *ntree_shader_node_output_get(bNode *node, int n) +{ + return reinterpret_cast<bNodeSocket *>(BLI_findlink(&node->outputs, n)); +} + /* Return true on success. */ static bool ntree_shader_expand_socket_default(bNodeTree *localtree, bNode *node, @@ -522,125 +532,21 @@ static void ntree_shader_groups_flatten(bNodeTree *localtree) BKE_ntree_update_main_tree(G.main, localtree, nullptr); } -/* Check whether shader has a displacement. - * - * Will also return a node and its socket which is connected to a displacement - * output. Additionally, link which is attached to the displacement output is - * also returned. - */ -static bool ntree_shader_has_displacement(bNodeTree *ntree, - bNode *output_node, - bNode **r_node, - bNodeSocket **r_socket, - bNodeLink **r_link) -{ - if (output_node == nullptr) { - /* We can't have displacement without output node, apparently. */ - return false; - } - /* Make sure sockets links pointers are correct. */ - BKE_ntree_update_main_tree(G.main, ntree, nullptr); - bNodeSocket *displacement = ntree_shader_node_find_input(output_node, "Displacement"); - - if (displacement == nullptr) { - /* Non-cycles node is used as an output. */ - return false; - } - - if ((displacement->link != nullptr) && !(displacement->link->flag & NODE_LINK_MUTED)) { - *r_node = displacement->link->fromnode; - *r_socket = displacement->link->fromsock; - *r_link = displacement->link; - return true; - } - return false; -} - -static void ntree_shader_relink_node_normal(bNodeTree *ntree, - bNode *node, - bNode *node_from, - bNodeSocket *socket_from) -{ - /* TODO(sergey): Can we do something smarter here than just a name-based - * matching? - */ - LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) { - if (STREQ(sock->identifier, "Normal") && sock->link == nullptr) { - /* It's a normal input and nothing is connected to it. */ - nodeAddLink(ntree, node_from, socket_from, node, sock); - } - else if (sock->link) { - bNodeLink *link = sock->link; - if (ELEM(link->fromnode->type, SH_NODE_NEW_GEOMETRY, SH_NODE_TEX_COORD) && - STREQ(link->fromsock->identifier, "Normal")) { - /* Linked to a geometry node normal output. */ - nodeAddLink(ntree, node_from, socket_from, node, sock); - } - } - } -} - -/* Use specified node and socket as an input for unconnected normal sockets. */ -static void ntree_shader_link_builtin_normal(bNodeTree *ntree, - bNode *node_from, - bNodeSocket *socket_from) -{ - LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { - if (node == node_from) { - /* Don't connect node itself! */ - continue; - } - if (node->tmp_flag == -2) { - /* This node is used inside the displacement tree. Skip to avoid cycles. */ - continue; - } - ntree_shader_relink_node_normal(ntree, node, node_from, socket_from); - } -} - -static void ntree_shader_bypass_bump_link(bNodeTree *ntree, bNode *bump_node, bNodeLink *bump_link) -{ - /* Bypass bump nodes. This replicates cycles "implicit" behavior. */ - bNodeSocket *bump_normal_input = ntree_shader_node_find_input(bump_node, "Normal"); - bNode *fromnode; - bNodeSocket *fromsock; - /* Default to builtin normals if there is no link. */ - if (bump_normal_input->link) { - fromsock = bump_normal_input->link->fromsock; - fromnode = bump_normal_input->link->fromnode; - } - else { - fromnode = nodeAddStaticNode(nullptr, ntree, SH_NODE_NEW_GEOMETRY); - fromsock = ntree_shader_node_find_output(fromnode, "Normal"); - } - /* Bypass the bump node by creating a link between the previous and next node. */ - nodeAddLink(ntree, fromnode, fromsock, bump_link->tonode, bump_link->tosock); - nodeRemLink(ntree, bump_link); -} - -static void ntree_shader_bypass_tagged_bump_nodes(bNodeTree *ntree) -{ - /* Bypass bump links inside copied nodes */ - LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &ntree->links) { - bNode *node = link->fromnode; - /* If node is a copy. */ - if (node->tmp_flag == -2 && node->type == SH_NODE_BUMP) { - ntree_shader_bypass_bump_link(ntree, node, link); - } - } - BKE_ntree_update_main_tree(G.main, ntree, nullptr); -} +struct branchIterData { + bool (*node_filter)(const bNode *node); + int node_count; +}; static bool ntree_branch_count_and_tag_nodes(bNode *fromnode, bNode *tonode, void *userdata) { - int *node_count = (int *)userdata; - if (fromnode->tmp_flag == -1) { - fromnode->tmp_flag = *node_count; - (*node_count)++; + branchIterData *iter = (branchIterData *)userdata; + if (fromnode->tmp_flag == -1 && (iter->node_filter == nullptr || iter->node_filter(fromnode))) { + fromnode->tmp_flag = iter->node_count; + iter->node_count++; } - if (tonode->tmp_flag == -1) { - tonode->tmp_flag = *node_count; - (*node_count)++; + if (tonode->tmp_flag == -1 && (iter->node_filter == nullptr || iter->node_filter(tonode))) { + tonode->tmp_flag = iter->node_count; + iter->node_count++; } return true; } @@ -650,6 +556,7 @@ static bool ntree_branch_count_and_tag_nodes(bNode *fromnode, bNode *tonode, voi * Returns input node copy. */ static bNode *ntree_shader_copy_branch(bNodeTree *ntree, bNode *start_node, + bool (*node_filter)(const bNode *node), void (*callback)(bNode *node, int user_data), int user_data) { @@ -659,10 +566,12 @@ static bNode *ntree_shader_copy_branch(bNodeTree *ntree, } /* Count and tag all nodes inside the displacement branch of the tree. */ start_node->tmp_flag = 0; - int node_count = 1; - nodeChainIterBackwards(ntree, start_node, ntree_branch_count_and_tag_nodes, &node_count, 1); + branchIterData iter_data; + iter_data.node_filter = node_filter; + iter_data.node_count = 1; + nodeChainIterBackwards(ntree, start_node, ntree_branch_count_and_tag_nodes, &iter_data, 1); /* Make a full copy of the branch */ - bNode **nodes_copy = static_cast<bNode **>(MEM_mallocN(sizeof(bNode *) * node_count, __func__)); + Array<bNode *> nodes_copy(iter_data.node_count); LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { if (node->tmp_flag >= 0) { int id = node->tmp_flag; @@ -690,225 +599,447 @@ static bNode *ntree_shader_copy_branch(bNodeTree *ntree, } /* Per node callback. */ if (callback) { - for (int i = 0; i < node_count; i++) { + for (int i = 0; i < iter_data.node_count; i++) { callback(nodes_copy[i], user_data); } } bNode *start_node_copy = nodes_copy[start_node->tmp_flag]; - MEM_freeN(nodes_copy); return start_node_copy; } -static void ntree_shader_copy_branch_displacement(bNodeTree *ntree, - bNode *displacement_node, - bNodeSocket *displacement_socket, - bNodeLink *displacement_link) +/* Generate emission node to convert regular data to closure sockets. + * Returns validity of the tree. + */ +static bool ntree_shader_implicit_closure_cast(bNodeTree *ntree) { - /* Replace displacement socket/node/link. */ - bNode *tonode = displacement_link->tonode; - bNodeSocket *tosock = displacement_link->tosock; - displacement_node = ntree_shader_copy_branch(ntree, displacement_node, nullptr, 0); - displacement_socket = ntree_shader_node_find_output(displacement_node, - displacement_socket->identifier); - nodeRemLink(ntree, displacement_link); - nodeAddLink(ntree, displacement_node, displacement_socket, tonode, tosock); - - BKE_ntree_update_main_tree(G.main, ntree, nullptr); + bool modified = false; + LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &ntree->links) { + if ((link->fromsock->type != SOCK_SHADER) && (link->tosock->type == SOCK_SHADER)) { + bNode *emission_node = nodeAddStaticNode(NULL, ntree, SH_NODE_EMISSION); + bNodeSocket *in_sock = ntree_shader_node_find_input(emission_node, "Color"); + bNodeSocket *out_sock = ntree_shader_node_find_output(emission_node, "Emission"); + nodeAddLink(ntree, link->fromnode, link->fromsock, emission_node, in_sock); + nodeAddLink(ntree, emission_node, out_sock, link->tonode, link->tosock); + nodeRemLink(ntree, link); + modified = true; + } + else if ((link->fromsock->type == SOCK_SHADER) && (link->tosock->type != SOCK_SHADER)) { + /* Meh. Not directly visible to the user. But better than nothing. */ + fprintf(stderr, "Shader Nodetree Error: Invalid implicit socket conversion\n"); + BKE_ntree_update_main_tree(G.main, ntree, nullptr); + return false; + } + } + if (modified) { + BKE_ntree_update_main_tree(G.main, ntree, nullptr); + } + return true; } -/* Re-link displacement output to unconnected normal sockets via bump node. - * This way material with have proper displacement in the viewport. - */ -static void ntree_shader_relink_displacement(bNodeTree *ntree, bNode *output_node) -{ - bNode *displacement_node; - bNodeSocket *displacement_socket; - bNodeLink *displacement_link; - if (!ntree_shader_has_displacement( - ntree, output_node, &displacement_node, &displacement_socket, &displacement_link)) { - /* There is no displacement output connected, nothing to re-link. */ - return; - } - - /* Copy the whole displacement branch to avoid cyclic dependency - * and issue when bypassing bump nodes. */ - ntree_shader_copy_branch_displacement( - ntree, displacement_node, displacement_socket, displacement_link); - /* Bypass bump nodes inside the copied branch to mimic cycles behavior. */ - ntree_shader_bypass_tagged_bump_nodes(ntree); - - /* Displacement Node may have changed because of branch copy and bump bypass. */ - ntree_shader_has_displacement( - ntree, output_node, &displacement_node, &displacement_socket, &displacement_link); - - /* We have to disconnect displacement output socket, otherwise we'll have - * cycles in the Cycles material :) - */ - nodeRemLink(ntree, displacement_link); - - /* Convert displacement vector to bump height. */ - bNode *dot_node = nodeAddStaticNode(nullptr, ntree, SH_NODE_VECTOR_MATH); - bNode *geo_node = nodeAddStaticNode(nullptr, ntree, SH_NODE_NEW_GEOMETRY); - bNodeSocket *normal_socket = ntree_shader_node_find_output(geo_node, "Normal"); - bNodeSocket *dot_input1 = static_cast<bNodeSocket *>(dot_node->inputs.first); - bNodeSocket *dot_input2 = static_cast<bNodeSocket *>(dot_input1->next); - dot_node->custom1 = NODE_VECTOR_MATH_DOT_PRODUCT; - - nodeAddLink(ntree, displacement_node, displacement_socket, dot_node, dot_input1); - nodeAddLink(ntree, geo_node, normal_socket, dot_node, dot_input2); - displacement_node = dot_node; - displacement_socket = ntree_shader_node_find_output(dot_node, "Value"); - - /* We can't connect displacement to normal directly, use bump node for that - * and hope that it gives good enough approximation. - */ - bNode *bump_node = nodeAddStaticNode(nullptr, ntree, SH_NODE_BUMP); - bNodeSocket *bump_input_socket = ntree_shader_node_find_input(bump_node, "Height"); - bNodeSocket *bump_output_socket = ntree_shader_node_find_output(bump_node, "Normal"); - BLI_assert(bump_input_socket != nullptr); - BLI_assert(bump_output_socket != nullptr); - /* Connect bump node to where displacement output was originally - * connected to. - */ - nodeAddLink(ntree, displacement_node, displacement_socket, bump_node, bump_input_socket); - - /* Tag as part of the new displacement tree. */ - dot_node->tmp_flag = -2; - geo_node->tmp_flag = -2; - bump_node->tmp_flag = -2; - - BKE_ntree_update_main_tree(G.main, ntree, nullptr); - - /* Connect all free-standing Normal inputs and relink geometry/coordinate nodes. */ - ntree_shader_link_builtin_normal(ntree, bump_node, bump_output_socket); - /* We modified the tree, it needs to be updated now. */ - BKE_ntree_update_main_tree(G.main, ntree, nullptr); +/* Socket already has a link to it. Add weights together. */ +static void ntree_weight_tree_merge_weight(bNodeTree *ntree, + bNode *UNUSED(fromnode), + bNodeSocket *fromsock, + bNode **tonode, + bNodeSocket **tosock) +{ + bNode *addnode = nodeAddStaticNode(NULL, ntree, SH_NODE_MATH); + addnode->custom1 = NODE_MATH_ADD; + addnode->tmp_flag = -2; /* Copy */ + bNodeSocket *addsock_out = ntree_shader_node_output_get(addnode, 0); + bNodeSocket *addsock_in0 = ntree_shader_node_input_get(addnode, 0); + bNodeSocket *addsock_in1 = ntree_shader_node_input_get(addnode, 1); + bNodeLink *oldlink = fromsock->link; + nodeAddLink(ntree, oldlink->fromnode, oldlink->fromsock, addnode, addsock_in0); + nodeAddLink(ntree, *tonode, *tosock, addnode, addsock_in1); + nodeRemLink(ntree, oldlink); + *tonode = addnode; + *tosock = addsock_out; } -static void node_tag_branch_as_derivative(bNode *node, int dx) +static bool ntree_weight_tree_tag_nodes(bNode *fromnode, bNode *tonode, void *userdata) { - if (dx) { - node->branch_tag = 1; + int *node_count = (int *)userdata; + bool to_node_from_weight_tree = ELEM(tonode->type, + SH_NODE_ADD_SHADER, + SH_NODE_MIX_SHADER, + SH_NODE_OUTPUT_WORLD, + SH_NODE_OUTPUT_MATERIAL, + SH_NODE_SHADERTORGB); + if (tonode->tmp_flag == -1 && to_node_from_weight_tree) { + tonode->tmp_flag = *node_count; + *node_count += (tonode->type == SH_NODE_MIX_SHADER) ? 4 : 1; } - else { - node->branch_tag = 2; + if (fromnode->tmp_flag == -1 && ELEM(fromnode->type, SH_NODE_ADD_SHADER, SH_NODE_MIX_SHADER)) { + fromnode->tmp_flag = *node_count; + *node_count += (fromnode->type == SH_NODE_MIX_SHADER) ? 4 : 1; } + return to_node_from_weight_tree; } -static bool ntree_shader_bump_branches(bNode *fromnode, bNode *UNUSED(tonode), void *userdata) +/* Invert evaluation order of the weight tree (add & mix closure nodes) to feed the closure nodes + * with their respective weights. */ +static void ntree_shader_weight_tree_invert(bNodeTree *ntree, bNode *output_node) { - bNodeTree *ntree = (bNodeTree *)userdata; + bNodeLink *displace_link = NULL; + bNodeSocket *displace_output = ntree_shader_node_find_input(output_node, "Displacement"); + if (displace_output && displace_output->link) { + /* Remove any displacement link to avoid tagging it later on. */ + displace_link = displace_output->link; + displace_output->link = NULL; + } + bNodeLink *thickness_link = NULL; + bNodeSocket *thickness_output = ntree_shader_node_find_input(output_node, "Thickness"); + if (thickness_output && thickness_output->link) { + /* Remove any thickness link to avoid tagging it later on. */ + thickness_link = thickness_output->link; + thickness_output->link = NULL; + } + /* Init tmp flag. */ + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + node->tmp_flag = -1; + } + /* Tag nodes from the weight tree. Only tag output node and mix/add shader nodes. */ + output_node->tmp_flag = 0; + int node_count = 1; + nodeChainIterBackwards(ntree, output_node, ntree_weight_tree_tag_nodes, &node_count, 0); + /* Make a mirror copy of the weight tree. */ + Array<bNode *> nodes_copy(node_count); + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + if (node->tmp_flag >= 0) { + int id = node->tmp_flag; - if (fromnode->type == SH_NODE_BUMP) { - bNodeSocket *height_dx_sock, *height_dy_sock, *bump_socket, *bump_dx_socket, *bump_dy_socket; - bNode *bump = fromnode; - bump_socket = ntree_shader_node_find_input(bump, "Height"); - bump_dx_socket = ntree_shader_node_find_input(bump, "Height_dx"); - bump_dy_socket = ntree_shader_node_find_input(bump, "Height_dy"); - if (bump_dx_socket->link) { - /* Avoid reconnecting the same bump twice. */ + switch (node->type) { + case SH_NODE_SHADERTORGB: + case SH_NODE_OUTPUT_WORLD: + case SH_NODE_OUTPUT_MATERIAL: { + /* Start the tree with full weight. */ + nodes_copy[id] = nodeAddStaticNode(NULL, ntree, SH_NODE_VALUE); + nodes_copy[id]->tmp_flag = -2; /* Copy */ + ((bNodeSocketValueFloat *)ntree_shader_node_output_get(nodes_copy[id], 0)->default_value) + ->value = 1.0f; + break; + } + case SH_NODE_ADD_SHADER: { + /* Simple passthrough node. Each original inputs will get the same weight. */ + /* TODO(fclem) Better use some kind of reroute node? */ + nodes_copy[id] = nodeAddStaticNode(NULL, ntree, SH_NODE_MATH); + nodes_copy[id]->custom1 = NODE_MATH_ADD; + nodes_copy[id]->tmp_flag = -2; /* Copy */ + ((bNodeSocketValueFloat *)ntree_shader_node_input_get(nodes_copy[id], 0)->default_value) + ->value = 0.0f; + break; + } + case SH_NODE_MIX_SHADER: { + /* We need multiple nodes to emulate the mix node in reverse. */ + bNode *fromnode, *tonode; + bNodeSocket *fromsock, *tosock; + int id_start = id; + /* output = (factor * input_weight) */ + nodes_copy[id] = nodeAddStaticNode(NULL, ntree, SH_NODE_MATH); + nodes_copy[id]->custom1 = NODE_MATH_MULTIPLY; + nodes_copy[id]->tmp_flag = -2; /* Copy */ + id++; + /* output = ((1.0 - factor) * input_weight) <=> (input_weight - factor * input_weight) */ + nodes_copy[id] = nodeAddStaticNode(NULL, ntree, SH_NODE_MATH); + nodes_copy[id]->custom1 = NODE_MATH_SUBTRACT; + nodes_copy[id]->tmp_flag = -2; /* Copy */ + id++; + /* Node sanitizes the input mix factor by clamping it. */ + nodes_copy[id] = nodeAddStaticNode(NULL, ntree, SH_NODE_MATH); + nodes_copy[id]->custom1 = NODE_MATH_ADD; + nodes_copy[id]->custom2 = SHD_MATH_CLAMP; + nodes_copy[id]->tmp_flag = -2; /* Copy */ + ((bNodeSocketValueFloat *)ntree_shader_node_input_get(nodes_copy[id], 0)->default_value) + ->value = 0.0f; + /* Copy default value if no link present. */ + bNodeSocket *fac_sock = ntree_shader_node_find_input(node, "Fac"); + if (!fac_sock->link) { + float default_value = ((bNodeSocketValueFloat *)fac_sock->default_value)->value; + bNodeSocket *dst_sock = ntree_shader_node_input_get(nodes_copy[id], 1); + ((bNodeSocketValueFloat *)dst_sock->default_value)->value = default_value; + } + id++; + /* Reroute the weight input to the 3 processing nodes. Simplify linking later-on. */ + /* TODO(fclem) Better use some kind of reroute node? */ + nodes_copy[id] = nodeAddStaticNode(NULL, ntree, SH_NODE_MATH); + nodes_copy[id]->custom1 = NODE_MATH_ADD; + nodes_copy[id]->tmp_flag = -2; /* Copy */ + ((bNodeSocketValueFloat *)ntree_shader_node_input_get(nodes_copy[id], 0)->default_value) + ->value = 0.0f; + id++; + /* Link between nodes for the substraction. */ + fromnode = nodes_copy[id_start]; + tonode = nodes_copy[id_start + 1]; + fromsock = ntree_shader_node_output_get(fromnode, 0); + tosock = ntree_shader_node_input_get(tonode, 1); + nodeAddLink(ntree, fromnode, fromsock, tonode, tosock); + /* Link mix input to first node. */ + fromnode = nodes_copy[id_start + 2]; + tonode = nodes_copy[id_start]; + fromsock = ntree_shader_node_output_get(fromnode, 0); + tosock = ntree_shader_node_input_get(tonode, 1); + nodeAddLink(ntree, fromnode, fromsock, tonode, tosock); + /* Link weight input to both multiply nodes. */ + fromnode = nodes_copy[id_start + 3]; + fromsock = ntree_shader_node_output_get(fromnode, 0); + tonode = nodes_copy[id_start]; + tosock = ntree_shader_node_input_get(tonode, 0); + nodeAddLink(ntree, fromnode, fromsock, tonode, tosock); + tonode = nodes_copy[id_start + 1]; + tosock = ntree_shader_node_input_get(tonode, 0); + nodeAddLink(ntree, fromnode, fromsock, tonode, tosock); + break; + } + default: + BLI_assert(0); + break; + } } - else if (bump_socket && bump_socket->link) { - bNodeLink *link = bump_socket->link; - bNode *height = link->fromnode; - bNode *height_dx = ntree_shader_copy_branch(ntree, height, node_tag_branch_as_derivative, 1); - bNode *height_dy = ntree_shader_copy_branch(ntree, height, node_tag_branch_as_derivative, 0); - height_dx_sock = ntree_shader_node_find_output(height_dx, link->fromsock->identifier); - height_dy_sock = ntree_shader_node_find_output(height_dy, link->fromsock->identifier); - nodeAddLink(ntree, height_dx, height_dx_sock, bump, bump_dx_socket); - nodeAddLink(ntree, height_dy, height_dy_sock, bump, bump_dy_socket); - /* We could end iter here, but other bump node could be plugged into other input sockets. */ + } + /* Recreate links between copied nodes. */ + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + if (node->tmp_flag >= 0) { + /* Naming can be confusing here. We use original nodelink name for from/to prefix. + * The final link is in reversed order. */ + int socket_index; + LISTBASE_FOREACH_INDEX (bNodeSocket *, sock, &node->inputs, socket_index) { + bNodeSocket *tosock; + bNode *tonode; + + switch (node->type) { + case SH_NODE_SHADERTORGB: + case SH_NODE_OUTPUT_WORLD: + case SH_NODE_OUTPUT_MATERIAL: + case SH_NODE_ADD_SHADER: { + tonode = nodes_copy[node->tmp_flag]; + tosock = ntree_shader_node_output_get(tonode, 0); + break; + } + case SH_NODE_MIX_SHADER: { + if (socket_index == 0) { + /* Mix Factor. */ + tonode = nodes_copy[node->tmp_flag + 2]; + tosock = ntree_shader_node_input_get(tonode, 1); + } + else if (socket_index == 1) { + /* Shader 1. */ + tonode = nodes_copy[node->tmp_flag + 1]; + tosock = ntree_shader_node_output_get(tonode, 0); + } + else { + /* Shader 2. */ + tonode = nodes_copy[node->tmp_flag]; + tosock = ntree_shader_node_output_get(tonode, 0); + } + break; + } + default: + BLI_assert(0); + break; + } + + if (sock->link) { + bNodeSocket *fromsock; + bNode *fromnode = sock->link->fromnode; + + switch (fromnode->type) { + case SH_NODE_ADD_SHADER: { + fromnode = nodes_copy[fromnode->tmp_flag]; + fromsock = ntree_shader_node_input_get(fromnode, 1); + if (fromsock->link) { + ntree_weight_tree_merge_weight(ntree, fromnode, fromsock, &tonode, &tosock); + } + break; + } + case SH_NODE_MIX_SHADER: { + fromnode = nodes_copy[fromnode->tmp_flag + 3]; + fromsock = ntree_shader_node_input_get(fromnode, 1); + if (fromsock->link) { + ntree_weight_tree_merge_weight(ntree, fromnode, fromsock, &tonode, &tosock); + } + break; + } + case SH_NODE_BACKGROUND: + case SH_NODE_BSDF_ANISOTROPIC: + case SH_NODE_BSDF_DIFFUSE: + case SH_NODE_BSDF_GLASS: + case SH_NODE_BSDF_GLOSSY: + case SH_NODE_BSDF_HAIR_PRINCIPLED: + case SH_NODE_BSDF_HAIR: + case SH_NODE_BSDF_PRINCIPLED: + case SH_NODE_BSDF_REFRACTION: + case SH_NODE_BSDF_TOON: + case SH_NODE_BSDF_TRANSLUCENT: + case SH_NODE_BSDF_TRANSPARENT: + case SH_NODE_BSDF_VELVET: + case SH_NODE_EEVEE_SPECULAR: + case SH_NODE_EMISSION: + case SH_NODE_HOLDOUT: + case SH_NODE_SUBSURFACE_SCATTERING: + case SH_NODE_VOLUME_ABSORPTION: + case SH_NODE_VOLUME_PRINCIPLED: + case SH_NODE_VOLUME_SCATTER: + fromsock = ntree_shader_node_find_input(fromnode, "Weight"); + if (fromsock->link) { + ntree_weight_tree_merge_weight(ntree, fromnode, fromsock, &tonode, &tosock); + } + break; + default: + fromsock = sock->link->fromsock; + break; + } + + /* Manually add the link to the socket to avoid calling + * BKE_ntree_update_main_tree(G.main, oop, nullptr. */ + fromsock->link = nodeAddLink(ntree, fromnode, fromsock, tonode, tosock); + BLI_assert(fromsock->link); + } + } } } - return true; + /* Restore displacement & thickness link. */ + if (displace_link) { + nodeAddLink( + ntree, displace_link->fromnode, displace_link->fromsock, output_node, displace_output); + } + if (thickness_link) { + nodeAddLink( + ntree, thickness_link->fromnode, thickness_link->fromsock, output_node, thickness_output); + } + BKE_ntree_update_main_tree(G.main, ntree, nullptr); } -static bool ntree_tag_bsdf_cb(bNode *fromnode, bNode *UNUSED(tonode), void *userdata) +static bool closure_node_filter(const bNode *node) { - switch (fromnode->type) { + switch (node->type) { + case SH_NODE_ADD_SHADER: + case SH_NODE_MIX_SHADER: + case SH_NODE_BACKGROUND: case SH_NODE_BSDF_ANISOTROPIC: - case SH_NODE_EEVEE_SPECULAR: - case SH_NODE_BSDF_GLOSSY: + case SH_NODE_BSDF_DIFFUSE: case SH_NODE_BSDF_GLASS: - fromnode->ssr_id = ((nTreeTags *)userdata)->ssr_id; - ((nTreeTags *)userdata)->ssr_id += 1; - break; - case SH_NODE_SUBSURFACE_SCATTERING: - fromnode->sss_id = ((nTreeTags *)userdata)->sss_id; - ((nTreeTags *)userdata)->sss_id += 1; - break; + case SH_NODE_BSDF_GLOSSY: + case SH_NODE_BSDF_HAIR_PRINCIPLED: + case SH_NODE_BSDF_HAIR: case SH_NODE_BSDF_PRINCIPLED: - fromnode->ssr_id = ((nTreeTags *)userdata)->ssr_id; - fromnode->sss_id = ((nTreeTags *)userdata)->sss_id; - ((nTreeTags *)userdata)->sss_id += 1; - ((nTreeTags *)userdata)->ssr_id += 1; - break; + case SH_NODE_BSDF_REFRACTION: + case SH_NODE_BSDF_TOON: + case SH_NODE_BSDF_TRANSLUCENT: + case SH_NODE_BSDF_TRANSPARENT: + case SH_NODE_BSDF_VELVET: + case SH_NODE_EEVEE_SPECULAR: + case SH_NODE_EMISSION: + case SH_NODE_HOLDOUT: + case SH_NODE_SUBSURFACE_SCATTERING: + case SH_NODE_VOLUME_ABSORPTION: + case SH_NODE_VOLUME_PRINCIPLED: + case SH_NODE_VOLUME_SCATTER: + return true; default: - /* We could return false here but since we - * allow the use of Closure as RGBA, we can have - * BSDF nodes linked to other BSDF nodes. */ - break; + return false; } +} +static bool shader_to_rgba_node_gather(bNode *UNUSED(fromnode), bNode *tonode, void *userdata) +{ + Vector<bNode *> &shader_to_rgba_nodes = *(Vector<bNode *> *)userdata; + if (tonode->tmp_flag == -1 && tonode->type == SH_NODE_SHADERTORGB) { + tonode->tmp_flag = 0; + shader_to_rgba_nodes.append(tonode); + } return true; } -/* EEVEE: Scan the ntree to set the Screen Space Reflection - * layer id of every specular node AND the Subsurface Scattering id of every SSS node. - */ -void ntree_shader_tag_nodes(bNodeTree *ntree, bNode *output_node, nTreeTags *tags) +/* Shader to rgba needs their associated closure duplicated and the weight tree generated for. */ +static void ntree_shader_shader_to_rgba_branch(bNodeTree *ntree, bNode *output_node) { - if (output_node == nullptr) { - return; + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + node->tmp_flag = -1; } - /* Make sure sockets links pointers are correct. */ - BKE_ntree_update_main_tree(G.main, ntree, nullptr); + /* First gather the shader_to_rgba nodes linked to the ouput. This is separate to avoid + * conflicting usage of the node->tmp_flag. */ + Vector<bNode *> shader_to_rgba_nodes; + nodeChainIterBackwards(ntree, output_node, shader_to_rgba_node_gather, &shader_to_rgba_nodes, 0); + + for (bNode *shader_to_rgba : shader_to_rgba_nodes) { + bNodeSocket *closure_input = ntree_shader_node_input_get(shader_to_rgba, 0); + if (closure_input->link == nullptr) { + continue; + } + bNode *start_node = closure_input->link->fromnode; + bNode *start_node_copy = ntree_shader_copy_branch( + ntree, start_node, closure_node_filter, nullptr, 0); + /* Replace node copy link. This assumes that every node possibly connected to the closure input + * has only one ouput. */ + bNodeSocket *closure_output = ntree_shader_node_output_get(start_node_copy, 0); + nodeRemLink(ntree, closure_input->link); + nodeAddLink(ntree, start_node_copy, closure_output, shader_to_rgba, closure_input); + BKE_ntree_update_main_tree(G.main, ntree, nullptr); + + ntree_shader_weight_tree_invert(ntree, shader_to_rgba); + } +} - nodeChainIterBackwards(ntree, output_node, ntree_tag_bsdf_cb, tags, 0); +static bool ntree_branch_node_tag(bNode *fromnode, bNode *tonode, void *UNUSED(userdata)) +{ + fromnode->tmp_flag = 1; + tonode->tmp_flag = 1; + return true; } -void ntreeGPUMaterialNodes(bNodeTree *localtree, - GPUMaterial *mat, - bool *has_surface_output, - bool *has_volume_output) +/* Avoid adding more node execution when multiple outputs are present. */ +/* NOTE(@fclem): This is also a workaround for the old EEVEE SSS implementation where only the + * first executed SSS node gets a SSS profile. */ +static void ntree_shader_pruned_unused(bNodeTree *ntree, bNode *output_node) { - bNodeTreeExec *exec; + bool changed = false; - bNode *output = ntreeShaderOutputNode(localtree, SHD_OUTPUT_EEVEE); + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + node->tmp_flag = 0; + } - ntree_shader_groups_remove_muted_links(localtree); - ntree_shader_groups_expand_inputs(localtree); + /* Avoid deleting the output node if it is the only node in the tree. */ + output_node->tmp_flag = 1; - ntree_shader_groups_flatten(localtree); + nodeChainIterBackwards(ntree, output_node, ntree_branch_node_tag, nullptr, 0); - if (output == nullptr) { - /* Search again, now including flattened nodes. */ - output = ntreeShaderOutputNode(localtree, SHD_OUTPUT_EEVEE); + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + if (node->type == SH_NODE_OUTPUT_AOV) { + nodeChainIterBackwards(ntree, node, ntree_branch_node_tag, nullptr, 0); + } } - /* Perform all needed modifications on the tree in order to support - * displacement/bump mapping. - */ - ntree_shader_relink_displacement(localtree, output); - - /* Duplicate bump height branches for manual derivatives. - */ - nodeChainIterBackwards(localtree, output, ntree_shader_bump_branches, localtree, 0); - LISTBASE_FOREACH (bNode *, node, &localtree->nodes) { - if (node->type == SH_NODE_OUTPUT_AOV) { - nodeChainIterBackwards(localtree, node, ntree_shader_bump_branches, localtree, 0); - nTreeTags tags = {}; - tags.ssr_id = 1.0; - tags.sss_id = 1.0; - ntree_shader_tag_nodes(localtree, node, &tags); + LISTBASE_FOREACH_MUTABLE (bNode *, node, &ntree->nodes) { + if (node->tmp_flag == 0) { + ntreeFreeLocalNode(ntree, node); + changed = true; } } - /* TODO(fclem): consider moving this to the gpu shader tree evaluation. */ - nTreeTags tags = {}; - tags.ssr_id = 1.0; - tags.sss_id = 1.0; - ntree_shader_tag_nodes(localtree, output, &tags); + if (changed) { + BKE_ntree_update_main_tree(G.main, ntree, nullptr); + } +} + +void ntreeGPUMaterialNodes(bNodeTree *localtree, GPUMaterial *mat) +{ + bNodeTreeExec *exec; + + ntree_shader_groups_remove_muted_links(localtree); + ntree_shader_groups_expand_inputs(localtree); + ntree_shader_groups_flatten(localtree); + + bNode *output = ntreeShaderOutputNode(localtree, SHD_OUTPUT_EEVEE); + + /* Tree is valid if it contains no undefined implicit socket type cast. */ + bool valid_tree = ntree_shader_implicit_closure_cast(localtree); + + if (valid_tree && output != NULL) { + ntree_shader_pruned_unused(localtree, output); + ntree_shader_shader_to_rgba_branch(localtree, output); + ntree_shader_weight_tree_invert(localtree, output); + } exec = ntreeShaderBeginExecTree(localtree); ntreeExecGPUNodes(exec, mat, output); @@ -918,23 +1049,6 @@ void ntreeGPUMaterialNodes(bNodeTree *localtree, } } ntreeShaderEndExecTree(exec); - - /* EEVEE: Find which material domain was used (volume, surface ...). */ - *has_surface_output = false; - *has_volume_output = false; - - if (output != nullptr) { - bNodeSocket *surface_sock = ntree_shader_node_find_input(output, "Surface"); - bNodeSocket *volume_sock = ntree_shader_node_find_input(output, "Volume"); - - if (surface_sock != nullptr) { - *has_surface_output = (nodeCountSocketLinks(localtree, surface_sock) > 0); - } - - if (volume_sock != nullptr) { - *has_volume_output = (nodeCountSocketLinks(localtree, volume_sock) > 0); - } - } } bNodeTreeExec *ntreeShaderBeginExecTree_internal(bNodeExecContext *context, diff --git a/source/blender/nodes/shader/node_shader_util.cc b/source/blender/nodes/shader/node_shader_util.cc index 728e2760f9a..c47b69e6b69 100644 --- a/source/blender/nodes/shader/node_shader_util.cc +++ b/source/blender/nodes/shader/node_shader_util.cc @@ -284,26 +284,15 @@ void ntreeExecGPUNodes(bNodeTreeExec *exec, GPUMaterial *mat, bNode *output_node } } -void node_shader_gpu_bump_tex_coord(GPUMaterial *mat, bNode *node, GPUNodeLink **link) +void node_shader_gpu_bump_tex_coord(GPUMaterial *mat, bNode *UNUSED(node), GPUNodeLink **link) { - if (node->branch_tag == 1) { - /* Add one time the value for derivative to the input vector. */ - GPU_link(mat, "dfdx_v3", *link, link); - } - else if (node->branch_tag == 2) { - /* Add one time the value for derivative to the input vector. */ - GPU_link(mat, "dfdy_v3", *link, link); - } - else { - /* nothing to do, reference center value. */ - } + GPU_link(mat, "differentiate_texco", *link, link); } void node_shader_gpu_default_tex_coord(GPUMaterial *mat, bNode *node, GPUNodeLink **link) { if (!*link) { *link = GPU_attribute(mat, CD_ORCO, ""); - GPU_link(mat, "generated_texco", GPU_builtin(GPU_VIEW_POSITION), *link, link); node_shader_gpu_bump_tex_coord(mat, node, link); } } diff --git a/source/blender/nodes/shader/nodes/node_shader_ambient_occlusion.cc b/source/blender/nodes/shader/nodes/node_shader_ambient_occlusion.cc index a24b4379e69..1e8df2e985d 100644 --- a/source/blender/nodes/shader/nodes/node_shader_ambient_occlusion.cc +++ b/source/blender/nodes/shader/nodes/node_shader_ambient_occlusion.cc @@ -36,7 +36,7 @@ static int node_shader_gpu_ambient_occlusion(GPUMaterial *mat, GPU_link(mat, "world_normals_get", &in[2].link); } - GPU_material_flag_set(mat, GPU_MATFLAG_DIFFUSE); + GPU_material_flag_set(mat, GPU_MATFLAG_AO); float inverted = (node->custom2 & SHD_AO_INSIDE) ? 1.0f : 0.0f; float f_samples = divide_ceil_u(node->custom1, 4); diff --git a/source/blender/nodes/shader/nodes/node_shader_background.cc b/source/blender/nodes/shader/nodes/node_shader_background.cc index f5d3b6811f1..ea5c1f541ea 100644 --- a/source/blender/nodes/shader/nodes/node_shader_background.cc +++ b/source/blender/nodes/shader/nodes/node_shader_background.cc @@ -9,6 +9,7 @@ static void node_declare(NodeDeclarationBuilder &b) { b.add_input<decl::Color>(N_("Color")).default_value({0.8f, 0.8f, 0.8f, 1.0f}); b.add_input<decl::Float>(N_("Strength")).default_value(1.0f).min(0.0f).max(1000000.0f); + b.add_input<decl::Float>(N_("Weight")).unavailable(); b.add_output<decl::Shader>(N_("Background")); } diff --git a/source/blender/nodes/shader/nodes/node_shader_bevel.cc b/source/blender/nodes/shader/nodes/node_shader_bevel.cc index 8bce38baf73..4ae60af9974 100644 --- a/source/blender/nodes/shader/nodes/node_shader_bevel.cc +++ b/source/blender/nodes/shader/nodes/node_shader_bevel.cc @@ -32,11 +32,7 @@ static int gpu_shader_bevel(GPUMaterial *mat, GPUNodeStack *out) { if (!in[1].link) { - GPU_link(mat, - "direction_transform_m4v3", - GPU_builtin(GPU_VIEW_NORMAL), - GPU_builtin(GPU_INVERSE_VIEW_MATRIX), - &in[1].link); + GPU_link(mat, "world_normals_get", &in[1].link); } return GPU_stack_link(mat, node, "node_bevel", in, out); diff --git a/source/blender/nodes/shader/nodes/node_shader_bsdf_anisotropic.cc b/source/blender/nodes/shader/nodes/node_shader_bsdf_anisotropic.cc index 13b4a034a51..761a833f377 100644 --- a/source/blender/nodes/shader/nodes/node_shader_bsdf_anisotropic.cc +++ b/source/blender/nodes/shader/nodes/node_shader_bsdf_anisotropic.cc @@ -24,6 +24,7 @@ static void node_declare(NodeDeclarationBuilder &b) .subtype(PROP_FACTOR); b.add_input<decl::Vector>(N_("Normal")).hide_value(); b.add_input<decl::Vector>(N_("Tangent")).hide_value(); + b.add_input<decl::Float>(N_("Weight")).unavailable(); b.add_output<decl::Shader>(N_("BSDF")); } @@ -51,13 +52,8 @@ static int node_shader_gpu_bsdf_anisotropic(GPUMaterial *mat, float use_multi_scatter = (node->custom1 == SHD_GLOSSY_MULTI_GGX) ? 1.0f : 0.0f; - return GPU_stack_link(mat, - node, - "node_bsdf_anisotropic", - in, - out, - GPU_constant(&use_multi_scatter), - GPU_constant(&node->ssr_id)); + return GPU_stack_link( + mat, node, "node_bsdf_anisotropic", in, out, GPU_constant(&use_multi_scatter)); } } // namespace blender::nodes::node_shader_bsdf_anisotropic_cc diff --git a/source/blender/nodes/shader/nodes/node_shader_bsdf_diffuse.cc b/source/blender/nodes/shader/nodes/node_shader_bsdf_diffuse.cc index 3f025b2ba39..5975e04450e 100644 --- a/source/blender/nodes/shader/nodes/node_shader_bsdf_diffuse.cc +++ b/source/blender/nodes/shader/nodes/node_shader_bsdf_diffuse.cc @@ -14,6 +14,7 @@ static void node_declare(NodeDeclarationBuilder &b) .max(1.0f) .subtype(PROP_FACTOR); b.add_input<decl::Vector>(N_("Normal")).hide_value(); + b.add_input<decl::Float>(N_("Weight")).unavailable(); b.add_output<decl::Shader>(N_("BSDF")); } diff --git a/source/blender/nodes/shader/nodes/node_shader_bsdf_glass.cc b/source/blender/nodes/shader/nodes/node_shader_bsdf_glass.cc index a1e5b705438..95869f13b7e 100644 --- a/source/blender/nodes/shader/nodes/node_shader_bsdf_glass.cc +++ b/source/blender/nodes/shader/nodes/node_shader_bsdf_glass.cc @@ -15,6 +15,7 @@ static void node_declare(NodeDeclarationBuilder &b) .subtype(PROP_FACTOR); b.add_input<decl::Float>(N_("IOR")).default_value(1.45f).min(0.0f).max(1000.0f); b.add_input<decl::Vector>(N_("Normal")).hide_value(); + b.add_input<decl::Float>(N_("Weight")).unavailable(); b.add_output<decl::Shader>(N_("BSDF")); } @@ -37,17 +38,11 @@ static int node_shader_gpu_bsdf_glass(GPUMaterial *mat, GPU_link(mat, "set_value_zero", &in[1].link); } - GPU_material_flag_set(mat, (eGPUMatFlag)(GPU_MATFLAG_GLOSSY | GPU_MATFLAG_REFRACT)); + GPU_material_flag_set(mat, GPU_MATFLAG_GLOSSY | GPU_MATFLAG_REFRACT); float use_multi_scatter = (node->custom1 == SHD_GLOSSY_MULTI_GGX) ? 1.0f : 0.0f; - return GPU_stack_link(mat, - node, - "node_bsdf_glass", - in, - out, - GPU_constant(&use_multi_scatter), - GPU_constant(&node->ssr_id)); + return GPU_stack_link(mat, node, "node_bsdf_glass", in, out, GPU_constant(&use_multi_scatter)); } } // namespace blender::nodes::node_shader_bsdf_glass_cc diff --git a/source/blender/nodes/shader/nodes/node_shader_bsdf_glossy.cc b/source/blender/nodes/shader/nodes/node_shader_bsdf_glossy.cc index 9f0e172a5fe..07062a9730e 100644 --- a/source/blender/nodes/shader/nodes/node_shader_bsdf_glossy.cc +++ b/source/blender/nodes/shader/nodes/node_shader_bsdf_glossy.cc @@ -14,6 +14,7 @@ static void node_declare(NodeDeclarationBuilder &b) .max(1.0f) .subtype(PROP_FACTOR); b.add_input<decl::Vector>(N_("Normal")).hide_value(); + b.add_input<decl::Float>(N_("Weight")).unavailable(); b.add_output<decl::Shader>(N_("BSDF")); } @@ -40,13 +41,7 @@ static int node_shader_gpu_bsdf_glossy(GPUMaterial *mat, float use_multi_scatter = (node->custom1 == SHD_GLOSSY_MULTI_GGX) ? 1.0f : 0.0f; - return GPU_stack_link(mat, - node, - "node_bsdf_glossy", - in, - out, - GPU_constant(&use_multi_scatter), - GPU_constant(&node->ssr_id)); + return GPU_stack_link(mat, node, "node_bsdf_glossy", in, out, GPU_constant(&use_multi_scatter)); } } // namespace blender::nodes::node_shader_bsdf_glossy_cc diff --git a/source/blender/nodes/shader/nodes/node_shader_bsdf_hair.cc b/source/blender/nodes/shader/nodes/node_shader_bsdf_hair.cc index ef87f380bb9..1a1ba13e886 100644 --- a/source/blender/nodes/shader/nodes/node_shader_bsdf_hair.cc +++ b/source/blender/nodes/shader/nodes/node_shader_bsdf_hair.cc @@ -27,6 +27,7 @@ static void node_declare(NodeDeclarationBuilder &b) .max(1.0f) .subtype(PROP_FACTOR); b.add_input<decl::Vector>(N_("Tangent")).hide_value(); + b.add_input<decl::Float>(N_("Weight")).unavailable(); b.add_output<decl::Shader>(N_("BSDF")); } diff --git a/source/blender/nodes/shader/nodes/node_shader_bsdf_hair_principled.cc b/source/blender/nodes/shader/nodes/node_shader_bsdf_hair_principled.cc index edae03bbd31..6495dcfffba 100644 --- a/source/blender/nodes/shader/nodes/node_shader_bsdf_hair_principled.cc +++ b/source/blender/nodes/shader/nodes/node_shader_bsdf_hair_principled.cc @@ -60,6 +60,7 @@ static void node_declare(NodeDeclarationBuilder &b) .max(1.0f) .subtype(PROP_FACTOR); b.add_input<decl::Float>(N_("Random")).hide_value(); + b.add_input<decl::Float>(N_("Weight")).unavailable(); b.add_output<decl::Shader>(N_("BSDF")); } @@ -108,6 +109,15 @@ static void node_shader_update_hair_principled(bNodeTree *ntree, bNode *node) } } +static int node_shader_gpu_hair_principled(GPUMaterial *mat, + bNode *node, + bNodeExecData *UNUSED(execdata), + GPUNodeStack *in, + GPUNodeStack *out) +{ + return GPU_stack_link(mat, node, "node_bsdf_hair_principled", in, out); +} + } // namespace blender::nodes::node_shader_bsdf_hair_principled_cc /* node type definition */ @@ -124,6 +134,7 @@ void register_node_type_sh_bsdf_hair_principled() node_type_size_preset(&ntype, NODE_SIZE_LARGE); node_type_init(&ntype, file_ns::node_shader_init_hair_principled); node_type_update(&ntype, file_ns::node_shader_update_hair_principled); + node_type_gpu(&ntype, file_ns::node_shader_gpu_hair_principled); nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_bsdf_principled.cc b/source/blender/nodes/shader/nodes/node_shader_bsdf_principled.cc index ba98a797f2a..a63c7aede04 100644 --- a/source/blender/nodes/shader/nodes/node_shader_bsdf_principled.cc +++ b/source/blender/nodes/shader/nodes/node_shader_bsdf_principled.cc @@ -103,6 +103,7 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_input<decl::Vector>(N_("Normal")).hide_value(); b.add_input<decl::Vector>(N_("Clearcoat Normal")).hide_value(); b.add_input<decl::Vector>(N_("Tangent")).hide_value(); + b.add_input<decl::Float>(N_("Weight")).unavailable(); b.add_output<decl::Shader>(N_("BSDF")); } @@ -128,8 +129,6 @@ static int node_shader_gpu_bsdf_principled(GPUMaterial *mat, GPUNodeStack *in, GPUNodeStack *out) { - GPUNodeLink *sss_scale; - /* Normals */ if (!in[22].link) { GPU_link(mat, "world_normals_get", &in[22].link); @@ -145,36 +144,17 @@ static int node_shader_gpu_bsdf_principled(GPUMaterial *mat, if (!in[24].link) { GPUNodeLink *orco = GPU_attribute(CD_ORCO, ""); GPU_link(mat, "tangent_orco_z", orco, &in[24].link); - GPU_link(mat, - "node_tangent", - GPU_builtin(GPU_WORLD_NORMAL), - in[24].link, - GPU_builtin(GPU_OBJECT_MATRIX), - &in[24].link); + GPU_link(mat, "node_tangent", in[24].link, &in[24].link); } #endif 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_subsurf = socket_not_zero(1) && use_diffuse; bool use_refract = socket_not_one(6) && socket_not_zero(17); + bool use_transparency = socket_not_one(21); bool use_clear = socket_not_zero(14); - /* SSS Profile */ - if (use_subsurf) { - bNodeSocket *socket = (bNodeSocket *)BLI_findlink(&node->original->inputs, 2); - bNodeSocketValueRGBA *socket_data = (bNodeSocketValueRGBA *)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]); - } - - if (in[2].link) { - sss_scale = in[2].link; - } - else { - GPU_link(mat, "set_rgb_one", &sss_scale); - } - - uint flag = GPU_MATFLAG_GLOSSY; + eGPUMaterialFlag flag = GPU_MATFLAG_GLOSSY; if (use_diffuse) { flag |= GPU_MATFLAG_DIFFUSE; } @@ -182,28 +162,37 @@ static int node_shader_gpu_bsdf_principled(GPUMaterial *mat, flag |= GPU_MATFLAG_REFRACT; } if (use_subsurf) { - flag |= GPU_MATFLAG_SSS; + flag |= GPU_MATFLAG_SUBSURFACE; + } + if (use_transparency) { + flag |= GPU_MATFLAG_TRANSPARENT; + } + + if (use_subsurf) { + bNodeSocket *socket = (bNodeSocket *)BLI_findlink(&node->original->inputs, 2); + bNodeSocketValueRGBA *socket_data = (bNodeSocketValueRGBA *)socket->default_value; + /* For some reason it seems that the socket value is in ARGB format. */ + use_subsurf = GPU_material_sss_profile_create(mat, &socket_data->value[1]); } - float f_use_diffuse = use_diffuse ? 1.0f : 0.0f; - float f_use_clearcoat = use_clear ? 1.0f : 0.0f; - float f_use_refraction = use_refract ? 1.0f : 0.0f; float use_multi_scatter = (node->custom1 == SHD_GLOSSY_MULTI_GGX) ? 1.0f : 0.0f; + float use_sss = (use_subsurf) ? 1.0f : 0.0f; + float use_diffuse_f = (use_diffuse) ? 1.0f : 0.0f; + float use_clear_f = (use_clear) ? 1.0f : 0.0f; + float use_refract_f = (use_refract) ? 1.0f : 0.0f; - GPU_material_flag_set(mat, (eGPUMatFlag)flag); + GPU_material_flag_set(mat, flag); return GPU_stack_link(mat, node, "node_bsdf_principled", in, out, - GPU_constant(&f_use_diffuse), - GPU_constant(&f_use_clearcoat), - GPU_constant(&f_use_refraction), + GPU_constant(&use_diffuse_f), + GPU_constant(&use_clear_f), + GPU_constant(&use_refract_f), GPU_constant(&use_multi_scatter), - GPU_constant(&node->ssr_id), - GPU_constant(&node->sss_id), - sss_scale); + GPU_uniform(&use_sss)); } static void node_shader_update_principled(bNodeTree *ntree, bNode *node) diff --git a/source/blender/nodes/shader/nodes/node_shader_bsdf_refraction.cc b/source/blender/nodes/shader/nodes/node_shader_bsdf_refraction.cc index 73f58477b67..e814eb223e5 100644 --- a/source/blender/nodes/shader/nodes/node_shader_bsdf_refraction.cc +++ b/source/blender/nodes/shader/nodes/node_shader_bsdf_refraction.cc @@ -15,6 +15,7 @@ static void node_declare(NodeDeclarationBuilder &b) .subtype(PROP_FACTOR); b.add_input<decl::Float>(N_("IOR")).default_value(1.45f).min(0.0f).max(1000.0f); b.add_input<decl::Vector>(N_("Normal")).hide_value(); + b.add_input<decl::Float>(N_("Weight")).unavailable(); b.add_output<decl::Shader>(N_("BSDF")); } diff --git a/source/blender/nodes/shader/nodes/node_shader_bsdf_toon.cc b/source/blender/nodes/shader/nodes/node_shader_bsdf_toon.cc index 9cb7c4c3bbb..c1c092e89c7 100644 --- a/source/blender/nodes/shader/nodes/node_shader_bsdf_toon.cc +++ b/source/blender/nodes/shader/nodes/node_shader_bsdf_toon.cc @@ -22,6 +22,7 @@ static void node_declare(NodeDeclarationBuilder &b) .max(1.0f) .subtype(PROP_FACTOR); b.add_input<decl::Vector>(N_("Normal")).hide_value(); + b.add_input<decl::Float>(N_("Weight")).unavailable(); b.add_output<decl::Shader>(N_("BSDF")); } diff --git a/source/blender/nodes/shader/nodes/node_shader_bsdf_translucent.cc b/source/blender/nodes/shader/nodes/node_shader_bsdf_translucent.cc index 19e61d8140d..fd0dd9f93de 100644 --- a/source/blender/nodes/shader/nodes/node_shader_bsdf_translucent.cc +++ b/source/blender/nodes/shader/nodes/node_shader_bsdf_translucent.cc @@ -9,6 +9,7 @@ static void node_declare(NodeDeclarationBuilder &b) { b.add_input<decl::Color>(N_("Color")).default_value({0.8f, 0.8f, 0.8f, 1.0f}); b.add_input<decl::Vector>(N_("Normal")).hide_value(); + b.add_input<decl::Float>(N_("Weight")).unavailable(); b.add_output<decl::Shader>(N_("BSDF")); } diff --git a/source/blender/nodes/shader/nodes/node_shader_bsdf_transparent.cc b/source/blender/nodes/shader/nodes/node_shader_bsdf_transparent.cc index 57fd7e3ef3b..291b3fdb2be 100644 --- a/source/blender/nodes/shader/nodes/node_shader_bsdf_transparent.cc +++ b/source/blender/nodes/shader/nodes/node_shader_bsdf_transparent.cc @@ -8,6 +8,7 @@ namespace blender::nodes::node_shader_bsdf_transparent_cc { static void node_declare(NodeDeclarationBuilder &b) { b.add_input<decl::Color>(N_("Color")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input<decl::Float>(N_("Weight")).unavailable(); b.add_output<decl::Shader>(N_("BSDF")); } @@ -17,6 +18,9 @@ static int node_shader_gpu_bsdf_transparent(GPUMaterial *mat, GPUNodeStack *in, GPUNodeStack *out) { + if (in[0].link || !is_zero_v3(in[0].vec)) { + GPU_material_flag_set(mat, GPU_MATFLAG_TRANSPARENT); + } return GPU_stack_link(mat, node, "node_bsdf_transparent", in, out); } diff --git a/source/blender/nodes/shader/nodes/node_shader_bsdf_velvet.cc b/source/blender/nodes/shader/nodes/node_shader_bsdf_velvet.cc index 75bdb5b6f54..c86d70aecbf 100644 --- a/source/blender/nodes/shader/nodes/node_shader_bsdf_velvet.cc +++ b/source/blender/nodes/shader/nodes/node_shader_bsdf_velvet.cc @@ -14,6 +14,7 @@ static void node_declare(NodeDeclarationBuilder &b) .max(1.0f) .subtype(PROP_FACTOR); b.add_input<decl::Vector>(N_("Normal")).hide_value(); + b.add_input<decl::Float>(N_("Weight")).unavailable(); b.add_output<decl::Shader>(N_("BSDF")); } diff --git a/source/blender/nodes/shader/nodes/node_shader_bump.cc b/source/blender/nodes/shader/nodes/node_shader_bump.cc index 5dd27e64ce3..ad2c56d96b5 100644 --- a/source/blender/nodes/shader/nodes/node_shader_bump.cc +++ b/source/blender/nodes/shader/nodes/node_shader_bump.cc @@ -27,8 +27,6 @@ static void node_declare(NodeDeclarationBuilder &b) .min(-1000.0f) .max(1000.0f) .hide_value(); - b.add_input<decl::Float>(N_("Height_dx")).default_value(1.0f).unavailable(); - b.add_input<decl::Float>(N_("Height_dy")).default_value(1.0f).unavailable(); b.add_input<decl::Vector>(N_("Normal")).min(-1.0f).max(1.0f).hide_value(); b.add_output<decl::Vector>(N_("Normal")); } @@ -46,23 +44,26 @@ static int gpu_shader_bump(GPUMaterial *mat, { /* If there is no Height input, the node becomes a no-op. */ if (!in[2].link) { - if (!in[5].link) { + if (!in[3].link) { return GPU_link(mat, "world_normals_get", &out[0].link); } else { /* Actually running the bump code would normalize, but Cycles handles it as total no-op. */ - return GPU_link(mat, "vector_copy", in[5].link, &out[0].link); + return GPU_link(mat, "vector_copy", in[3].link, &out[0].link); } } - if (!in[5].link) { - GPU_link(mat, "world_normals_get", &in[5].link); + if (!in[3].link) { + GPU_link(mat, "world_normals_get", &in[3].link); } + const char *height_function = GPU_material_split_sub_function(mat, GPU_FLOAT, &in[2].link); + + GPUNodeLink *dheight = GPU_differentiate_float_function(height_function); + float invert = (node->custom1) ? -1.0 : 1.0; - return GPU_stack_link( - mat, node, "node_bump", in, out, GPU_builtin(GPU_VIEW_POSITION), GPU_constant(&invert)); + return GPU_stack_link(mat, node, "node_bump", in, out, dheight, GPU_constant(&invert)); } } // namespace blender::nodes::node_shader_bump_cc diff --git a/source/blender/nodes/shader/nodes/node_shader_camera.cc b/source/blender/nodes/shader/nodes/node_shader_camera.cc index 9d2858f56f7..99c82582456 100644 --- a/source/blender/nodes/shader/nodes/node_shader_camera.cc +++ b/source/blender/nodes/shader/nodes/node_shader_camera.cc @@ -22,11 +22,7 @@ static int gpu_shader_camera(GPUMaterial *mat, GPUNodeStack *in, GPUNodeStack *out) { - GPUNodeLink *viewvec; - - viewvec = GPU_builtin(GPU_VIEW_POSITION); - GPU_link(mat, "invert_z", viewvec, &viewvec); - return GPU_stack_link(mat, node, "camera", in, out, viewvec); + return GPU_stack_link(mat, node, "camera", in, out); } } // namespace blender::nodes::node_shader_camera_cc diff --git a/source/blender/nodes/shader/nodes/node_shader_displacement.cc b/source/blender/nodes/shader/nodes/node_shader_displacement.cc index 88d1fac13a4..6591396adda 100644 --- a/source/blender/nodes/shader/nodes/node_shader_displacement.cc +++ b/source/blender/nodes/shader/nodes/node_shader_displacement.cc @@ -33,16 +33,11 @@ static int gpu_shader_displacement(GPUMaterial *mat, GPUNodeStack *out) { if (!in[3].link) { - GPU_link(mat, - "direction_transform_m4v3", - GPU_builtin(GPU_VIEW_NORMAL), - GPU_builtin(GPU_INVERSE_VIEW_MATRIX), - &in[3].link); + GPU_link(mat, "world_normals_get", &in[3].link); } if (node->custom1 == SHD_SPACE_OBJECT) { - return GPU_stack_link( - mat, node, "node_displacement_object", in, out, GPU_builtin(GPU_OBJECT_MATRIX)); + return GPU_stack_link(mat, node, "node_displacement_object", in, out); } return GPU_stack_link(mat, node, "node_displacement_world", in, out); diff --git a/source/blender/nodes/shader/nodes/node_shader_eevee_specular.cc b/source/blender/nodes/shader/nodes/node_shader_eevee_specular.cc index c4aed523f61..d68b0c0c37c 100644 --- a/source/blender/nodes/shader/nodes/node_shader_eevee_specular.cc +++ b/source/blender/nodes/shader/nodes/node_shader_eevee_specular.cc @@ -33,9 +33,12 @@ static void node_declare(NodeDeclarationBuilder &b) .subtype(PROP_FACTOR); b.add_input<decl::Vector>(N_("Clear Coat Normal")).hide_value(); b.add_input<decl::Float>(N_("Ambient Occlusion")).hide_value(); + b.add_input<decl::Float>(N_("Weight")).unavailable(); b.add_output<decl::Shader>(N_("BSDF")); } +#define socket_not_zero(sock) (in[sock].link || (clamp_f(in[sock].vec[0], 0.0f, 1.0f) > 1e-5f)) + static int node_shader_gpu_eevee_specular(GPUMaterial *mat, bNode *node, bNodeExecData *UNUSED(execdata), @@ -59,9 +62,11 @@ static int node_shader_gpu_eevee_specular(GPUMaterial *mat, GPU_link(mat, "set_value", GPU_constant(&one), &in[9].link); } - GPU_material_flag_set(mat, static_cast<eGPUMatFlag>(GPU_MATFLAG_DIFFUSE | GPU_MATFLAG_GLOSSY)); + GPU_material_flag_set(mat, GPU_MATFLAG_DIFFUSE | GPU_MATFLAG_GLOSSY); + + float use_clear = (socket_not_zero(6)) ? 1.0f : 0.0f; - return GPU_stack_link(mat, node, "node_eevee_specular", in, out, GPU_constant(&node->ssr_id)); + return GPU_stack_link(mat, node, "node_eevee_specular", in, out, GPU_constant(&use_clear)); } } // namespace blender::nodes::node_shader_eevee_specular_cc diff --git a/source/blender/nodes/shader/nodes/node_shader_emission.cc b/source/blender/nodes/shader/nodes/node_shader_emission.cc index d4d88857cd2..be98f096ce5 100644 --- a/source/blender/nodes/shader/nodes/node_shader_emission.cc +++ b/source/blender/nodes/shader/nodes/node_shader_emission.cc @@ -9,6 +9,7 @@ static void node_declare(NodeDeclarationBuilder &b) { b.add_input<decl::Color>(N_("Color")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); b.add_input<decl::Float>(N_("Strength")).default_value(1.0f).min(0.0f).max(1000000.0f); + b.add_input<decl::Float>(N_("Weight")).unavailable(); b.add_output<decl::Shader>(N_("Emission")); } @@ -18,7 +19,8 @@ static int node_shader_gpu_emission(GPUMaterial *mat, GPUNodeStack *in, GPUNodeStack *out) { - return GPU_stack_link(mat, node, "node_emission", in, out, GPU_builtin(GPU_VIEW_NORMAL)); + GPU_material_flag_set(mat, GPU_MATFLAG_EMISSION); + return GPU_stack_link(mat, node, "node_emission", in, out); } } // namespace blender::nodes::node_shader_emission_cc diff --git a/source/blender/nodes/shader/nodes/node_shader_fresnel.cc b/source/blender/nodes/shader/nodes/node_shader_fresnel.cc index 47b2c6b97d4..7b771d7dafd 100644 --- a/source/blender/nodes/shader/nodes/node_shader_fresnel.cc +++ b/source/blender/nodes/shader/nodes/node_shader_fresnel.cc @@ -19,14 +19,10 @@ static int node_shader_gpu_fresnel(GPUMaterial *mat, GPUNodeStack *out) { if (!in[1].link) { - in[1].link = GPU_builtin(GPU_VIEW_NORMAL); - } - else { - GPU_link( - mat, "direction_transform_m4v3", in[1].link, GPU_builtin(GPU_VIEW_MATRIX), &in[1].link); + GPU_link(mat, "world_normals_get", &in[1].link); } - return GPU_stack_link(mat, node, "node_fresnel", in, out, GPU_builtin(GPU_VIEW_POSITION)); + return GPU_stack_link(mat, node, "node_fresnel", in, out); } } // namespace blender::nodes::node_shader_fresnel_cc diff --git a/source/blender/nodes/shader/nodes/node_shader_geometry.cc b/source/blender/nodes/shader/nodes/node_shader_geometry.cc index 5bd984ddfc4..47df932f9d4 100644 --- a/source/blender/nodes/shader/nodes/node_shader_geometry.cc +++ b/source/blender/nodes/shader/nodes/node_shader_geometry.cc @@ -24,29 +24,17 @@ static int node_shader_gpu_geometry(GPUMaterial *mat, GPUNodeStack *in, GPUNodeStack *out) { - /* HACK: Don't request GPU_BARYCENTRIC_TEXCO if not used because it will + /* HACK: Don't request GPU_MATFLAG_BARYCENTRIC if not used because it will * trigger the use of geometry shader (and the performance penalty it implies). */ - const float val[4] = {0.0f, 0.0f, 0.0f, 0.0f}; - GPUNodeLink *bary_link = (!out[5].hasoutput) ? GPU_constant(val) : - GPU_builtin(GPU_BARYCENTRIC_TEXCO); if (out[5].hasoutput) { GPU_material_flag_set(mat, GPU_MATFLAG_BARYCENTRIC); } /* Opti: don't request orco if not needed. */ + const float val[4] = {0.0f, 0.0f, 0.0f, 0.0f}; GPUNodeLink *orco_link = (!out[2].hasoutput) ? GPU_constant(val) : GPU_attribute(mat, CD_ORCO, ""); - const bool success = GPU_stack_link(mat, - node, - "node_geometry", - in, - out, - GPU_builtin(GPU_VIEW_POSITION), - GPU_builtin(GPU_WORLD_NORMAL), - orco_link, - GPU_builtin(GPU_OBJECT_MATRIX), - GPU_builtin(GPU_INVERSE_VIEW_MATRIX), - bary_link); + const bool success = GPU_stack_link(mat, node, "node_geometry", in, out, orco_link); int i; LISTBASE_FOREACH_INDEX (bNodeSocket *, sock, &node->outputs, i) { @@ -55,7 +43,7 @@ static int node_shader_gpu_geometry(GPUMaterial *mat, * This is the case for interpolated, non linear functions. * The resulting vector can still be a bit wrong but not as much. * (see T70644) */ - if (node->branch_tag != 0 && ELEM(i, 1, 2, 4)) { + if (ELEM(i, 1, 2, 4)) { GPU_link(mat, "vector_math_normalize", out[i].link, diff --git a/source/blender/nodes/shader/nodes/node_shader_holdout.cc b/source/blender/nodes/shader/nodes/node_shader_holdout.cc index b1a9ce75e1f..6a21fab28db 100644 --- a/source/blender/nodes/shader/nodes/node_shader_holdout.cc +++ b/source/blender/nodes/shader/nodes/node_shader_holdout.cc @@ -7,6 +7,7 @@ namespace blender::nodes::node_shader_holdout_cc { static void node_declare(NodeDeclarationBuilder &b) { + b.add_input<decl::Float>(N_("Weight")).unavailable(); b.add_output<decl::Shader>(N_("Holdout")); } diff --git a/source/blender/nodes/shader/nodes/node_shader_layer_weight.cc b/source/blender/nodes/shader/nodes/node_shader_layer_weight.cc index b5097e71657..69825e472fb 100644 --- a/source/blender/nodes/shader/nodes/node_shader_layer_weight.cc +++ b/source/blender/nodes/shader/nodes/node_shader_layer_weight.cc @@ -20,14 +20,10 @@ static int node_shader_gpu_layer_weight(GPUMaterial *mat, GPUNodeStack *out) { if (!in[1].link) { - in[1].link = GPU_builtin(GPU_VIEW_NORMAL); - } - else { - GPU_link( - mat, "direction_transform_m4v3", in[1].link, GPU_builtin(GPU_VIEW_MATRIX), &in[1].link); + GPU_link(mat, "world_normals_get", &in[1].link); } - return GPU_stack_link(mat, node, "node_layer_weight", in, out, GPU_builtin(GPU_VIEW_POSITION)); + return GPU_stack_link(mat, node, "node_layer_weight", in, out); } } // namespace blender::nodes::node_shader_layer_weight_cc diff --git a/source/blender/nodes/shader/nodes/node_shader_normal_map.cc b/source/blender/nodes/shader/nodes/node_shader_normal_map.cc index f8a5e342ae3..d51d8def945 100644 --- a/source/blender/nodes/shader/nodes/node_shader_normal_map.cc +++ b/source/blender/nodes/shader/nodes/node_shader_normal_map.cc @@ -83,18 +83,16 @@ static int gpu_shader_normal_map(GPUMaterial *mat, GPU_link(mat, color_to_normal_fnc_name, newnormal, &newnormal); switch (nm->space) { case SHD_SPACE_TANGENT: + GPU_material_flag_set(mat, GPU_MATFLAG_OBJECT_INFO); GPU_link(mat, "node_normal_map", - GPU_builtin(GPU_OBJECT_INFO), GPU_attribute(mat, CD_TANGENT, nm->uv_map), - GPU_builtin(GPU_WORLD_NORMAL), newnormal, &newnormal); break; case SHD_SPACE_OBJECT: case SHD_SPACE_BLENDER_OBJECT: - GPU_link( - mat, "direction_transform_m4v3", newnormal, GPU_builtin(GPU_OBJECT_MATRIX), &newnormal); + GPU_link(mat, "normal_transform_object_to_world", newnormal, &newnormal); break; case SHD_SPACE_WORLD: case SHD_SPACE_BLENDER_WORLD: @@ -102,8 +100,7 @@ static int gpu_shader_normal_map(GPUMaterial *mat, break; } - GPUNodeLink *oldnormal = GPU_builtin(GPU_WORLD_NORMAL); - GPU_link(mat, "node_normal_map_mix", strength, newnormal, oldnormal, &out[0].link); + GPU_link(mat, "node_normal_map_mix", strength, newnormal, &out[0].link); return true; } diff --git a/source/blender/nodes/shader/nodes/node_shader_object_info.cc b/source/blender/nodes/shader/nodes/node_shader_object_info.cc index 03c1a018d37..8985ab6d0e9 100644 --- a/source/blender/nodes/shader/nodes/node_shader_object_info.cc +++ b/source/blender/nodes/shader/nodes/node_shader_object_info.cc @@ -23,15 +23,8 @@ static int node_shader_gpu_object_info(GPUMaterial *mat, { Material *ma = GPU_material_get_material(mat); float index = ma ? ma->index : 0.0f; - return GPU_stack_link(mat, - node, - "node_object_info", - in, - out, - GPU_builtin(GPU_OBJECT_MATRIX), - GPU_builtin(GPU_OBJECT_COLOR), - GPU_builtin(GPU_OBJECT_INFO), - GPU_constant(&index)); + GPU_material_flag_set(mat, GPU_MATFLAG_OBJECT_INFO); + return GPU_stack_link(mat, node, "node_object_info", in, out, GPU_constant(&index)); } } // namespace blender::nodes::node_shader_object_info_cc diff --git a/source/blender/nodes/shader/nodes/node_shader_output_aov.cc b/source/blender/nodes/shader/nodes/node_shader_output_aov.cc index c0a116675ab..78dabc5c1c2 100644 --- a/source/blender/nodes/shader/nodes/node_shader_output_aov.cc +++ b/source/blender/nodes/shader/nodes/node_shader_output_aov.cc @@ -35,12 +35,16 @@ static int node_shader_gpu_output_aov(GPUMaterial *mat, { GPUNodeLink *outlink; NodeShaderOutputAOV *aov = (NodeShaderOutputAOV *)node->storage; - /* Keep in sync with `renderpass_lib.glsl#render_pass_aov_hash` and - * `EEVEE_renderpasses_aov_hash`. */ - unsigned int hash = BLI_hash_string(aov->name) << 1; - GPU_stack_link(mat, node, "node_output_aov", in, out, &outlink); - GPU_material_add_output_link_aov(mat, outlink, hash); + uint hash = BLI_hash_string(aov->name); + /* WORKAROUND: We don't support int/uint constants for now. So make sure the aliasing works. + * We cast back to uint in GLSL. */ + BLI_STATIC_ASSERT(sizeof(float) == sizeof(uint), + "GPUCodegen: AOV hash needs float and uint to be the same size."); + GPUNodeLink *hash_link = GPU_constant((float *)&hash); + GPU_material_flag_set(mat, GPU_MATFLAG_AOV); + GPU_stack_link(mat, node, "node_output_aov", in, out, hash_link, &outlink); + GPU_material_add_output_link_aov(mat, outlink, hash); return true; } diff --git a/source/blender/nodes/shader/nodes/node_shader_output_material.cc b/source/blender/nodes/shader/nodes/node_shader_output_material.cc index 3329ab7f8a0..54475d547da 100644 --- a/source/blender/nodes/shader/nodes/node_shader_output_material.cc +++ b/source/blender/nodes/shader/nodes/node_shader_output_material.cc @@ -12,39 +12,33 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_input<decl::Shader>(N_("Surface")); b.add_input<decl::Shader>(N_("Volume")); b.add_input<decl::Vector>(N_("Displacement")).hide_value(); + b.add_input<decl::Float>(N_("Thickness")).hide_value(); } static int node_shader_gpu_output_material(GPUMaterial *mat, - bNode *node, + bNode *UNUSED(node), bNodeExecData *UNUSED(execdata), GPUNodeStack *in, - GPUNodeStack *out) + GPUNodeStack *UNUSED(out)) { - GPUNodeLink *outlink, *alpha_threshold_link, *shadow_threshold_link; - Material *ma = GPU_material_get_material(mat); - - static float no_alpha_threshold = -1.0f; - if (ma) { - alpha_threshold_link = GPU_uniform((ma->blend_method == MA_BM_CLIP) ? &ma->alpha_threshold : - &no_alpha_threshold); - shadow_threshold_link = GPU_uniform((ma->blend_shadow == MA_BS_CLIP) ? &ma->alpha_threshold : - &no_alpha_threshold); + GPUNodeLink *outlink_surface, *outlink_volume, *outlink_displacement, *outlink_thickness; + /* Passthrough node in order to do the right socket conversions (important for displacement). */ + if (in[0].link) { + GPU_link(mat, "node_output_material_surface", in[0].link, &outlink_surface); + GPU_material_output_surface(mat, outlink_surface); } - else { - alpha_threshold_link = GPU_uniform(&no_alpha_threshold); - shadow_threshold_link = GPU_uniform(&no_alpha_threshold); + if (in[1].link) { + GPU_link(mat, "node_output_material_volume", in[1].link, &outlink_volume); + GPU_material_output_volume(mat, outlink_volume); + } + if (in[2].link) { + GPU_link(mat, "node_output_material_displacement", in[2].link, &outlink_displacement); + GPU_material_output_displacement(mat, outlink_displacement); + } + if (in[3].link) { + GPU_link(mat, "node_output_material_thickness", in[3].link, &outlink_thickness); + GPU_material_output_thickness(mat, outlink_thickness); } - - GPU_stack_link(mat, - node, - "node_output_material", - in, - out, - alpha_threshold_link, - shadow_threshold_link, - &outlink); - GPU_material_output_link(mat, outlink); - return true; } diff --git a/source/blender/nodes/shader/nodes/node_shader_output_world.cc b/source/blender/nodes/shader/nodes/node_shader_output_world.cc index 77146876d9d..b0cf4c80bc5 100644 --- a/source/blender/nodes/shader/nodes/node_shader_output_world.cc +++ b/source/blender/nodes/shader/nodes/node_shader_output_world.cc @@ -12,16 +12,20 @@ static void node_declare(NodeDeclarationBuilder &b) } static int node_shader_gpu_output_world(GPUMaterial *mat, - bNode *node, + bNode *UNUSED(node), bNodeExecData *UNUSED(execdata), GPUNodeStack *in, - GPUNodeStack *out) + GPUNodeStack *UNUSED(out)) { - GPUNodeLink *outlink; - - GPU_stack_link(mat, node, "node_output_world", in, out, &outlink); - GPU_material_output_link(mat, outlink); - + GPUNodeLink *outlink_surface, *outlink_volume; + if (in[0].link) { + GPU_link(mat, "node_output_world_surface", in[0].link, &outlink_surface); + GPU_material_output_surface(mat, outlink_surface); + } + if (in[1].link) { + GPU_link(mat, "node_output_world_volume", in[1].link, &outlink_volume); + GPU_material_output_volume(mat, outlink_volume); + } return true; } diff --git a/source/blender/nodes/shader/nodes/node_shader_particle_info.cc b/source/blender/nodes/shader/nodes/node_shader_particle_info.cc index 66e859a8c69..71adbd5e5c4 100644 --- a/source/blender/nodes/shader/nodes/node_shader_particle_info.cc +++ b/source/blender/nodes/shader/nodes/node_shader_particle_info.cc @@ -28,16 +28,9 @@ static int gpu_shader_particle_info(GPUMaterial *mat, GPUNodeStack *in, GPUNodeStack *out) { - - return GPU_stack_link(mat, - node, - "particle_info", - in, - out, - GPU_builtin(GPU_PARTICLE_SCALAR_PROPS), - GPU_builtin(GPU_PARTICLE_LOCATION), - GPU_builtin(GPU_PARTICLE_VELOCITY), - GPU_builtin(GPU_PARTICLE_ANG_VELOCITY)); + GPU_material_flag_set(mat, GPU_MATFLAG_OBJECT_INFO); + /* TODO(fclem) Pass particle data in obinfo. */ + return GPU_stack_link(mat, node, "particle_info", in, out); } } // namespace blender::nodes::node_shader_particle_info_cc diff --git a/source/blender/nodes/shader/nodes/node_shader_shader_to_rgb.cc b/source/blender/nodes/shader/nodes/node_shader_shader_to_rgb.cc index 11dfa03ea40..0404158a803 100644 --- a/source/blender/nodes/shader/nodes/node_shader_shader_to_rgb.cc +++ b/source/blender/nodes/shader/nodes/node_shader_shader_to_rgb.cc @@ -18,9 +18,7 @@ static int node_shader_gpu_shadertorgb(GPUMaterial *mat, GPUNodeStack *in, GPUNodeStack *out) { - /* Because node_shader_to_rgba is using fallback_cubemap() - * we need to tag material as glossy. */ - GPU_material_flag_set(mat, GPU_MATFLAG_GLOSSY); + GPU_material_flag_set(mat, GPU_MATFLAG_SHADER_TO_RGBA); return GPU_stack_link(mat, node, "node_shader_to_rgba", in, out); } diff --git a/source/blender/nodes/shader/nodes/node_shader_subsurface_scattering.cc b/source/blender/nodes/shader/nodes/node_shader_subsurface_scattering.cc index 17173ef020a..e3ff4c28604 100644 --- a/source/blender/nodes/shader/nodes/node_shader_subsurface_scattering.cc +++ b/source/blender/nodes/shader/nodes/node_shader_subsurface_scattering.cc @@ -25,6 +25,7 @@ static void node_declare(NodeDeclarationBuilder &b) .max(1.0f) .subtype(PROP_FACTOR); b.add_input<decl::Vector>(N_("Normal")).hide_value(); + b.add_input<decl::Float>(N_("Weight")).unavailable(); b.add_output<decl::Shader>(N_("BSSRDF")); } @@ -49,19 +50,16 @@ static int node_shader_gpu_subsurface_scattering(GPUMaterial *mat, GPU_link(mat, "world_normals_get", &in[5].link); } - if (node->sss_id > 0) { - bNodeSocket *socket = (bNodeSocket *)BLI_findlink(&node->original->inputs, 2); - bNodeSocketValueRGBA *socket_data = (bNodeSocketValueRGBA *)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]); + bNodeSocket *socket = (bNodeSocket *)BLI_findlink(&node->original->inputs, 2); + bNodeSocketValueRGBA *socket_data = (bNodeSocketValueRGBA *)socket->default_value; + /* For some reason it seems that the socket value is in ARGB format. */ + bool use_subsurf = 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). */ - GPU_material_flag_set(mat, (eGPUMatFlag)(GPU_MATFLAG_DIFFUSE | GPU_MATFLAG_SSS)); - } + float use_sss = (use_subsurf) ? 1.0f : 0.0f; + + GPU_material_flag_set(mat, GPU_MATFLAG_DIFFUSE | GPU_MATFLAG_SUBSURFACE); - return GPU_stack_link( - mat, node, "node_subsurface_scattering", in, out, GPU_constant(&node->sss_id)); + return GPU_stack_link(mat, node, "node_subsurface_scattering", in, out, GPU_uniform(&use_sss)); } static void node_shader_update_subsurface_scattering(bNodeTree *ntree, bNode *node) diff --git a/source/blender/nodes/shader/nodes/node_shader_tangent.cc b/source/blender/nodes/shader/nodes/node_shader_tangent.cc index 25489632594..8e27ebed21b 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tangent.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tangent.cc @@ -73,14 +73,7 @@ static int node_shader_gpu_tangent(GPUMaterial *mat, GPU_link(mat, "tangent_orco_z", orco, &orco); } - return GPU_stack_link(mat, - node, - "node_tangent", - in, - out, - GPU_builtin(GPU_WORLD_NORMAL), - orco, - GPU_builtin(GPU_OBJECT_MATRIX)); + return GPU_stack_link(mat, node, "node_tangent", in, out, orco); } } // namespace blender::nodes::node_shader_tangent_cc diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_coord.cc b/source/blender/nodes/shader/nodes/node_shader_tex_coord.cc index ac986787d65..ae683386bac 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_coord.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tex_coord.cc @@ -35,24 +35,18 @@ static int node_shader_gpu_tex_coord(GPUMaterial *mat, { Object *ob = (Object *)node->id; - GPUNodeLink *inv_obmat = (ob != nullptr) ? GPU_uniform(&ob->imat[0][0]) : - GPU_builtin(GPU_INVERSE_OBJECT_MATRIX); + /* Use special matrix to let the shader branch to using the render object's matrix. */ + float dummy_matrix[4][4]; + dummy_matrix[3][3] = 0.0f; + GPUNodeLink *inv_obmat = (ob != NULL) ? GPU_uniform(&ob->imat[0][0]) : + GPU_uniform(&dummy_matrix[0][0]); /* Opti: don't request orco if not needed. */ - const float default_coords[4] = {0.0f, 0.0f, 0.0f, 0.0f}; - GPUNodeLink *orco = (!out[0].hasoutput) ? GPU_constant(default_coords) : - GPU_attribute(mat, CD_ORCO, ""); + float4 zero(0.0f); + GPUNodeLink *orco = (!out[0].hasoutput) ? GPU_constant(zero) : GPU_attribute(mat, CD_ORCO, ""); GPUNodeLink *mtface = GPU_attribute(mat, CD_MTFACE, ""); - GPUNodeLink *viewpos = GPU_builtin(GPU_VIEW_POSITION); - GPUNodeLink *worldnor = GPU_builtin(GPU_WORLD_NORMAL); - GPUNodeLink *texcofacs = GPU_builtin(GPU_CAMERA_TEXCO_FACTORS); - if (out[0].hasoutput) { - GPU_link(mat, "generated_from_orco", orco, &orco); - } - - GPU_stack_link( - mat, node, "node_tex_coord", in, out, viewpos, worldnor, inv_obmat, texcofacs, orco, mtface); + GPU_stack_link(mat, node, "node_tex_coord", in, out, inv_obmat, orco, mtface); int i; LISTBASE_FOREACH_INDEX (bNodeSocket *, sock, &node->outputs, i) { @@ -61,7 +55,7 @@ static int node_shader_gpu_tex_coord(GPUMaterial *mat, * This is the case for interpolated, non linear functions. * The resulting vector can still be a bit wrong but not as much. * (see T70644) */ - if (node->branch_tag != 0 && ELEM(i, 1, 6)) { + if (ELEM(i, 1, 6)) { GPU_link(mat, "vector_math_normalize", out[i].link, diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_environment.cc b/source/blender/nodes/shader/nodes/node_shader_tex_environment.cc index 41456091e1d..dbff092234b 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_environment.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tex_environment.cc @@ -44,15 +44,13 @@ static int node_shader_gpu_tex_environment(GPUMaterial *mat, GPUNodeLink *outalpha; - if (!ima) { + /* HACK(fclem): For lookdev mode: do not compile an empty environment and just create an empty + * texture entry point. We manually bind to it after DRW_shgroup_add_material_resources(). */ + if (!ima && !GPU_material_flag_get(mat, GPU_MATFLAG_LOOKDEV_HACK)) { return GPU_stack_link(mat, node, "node_tex_environment_empty", in, out); } - if (!in[0].link) { - GPU_link(mat, "node_tex_environment_texco", GPU_builtin(GPU_VIEW_POSITION), &in[0].link); - node_shader_gpu_bump_tex_coord(mat, node, &in[0].link); - } - + node_shader_gpu_default_tex_coord(mat, node, &in[0].link); node_shader_gpu_tex_mapping(mat, node, in, out); /* Compute texture coordinate. */ @@ -92,7 +90,7 @@ static int node_shader_gpu_tex_environment(GPUMaterial *mat, /* Sample texture with correct interpolation. */ GPU_link(mat, gpu_fn, in[0].link, GPU_image(mat, ima, iuser, sampler), &out[0].link, &outalpha); - if (out[0].hasoutput) { + if (out[0].hasoutput && ima) { if (ELEM(ima->alpha_mode, IMA_ALPHA_IGNORE, IMA_ALPHA_CHANNEL_PACKED) || IMB_colormanagement_space_name_is_data(ima->colorspace_settings.name)) { /* Don't let alpha affect color output in these cases. */ diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_image.cc b/source/blender/nodes/shader/nodes/node_shader_tex_image.cc index 19b5a50cd6b..d97f7d0375d 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_image.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tex_image.cc @@ -88,13 +88,11 @@ static int node_shader_gpu_tex_image(GPUMaterial *mat, } case SHD_PROJ_BOX: { gpu_node_name = use_cubic ? "tex_box_sample_cubic" : "tex_box_sample_linear"; - GPUNodeLink *wnor, *col1, *col2, *col3; - GPUNodeLink *vnor = GPU_builtin(GPU_WORLD_NORMAL); - GPUNodeLink *ob_mat = GPU_builtin(GPU_OBJECT_MATRIX); + GPUNodeLink *vnor, *wnor, *col1, *col2, *col3; GPUNodeLink *blend = GPU_uniform(&tex->projection_blend); GPUNodeLink *gpu_image = GPU_image(mat, ima, iuser, sampler_state); - /* equivalent to normal_world_to_object */ - GPU_link(mat, "normal_transform_transposed_m4v3", vnor, ob_mat, &wnor); + GPU_link(mat, "world_normals_get", &vnor); + GPU_link(mat, "normal_transform_world_to_object", vnor, &wnor); GPU_link(mat, gpu_node_name, in[0].link, wnor, gpu_image, &col1, &col2, &col3); GPU_link(mat, "tex_box_blend", wnor, col1, col2, col3, blend, &out[0].link, &out[1].link); break; diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_pointdensity.cc b/source/blender/nodes/shader/nodes/node_shader_tex_pointdensity.cc index f628d1ec77b..fcd1d4973ff 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_pointdensity.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tex_pointdensity.cc @@ -65,7 +65,7 @@ static void node_shader_buts_tex_pointdensity(uiLayout *layout, static void node_shader_init_tex_pointdensity(bNodeTree *UNUSED(ntree), bNode *node) { - NodeShaderTexPointDensity *point_density = MEM_cnew<NodeShaderTexPointDensity>("new pd node"); + NodeShaderTexPointDensity *point_density = MEM_new<NodeShaderTexPointDensity>("new pd node"); point_density->resolution = 100; point_density->radius = 0.3f; point_density->space = SHD_POINTDENSITY_SPACE_OBJECT; @@ -79,7 +79,7 @@ static void node_shader_free_tex_pointdensity(bNode *node) PointDensity *pd = &point_density->pd; RE_point_density_free(pd); BKE_texture_pointdensity_free_data(pd); - memset(pd, 0, sizeof(*pd)); + *pd = dna::shallow_zero_initialize(); MEM_freeN(point_density); } @@ -90,7 +90,7 @@ static void node_shader_copy_tex_pointdensity(bNodeTree *UNUSED(dest_ntree), dest_node->storage = MEM_dupallocN(src_node->storage); NodeShaderTexPointDensity *point_density = (NodeShaderTexPointDensity *)dest_node->storage; PointDensity *pd = &point_density->pd; - memset(pd, 0, sizeof(*pd)); + *pd = dna::shallow_zero_initialize(); } } // namespace blender::nodes::node_shader_tex_pointdensity_cc diff --git a/source/blender/nodes/shader/nodes/node_shader_vector_displacement.cc b/source/blender/nodes/shader/nodes/node_shader_vector_displacement.cc index 04cf0b72f32..9996e91177a 100644 --- a/source/blender/nodes/shader/nodes/node_shader_vector_displacement.cc +++ b/source/blender/nodes/shader/nodes/node_shader_vector_displacement.cc @@ -24,23 +24,20 @@ static int gpu_shader_vector_displacement(GPUMaterial *mat, GPUNodeStack *in, GPUNodeStack *out) { - if (node->custom1 == SHD_SPACE_TANGENT) { - return GPU_stack_link(mat, - node, - "node_vector_displacement_tangent", - in, - out, - GPU_attribute(mat, CD_TANGENT, ""), - GPU_builtin(GPU_WORLD_NORMAL), - GPU_builtin(GPU_OBJECT_MATRIX), - GPU_builtin(GPU_VIEW_MATRIX)); + switch (node->custom1) { + case SHD_SPACE_TANGENT: + return GPU_stack_link(mat, + node, + "node_vector_displacement_tangent", + in, + out, + GPU_attribute(mat, CD_TANGENT, "")); + case SHD_SPACE_OBJECT: + return GPU_stack_link(mat, node, "node_vector_displacement_object", in, out); + case SHD_SPACE_WORLD: + default: + return GPU_stack_link(mat, node, "node_vector_displacement_world", in, out); } - if (node->custom1 == SHD_SPACE_OBJECT) { - return GPU_stack_link( - mat, node, "node_vector_displacement_object", in, out, GPU_builtin(GPU_OBJECT_MATRIX)); - } - - return GPU_stack_link(mat, node, "node_vector_displacement_world", in, out); } } // namespace blender::nodes::node_shader_vector_displacement_cc 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 a041492fb13..b35f686e331 100644 --- a/source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc +++ b/source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc @@ -69,9 +69,9 @@ static int gpu_shader_vector_rotate(GPUMaterial *mat, return 0; } -static float3 sh_node_vector_rotate_around_axis(const float3 vector, - const float3 center, - const float3 axis, +static float3 sh_node_vector_rotate_around_axis(const float3 &vector, + const float3 ¢er, + const float3 &axis, const float angle) { float3 result = vector - center; @@ -81,9 +81,9 @@ static float3 sh_node_vector_rotate_around_axis(const float3 vector, return result + center; } -static float3 sh_node_vector_rotate_euler(const float3 vector, - const float3 center, - const float3 rotation, +static float3 sh_node_vector_rotate_euler(const float3 &vector, + const float3 ¢er, + const float3 &rotation, const bool invert) { float mat[3][3]; @@ -105,13 +105,15 @@ static const fn::MultiFunction *get_multi_function(bNode &node) case NODE_VECTOR_ROTATE_TYPE_AXIS: { if (invert) { static fn::CustomMF_SI_SI_SI_SI_SO<float3, float3, float3, float, float3> fn{ - "Rotate Axis", [](float3 in, float3 center, float3 axis, float angle) { + "Rotate Axis", + [](const float3 &in, const float3 ¢er, const float3 &axis, float angle) { return sh_node_vector_rotate_around_axis(in, center, axis, -angle); }}; return &fn; } static fn::CustomMF_SI_SI_SI_SI_SO<float3, float3, float3, float, float3> fn{ - "Rotate Axis", [](float3 in, float3 center, float3 axis, float angle) { + "Rotate Axis", + [](const float3 &in, const float3 ¢er, const float3 &axis, float angle) { return sh_node_vector_rotate_around_axis(in, center, axis, angle); }}; return &fn; @@ -120,13 +122,13 @@ static const fn::MultiFunction *get_multi_function(bNode &node) float3 axis = float3(1.0f, 0.0f, 0.0f); if (invert) { static fn::CustomMF_SI_SI_SI_SO<float3, float3, float, float3> fn{ - "Rotate X-Axis", [=](float3 in, float3 center, float angle) { + "Rotate X-Axis", [=](const float3 &in, const float3 ¢er, float angle) { return sh_node_vector_rotate_around_axis(in, center, axis, -angle); }}; return &fn; } static fn::CustomMF_SI_SI_SI_SO<float3, float3, float, float3> fn{ - "Rotate X-Axis", [=](float3 in, float3 center, float angle) { + "Rotate X-Axis", [=](const float3 &in, const float3 ¢er, float angle) { return sh_node_vector_rotate_around_axis(in, center, axis, angle); }}; return &fn; @@ -135,13 +137,13 @@ static const fn::MultiFunction *get_multi_function(bNode &node) float3 axis = float3(0.0f, 1.0f, 0.0f); if (invert) { static fn::CustomMF_SI_SI_SI_SO<float3, float3, float, float3> fn{ - "Rotate Y-Axis", [=](float3 in, float3 center, float angle) { + "Rotate Y-Axis", [=](const float3 &in, const float3 ¢er, float angle) { return sh_node_vector_rotate_around_axis(in, center, axis, -angle); }}; return &fn; } static fn::CustomMF_SI_SI_SI_SO<float3, float3, float, float3> fn{ - "Rotate Y-Axis", [=](float3 in, float3 center, float angle) { + "Rotate Y-Axis", [=](const float3 &in, const float3 ¢er, float angle) { return sh_node_vector_rotate_around_axis(in, center, axis, angle); }}; return &fn; @@ -150,13 +152,13 @@ static const fn::MultiFunction *get_multi_function(bNode &node) float3 axis = float3(0.0f, 0.0f, 1.0f); if (invert) { static fn::CustomMF_SI_SI_SI_SO<float3, float3, float, float3> fn{ - "Rotate Z-Axis", [=](float3 in, float3 center, float angle) { + "Rotate Z-Axis", [=](const float3 &in, const float3 ¢er, float angle) { return sh_node_vector_rotate_around_axis(in, center, axis, -angle); }}; return &fn; } static fn::CustomMF_SI_SI_SI_SO<float3, float3, float, float3> fn{ - "Rotate Z-Axis", [=](float3 in, float3 center, float angle) { + "Rotate Z-Axis", [=](const float3 &in, const float3 ¢er, float angle) { return sh_node_vector_rotate_around_axis(in, center, axis, angle); }}; return &fn; @@ -164,13 +166,13 @@ static const fn::MultiFunction *get_multi_function(bNode &node) case NODE_VECTOR_ROTATE_TYPE_EULER_XYZ: { if (invert) { static fn::CustomMF_SI_SI_SI_SO<float3, float3, float3, float3> fn{ - "Rotate Euler", [](float3 in, float3 center, float3 rotation) { + "Rotate Euler", [](const float3 &in, const float3 ¢er, const float3 &rotation) { return sh_node_vector_rotate_euler(in, center, rotation, true); }}; return &fn; } static fn::CustomMF_SI_SI_SI_SO<float3, float3, float3, float3> fn{ - "Rotate Euler", [](float3 in, float3 center, float3 rotation) { + "Rotate Euler", [](const float3 &in, const float3 ¢er, const float3 &rotation) { return sh_node_vector_rotate_euler(in, center, rotation, false); }}; return &fn; diff --git a/source/blender/nodes/shader/nodes/node_shader_vector_transform.cc b/source/blender/nodes/shader/nodes/node_shader_vector_transform.cc index d610e1309a7..de588f9005f 100644 --- a/source/blender/nodes/shader/nodes/node_shader_vector_transform.cc +++ b/source/blender/nodes/shader/nodes/node_shader_vector_transform.cc @@ -43,7 +43,7 @@ static void node_shader_init_vect_transform(bNodeTree *UNUSED(ntree), bNode *nod node->storage = vect; } -static GPUNodeLink *get_gpulink_matrix_from_to(short from, short to) +static const char *get_gpufn_name_from_to(short from, short to, bool is_direction) { switch (from) { case SHD_VECT_TRANSFORM_SPACE_OBJECT: @@ -51,9 +51,11 @@ static GPUNodeLink *get_gpulink_matrix_from_to(short from, short to) case SHD_VECT_TRANSFORM_SPACE_OBJECT: return nullptr; case SHD_VECT_TRANSFORM_SPACE_WORLD: - return GPU_builtin(GPU_OBJECT_MATRIX); + return is_direction ? "direction_transform_object_to_world" : + "point_transform_object_to_world"; case SHD_VECT_TRANSFORM_SPACE_CAMERA: - return GPU_builtin(GPU_LOC_TO_VIEW_MATRIX); + return is_direction ? "direction_transform_object_to_view" : + "point_transform_object_to_view"; } break; case SHD_VECT_TRANSFORM_SPACE_WORLD: @@ -61,9 +63,11 @@ static GPUNodeLink *get_gpulink_matrix_from_to(short from, short to) case SHD_VECT_TRANSFORM_SPACE_WORLD: return nullptr; case SHD_VECT_TRANSFORM_SPACE_CAMERA: - return GPU_builtin(GPU_VIEW_MATRIX); + return is_direction ? "direction_transform_world_to_view" : + "point_transform_world_to_view"; case SHD_VECT_TRANSFORM_SPACE_OBJECT: - return GPU_builtin(GPU_INVERSE_OBJECT_MATRIX); + return is_direction ? "direction_transform_world_to_object" : + "point_transform_world_to_object"; } break; case SHD_VECT_TRANSFORM_SPACE_CAMERA: @@ -71,14 +75,17 @@ static GPUNodeLink *get_gpulink_matrix_from_to(short from, short to) case SHD_VECT_TRANSFORM_SPACE_CAMERA: return nullptr; case SHD_VECT_TRANSFORM_SPACE_WORLD: - return GPU_builtin(GPU_INVERSE_VIEW_MATRIX); + return is_direction ? "direction_transform_view_to_world" : + "point_transform_view_to_world"; case SHD_VECT_TRANSFORM_SPACE_OBJECT: - return GPU_builtin(GPU_INVERSE_LOC_TO_VIEW_MATRIX); + return is_direction ? "direction_transform_view_to_object" : + "point_transform_view_to_object"; } break; } - return nullptr; + return NULL; } + static int gpu_shader_vect_transform(GPUMaterial *mat, bNode *node, bNodeExecData *UNUSED(execdata), @@ -86,11 +93,6 @@ static int gpu_shader_vect_transform(GPUMaterial *mat, GPUNodeStack *out) { struct GPUNodeLink *inputlink; - struct GPUNodeLink *fromto; - - const char *vtransform = "direction_transform_m4v3"; - const char *ptransform = "point_transform_m4v3"; - const char *func_name = nullptr; NodeShaderVectTransform *nodeprop = (NodeShaderVectTransform *)node->storage; @@ -101,17 +103,20 @@ static int gpu_shader_vect_transform(GPUMaterial *mat, inputlink = GPU_constant(in[0].vec); } - fromto = get_gpulink_matrix_from_to(nodeprop->convert_from, nodeprop->convert_to); + const bool is_direction = (nodeprop->type != SHD_VECT_TRANSFORM_TYPE_POINT); + const char *func_name = get_gpufn_name_from_to( + nodeprop->convert_from, nodeprop->convert_to, is_direction); - func_name = (nodeprop->type == SHD_VECT_TRANSFORM_TYPE_POINT) ? ptransform : vtransform; - if (fromto) { + if (func_name) { /* For cycles we have inverted Z */ /* TODO: pass here the correct matrices */ if (nodeprop->convert_from == SHD_VECT_TRANSFORM_SPACE_CAMERA && nodeprop->convert_to != SHD_VECT_TRANSFORM_SPACE_CAMERA) { GPU_link(mat, "invert_z", inputlink, &inputlink); } - GPU_link(mat, func_name, inputlink, fromto, &out[0].link); + + GPU_link(mat, func_name, inputlink, &out[0].link); + if (nodeprop->convert_to == SHD_VECT_TRANSFORM_SPACE_CAMERA && nodeprop->convert_from != SHD_VECT_TRANSFORM_SPACE_CAMERA) { GPU_link(mat, "invert_z", out[0].link, &out[0].link); diff --git a/source/blender/nodes/shader/nodes/node_shader_volume_absorption.cc b/source/blender/nodes/shader/nodes/node_shader_volume_absorption.cc index e0fef6b0d19..930fa6e5fb9 100644 --- a/source/blender/nodes/shader/nodes/node_shader_volume_absorption.cc +++ b/source/blender/nodes/shader/nodes/node_shader_volume_absorption.cc @@ -9,6 +9,7 @@ static void node_declare(NodeDeclarationBuilder &b) { b.add_input<decl::Color>(N_("Color")).default_value({0.8f, 0.8f, 0.8f, 1.0f}); b.add_input<decl::Float>(N_("Density")).default_value(1.0f).min(0.0f).max(1000.0f); + b.add_input<decl::Float>(N_("Weight")).unavailable(); b.add_output<decl::Shader>(N_("Volume")); } diff --git a/source/blender/nodes/shader/nodes/node_shader_volume_principled.cc b/source/blender/nodes/shader/nodes/node_shader_volume_principled.cc index 980782f571c..d414b4b2ef7 100644 --- a/source/blender/nodes/shader/nodes/node_shader_volume_principled.cc +++ b/source/blender/nodes/shader/nodes/node_shader_volume_principled.cc @@ -27,6 +27,7 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_input<decl::Color>(N_("Blackbody Tint")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); b.add_input<decl::Float>(N_("Temperature")).default_value(1000.0f).min(0.0f).max(6500.0f); b.add_input<decl::String>(N_("Temperature Attribute")); + b.add_input<decl::Float>(N_("Weight")).unavailable(); b.add_output<decl::Shader>(N_("Volume")); } diff --git a/source/blender/nodes/shader/nodes/node_shader_volume_scatter.cc b/source/blender/nodes/shader/nodes/node_shader_volume_scatter.cc index 6408efb769d..0c6859ad1fb 100644 --- a/source/blender/nodes/shader/nodes/node_shader_volume_scatter.cc +++ b/source/blender/nodes/shader/nodes/node_shader_volume_scatter.cc @@ -14,6 +14,7 @@ static void node_declare(NodeDeclarationBuilder &b) .min(-1.0f) .max(1.0f) .subtype(PROP_FACTOR); + b.add_input<decl::Float>(N_("Weight")).unavailable(); b.add_output<decl::Shader>(N_("Volume")); } diff --git a/source/blender/nodes/shader/nodes/node_shader_wireframe.cc b/source/blender/nodes/shader/nodes/node_shader_wireframe.cc index 1204c3a678f..6a1acda3353 100644 --- a/source/blender/nodes/shader/nodes/node_shader_wireframe.cc +++ b/source/blender/nodes/shader/nodes/node_shader_wireframe.cc @@ -28,17 +28,11 @@ static int node_shader_gpu_wireframe(GPUMaterial *mat, GPU_material_flag_set(mat, GPU_MATFLAG_BARYCENTRIC); /* node->custom1 is use_pixel_size */ if (node->custom1) { - return GPU_stack_link( - mat, node, "node_wireframe_screenspace", in, out, GPU_builtin(GPU_BARYCENTRIC_TEXCO)); + return GPU_stack_link(mat, node, "node_wireframe_screenspace", in, out); + } + else { + return GPU_stack_link(mat, node, "node_wireframe", in, out); } - - return GPU_stack_link(mat, - node, - "node_wireframe", - in, - out, - GPU_builtin(GPU_BARYCENTRIC_TEXCO), - GPU_builtin(GPU_BARYCENTRIC_DIST)); } } // namespace blender::nodes::node_shader_wireframe_cc diff --git a/source/blender/python/bmesh/bmesh_py_types.c b/source/blender/python/bmesh/bmesh_py_types.c index 46f89dd4103..baa2b0deb71 100644 --- a/source/blender/python/bmesh/bmesh_py_types.c +++ b/source/blender/python/bmesh/bmesh_py_types.c @@ -1061,8 +1061,9 @@ PyDoc_STRVAR(bpy_bmesh_from_object_doc, " :arg cage: Get the mesh as a deformed cage.\n" " :type cage: boolean\n" " :arg face_normals: Calculate face normals.\n" + " :type face_normals: boolean\n" " :arg vertex_normals: Calculate vertex normals.\n" - " :type face_normals: boolean\n"); + " :type vertex_normals: boolean\n"); static PyObject *bpy_bmesh_from_object(BPy_BMesh *self, PyObject *args, PyObject *kw) { static const char *kwlist[] = { @@ -1083,7 +1084,7 @@ static PyObject *bpy_bmesh_from_object(BPy_BMesh *self, PyObject *args, PyObject if (!PyArg_ParseTupleAndKeywords(args, kw, - "OO|$O&O&:from_object", + "OO|$O&O&O&:from_object", (char **)kwlist, &py_object, &py_depsgraph, @@ -1190,7 +1191,7 @@ static PyObject *bpy_bmesh_from_mesh(BPy_BMesh *self, PyObject *args, PyObject * if (!PyArg_ParseTupleAndKeywords(args, kw, - "O|$O&O&i:from_mesh", + "O|$O&O&O&i:from_mesh", (char **)kwlist, &py_mesh, PyC_ParseBool, @@ -3634,8 +3635,8 @@ void BPy_BM_init_types(void) BPy_BMLoopSeq_Type.tp_methods = bpy_bmloopseq_methods; BPy_BMIter_Type.tp_methods = NULL; - /*BPy_BMElem_Check() uses bpy_bm_elem_hash() to check types. - * if this changes update the macro */ + /* #BPy_BMElem_Check() uses #bpy_bm_elem_hash() to check types. + * if this changes update the macro. */ BPy_BMesh_Type.tp_hash = bpy_bm_hash; BPy_BMVert_Type.tp_hash = bpy_bm_elem_hash; BPy_BMEdge_Type.tp_hash = bpy_bm_elem_hash; diff --git a/source/blender/python/bmesh/bmesh_py_types_customdata.c b/source/blender/python/bmesh/bmesh_py_types_customdata.c index 1686f99df99..e591dfa2929 100644 --- a/source/blender/python/bmesh/bmesh_py_types_customdata.c +++ b/source/blender/python/bmesh/bmesh_py_types_customdata.c @@ -90,7 +90,7 @@ PyDoc_STRVAR( PyDoc_STRVAR(bpy_bmlayeraccess_collection__bevel_weight_doc, "Bevel weight float in [0 - 1].\n\n:type: :class:`BMLayerCollection`"); PyDoc_STRVAR(bpy_bmlayeraccess_collection__crease_doc, - "Edge crease for subdivision surface - float in [0 - 1].\n\n:type: " + "Crease for subdivision surface - float in [0 - 1].\n\n:type: " ":class:`BMLayerCollection`"); PyDoc_STRVAR( bpy_bmlayeraccess_collection__uv_doc, @@ -210,6 +210,11 @@ static PyGetSetDef bpy_bmlayeraccess_vert_getseters[] = { (setter)NULL, bpy_bmlayeraccess_collection__bevel_weight_doc, (void *)CD_BWEIGHT}, + {"crease", + (getter)bpy_bmlayeraccess_collection_get, + (setter)NULL, + bpy_bmlayeraccess_collection__crease_doc, + (void *)CD_CREASE}, {"skin", (getter)bpy_bmlayeraccess_collection_get, (setter)NULL, diff --git a/source/blender/python/generic/py_capi_utils.h b/source/blender/python/generic/py_capi_utils.h index 0f94b264159..ecb6db2b82c 100644 --- a/source/blender/python/generic/py_capi_utils.h +++ b/source/blender/python/generic/py_capi_utils.h @@ -8,6 +8,10 @@ #ifndef __PY_CAPI_UTILS_H__ #define __PY_CAPI_UTILS_H__ +#ifdef __cplusplus +extern "C" { +#endif + #include "BLI_sys_types.h" #include "BLI_utildefines_variadic.h" @@ -273,3 +277,7 @@ bool PyC_StructFmt_type_is_byte(char format); bool PyC_StructFmt_type_is_bool(char format); #endif /* __PY_CAPI_UTILS_H__ */ + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/python/gpu/CMakeLists.txt b/source/blender/python/gpu/CMakeLists.txt index 644bbd18173..e726cb7883d 100644 --- a/source/blender/python/gpu/CMakeLists.txt +++ b/source/blender/python/gpu/CMakeLists.txt @@ -29,6 +29,7 @@ set(SRC gpu_py_offscreen.c gpu_py_platform.c gpu_py_select.c + gpu_py_shader_create_info.cc gpu_py_shader.c gpu_py_state.c gpu_py_texture.c diff --git a/source/blender/python/gpu/gpu_py_buffer.c b/source/blender/python/gpu/gpu_py_buffer.c index e0c20b64c63..020535d002a 100644 --- a/source/blender/python/gpu/gpu_py_buffer.c +++ b/source/blender/python/gpu/gpu_py_buffer.c @@ -277,7 +277,10 @@ static int pygpu_buffer__tp_traverse(BPyGPUBuffer *self, visitproc visit, void * static int pygpu_buffer__tp_clear(BPyGPUBuffer *self) { - Py_CLEAR(self->parent); + if (self->parent) { + Py_CLEAR(self->parent); + self->buf.as_void = NULL; + } return 0; } @@ -287,7 +290,7 @@ static void pygpu_buffer__tp_dealloc(BPyGPUBuffer *self) PyObject_GC_UnTrack(self); Py_CLEAR(self->parent); } - else { + else if (self->buf.as_void) { MEM_freeN(self->buf.as_void); } @@ -394,9 +397,16 @@ static PyObject *pygpu_buffer__tp_new(PyTypeObject *UNUSED(type), PyObject *args return NULL; } - if (pygpu_buffer_dimensions_tot_len_compare(shape, shape_len, pybuffer.shape, pybuffer.ndim)) { + Py_ssize_t *pybuffer_shape = pybuffer.shape; + Py_ssize_t pybuffer_ndim = pybuffer.ndim; + if (!pybuffer_shape) { + pybuffer_shape = &pybuffer.len; + pybuffer_ndim = 1; + } + + if (pygpu_buffer_dimensions_tot_len_compare(shape, shape_len, pybuffer_shape, pybuffer_ndim)) { buffer = pygpu_buffer_make_from_data( - init, pygpu_dataformat.value_found, pybuffer.ndim, shape, pybuffer.buf); + init, pygpu_dataformat.value_found, shape_len, shape, pybuffer.buf); } PyBuffer_Release(&pybuffer); @@ -594,23 +604,31 @@ static void pygpu_buffer_strides_calc(const eGPUDataFormat format, } /* Here is the buffer interface function */ -static int pygpu_buffer__bf_getbuffer(BPyGPUBuffer *self, Py_buffer *view, int UNUSED(flags)) +static int pygpu_buffer__bf_getbuffer(BPyGPUBuffer *self, Py_buffer *view, int flags) { if (view == NULL) { PyErr_SetString(PyExc_ValueError, "NULL view in getbuffer"); return -1; } + memset(view, 0, sizeof(*view)); + view->obj = (PyObject *)self; view->buf = (void *)self->buf.as_void; view->len = bpygpu_Buffer_size(self); view->readonly = 0; view->itemsize = GPU_texture_dataformat_size(self->format); - view->format = (char *)pygpu_buffer_formatstr(self->format); - view->ndim = self->shape_len; - view->shape = self->shape; - view->strides = MEM_mallocN(view->ndim * sizeof(*view->strides), "BPyGPUBuffer strides"); - pygpu_buffer_strides_calc(self->format, view->ndim, view->shape, view->strides); + if (flags & PyBUF_FORMAT) { + view->format = (char *)pygpu_buffer_formatstr(self->format); + } + if (flags & PyBUF_ND) { + view->ndim = self->shape_len; + view->shape = self->shape; + } + if (flags & PyBUF_STRIDES) { + view->strides = MEM_mallocN(view->ndim * sizeof(*view->strides), "BPyGPUBuffer strides"); + pygpu_buffer_strides_calc(self->format, view->ndim, view->shape, view->strides); + } view->suboffsets = NULL; view->internal = NULL; diff --git a/source/blender/python/gpu/gpu_py_element.c b/source/blender/python/gpu/gpu_py_element.c index f2836576659..04975fcef96 100644 --- a/source/blender/python/gpu/gpu_py_element.c +++ b/source/blender/python/gpu/gpu_py_element.c @@ -70,12 +70,14 @@ static PyObject *pygpu_IndexBuf__tp_new(PyTypeObject *UNUSED(type), PyObject *ar if (pybuffer.ndim != 1 && pybuffer.shape[1] != verts_per_prim) { PyErr_Format(PyExc_ValueError, "Each primitive must exactly %d indices", verts_per_prim); + PyBuffer_Release(&pybuffer); return NULL; } if (pybuffer.itemsize != 4 || PyC_StructFmt_type_is_float_any(PyC_StructFmt_type_from_str(pybuffer.format))) { PyErr_Format(PyExc_ValueError, "Each index must be an 4-bytes integer value"); + PyBuffer_Release(&pybuffer); return NULL; } diff --git a/source/blender/python/gpu/gpu_py_shader.c b/source/blender/python/gpu/gpu_py_shader.c index c74b3e173d1..9fe4bdcbaa0 100644 --- a/source/blender/python/gpu/gpu_py_shader.c +++ b/source/blender/python/gpu/gpu_py_shader.c @@ -537,16 +537,15 @@ static PyObject *pygpu_shader_uniform_block(BPyGPUShader *self, PyObject *args) return NULL; } - int slot = GPU_shader_get_uniform_block(self->shader, name); - if (slot == -1) { + int binding = GPU_shader_get_uniform_block_binding(self->shader, name); + if (binding == -1) { PyErr_SetString( PyExc_BufferError, - "GPUShader.uniform_buffer: uniform block not found, make sure the name is correct"); + "GPUShader.uniform_block: uniform block not found, make sure the name is correct"); return NULL; } - GPU_uniformbuf_bind(py_ubo->ubo, slot); - GPU_shader_uniform_1i(self->shader, name, slot); + GPU_uniformbuf_bind(py_ubo->ubo, binding); Py_RETURN_NONE; } @@ -831,6 +830,38 @@ static PyObject *pygpu_shader_code_from_builtin(BPyGPUShader *UNUSED(self), PyOb return r_dict; } +PyDoc_STRVAR(pygpu_shader_create_from_info_doc, + ".. function:: create_from_info(shader_info)\n" + "\n" + " Create shader from a GPUShaderCreateInfo.\n" + "\n" + " :param shader_info: GPUShaderCreateInfo\n" + " :type shader_info: :class:`bpy.types.GPUShaderCreateInfo`\n" + " :return: Shader object corresponding to the given name.\n" + " :rtype: :class:`bpy.types.GPUShader`\n"); +static PyObject *pygpu_shader_create_from_info(BPyGPUShader *UNUSED(self), + BPyGPUShaderCreateInfo *o) +{ + if (!BPyGPUShaderCreateInfo_Check(o)) { + PyErr_Format(PyExc_TypeError, "Expected a GPUShaderCreateInfo, got %s", Py_TYPE(o)->tp_name); + return NULL; + } + + char error[128]; + if (!GPU_shader_create_info_check_error(o->info, error)) { + PyErr_SetString(PyExc_Exception, error); + return NULL; + } + + GPUShader *shader = GPU_shader_create_from_info(o->info); + if (!shader) { + PyErr_SetString(PyExc_Exception, "Shader Compile Error, see console for more details"); + return NULL; + } + + return BPyGPUShader_CreatePyObject(shader, false); +} + static struct PyMethodDef pygpu_shader_module__tp_methods[] = { {"unbind", (PyCFunction)pygpu_shader_unbind, METH_NOARGS, pygpu_shader_unbind_doc}, {"from_builtin", @@ -841,6 +872,10 @@ static struct PyMethodDef pygpu_shader_module__tp_methods[] = { (PyCFunction)pygpu_shader_code_from_builtin, METH_O, pygpu_shader_code_from_builtin_doc}, + {"create_from_info", + (PyCFunction)pygpu_shader_create_from_info, + METH_O, + pygpu_shader_create_from_info_doc}, {NULL, NULL, 0, NULL}, }; diff --git a/source/blender/python/gpu/gpu_py_shader.h b/source/blender/python/gpu/gpu_py_shader.h index eb4810efeac..b5944c4b3a0 100644 --- a/source/blender/python/gpu/gpu_py_shader.h +++ b/source/blender/python/gpu/gpu_py_shader.h @@ -6,6 +6,12 @@ #pragma once +/* Make sure that there is always a reference count for PyObjects of type String as the strings are + * passed by reference in the #GPUStageInterfaceInfo and #GPUShaderCreateInfo APIs. */ +#define USE_GPU_PY_REFERENCES + +/* gpu_py_shader.c */ + extern PyTypeObject BPyGPUShader_Type; #define BPyGPUShader_Check(v) (Py_TYPE(v) == &BPyGPUShader_Type) @@ -18,3 +24,44 @@ typedef struct BPyGPUShader { PyObject *BPyGPUShader_CreatePyObject(struct GPUShader *shader, bool is_builtin); PyObject *bpygpu_shader_init(void); + +#ifdef __cplusplus +extern "C" { +#endif + +/* gpu_py_shader_create_info.cc */ + +extern PyTypeObject BPyGPUShaderCreateInfo_Type; +extern PyTypeObject BPyGPUStageInterfaceInfo_Type; + +#define BPyGPUShaderCreateInfo_Check(v) (Py_TYPE(v) == &BPyGPUShaderCreateInfo_Type) +#define BPyGPUStageInterfaceInfo_Check(v) (Py_TYPE(v) == &BPyGPUStageInterfaceInfo_Type) + +typedef struct BPyGPUStageInterfaceInfo { + PyObject_VAR_HEAD + struct GPUStageInterfaceInfo *interface; +#ifdef USE_GPU_PY_REFERENCES + /* Just to keep a user to prevent freeing buf's we're using. */ + PyObject *references; +#endif +} BPyGPUStageInterfaceInfo; + +typedef struct BPyGPUShaderCreateInfo { + PyObject_VAR_HEAD + struct GPUShaderCreateInfo *info; +#ifdef USE_GPU_PY_REFERENCES + /* Just to keep a user to prevent freeing buf's we're using. */ + PyObject *vertex_source; + PyObject *fragment_source; + PyObject *typedef_source; + PyObject *references; +#endif + size_t constants_total_size; +} BPyGPUShaderCreateInfo; + +PyObject *BPyGPUStageInterfaceInfo_CreatePyObject(struct GPUStageInterfaceInfo *interface); +PyObject *BPyGPUShaderCreateInfo_CreatePyObject(struct GPUShaderCreateInfo *info); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/python/gpu/gpu_py_shader_create_info.cc b/source/blender/python/gpu/gpu_py_shader_create_info.cc new file mode 100644 index 00000000000..3b043c605fa --- /dev/null +++ b/source/blender/python/gpu/gpu_py_shader_create_info.cc @@ -0,0 +1,1130 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup bpygpu + * + * - Use `bpygpu_` for local API. + * - Use `BPyGPU` for public API. + */ + +#include <Python.h> + +#include "BLI_utildefines.h" + +#include "GPU_shader.h" +#include "intern/gpu_shader_create_info.hh" + +#include "../generic/py_capi_utils.h" +#include "../generic/python_utildefines.h" + +#include "gpu_py_shader.h" /* own include */ + +//#define USE_PYGPU_SHADER_INFO_IMAGE_METHOD + +using blender::gpu::shader::DualBlend; +using blender::gpu::shader::Frequency; +using blender::gpu::shader::ImageType; +using blender::gpu::shader::ShaderCreateInfo; +using blender::gpu::shader::StageInterfaceInfo; +using blender::gpu::shader::Type; + +#ifdef USE_PYGPU_SHADER_INFO_IMAGE_METHOD +using blender::gpu::shader::Qualifier; + +# define PYDOC_QUALIFIERS \ + " - ``NO_RESTRICT``\n" \ + " - ``READ``\n" \ + " - ``WRITE``\n" +static const struct PyC_FlagSet pygpu_qualifiers[] = { + {(int)Qualifier::NO_RESTRICT, "NO_RESTRICT"}, + {(int)Qualifier::READ, "READ"}, + {(int)Qualifier::WRITE, "WRITE"}, + {0, nullptr}, +}; +#endif + +#define PYDOC_TYPE_LIST \ + " - ``FLOAT``\n" \ + " - ``VEC2``\n" \ + " - ``VEC3``\n" \ + " - ``VEC4``\n" \ + " - ``MAT3``\n" \ + " - ``MAT4``\n" \ + " - ``UINT``\n" \ + " - ``UVEC2``\n" \ + " - ``UVEC3``\n" \ + " - ``UVEC4``\n" \ + " - ``INT``\n" \ + " - ``IVEC2``\n" \ + " - ``IVEC3``\n" \ + " - ``IVEC4``\n" \ + " - ``BOOL``\n" +static const struct PyC_StringEnumItems pygpu_attrtype_items[] = { + {(int)Type::FLOAT, "FLOAT"}, + {(int)Type::VEC2, "VEC2"}, + {(int)Type::VEC3, "VEC3"}, + {(int)Type::VEC4, "VEC4"}, + {(int)Type::MAT3, "MAT3"}, + {(int)Type::MAT4, "MAT4"}, + {(int)Type::UINT, "UINT"}, + {(int)Type::UVEC2, "UVEC2"}, + {(int)Type::UVEC3, "UVEC3"}, + {(int)Type::UVEC4, "UVEC4"}, + {(int)Type::INT, "INT"}, + {(int)Type::IVEC2, "IVEC2"}, + {(int)Type::IVEC3, "IVEC3"}, + {(int)Type::IVEC4, "IVEC4"}, + {(int)Type::BOOL, "BOOL"}, + {0, nullptr}, +}; + +#define PYDOC_IMAGE_TYPES \ + " - ``FLOAT_BUFFER``\n" \ + " - ``FLOAT_1D``\n" \ + " - ``FLOAT_1D_ARRAY``\n" \ + " - ``FLOAT_2D``\n" \ + " - ``FLOAT_2D_ARRAY``\n" \ + " - ``FLOAT_3D``\n" \ + " - ``FLOAT_CUBE``\n" \ + " - ``FLOAT_CUBE_ARRAY``\n" \ + " - ``INT_BUFFER``\n" \ + " - ``INT_1D``\n" \ + " - ``INT_1D_ARRAY``\n" \ + " - ``INT_2D``\n" \ + " - ``INT_2D_ARRAY``\n" \ + " - ``INT_3D``\n" \ + " - ``INT_CUBE``\n" \ + " - ``INT_CUBE_ARRAY``\n" \ + " - ``UINT_BUFFER``\n" \ + " - ``UINT_1D``\n" \ + " - ``UINT_1D_ARRAY``\n" \ + " - ``UINT_2D``\n" \ + " - ``UINT_2D_ARRAY``\n" \ + " - ``UINT_3D``\n" \ + " - ``UINT_CUBE``\n" \ + " - ``UINT_CUBE_ARRAY``\n" \ + " - ``SHADOW_2D``\n" \ + " - ``SHADOW_2D_ARRAY``\n" \ + " - ``SHADOW_CUBE``\n" \ + " - ``SHADOW_CUBE_ARRAY``\n" \ + " - ``DEPTH_2D``\n" \ + " - ``DEPTH_2D_ARRAY``\n" \ + " - ``DEPTH_CUBE``\n" \ + " - ``DEPTH_CUBE_ARRAY``\n" +static const struct PyC_StringEnumItems pygpu_imagetype_items[] = { + {(int)ImageType::FLOAT_BUFFER, "FLOAT_BUFFER"}, + {(int)ImageType::FLOAT_1D, "FLOAT_1D"}, + {(int)ImageType::FLOAT_1D_ARRAY, "FLOAT_1D_ARRAY"}, + {(int)ImageType::FLOAT_2D, "FLOAT_2D"}, + {(int)ImageType::FLOAT_2D_ARRAY, "FLOAT"}, + {(int)ImageType::FLOAT_3D, "FLOAT_2D_ARRAY"}, + {(int)ImageType::FLOAT_CUBE, "FLOAT_CUBE"}, + {(int)ImageType::FLOAT_CUBE_ARRAY, "FLOAT_CUBE_ARRAY"}, + {(int)ImageType::INT_BUFFER, "INT_BUFFER"}, + {(int)ImageType::INT_1D, "INT_1D"}, + {(int)ImageType::INT_1D_ARRAY, "INT_1D_ARRAY"}, + {(int)ImageType::INT_2D, "INT_2D"}, + {(int)ImageType::INT_2D_ARRAY, "INT_2D_ARRAY"}, + {(int)ImageType::INT_3D, "INT_3D"}, + {(int)ImageType::INT_CUBE, "INT_CUBE"}, + {(int)ImageType::INT_CUBE_ARRAY, "INT_CUBE_ARRAY"}, + {(int)ImageType::UINT_BUFFER, "UINT_BUFFER"}, + {(int)ImageType::UINT_1D, "UINT_1D"}, + {(int)ImageType::UINT_1D_ARRAY, "UINT_1D_ARRAY"}, + {(int)ImageType::UINT_2D, "UINT_2D"}, + {(int)ImageType::UINT_2D_ARRAY, "UINT_2D_ARRAY"}, + {(int)ImageType::UINT_3D, "UINT_3D"}, + {(int)ImageType::UINT_CUBE, "UINT_CUBE"}, + {(int)ImageType::UINT_CUBE_ARRAY, "UINT_CUBE_ARRAY"}, + {(int)ImageType::SHADOW_2D, "SHADOW_2D"}, + {(int)ImageType::SHADOW_2D_ARRAY, "SHADOW_2D_ARRAY"}, + {(int)ImageType::SHADOW_CUBE, "SHADOW_CUBE"}, + {(int)ImageType::SHADOW_CUBE_ARRAY, "SHADOW_CUBE_ARRAY"}, + {(int)ImageType::DEPTH_2D, "DEPTH_2D"}, + {(int)ImageType::DEPTH_2D_ARRAY, "DEPTH_2D_ARRAY"}, + {(int)ImageType::DEPTH_CUBE, "DEPTH_CUBE"}, + {(int)ImageType::DEPTH_CUBE_ARRAY, "DEPTH_CUBE_ARRAY"}, + {0, nullptr}, +}; + +static const struct PyC_StringEnumItems pygpu_dualblend_items[] = { + {(int)DualBlend::NONE, "NONE"}, + {(int)DualBlend::SRC_0, "SRC_0"}, + {(int)DualBlend::SRC_1, "SRC_1"}, + {0, nullptr}, +}; + +/* -------------------------------------------------------------------- */ +/** \name GPUStageInterfaceInfo Methods + * \{ */ + +static bool pygpu_interface_info_get_args(BPyGPUStageInterfaceInfo *self, + PyObject *args, + const char *format, + Type *r_type, + const char **r_name) +{ + struct PyC_StringEnum pygpu_type = {pygpu_attrtype_items}; + PyObject *py_name; + + if (!PyArg_ParseTuple(args, format, PyC_ParseStringEnum, &pygpu_type, &py_name)) { + return false; + } + + const char *name = PyUnicode_AsUTF8(py_name); + if (name == nullptr) { + return false; + } + +#ifdef USE_GPU_PY_REFERENCES + PyList_Append(self->references, (PyObject *)py_name); +#endif + + *r_type = (Type)pygpu_type.value_found; + *r_name = name; + return true; +} + +PyDoc_STRVAR(pygpu_interface_info_smooth_doc, + ".. method:: smooth(type, name)\n" + "\n" + " Add an attribute with qualifier of type `smooth` to the interface block.\n" + "\n" + " :param type: One of these types:\n" + "\n" PYDOC_TYPE_LIST + "\n" + " :type type: str\n" + " :param name: name of the attribute.\n" + " :type name: str\n"); +static PyObject *pygpu_interface_info_smooth(BPyGPUStageInterfaceInfo *self, PyObject *args) +{ + Type type; + const char *name; + if (!pygpu_interface_info_get_args(self, args, "O&O:smooth", &type, &name)) { + return nullptr; + } + + StageInterfaceInfo *interface = reinterpret_cast<StageInterfaceInfo *>(self->interface); + interface->smooth(type, name); + Py_RETURN_NONE; +} + +PyDoc_STRVAR(pygpu_interface_info_flat_doc, + ".. method:: flat(type, name)\n" + "\n" + " Add an attribute with qualifier of type `flat` to the interface block.\n" + "\n" + " :param type: One of these types:\n" + "\n" PYDOC_TYPE_LIST + "\n" + " :type type: str\n" + " :param name: name of the attribute.\n" + " :type name: str\n"); +static PyObject *pygpu_interface_info_flat(BPyGPUStageInterfaceInfo *self, PyObject *args) +{ + Type type; + const char *name; + if (!pygpu_interface_info_get_args(self, args, "O&O:flat", &type, &name)) { + return nullptr; + } + + StageInterfaceInfo *interface = reinterpret_cast<StageInterfaceInfo *>(self->interface); + interface->flat(type, name); + Py_RETURN_NONE; +} + +PyDoc_STRVAR( + pygpu_interface_info_no_perspective_doc, + ".. method:: no_perspective(type, name)\n" + "\n" + " Add an attribute with qualifier of type `no_perspective` to the interface block.\n" + "\n" + " :param type: One of these types:\n" + "\n" PYDOC_TYPE_LIST + "\n" + " :type type: str\n" + " :param name: name of the attribute.\n" + " :type name: str\n"); +static PyObject *pygpu_interface_info_no_perspective(BPyGPUStageInterfaceInfo *self, + PyObject *args) +{ + Type type; + const char *name; + if (!pygpu_interface_info_get_args(self, args, "O&O:no_perspective", &type, &name)) { + return nullptr; + } + + StageInterfaceInfo *interface = reinterpret_cast<StageInterfaceInfo *>(self->interface); + interface->no_perspective(type, name); + Py_RETURN_NONE; +} + +static struct PyMethodDef pygpu_interface_info__tp_methods[] = { + {"smooth", + (PyCFunction)pygpu_interface_info_smooth, + METH_VARARGS, + pygpu_interface_info_smooth_doc}, + {"flat", (PyCFunction)pygpu_interface_info_flat, METH_VARARGS, pygpu_interface_info_flat_doc}, + {"no_perspective", + (PyCFunction)pygpu_interface_info_no_perspective, + METH_VARARGS, + pygpu_interface_info_no_perspective_doc}, + {nullptr, nullptr, 0, nullptr}, +}; + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name GPUStageInterfaceInfo Getters and Setters + * \{ */ + +PyDoc_STRVAR(pygpu_interface_info_name_doc, + "Name of the interface block.\n" + "\n" + ":type: str"); +static PyObject *pygpu_interface_info_name_get(BPyGPUStageInterfaceInfo *self, + void *UNUSED(closure)) +{ + StageInterfaceInfo *interface = reinterpret_cast<StageInterfaceInfo *>(self->interface); + return PyUnicode_FromString(interface->name.c_str()); +} + +static PyGetSetDef pygpu_interface_info__tp_getseters[] = { + {"name", + (getter)pygpu_interface_info_name_get, + (setter) nullptr, + pygpu_interface_info_name_doc, + nullptr}, + {nullptr, nullptr, nullptr, nullptr, nullptr} /* Sentinel */ +}; + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name GPUStageInterfaceInfo Type + * \{ */ + +static PyObject *pygpu_interface_info__tp_new(PyTypeObject *UNUSED(type), + PyObject *args, + PyObject *kwds) +{ + if (kwds) { + PyErr_SetString(PyExc_TypeError, "no keywords are expected"); + return nullptr; + } + + const char *name; + if (!PyArg_ParseTuple(args, "s:GPUStageInterfaceInfo.__new__*", &name)) { + return nullptr; + } + + StageInterfaceInfo *interface = new StageInterfaceInfo(name, ""); + GPUStageInterfaceInfo *interface_info = reinterpret_cast<GPUStageInterfaceInfo *>(interface); + + auto *self = BPyGPUStageInterfaceInfo_CreatePyObject(interface_info); + +#ifdef USE_GPU_PY_REFERENCES + PyObject *py_name = PyTuple_GET_ITEM(args, 0); + PyList_Append(((BPyGPUStageInterfaceInfo *)self)->references, py_name); +#endif + + return self; +} + +#ifdef USE_GPU_PY_REFERENCES + +static int pygpu_interface_info__tp_traverse(PyObject *self, visitproc visit, void *arg) +{ + BPyGPUStageInterfaceInfo *py_interface = reinterpret_cast<BPyGPUStageInterfaceInfo *>(self); + Py_VISIT(py_interface->references); + return 0; +} + +static int pygpu_interface_info__tp_clear(PyObject *self) +{ + BPyGPUStageInterfaceInfo *py_interface = reinterpret_cast<BPyGPUStageInterfaceInfo *>(self); + Py_CLEAR(py_interface->references); + return 0; +} + +#endif + +static void pygpu_interface_info__tp_dealloc(PyObject *self) +{ + BPyGPUStageInterfaceInfo *py_interface = reinterpret_cast<BPyGPUStageInterfaceInfo *>(self); + StageInterfaceInfo *interface = reinterpret_cast<StageInterfaceInfo *>(py_interface->interface); + delete interface; + +#ifdef USE_GPU_PY_REFERENCES + PyObject_GC_UnTrack(self); + if (py_interface->references) { + pygpu_interface_info__tp_clear(self); + Py_CLEAR(py_interface->references); + } +#endif + + Py_TYPE(self)->tp_free((PyObject *)self); +} + +PyDoc_STRVAR(pygpu_interface_info__tp_doc, + ".. class:: GPUStageInterfaceInfo(name)\n" + "\n" + " List of varyings between shader stages.\n\n" + "\n" + " :param name: Name of the interface block.\n" + " :type value: str\n"); +constexpr PyTypeObject pygpu_interface_info_type() +{ + PyTypeObject pytype = {PyVarObject_HEAD_INIT(nullptr, 0)}; + pytype.tp_name = "GPUStageInterfaceInfo"; + pytype.tp_basicsize = sizeof(BPyGPUStageInterfaceInfo); + pytype.tp_dealloc = pygpu_interface_info__tp_dealloc; + pytype.tp_doc = pygpu_interface_info__tp_doc; +#ifdef USE_GPU_PY_REFERENCES + pytype.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC; + pytype.tp_traverse = pygpu_interface_info__tp_traverse; + pytype.tp_clear = pygpu_interface_info__tp_clear; +#else + pytype.tp_flags = Py_TPFLAGS_DEFAULT, +#endif + pytype.tp_methods = pygpu_interface_info__tp_methods; + pytype.tp_getset = pygpu_interface_info__tp_getseters; + pytype.tp_new = pygpu_interface_info__tp_new; + return pytype; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name GPUShaderCreateInfo Methods + * \{ */ + +PyDoc_STRVAR(pygpu_shader_info_vertex_in_doc, + ".. method:: vertex_in(slot, type, name)\n" + "\n" + " Add a vertex shader input attribute.\n" + "\n" + " :param slot: The attribute index.\n" + " :type slot: int\n" + " :param type: One of these types:\n" + "\n" PYDOC_TYPE_LIST + "\n" + " :type type: str\n" + " :param name: name of the attribute.\n" + " :type name: str\n"); +static PyObject *pygpu_shader_info_vertex_in(BPyGPUShaderCreateInfo *self, PyObject *args) +{ + int slot; + struct PyC_StringEnum pygpu_type = {pygpu_attrtype_items}; + const char *param; + + if (!PyArg_ParseTuple(args, "iO&s:vertex_in", &slot, PyC_ParseStringEnum, &pygpu_type, ¶m)) { + return nullptr; + } + +#ifdef USE_GPU_PY_REFERENCES + PyObject *py_name = PyTuple_GET_ITEM(args, 2); + PyList_Append(self->references, py_name); +#endif + + ShaderCreateInfo *info = reinterpret_cast<ShaderCreateInfo *>(self->info); + info->vertex_in(slot, (Type)pygpu_type.value_found, param); + Py_RETURN_NONE; +} + +PyDoc_STRVAR(pygpu_shader_info_vertex_out_doc, + ".. method:: vertex_out(interface)\n" + "\n" + " Add a vertex shader output interface block.\n" + "\n" + " :param interface: Object describing the block.\n" + " :type interface: :class:`gpu.types.GPUStageInterfaceInfo`\n"); +static PyObject *pygpu_shader_info_vertex_out(BPyGPUShaderCreateInfo *self, + BPyGPUStageInterfaceInfo *o) +{ + if (!BPyGPUStageInterfaceInfo_Check(o)) { + PyErr_Format(PyExc_TypeError, "Expected a GPUStageInterfaceInfo, got %s", Py_TYPE(o)->tp_name); + return nullptr; + } + +#ifdef USE_GPU_PY_REFERENCES + PyList_Append(self->references, (PyObject *)o); +#endif + + ShaderCreateInfo *info = reinterpret_cast<ShaderCreateInfo *>(self->info); + StageInterfaceInfo *interface = reinterpret_cast<StageInterfaceInfo *>(o->interface); + info->vertex_out(*interface); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(pygpu_shader_info_fragment_out_doc, + ".. method:: fragment_out(slot, type, name, blend='NONE')\n" + "\n" + " Specify a fragment output corresponding to a framebuffer target slot.\n" + "\n" + " :param slot: The attribute index.\n" + " :type slot: int\n" + " :param type: One of these types:\n" + "\n" PYDOC_TYPE_LIST + "\n" + " :type type: str\n" + " :param name: Name of the attribute.\n" + " :type name: str\n" + " :param blend: Dual Source Blending Index. It can be 'NONE', 'SRC_0' or 'SRC_1'.\n" + " :type blend: str\n"); +static PyObject *pygpu_shader_info_fragment_out(BPyGPUShaderCreateInfo *self, + PyObject *args, + PyObject *kwds) +{ + int slot; + struct PyC_StringEnum pygpu_type = {pygpu_attrtype_items}; + const char *name; + struct PyC_StringEnum blend_type = {pygpu_dualblend_items, (int)DualBlend::NONE}; + + static const char *_keywords[] = {"slot", "type", "name", "blend", nullptr}; + static _PyArg_Parser _parser = { + "i" /* `slot` */ + "O&" /* `type` */ + "s" /* `name` */ + "|$" /* Optional keyword only arguments. */ + "O&" /* `blend` */ + ":fragment_out", + _keywords, + nullptr, + }; + if (!_PyArg_ParseTupleAndKeywordsFast(args, + kwds, + &_parser, + &slot, + PyC_ParseStringEnum, + &pygpu_type, + &name, + PyC_ParseStringEnum, + &blend_type)) { + return nullptr; + } + +#ifdef USE_GPU_PY_REFERENCES + PyObject *py_name = PyTuple_GET_ITEM(args, 2); + PyList_Append(self->references, py_name); +#endif + + ShaderCreateInfo *info = reinterpret_cast<ShaderCreateInfo *>(self->info); + info->fragment_out(slot, (Type)pygpu_type.value_found, name, (DualBlend)blend_type.value_found); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR( + pygpu_shader_info_uniform_buf_doc, + ".. method:: uniform_buf(slot, type_name, name)\n" + "\n" + " Specify a uniform variable whose type can be one of those declared in `typedef_source`.\n" + "\n" + " :param slot: The uniform variable index.\n" + " :type slot: int\n" + " :param type_name: Name of the data type. It can be a struct type defined in the source " + "passed through the :meth:`gpu.types.GPUShaderCreateInfo.typedef_source`.\n" + " :type type_name: str\n" + " :param name: The uniform variable name.\n" + " :type name: str\n"); +static PyObject *pygpu_shader_info_uniform_buf(BPyGPUShaderCreateInfo *self, PyObject *args) +{ + int slot; + const char *type_name; + const char *name; + + if (!PyArg_ParseTuple(args, "iss:uniform_buf", &slot, &type_name, &name)) { + return nullptr; + } + +#ifdef USE_GPU_PY_REFERENCES + PyList_Append(self->references, PyTuple_GET_ITEM(args, 1)); /* type_name */ + PyList_Append(self->references, PyTuple_GET_ITEM(args, 2)); /* name */ +#endif + + ShaderCreateInfo *info = reinterpret_cast<ShaderCreateInfo *>(self->info); + info->uniform_buf(slot, type_name, name); + + Py_RETURN_NONE; +} + +#ifdef USE_PYGPU_SHADER_INFO_IMAGE_METHOD +PyDoc_STRVAR( + pygpu_shader_info_image_doc, + ".. method:: image(slot, format, type, name, qualifiers={'NO_RESTRICT'})\n" + "\n" + " Specify an image resource used for arbitrary load and store operations.\n" + "\n" + " :param slot: The image resource index.\n" + " :type slot: int\n" + " :param format: The GPUTexture format that is passed to the shader. Possible values are:\n" + "" PYDOC_TEX_FORMAT_ITEMS + " :type format: str\n" + " :param type: The data type describing how the image is to be read in the shader. " + "Possible values are:\n" + "\n" PYDOC_IMAGE_TYPES + "\n" + " :type type: str\n" + " :param name: The image resource name.\n" + " :type name: str\n" + " :param qualifiers: Set containing values that describe how the image resource is to be " + "read or written. Possible values are:\n" + "" PYDOC_QUALIFIERS + "" + " :type qualifiers: set\n"); +static PyObject *pygpu_shader_info_image(BPyGPUShaderCreateInfo *self, + PyObject *args, + PyObject *kwds) +{ + int slot; + struct PyC_StringEnum pygpu_texformat = {pygpu_textureformat_items}; + struct PyC_StringEnum pygpu_imagetype = {pygpu_imagetype_items}; + const char *name; + PyObject *py_qualifiers = nullptr; + Qualifier qualifier = Qualifier::NO_RESTRICT; + + static const char *_keywords[] = {"slot", "format", "type", "name", "qualifiers", nullptr}; + static _PyArg_Parser _parser = { + "i" /* `slot` */ + "O&" /* `format` */ + "O&" /* `type` */ + "s" /* `name` */ + "|$" /* Optional keyword only arguments. */ + "O" /* `qualifiers` */ + ":image", + _keywords, + nullptr, + }; + if (!_PyArg_ParseTupleAndKeywordsFast(args, + kwds, + &_parser, + &slot, + PyC_ParseStringEnum, + &pygpu_texformat, + PyC_ParseStringEnum, + &pygpu_imagetype, + &name, + &py_qualifiers)) { + return nullptr; + } + + if (py_qualifiers && + PyC_FlagSet_ToBitfield( + pygpu_qualifiers, py_qualifiers, (int *)&qualifier, "shader_info.image") == -1) { + return nullptr; + } + +# ifdef USE_GPU_PY_REFERENCES + PyList_Append(self->references, PyTuple_GET_ITEM(args, 3)); /* name */ +# endif + + ShaderCreateInfo *info = reinterpret_cast<ShaderCreateInfo *>(self->info); + info->image(slot, + (eGPUTextureFormat)pygpu_texformat.value_found, + qualifier, + (ImageType)pygpu_imagetype.value_found, + name); + + Py_RETURN_NONE; +} +#endif + +PyDoc_STRVAR( + pygpu_shader_info_sampler_doc, + ".. method:: sampler(slot, type, name)\n" + "\n" + " Specify an image texture sampler.\n" + "\n" + " :param slot: The image texture sampler index.\n" + " :type slot: int\n" + " :param type: The data type describing the format of each sampler unit. Possible values " + "are:\n" + "\n" PYDOC_IMAGE_TYPES + "\n" + " :type type: str\n" + " :param name: The image texture sampler name.\n" + " :type name: str\n"); +static PyObject *pygpu_shader_info_sampler(BPyGPUShaderCreateInfo *self, PyObject *args) +{ + int slot; + struct PyC_StringEnum pygpu_samplertype = {pygpu_imagetype_items}; + const char *name; + + if (!PyArg_ParseTuple( + args, "iO&s:sampler", &slot, PyC_ParseStringEnum, &pygpu_samplertype, &name)) { + return nullptr; + } + +#ifdef USE_GPU_PY_REFERENCES + PyList_Append(self->references, PyTuple_GET_ITEM(args, 2)); /* name */ +#endif + + ShaderCreateInfo *info = reinterpret_cast<ShaderCreateInfo *>(self->info); + info->sampler(slot, (ImageType)pygpu_samplertype.value_found, name); + + Py_RETURN_NONE; +} + +static int constant_type_size(Type type) +{ + switch (type) { + case Type::BOOL: + case Type::FLOAT: + case Type::INT: + case Type::UINT: + return 4; + break; + case Type::VEC2: + case Type::UVEC2: + case Type::IVEC2: + return 8; + break; + case Type::VEC3: + case Type::UVEC3: + case Type::IVEC3: + return 12; + break; + case Type::VEC4: + case Type::UVEC4: + case Type::IVEC4: + return 16; + break; + case Type::MAT3: + return 36 + 3 * 4; + case Type::MAT4: + return 64; + break; + } + BLI_assert(false); + return -1; +} + +static int constants_calc_size(ShaderCreateInfo *info) +{ + int size_prev = 0; + int size_last = 0; + for (const ShaderCreateInfo::PushConst &uniform : info->push_constants_) { + int pad = 0; + int size = constant_type_size(uniform.type); + if (size_last && size_last != size) { + /* Calc pad. */ + int pack = (size == 8) ? 8 : 16; + if (size_last < size) { + pad = pack - (size_last % pack); + } + else { + pad = size_prev % pack; + } + } + else if (size == 12) { + /* It is still unclear how Vulkan handles padding for `vec3` constants. For now let's follow + * the rules of the `std140` layout. */ + pad = 4; + } + size_prev += pad + size * std::max(1, uniform.array_size); + size_last = size; + } + return size_prev + (size_prev % 16); +} + +PyDoc_STRVAR(pygpu_shader_info_push_constant_doc, + ".. method:: push_constant(type, name, size=0)\n" + "\n" + " Specify a global access constant.\n" + "\n" + " :param type: One of these types:\n" + "\n" PYDOC_TYPE_LIST + "\n" + " :type type: str\n" + " :param name: Name of the constant.\n" + " :type name: str\n" + " :param size: If not zero, indicates that the constant is an array with the " + "specified size.\n" + " :type size: uint\n"); +static PyObject *pygpu_shader_info_push_constant(BPyGPUShaderCreateInfo *self, + PyObject *args, + PyObject *kwds) +{ + struct PyC_StringEnum pygpu_type = {pygpu_attrtype_items}; + const char *name = nullptr; + int array_size = 0; + + static const char *_keywords[] = {"type", "name", "size", nullptr}; + static _PyArg_Parser _parser = { + "O&" /* `type` */ + "s" /* `name` */ + "|" /* Optional arguments. */ + "I" /* `size` */ + ":push_constant", + _keywords, + nullptr, + }; + if (!_PyArg_ParseTupleAndKeywordsFast( + args, kwds, &_parser, PyC_ParseStringEnum, &pygpu_type, &name, &array_size)) { + return nullptr; + } + +#ifdef USE_GPU_PY_REFERENCES + PyObject *py_name = PyTuple_GET_ITEM(args, 1); + PyList_Append(self->references, py_name); +#endif + + ShaderCreateInfo *info = reinterpret_cast<ShaderCreateInfo *>(self->info); + info->push_constant((Type)pygpu_type.value_found, name, array_size); + +#define VULKAN_LIMIT 128 + int size = constants_calc_size(info); + if (size > VULKAN_LIMIT) { + printf("Push constants have a minimum supported size of " + STRINGIFY(VULKAN_LIMIT) + " bytes, however the constants added so far already reach %d bytes. Consider using UBO.\n", size); + } +#undef VULKAN_LIMIT + + Py_RETURN_NONE; +} + +PyDoc_STRVAR( + pygpu_shader_info_vertex_source_doc, + ".. method:: vertex_source(source)\n" + "\n" + " Vertex shader source code written in GLSL.\n" + "\n" + " Example:\n" + "\n" + " .. code-block:: python\n" + "\n" + " \"void main {gl_Position = vec4(pos, 1.0);}\"\n" + "\n" + " :param source: The vertex shader source code.\n" + " :type source: str\n" + "\n" + " .. seealso:: `GLSL Cross Compilation " + "<https://wiki.blender.org/wiki/EEVEE_%26_Viewport/GPU_Module/GLSL_Cross_Compilation>`__\n"); +static PyObject *pygpu_shader_info_vertex_source(BPyGPUShaderCreateInfo *self, PyObject *o) +{ + const char *vertex_source = PyUnicode_AsUTF8(o); + if (vertex_source == nullptr) { + PyErr_Format(PyExc_ValueError, "expected a string, got %s", Py_TYPE(o)->tp_name); + return nullptr; + } + +#ifdef USE_GPU_PY_REFERENCES + if (self->vertex_source) { + Py_DECREF(self->vertex_source); + } + + self->vertex_source = o; + Py_INCREF(o); +#endif + + ShaderCreateInfo *info = reinterpret_cast<ShaderCreateInfo *>(self->info); + info->vertex_source("common_colormanagement_lib.glsl"); + info->vertex_source_generated = vertex_source; + + Py_RETURN_NONE; +} + +PyDoc_STRVAR( + pygpu_shader_info_fragment_source_doc, + ".. method:: fragment_source(source)\n" + "\n" + " Fragment shader source code written in GLSL.\n" + "\n" + " Example:\n" + "\n" + " .. code-block:: python\n" + "\n" + " \"void main {fragColor = vec4(0.0, 0.0, 0.0, 1.0);}\"\n" + "\n" + " :param source: The fragment shader source code.\n" + " :type source: str\n" + "\n" + " .. seealso:: `GLSL Cross Compilation " + "<https://wiki.blender.org/wiki/EEVEE_%26_Viewport/GPU_Module/GLSL_Cross_Compilation>`__\n"); +static PyObject *pygpu_shader_info_fragment_source(BPyGPUShaderCreateInfo *self, PyObject *o) +{ + const char *fragment_source = PyUnicode_AsUTF8(o); + if (fragment_source == nullptr) { + PyErr_Format(PyExc_ValueError, "expected a string, got %s", Py_TYPE(o)->tp_name); + return nullptr; + } + +#ifdef USE_GPU_PY_REFERENCES + if (self->fragment_source) { + Py_DECREF(self->fragment_source); + } + + self->fragment_source = o; + Py_INCREF(o); +#endif + + ShaderCreateInfo *info = reinterpret_cast<ShaderCreateInfo *>(self->info); + info->fragment_source("common_colormanagement_lib.glsl"); + info->fragment_source_generated = fragment_source; + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(pygpu_shader_info_typedef_source_doc, + ".. method:: typedef_source(source)\n" + "\n" + " Source code included before resource declaration. " + "Useful for defining structs used by Uniform Buffers.\n" + "\n" + " Example:\n" + "\n" + ".. code-block:: python\n" + "\n" + " \"struct MyType {int foo; float bar;};\"\n" + "\n" + " :param source: The source code defining types.\n" + " :type source: str\n"); +static PyObject *pygpu_shader_info_typedef_source(BPyGPUShaderCreateInfo *self, PyObject *o) +{ + const char *typedef_source = PyUnicode_AsUTF8(o); + if (typedef_source == nullptr) { + PyErr_Format(PyExc_ValueError, "expected a string, got %s", Py_TYPE(o)->tp_name); + return nullptr; + } + +#ifdef USE_GPU_PY_REFERENCES + if (self->typedef_source) { + Py_DECREF(self->typedef_source); + } + + self->typedef_source = o; + Py_INCREF(o); +#endif + + ShaderCreateInfo *info = reinterpret_cast<ShaderCreateInfo *>(self->info); +#if 0 + if (info->typedef_sources_.is_empty()) { + info->typedef_source("GPU_shader_shared_utils.h"); + } +#endif + info->typedef_source_generated = typedef_source; + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(pygpu_shader_info_define_doc, + ".. method:: define(name, value)\n" + "\n" + " Add a preprocessing define directive. In GLSL it would be something like:\n" + "\n" + ".. code-block:: glsl\n" + "\n" + " #define name value\n" + "\n" + " :param name: Token name.\n" + " :type name: str\n" + " :param value: Text that replaces token occurrences.\n" + " :type value: str\n"); +static PyObject *pygpu_shader_info_define(BPyGPUShaderCreateInfo *self, PyObject *args) +{ + const char *name; + const char *value = nullptr; + + if (!PyArg_ParseTuple(args, "s|s:define", &name, &value)) { + return nullptr; + } + +#ifdef USE_GPU_PY_REFERENCES + PyList_Append(self->references, PyTuple_GET_ITEM(args, 0)); /* name */ + if (value) { + PyList_Append(self->references, PyTuple_GET_ITEM(args, 1)); /* value */ + } +#endif + + ShaderCreateInfo *info = reinterpret_cast<ShaderCreateInfo *>(self->info); + if (value) { + info->define(name, value); + } + else { + info->define(name); + } + + Py_RETURN_NONE; +} + +static struct PyMethodDef pygpu_shader_info__tp_methods[] = { + {"vertex_in", + (PyCFunction)pygpu_shader_info_vertex_in, + METH_VARARGS, + pygpu_shader_info_vertex_in_doc}, + {"vertex_out", + (PyCFunction)pygpu_shader_info_vertex_out, + METH_O, + pygpu_shader_info_vertex_out_doc}, + {"fragment_out", + (PyCFunction)(void *)pygpu_shader_info_fragment_out, + METH_VARARGS | METH_KEYWORDS, + pygpu_shader_info_fragment_out_doc}, + {"uniform_buf", + (PyCFunction)(void *)pygpu_shader_info_uniform_buf, + METH_VARARGS, + pygpu_shader_info_uniform_buf_doc}, +#ifdef USE_PYGPU_SHADER_INFO_IMAGE_METHOD + {"image", + (PyCFunction)(void *)pygpu_shader_info_image, + METH_VARARGS | METH_KEYWORDS, + pygpu_shader_info_image_doc}, +#endif + {"sampler", + (PyCFunction)pygpu_shader_info_sampler, + METH_VARARGS, + pygpu_shader_info_sampler_doc}, + {"push_constant", + (PyCFunction)(void *)pygpu_shader_info_push_constant, + METH_VARARGS | METH_KEYWORDS, + pygpu_shader_info_push_constant_doc}, + {"vertex_source", + (PyCFunction)pygpu_shader_info_vertex_source, + METH_O, + pygpu_shader_info_vertex_source_doc}, + {"fragment_source", + (PyCFunction)pygpu_shader_info_fragment_source, + METH_O, + pygpu_shader_info_fragment_source_doc}, + {"typedef_source", + (PyCFunction)pygpu_shader_info_typedef_source, + METH_O, + pygpu_shader_info_typedef_source_doc}, + {"define", (PyCFunction)pygpu_shader_info_define, METH_VARARGS, pygpu_shader_info_define_doc}, + {nullptr, nullptr, 0, nullptr}, +}; + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name GPUShaderCreateInfo Init + * \{ */ + +static PyObject *pygpu_shader_info__tp_new(PyTypeObject *UNUSED(type), + PyObject *args, + PyObject *kwds) +{ + if (PyTuple_Size(args) || kwds) { + PyErr_SetString(PyExc_TypeError, "no args or keywords are expected"); + return nullptr; + } + + ShaderCreateInfo *info = new ShaderCreateInfo("pyGPU_Shader"); + GPUShaderCreateInfo *shader_info = reinterpret_cast<GPUShaderCreateInfo *>(info); + + return BPyGPUShaderCreateInfo_CreatePyObject(shader_info); +} + +#ifdef USE_GPU_PY_REFERENCES + +static int pygpu_shader_info__tp_traverse(PyObject *self, visitproc visit, void *arg) +{ + BPyGPUShaderCreateInfo *py_info = reinterpret_cast<BPyGPUShaderCreateInfo *>(self); + Py_VISIT(py_info->vertex_source); + Py_VISIT(py_info->fragment_source); + Py_VISIT(py_info->references); + return 0; +} + +static int pygpu_shader_info__tp_clear(PyObject *self) +{ + BPyGPUShaderCreateInfo *py_info = reinterpret_cast<BPyGPUShaderCreateInfo *>(self); + Py_CLEAR(py_info->vertex_source); + Py_CLEAR(py_info->fragment_source); + Py_CLEAR(py_info->references); + return 0; +} + +#endif + +static void pygpu_shader_info__tp_dealloc(PyObject *self) +{ + BPyGPUShaderCreateInfo *py_info = reinterpret_cast<BPyGPUShaderCreateInfo *>(self); + ShaderCreateInfo *info = reinterpret_cast<ShaderCreateInfo *>(py_info->info); + delete info; + +#ifdef USE_GPU_PY_REFERENCES + PyObject_GC_UnTrack(self); + if (py_info->references || py_info->vertex_source || py_info->fragment_source) { + pygpu_shader_info__tp_clear(self); + Py_XDECREF(py_info->vertex_source); + Py_XDECREF(py_info->fragment_source); + Py_XDECREF(py_info->references); + } +#endif + + Py_TYPE(self)->tp_free((PyObject *)self); +} + +PyDoc_STRVAR(pygpu_shader_info__tp_doc, + ".. class:: GPUShaderCreateInfo()\n" + "\n" + " Stores and describes types and variables that are used in shader sources.\n"); +constexpr PyTypeObject pygpu_shader_info_type() +{ + PyTypeObject pytype = {PyVarObject_HEAD_INIT(nullptr, 0)}; + pytype.tp_name = "GPUShaderCreateInfo"; + pytype.tp_basicsize = sizeof(BPyGPUShaderCreateInfo); + pytype.tp_dealloc = pygpu_shader_info__tp_dealloc; + pytype.tp_doc = pygpu_shader_info__tp_doc; +#ifdef USE_GPU_PY_REFERENCES + pytype.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC; + pytype.tp_traverse = pygpu_shader_info__tp_traverse; + pytype.tp_clear = pygpu_shader_info__tp_clear; +#else + pytype.tp_flags = Py_TPFLAGS_DEFAULT, +#endif + pytype.tp_methods = pygpu_shader_info__tp_methods; + pytype.tp_new = pygpu_shader_info__tp_new; + return pytype; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Public API + * \{ */ + +PyTypeObject BPyGPUStageInterfaceInfo_Type = pygpu_interface_info_type(); +PyTypeObject BPyGPUShaderCreateInfo_Type = pygpu_shader_info_type(); + +PyObject *BPyGPUStageInterfaceInfo_CreatePyObject(GPUStageInterfaceInfo *interface) +{ + BPyGPUStageInterfaceInfo *self; + +#ifdef USE_GPU_PY_REFERENCES + self = (BPyGPUStageInterfaceInfo *)_PyObject_GC_New(&BPyGPUStageInterfaceInfo_Type); + self->references = PyList_New(0); +#else + self = PyObject_New(BPyGPUStageInterfaceInfo, &BPyGPUStageInterfaceInfo_Type); +#endif + + self->interface = interface; + + return (PyObject *)self; +} + +PyObject *BPyGPUShaderCreateInfo_CreatePyObject(GPUShaderCreateInfo *info) +{ + BPyGPUShaderCreateInfo *self; + +#ifdef USE_GPU_PY_REFERENCES + self = (BPyGPUShaderCreateInfo *)_PyObject_GC_New(&BPyGPUShaderCreateInfo_Type); + self->vertex_source = nullptr; + self->fragment_source = nullptr; + self->typedef_source = nullptr; + self->references = PyList_New(0); +#else + self = PyObject_New(BPyGPUShaderCreateInfo, &BPyGPUShaderCreateInfo_Type); +#endif + + self->info = info; + self->constants_total_size = 0; + + return (PyObject *)self; +} + +/** \} */ diff --git a/source/blender/python/gpu/gpu_py_types.c b/source/blender/python/gpu/gpu_py_types.c index 6dff70ad530..65201df4a9e 100644 --- a/source/blender/python/gpu/gpu_py_types.c +++ b/source/blender/python/gpu/gpu_py_types.c @@ -59,6 +59,12 @@ PyObject *bpygpu_types_init(void) if (PyType_Ready(&BPyGPUUniformBuf_Type) < 0) { return NULL; } + if (PyType_Ready(&BPyGPUShaderCreateInfo_Type) < 0) { + return NULL; + } + if (PyType_Ready(&BPyGPUStageInterfaceInfo_Type) < 0) { + return NULL; + } PyModule_AddType(submodule, &BPyGPU_BufferType); PyModule_AddType(submodule, &BPyGPUVertFormat_Type); @@ -70,6 +76,8 @@ PyObject *bpygpu_types_init(void) PyModule_AddType(submodule, &BPyGPUTexture_Type); PyModule_AddType(submodule, &BPyGPUFrameBuffer_Type); PyModule_AddType(submodule, &BPyGPUUniformBuf_Type); + PyModule_AddType(submodule, &BPyGPUShaderCreateInfo_Type); + PyModule_AddType(submodule, &BPyGPUStageInterfaceInfo_Type); return submodule; } diff --git a/source/blender/python/gpu/gpu_py_uniformbuffer.c b/source/blender/python/gpu/gpu_py_uniformbuffer.c index f5a0af860b4..f8f88d61cf6 100644 --- a/source/blender/python/gpu/gpu_py_uniformbuffer.c +++ b/source/blender/python/gpu/gpu_py_uniformbuffer.c @@ -64,26 +64,37 @@ static PyObject *pygpu_uniformbuffer__tp_new(PyTypeObject *UNUSED(self), BPYGPU_IS_INIT_OR_ERROR_OBJ; GPUUniformBuf *ubo = NULL; - BPyGPUBuffer *pybuffer_obj; + PyObject *pybuffer_obj; char err_out[256] = "unknown error. See console"; static const char *_keywords[] = {"data", NULL}; static _PyArg_Parser _parser = { - "O!" /* `data` */ + "O" /* `data` */ ":GPUUniformBuf.__new__", _keywords, 0, }; - if (!_PyArg_ParseTupleAndKeywordsFast(args, kwds, &_parser, &BPyGPU_BufferType, &pybuffer_obj)) { + if (!_PyArg_ParseTupleAndKeywordsFast(args, kwds, &_parser, &pybuffer_obj)) { return NULL; } - if (GPU_context_active_get()) { - ubo = GPU_uniformbuf_create_ex( - bpygpu_Buffer_size(pybuffer_obj), pybuffer_obj->buf.as_void, "python_uniformbuffer"); + if (!GPU_context_active_get()) { + STRNCPY(err_out, "No active GPU context found"); } else { - STRNCPY(err_out, "No active GPU context found"); + Py_buffer pybuffer; + if (PyObject_GetBuffer(pybuffer_obj, &pybuffer, PyBUF_SIMPLE) == -1) { + /* PyObject_GetBuffer raise a PyExc_BufferError */ + return NULL; + } + + if ((pybuffer.len % 16) != 0) { + STRNCPY(err_out, "UBO is not padded to size of vec4"); + } + else { + ubo = GPU_uniformbuf_create_ex(pybuffer.len, pybuffer.buf, "python_uniformbuffer"); + } + PyBuffer_Release(&pybuffer); } if (ubo == NULL) { @@ -102,11 +113,14 @@ static PyObject *pygpu_uniformbuffer_update(BPyGPUUniformBuf *self, PyObject *ob { BPYGPU_UNIFORMBUF_CHECK_OBJ(self); - if (!BPyGPU_Buffer_Check(obj)) { + Py_buffer pybuffer; + if (PyObject_GetBuffer(obj, &pybuffer, PyBUF_SIMPLE) == -1) { + /* PyObject_GetBuffer raise a PyExc_BufferError */ return NULL; } - GPU_uniformbuf_update(self->ubo, ((BPyGPUBuffer *)obj)->buf.as_void); + GPU_uniformbuf_update(self->ubo, pybuffer.buf); + PyBuffer_Release(&pybuffer); Py_RETURN_NONE; } @@ -151,8 +165,8 @@ PyDoc_STRVAR(pygpu_uniformbuffer__tp_doc, "\n" " This object gives access to off uniform buffers.\n" "\n" - " :arg data: Buffer object.\n" - " :type data: :class:`gpu.types.Buffer`\n"); + " :arg data: Data to fill the buffer.\n" + " :type data: object exposing buffer interface\n"); PyTypeObject BPyGPUUniformBuf_Type = { PyVarObject_HEAD_INIT(NULL, 0).tp_name = "GPUUniformBuf", .tp_basicsize = sizeof(BPyGPUUniformBuf), diff --git a/source/blender/sequencer/intern/effects.c b/source/blender/sequencer/intern/effects.c index 70f83485bb5..0192f4f625c 100644 --- a/source/blender/sequencer/intern/effects.c +++ b/source/blender/sequencer/intern/effects.c @@ -3381,7 +3381,7 @@ static ImBuf *do_text_effect(const SeqRenderData *context, /* vars for calculating wordwrap and optional box */ struct { struct ResultBLF info; - rctf rect; + rcti rect; } wrap; BLF_boundbox_ex(font, data->text, sizeof(data->text), &wrap.rect, &wrap.info); @@ -3391,10 +3391,10 @@ static ImBuf *do_text_effect(const SeqRenderData *context, } else { if (data->align == SEQ_TEXT_ALIGN_X_RIGHT) { - x -= BLI_rctf_size_x(&wrap.rect); + x -= BLI_rcti_size_x(&wrap.rect); } else if (data->align == SEQ_TEXT_ALIGN_X_CENTER) { - x -= BLI_rctf_size_x(&wrap.rect) / 2; + x -= BLI_rcti_size_x(&wrap.rect) / 2; } if (data->align_y == SEQ_TEXT_ALIGN_Y_TOP) { diff --git a/source/blender/windowmanager/intern/wm_draw.c b/source/blender/windowmanager/intern/wm_draw.c index 3c8474b1b6c..4b506564260 100644 --- a/source/blender/windowmanager/intern/wm_draw.c +++ b/source/blender/windowmanager/intern/wm_draw.c @@ -24,6 +24,7 @@ #include "BLI_utildefines.h" #include "BKE_context.h" +#include "BKE_global.h" #include "BKE_image.h" #include "BKE_main.h" #include "BKE_scene.h" @@ -459,13 +460,24 @@ static void wm_draw_region_buffer_create(ARegion *region, bool stereo, bool use_ } } -static void wm_draw_region_bind(ARegion *region, int view) +static bool wm_draw_region_bind(bContext *C, ARegion *region, int view) { if (!region->draw_buffer) { - return; + return true; } if (region->draw_buffer->viewport) { + if (G.is_rendering && C != NULL) { + Scene *scene = CTX_data_scene(C); + RenderEngineType *render_engine_type = RE_engines_find(scene->r.engine); + if (RE_engine_is_opengl(render_engine_type)) { + /* Do not try to acquire the viewport as this would be locking at the moment. + * But tag the viewport to update after the rendering finishes. */ + GPU_viewport_tag_update(region->draw_buffer->viewport); + return false; + } + } + GPU_viewport_bind(region->draw_buffer->viewport, view, ®ion->winrct); } else { @@ -478,6 +490,7 @@ static void wm_draw_region_bind(ARegion *region, int view) } region->draw_buffer->bound_view = view; + return true; } static void wm_draw_region_unbind(ARegion *region) @@ -700,9 +713,10 @@ static void wm_draw_window_offscreen(bContext *C, wmWindow *win, bool stereo) wm_draw_region_stereo_set(bmain, area, region, sview); } - wm_draw_region_bind(region, view); - ED_region_do_draw(C, region); - wm_draw_region_unbind(region); + if (wm_draw_region_bind(C, region, view)) { + ED_region_do_draw(C, region); + wm_draw_region_unbind(region); + } } if (use_viewport) { GPUViewport *viewport = region->draw_buffer->viewport; @@ -711,9 +725,10 @@ static void wm_draw_window_offscreen(bContext *C, wmWindow *win, bool stereo) } else { wm_draw_region_buffer_create(region, false, use_viewport); - wm_draw_region_bind(region, 0); - ED_region_do_draw(C, region); - wm_draw_region_unbind(region); + if (wm_draw_region_bind(C, region, 0)) { + ED_region_do_draw(C, region); + wm_draw_region_unbind(region); + } } GPU_debug_group_end(); @@ -744,10 +759,11 @@ static void wm_draw_window_offscreen(bContext *C, wmWindow *win, bool stereo) } wm_draw_region_buffer_create(region, false, false); - wm_draw_region_bind(region, 0); - GPU_clear_color(0.0f, 0.0f, 0.0f, 0.0f); - ED_region_do_draw(C, region); - wm_draw_region_unbind(region); + if (wm_draw_region_bind(C, region, 0)) { + GPU_clear_color(0.0f, 0.0f, 0.0f, 0.0f); + ED_region_do_draw(C, region); + wm_draw_region_unbind(region); + } GPU_debug_group_end(); @@ -1102,10 +1118,11 @@ void wm_draw_region_test(bContext *C, ScrArea *area, ARegion *region) /* Function for redraw timer benchmark. */ bool use_viewport = WM_region_use_viewport(area, region); wm_draw_region_buffer_create(region, false, use_viewport); - wm_draw_region_bind(region, 0); - ED_region_do_draw(C, region); - wm_draw_region_unbind(region); - region->do_draw = false; + if (wm_draw_region_bind(C, region, 0)) { + ED_region_do_draw(C, region); + wm_draw_region_unbind(region); + region->do_draw = false; + } } void WM_redraw_windows(bContext *C) @@ -1141,7 +1158,7 @@ void WM_draw_region_viewport_ensure(ARegion *region, short space_type) void WM_draw_region_viewport_bind(ARegion *region) { - wm_draw_region_bind(region, 0); + wm_draw_region_bind(NULL, region, 0); } void WM_draw_region_viewport_unbind(ARegion *region) diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index 84c40c42adc..87588c40b57 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -2691,6 +2691,51 @@ static int wm_action_not_handled(int action) return action == WM_HANDLER_CONTINUE || action == (WM_HANDLER_BREAK | WM_HANDLER_MODAL); } +static const char *keymap_handler_log_action_str(const int action) +{ + if (action & WM_HANDLER_BREAK) { + return "handled"; + } + if (action & WM_HANDLER_HANDLED) { + return "handled (and pass on)"; + } + return "un-handled"; +} + +static const char *keymap_handler_log_kmi_event_str(const wmKeyMapItem *kmi, + char *buf, + size_t buf_maxlen) +{ + /* 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. */ + WM_keymap_item_to_string(kmi, false, buf, buf_maxlen); + return buf; +} + +static const char *keymap_handler_log_kmi_op_str(bContext *C, + const wmKeyMapItem *kmi, + char *buf, + size_t buf_maxlen) +{ + /* 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); + } + } + BLI_snprintf(buf, buf_maxlen, "%s(%s)", kmi->idname, kmi_props ? kmi_props : ""); + if (kmi_props != NULL) { + MEM_freeN(kmi_props); + } + return buf; +} + #define PRINT \ if (do_debug_handler) \ printf @@ -2722,52 +2767,26 @@ static int wm_handlers_do_keymap_with_keymap_handler( if (wm_eventmatch(event, kmi)) { struct wmEventHandler_KeymapPost keymap_post = handler->post; - 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); + char op_buf[512]; + char kmi_buf[128]; + CLOG_INFO(WM_LOG_HANDLERS, + 2, + "keymap '%s', %s, %s, event: %s", + keymap->idname, + keymap_handler_log_kmi_op_str(C, kmi, op_buf, sizeof(op_buf)), + keymap_handler_log_action_str(action), + keymap_handler_log_kmi_event_str(kmi, kmi_buf, sizeof(kmi_buf))); + if (action & WM_HANDLER_BREAK) { /* Not always_pass here, it denotes removed handler_base. */ - CLOG_INFO(WM_LOG_HANDLERS, 2, "handled! '%s'", kmi->idname); if (keymap_post.post_fn != NULL) { keymap_post.post_fn(keymap, kmi, keymap_post.user_data); } break; } - if (action & WM_HANDLER_HANDLED) { - CLOG_INFO(WM_LOG_HANDLERS, 2, "handled - and pass on! '%s'", kmi->idname); - } - else { - CLOG_INFO(WM_LOG_HANDLERS, 2, "un-handled '%s'", kmi->idname); - } } } } @@ -3249,7 +3268,7 @@ static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers) event->keymodifier = event->prev_press_keymodifier; event->direction = direction; - CLOG_INFO(WM_LOG_HANDLERS, 1, "handling PRESS_DRAG"); + CLOG_INFO(WM_LOG_HANDLERS, 1, "handling CLICK_DRAG"); action |= wm_handlers_do_intern(C, win, event, handlers); @@ -3262,13 +3281,17 @@ static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers) win->event_queue_check_click = false; if (!((action & WM_HANDLER_BREAK) == 0 || wm_action_not_handled(action))) { /* Only disable when handled as other handlers may use this drag event. */ + CLOG_INFO(WM_LOG_HANDLERS, 3, "canceling CLICK_DRAG: drag was generated & handled"); win->event_queue_check_drag = false; } } } } else { - win->event_queue_check_drag = false; + if (win->event_queue_check_drag) { + CLOG_INFO(WM_LOG_HANDLERS, 3, "canceling CLICK_DRAG: motion event was handled"); + win->event_queue_check_drag = false; + } } } else if (ISKEYBOARD_OR_BUTTON(event->type)) { @@ -3282,7 +3305,10 @@ static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers) if (event->val == KM_PRESS) { if ((event->flag & WM_EVENT_IS_REPEAT) == 0) { win->event_queue_check_click = true; + + CLOG_INFO(WM_LOG_HANDLERS, 3, "detecting CLICK_DRAG: press event detected"); win->event_queue_check_drag = true; + win->event_queue_check_drag_handled = false; } } @@ -3293,6 +3319,8 @@ static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers) /* Support releasing modifier keys without canceling the drag event, see T89989. */ } else { + CLOG_INFO( + WM_LOG_HANDLERS, 3, "CLICK_DRAG: canceling (release event didn't match press)"); win->event_queue_check_drag = false; } } @@ -3305,7 +3333,12 @@ static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers) if (win->event_queue_check_click == true) { if (WM_event_drag_test(event, event->prev_press_xy)) { win->event_queue_check_click = false; - win->event_queue_check_drag = false; + if (win->event_queue_check_drag) { + CLOG_INFO(WM_LOG_HANDLERS, + 3, + "CLICK_DRAG: canceling (key-release exceeds drag threshold)"); + win->event_queue_check_drag = false; + } } else { /* Position is where the actual click happens, for more @@ -3315,7 +3348,7 @@ static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers) copy_v2_v2_int(event->xy, event->prev_press_xy); event->val = KM_CLICK; - CLOG_INFO(WM_LOG_HANDLERS, 1, "handling CLICK"); + CLOG_INFO(WM_LOG_HANDLERS, 1, "CLICK: handling"); action |= wm_handlers_do_intern(C, win, event, handlers); @@ -3339,7 +3372,14 @@ static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers) } else { win->event_queue_check_click = false; - win->event_queue_check_drag = false; + + if (win->event_queue_check_drag) { + CLOG_INFO(WM_LOG_HANDLERS, + 3, + "CLICK_DRAG: canceling (button event was handled: value=%d)", + event->val); + win->event_queue_check_drag = false; + } } } else if (ISMOUSE_WHEEL(event->type) || ISMOUSE_GESTURE(event->type)) { @@ -4932,7 +4972,7 @@ static void wm_event_state_update_and_click_set(const GHOST_TEventType type, /* Double click test. */ if (wm_event_is_double_click(event)) { - CLOG_INFO(WM_LOG_HANDLERS, 1, "Send double click"); + CLOG_INFO(WM_LOG_HANDLERS, 1, "DBL_CLICK: detected"); event->val = KM_DBL_CLICK; } else if (event->val == KM_PRESS) { diff --git a/source/blender/windowmanager/intern/wm_toolsystem.c b/source/blender/windowmanager/intern/wm_toolsystem.c index a8d4ca0f02b..388abe21578 100644 --- a/source/blender/windowmanager/intern/wm_toolsystem.c +++ b/source/blender/windowmanager/intern/wm_toolsystem.c @@ -551,7 +551,7 @@ 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}; + bool space_type_has_tools[SPACE_TYPE_NUM] = {0}; LISTBASE_FOREACH (bToolRef *, tref, &workspace->tools) { space_type_has_tools[tref->space_type] = true; } diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c index 89bb6906a22..382a37e09e5 100644 --- a/source/blender/windowmanager/intern/wm_window.c +++ b/source/blender/windowmanager/intern/wm_window.c @@ -1541,7 +1541,15 @@ void wm_ghost_init(bContext *C) } g_system = GHOST_CreateSystem(); - GHOST_SystemInitDebug(g_system, G.debug & G_DEBUG_GHOST); + + GHOST_Debug debug = {0}; + if (G.debug & G_DEBUG_GHOST) { + debug.flags |= GHOST_kDebugDefault; + } + if (G.debug & G_DEBUG_WINTAB) { + debug.flags |= GHOST_kDebugWintab; + } + GHOST_SystemInitDebug(g_system, debug); if (C != NULL) { GHOST_AddEventConsumer(g_system, consumer); diff --git a/source/creator/creator_args.c b/source/creator/creator_args.c index 05b7f1bcb85..b3f5d24ee8c 100644 --- a/source/creator/creator_args.c +++ b/source/creator/creator_args.c @@ -576,6 +576,7 @@ static int arg_handle_print_help(int UNUSED(argc), const char **UNUSED(argv), vo BLI_args_print_arg_doc(ba, "--debug-depsgraph-pretty"); BLI_args_print_arg_doc(ba, "--debug-depsgraph-uuid"); BLI_args_print_arg_doc(ba, "--debug-ghost"); + BLI_args_print_arg_doc(ba, "--debug-wintab"); BLI_args_print_arg_doc(ba, "--debug-gpu"); BLI_args_print_arg_doc(ba, "--debug-gpu-force-workarounds"); BLI_args_print_arg_doc(ba, "--debug-wm"); @@ -943,6 +944,12 @@ static const char arg_handle_debug_mode_generic_set_doc_wm[] = "\n\t" "Enable debug messages for the window manager, shows all operators in search, shows " "keymap errors."; +static const char arg_handle_debug_mode_generic_set_doc_ghost[] = + "\n\t" + "Enable debug messages for Ghost (Linux only)."; +static const char arg_handle_debug_mode_generic_set_doc_wintab[] = + "\n\t" + "Enable debug messages for Wintab."; # ifdef WITH_XR_OPENXR static const char arg_handle_debug_mode_generic_set_doc_xr[] = "\n\t" @@ -2130,8 +2137,13 @@ void main_args_setup(bContext *C, bArgs *ba) BLI_args_add(ba, NULL, "--debug-ghost", - CB_EX(arg_handle_debug_mode_generic_set, handlers), + CB_EX(arg_handle_debug_mode_generic_set, ghost), (void *)G_DEBUG_GHOST); + BLI_args_add(ba, + NULL, + "--debug-wintab", + CB_EX(arg_handle_debug_mode_generic_set, wintab), + (void *)G_DEBUG_WINTAB); BLI_args_add(ba, NULL, "--debug-all", CB(arg_handle_debug_mode_all), NULL); BLI_args_add(ba, NULL, "--debug-io", CB(arg_handle_debug_mode_io), NULL); diff --git a/source/tools b/source/tools -Subproject 1e658ca996f11e5ff3398d89bd81f5b719304a5 +Subproject 4c1e01e3e309282beb1af3b1eddb2c7f9a666b5 diff --git a/tests/python/CMakeLists.txt b/tests/python/CMakeLists.txt index a0b2e6207bf..38c3fc4389a 100644 --- a/tests/python/CMakeLists.txt +++ b/tests/python/CMakeLists.txt @@ -627,13 +627,13 @@ endif() if(WITH_CYCLES OR WITH_OPENGL_RENDER_TESTS) if(NOT OPENIMAGEIO_IDIFF) - MESSAGE(STATUS "Disabling Cycles tests because OIIO idiff does not exist") + MESSAGE(WARNING "Disabling render tests because OIIO idiff does not exist") elseif(NOT EXISTS "${TEST_SRC_DIR}/render/shader") - MESSAGE(STATUS "Disabling Cycles tests because tests folder does not exist at ${TEST_SRC_DIR}") + MESSAGE(WARNING "Disabling render tests because tests folder does not exist at ${TEST_SRC_DIR}") elseif(NOT WITH_COMPOSITOR) - MESSAGE(STATUS "Disabling Cycles tests because WITH_COMPOSITOR is disabled") + MESSAGE(WARNING "Disabling render tests because WITH_COMPOSITOR is disabled") elseif(NOT WITH_OPENCOLORIO) - MESSAGE(STATUS "Disabling Cycles tests because WITH_OPENCOLORIO is disabled") + MESSAGE(WARNING "Disabling render tests because WITH_OPENCOLORIO is disabled") else() set(render_tests camera |