UI: Asset Shelf (Experimental Feature) #104831
|
@ -528,10 +528,10 @@ set(XR_OPENXR_SDK_HASH a2623ebab3d0b340bc16311b14f02075)
|
|||
set(XR_OPENXR_SDK_HASH_TYPE MD5)
|
||||
set(XR_OPENXR_SDK_FILE OpenXR-SDK-${XR_OPENXR_SDK_VERSION}.tar.gz)
|
||||
|
||||
set(WL_PROTOCOLS_VERSION 1.21)
|
||||
set(WL_PROTOCOLS_VERSION 1.31)
|
||||
set(WL_PROTOCOLS_FILE wayland-protocols-${WL_PROTOCOLS_VERSION}.tar.gz)
|
||||
set(WL_PROTOCOLS_URI https://gitlab.freedesktop.org/wayland/wayland-protocols/-/archive/${WL_PROTOCOLS_VERSION}/${WL_PROTOCOLS_FILE})
|
||||
set(WL_PROTOCOLS_HASH af5ca07e13517cdbab33504492cef54a)
|
||||
set(WL_PROTOCOLS_HASH a28ff59a56e2ebb746048b6ef8d931d6)
|
||||
set(WL_PROTOCOLS_HASH_TYPE MD5)
|
||||
|
||||
set(WAYLAND_VERSION 1.21.0)
|
||||
|
|
|
@ -245,6 +245,7 @@ if(WITH_BOOST)
|
|||
if(WITH_USD AND USD_PYTHON_SUPPORT)
|
||||
list(APPEND _boost_FIND_COMPONENTS python${PYTHON_VERSION_NO_DOTS})
|
||||
endif()
|
||||
set(Boost_NO_WARN_NEW_VERSIONS ON)
|
||||
find_package(Boost COMPONENTS ${_boost_FIND_COMPONENTS})
|
||||
|
||||
# Boost Python is separate to avoid linking Python into tests that don't need it.
|
||||
|
|
|
@ -394,6 +394,7 @@ if(WITH_BOOST)
|
|||
list(APPEND __boost_packages python${PYTHON_VERSION_NO_DOTS})
|
||||
endif()
|
||||
list(APPEND __boost_packages system)
|
||||
set(Boost_NO_WARN_NEW_VERSIONS ON)
|
||||
find_package(Boost 1.48 COMPONENTS ${__boost_packages})
|
||||
if(NOT Boost_FOUND)
|
||||
# try to find non-multithreaded if -mt not found, this flag
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
Project: fast_float
|
||||
URL: https://github.com/fastfloat/fast_float
|
||||
License: MIT
|
||||
Upstream version: 3.4.0 (b7f9d6c)
|
||||
Upstream version: 4.0.0 (fbd5bd7, 2023 Mar 31)
|
||||
Local modifications:
|
||||
|
||||
- Took only the fast_float.h header and the license/readme files
|
||||
|
|
|
@ -1,12 +1,5 @@
|
|||
## 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
|
||||
|
@ -28,8 +21,8 @@ struct from_chars_result {
|
|||
```
|
||||
|
||||
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),
|
||||
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.
|
||||
|
||||
|
@ -47,7 +40,7 @@ Example:
|
|||
``` C++
|
||||
#include "fast_float/fast_float.h"
|
||||
#include <iostream>
|
||||
|
||||
|
||||
int main() {
|
||||
const std::string input = "3.1416 xyz ";
|
||||
double result;
|
||||
|
@ -60,39 +53,60 @@ int main() {
|
|||
|
||||
|
||||
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
|
||||
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 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.
|
||||
* 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.
|
||||
* For values that are either very large or very small (e.g., `1e9999`), we represent it using the infinity or negative infinity value and the returned `ec` is set to `std::errc::result_out_of_range`.
|
||||
|
||||
We support Visual Studio, macOS, Linux, freeBSD. We support big and little endian. We support 32-bit and 64-bit systems.
|
||||
|
||||
We assume that the rounding mode is set to nearest (`std::fegetround() == FE_TONEAREST`).
|
||||
|
||||
## C++20: compile-time evaluation (constexpr)
|
||||
|
||||
In C++20, you may use `fast_float::from_chars` to parse strings
|
||||
at compile-time, as in the following example:
|
||||
|
||||
```C++
|
||||
// consteval forces compile-time evaluation of the function in C++20.
|
||||
consteval double parse(std::string_view input) {
|
||||
double result;
|
||||
auto answer = fast_float::from_chars(input.data(), input.data()+input.size(), result);
|
||||
if(answer.ec != std::errc()) { return -1.0; }
|
||||
return result;
|
||||
}
|
||||
|
||||
// This function should compile to a function which
|
||||
// merely returns 3.1415.
|
||||
constexpr double constexptest() {
|
||||
return parse("3.1415 input");
|
||||
}
|
||||
```
|
||||
|
||||
## 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
|
||||
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.,
|
||||
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;
|
||||
|
@ -104,25 +118,62 @@ int main() {
|
|||
}
|
||||
```
|
||||
|
||||
You can parse delimited numbers:
|
||||
```C++
|
||||
const std::string input = "234532.3426362,7869234.9823,324562.645";
|
||||
double result;
|
||||
auto answer = fast_float::from_chars(input.data(), input.data()+input.size(), result);
|
||||
if(answer.ec != std::errc()) {
|
||||
// check error
|
||||
}
|
||||
// we have result == 234532.3426362.
|
||||
if(answer.ptr[0] != ',') {
|
||||
// unexpected delimiter
|
||||
}
|
||||
answer = fast_float::from_chars(answer.ptr + 1, input.data()+input.size(), result);
|
||||
if(answer.ec != std::errc()) {
|
||||
// check error
|
||||
}
|
||||
// we have result == 7869234.9823.
|
||||
if(answer.ptr[0] != ',') {
|
||||
// unexpected delimiter
|
||||
}
|
||||
answer = fast_float::from_chars(answer.ptr + 1, input.data()+input.size(), result);
|
||||
if(answer.ec != std::errc()) {
|
||||
// check error
|
||||
}
|
||||
// we have result == 324562.645.
|
||||
```
|
||||
|
||||
## Reference
|
||||
|
||||
- Daniel Lemire, [Number Parsing at a Gigabyte per Second](https://arxiv.org/abs/2101.11408), Software: Pratice and Experience 51 (8), 2021.
|
||||
## Relation With Other Work
|
||||
|
||||
The fast_float library is part of:
|
||||
|
||||
- GCC (as of version 12): the `from_chars` function in GCC relies on fast_float.
|
||||
- [WebKit](https://github.com/WebKit/WebKit), the engine behind Safari (Apple's web browser)
|
||||
|
||||
|
||||
The fastfloat algorithm is part of the [LLVM standard libraries](https://github.com/llvm/llvm-project/commit/87c016078ad72c46505461e4ff8bfa04819fe7ba).
|
||||
|
||||
There is a [derived implementation part of AdaCore](https://github.com/AdaCore/VSS).
|
||||
|
||||
|
||||
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).
|
||||
|
||||
## References
|
||||
|
||||
- Daniel Lemire, [Number Parsing at a Gigabyte per Second](https://arxiv.org/abs/2101.11408), Software: Practice and Experience 51 (8), 2021.
|
||||
- Noble Mushtak, Daniel Lemire, [Fast Number Parsing Without Fallback](https://arxiv.org/abs/2212.06644), Software: Practice and Experience (to appear)
|
||||
|
||||
## 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 Java port of the fast_float library](https://github.com/wrandelshofer/FastDoubleParser) called `FastDoubleParser`. It used for important systems such as [Jackson](https://github.com/FasterXML/jackson-core).
|
||||
- [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).
|
||||
|
@ -135,14 +186,14 @@ It can parse random floating-point numbers at a speed of 1 GB/s on some systems.
|
|||
<img src="http://lemire.me/blog/wp-content/uploads/2020/11/fastfloat_speed.png" width="400">
|
||||
|
||||
```
|
||||
$ ./build/benchmarks/benchmark
|
||||
$ ./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
|
||||
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.
|
||||
|
@ -183,23 +234,23 @@ You should change the `GIT_TAG` line so that you recover the version you wish to
|
|||
|
||||
## Using as single header
|
||||
|
||||
The script `script/amalgamate.py` may be used to generate a 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.
|
||||
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
|
||||
https://github.com/fastfloat/fast_float/releases/download/v3.4.0/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
|
||||
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
|
||||
The library includes code adapted from Google Wuffs (written by Nigel Tao) which was originally published
|
||||
under the Apache 2.0 license.
|
||||
|
||||
## License
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -378,6 +378,14 @@ elseif(WITH_GHOST_X11 OR WITH_GHOST_WAYLAND)
|
|||
unset(_name)
|
||||
endmacro()
|
||||
|
||||
macro(generate_protocol_bindings_when_found PROT_DEF FOUND_VAR)
|
||||
if(EXISTS ${PROT_DEF})
|
||||
generate_protocol_bindings(${PROT_DEF})
|
||||
set(${FOUND_VAR} TRUE)
|
||||
else()
|
||||
set(${FOUND_VAR} FALSE)
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
list(APPEND INC_SYS
|
||||
${INC_DST}
|
||||
|
@ -402,6 +410,17 @@ elseif(WITH_GHOST_X11 OR WITH_GHOST_WAYLAND)
|
|||
generate_protocol_bindings(
|
||||
"${WAYLAND_PROTOCOLS_DIR}/staging/xdg-activation/xdg-activation-v1.xml"
|
||||
)
|
||||
# Fractional scale.
|
||||
generate_protocol_bindings_when_found(
|
||||
"${WAYLAND_PROTOCOLS_DIR}/staging/fractional-scale/fractional-scale-v1.xml"
|
||||
_has_fractional_scale
|
||||
)
|
||||
if(_has_fractional_scale)
|
||||
# Viewport (only required when fractional scale is in use).
|
||||
generate_protocol_bindings(
|
||||
"${WAYLAND_PROTOCOLS_DIR}/stable/viewporter/viewporter.xml"
|
||||
)
|
||||
endif()
|
||||
# Pointer-constraints.
|
||||
generate_protocol_bindings(
|
||||
"${WAYLAND_PROTOCOLS_DIR}/unstable/pointer-constraints/pointer-constraints-unstable-v1.xml"
|
||||
|
@ -427,6 +446,11 @@ elseif(WITH_GHOST_X11 OR WITH_GHOST_WAYLAND)
|
|||
|
||||
add_definitions(-DWITH_GHOST_WAYLAND)
|
||||
|
||||
if(_has_fractional_scale)
|
||||
add_definitions(-DWITH_GHOST_WAYLAND_FRACTIONAL_SCALE)
|
||||
endif()
|
||||
unset(_has_fractional_scale)
|
||||
|
||||
if(NOT WITH_GHOST_WAYLAND_APP_ID STREQUAL "")
|
||||
add_definitions(-DWITH_GHOST_WAYLAND_APP_ID=${WITH_GHOST_WAYLAND_APP_ID})
|
||||
endif()
|
||||
|
|
|
@ -61,6 +61,10 @@
|
|||
#include <tablet-unstable-v2-client-protocol.h>
|
||||
#include <xdg-activation-v1-client-protocol.h>
|
||||
#include <xdg-output-unstable-v1-client-protocol.h>
|
||||
#ifdef WITH_GHOST_WAYLAND_FRACTIONAL_SCALE
|
||||
# include <fractional-scale-v1-client-protocol.h>
|
||||
# include <viewporter-client-protocol.h>
|
||||
#endif
|
||||
|
||||
/* Decorations `xdg_decor`. */
|
||||
#include <xdg-decoration-unstable-v1-client-protocol.h>
|
||||
|
@ -944,6 +948,10 @@ struct GWL_Display {
|
|||
struct zwp_relative_pointer_manager_v1 *wp_relative_pointer_manager = nullptr;
|
||||
struct zwp_primary_selection_device_manager_v1 *wp_primary_selection_device_manager = nullptr;
|
||||
struct xdg_activation_v1 *xdg_activation_manager = nullptr;
|
||||
#ifdef WITH_GHOST_WAYLAND_FRACTIONAL_SCALE
|
||||
struct wp_fractional_scale_manager_v1 *wp_fractional_scale_manager = nullptr;
|
||||
struct wp_viewporter *wp_viewporter = nullptr;
|
||||
#endif
|
||||
|
||||
struct zwp_pointer_constraints_v1 *wp_pointer_constraints = nullptr;
|
||||
struct zwp_pointer_gestures_v1 *wp_pointer_gestures = nullptr;
|
||||
|
@ -3006,19 +3014,6 @@ static void gesture_pinch_handle_begin(void *data,
|
|||
if (wl_surface *wl_surface_focus = seat->pointer.wl_surface_window) {
|
||||
win = ghost_wl_surface_user_data(wl_surface_focus);
|
||||
}
|
||||
/* NOTE(@ideasman42): Blender's use of track-pad coordinates is inconsistent and needs work.
|
||||
* This isn't specific to WAYLAND, in practice they tend to work well enough in most cases.
|
||||
* Some operators scale by the UI scale, some don't.
|
||||
* Even this window scale is not correct because it doesn't account for:
|
||||
* 1) Fractional window scale.
|
||||
* 2) Blender's UI scale preference (which GHOST doesn't know about).
|
||||
*
|
||||
* If support for this were all that was needed it could be handled in GHOST,
|
||||
* however as the operators are not even using coordinates compatible with each other,
|
||||
* it would be better to resolve this by passing rotation & zoom levels directly,
|
||||
* instead of attempting to handle them as cursor coordinates.
|
||||
*/
|
||||
const wl_fixed_t win_scale = win ? win->scale() : 1;
|
||||
|
||||
/* NOTE(@ideasman42): Scale factors match Blender's operators & default preferences.
|
||||
* For these values to work correctly, operator logic will need to be changed not to scale input
|
||||
|
@ -3033,10 +3028,30 @@ static void gesture_pinch_handle_begin(void *data,
|
|||
seat->pointer_gesture_pinch.scale.value = wl_fixed_from_int(1);
|
||||
/* The value 300 matches a value used in clip & image zoom operators.
|
||||
* It seems OK for the 3D view too. */
|
||||
seat->pointer_gesture_pinch.scale.factor = 300 * win_scale;
|
||||
seat->pointer_gesture_pinch.scale.factor = 300;
|
||||
/* The value 5 is used on macOS and roughly maps 1:1 with turntable rotation,
|
||||
* although preferences can scale the sensitivity (which would be skipped ideally). */
|
||||
seat->pointer_gesture_pinch.rotation.factor = 5 * win_scale;
|
||||
seat->pointer_gesture_pinch.rotation.factor = 5;
|
||||
|
||||
if (win) {
|
||||
/* NOTE(@ideasman42): Blender's use of track-pad coordinates is inconsistent and needs work.
|
||||
* This isn't specific to WAYLAND, in practice they tend to work well enough in most cases.
|
||||
* Some operators scale by the UI scale, some don't.
|
||||
* Even this window scale is not correct because it doesn't account for:
|
||||
* 1) Fractional window scale.
|
||||
* 2) Blender's UI scale preference (which GHOST doesn't know about).
|
||||
*
|
||||
* If support for this were all that was needed it could be handled in GHOST,
|
||||
* however as the operators are not even using coordinates compatible with each other,
|
||||
* it would be better to resolve this by passing rotation & zoom levels directly,
|
||||
* instead of attempting to handle them as cursor coordinates.
|
||||
*/
|
||||
const struct GWL_WindowScaleParams &scale_params = win->scale_params();
|
||||
seat->pointer_gesture_pinch.scale.factor = gwl_window_scale_int_to(
|
||||
scale_params, seat->pointer_gesture_pinch.scale.factor);
|
||||
seat->pointer_gesture_pinch.rotation.factor = gwl_window_scale_int_to(
|
||||
scale_params, seat->pointer_gesture_pinch.rotation.factor);
|
||||
}
|
||||
}
|
||||
|
||||
static void gesture_pinch_handle_update(void *data,
|
||||
|
@ -5167,6 +5182,47 @@ static void gwl_registry_xdg_activation_remove(GWL_Display *display,
|
|||
*value_p = nullptr;
|
||||
}
|
||||
|
||||
/* #GWL_Display.wp_fractional_scale_manger */
|
||||
|
||||
#ifdef WITH_GHOST_WAYLAND_FRACTIONAL_SCALE
|
||||
|
||||
static void gwl_registry_wp_fractional_scale_manager_add(GWL_Display *display,
|
||||
const GWL_RegisteryAdd_Params *params)
|
||||
{
|
||||
display->wp_fractional_scale_manager = static_cast<wp_fractional_scale_manager_v1 *>(
|
||||
wl_registry_bind(
|
||||
display->wl_registry, params->name, &wp_fractional_scale_manager_v1_interface, 1));
|
||||
gwl_registry_entry_add(display, params, nullptr);
|
||||
}
|
||||
static void gwl_registry_wp_fractional_scale_manager_remove(GWL_Display *display,
|
||||
void * /*user_data*/,
|
||||
const bool /*on_exit*/)
|
||||
{
|
||||
struct wp_fractional_scale_manager_v1 **value_p = &display->wp_fractional_scale_manager;
|
||||
wp_fractional_scale_manager_v1_destroy(*value_p);
|
||||
*value_p = nullptr;
|
||||
}
|
||||
|
||||
/* #GWL_Display.wl_viewport */
|
||||
|
||||
static void gwl_registry_wp_viewporter_add(GWL_Display *display,
|
||||
const GWL_RegisteryAdd_Params *params)
|
||||
{
|
||||
display->wp_viewporter = static_cast<wp_viewporter *>(
|
||||
wl_registry_bind(display->wl_registry, params->name, &wp_viewporter_interface, 1));
|
||||
gwl_registry_entry_add(display, params, nullptr);
|
||||
}
|
||||
static void gwl_registry_wp_viewporter_remove(GWL_Display *display,
|
||||
void * /*user_data*/,
|
||||
const bool /*on_exit*/)
|
||||
{
|
||||
struct wp_viewporter **value_p = &display->wp_viewporter;
|
||||
wp_viewporter_destroy(*value_p);
|
||||
*value_p = nullptr;
|
||||
}
|
||||
|
||||
#endif /* WITH_GHOST_WAYLAND_FRACTIONAL_SCALE */
|
||||
|
||||
/* #GWL_Display.wp_primary_selection_device_manager */
|
||||
|
||||
static void gwl_registry_wp_primary_selection_device_manager_add(
|
||||
|
@ -5276,6 +5332,20 @@ static const GWL_RegistryHandler gwl_registry_handlers[] = {
|
|||
/*update_fn*/ nullptr,
|
||||
/*remove_fn*/ gwl_registry_xdg_activation_remove,
|
||||
},
|
||||
#ifdef WITH_GHOST_WAYLAND_FRACTIONAL_SCALE
|
||||
{
|
||||
/*interface_p*/ &wp_fractional_scale_manager_v1_interface.name,
|
||||
/*add_fn*/ gwl_registry_wp_fractional_scale_manager_add,
|
||||
/*update_fn*/ nullptr,
|
||||
/*remove_fn*/ gwl_registry_wp_fractional_scale_manager_remove,
|
||||
},
|
||||
{
|
||||
/*interface_p*/ &wp_viewporter_interface.name,
|
||||
/*add_fn*/ gwl_registry_wp_viewporter_add,
|
||||
/*update_fn*/ nullptr,
|
||||
/*remove_fn*/ gwl_registry_wp_viewporter_remove,
|
||||
},
|
||||
#endif /* WITH_GHOST_WAYLAND_FRACTIONAL_SCALE */
|
||||
/* Display outputs. */
|
||||
{
|
||||
/*interface_p*/ &wl_output_interface.name,
|
||||
|
@ -6840,6 +6910,17 @@ struct xdg_activation_v1 *GHOST_SystemWayland::xdg_activation_manager()
|
|||
return display_->xdg_activation_manager;
|
||||
}
|
||||
|
||||
#ifdef WITH_GHOST_WAYLAND_FRACTIONAL_SCALE
|
||||
struct wp_fractional_scale_manager_v1 *GHOST_SystemWayland::wp_fractional_scale_manager()
|
||||
{
|
||||
return display_->wp_fractional_scale_manager;
|
||||
}
|
||||
struct wp_viewporter *GHOST_SystemWayland::wp_viewporter()
|
||||
{
|
||||
return display_->wp_viewporter;
|
||||
}
|
||||
#endif /* WITH_GHOST_WAYLAND_FRACTIONAL_SCALE */
|
||||
|
||||
struct zwp_pointer_gestures_v1 *GHOST_SystemWayland::wp_pointer_gestures()
|
||||
{
|
||||
return display_->wp_pointer_gestures;
|
||||
|
@ -7055,7 +7136,7 @@ bool GHOST_SystemWayland::window_cursor_grab_set(const GHOST_TGrabCursorMode mod
|
|||
const GHOST_Rect *wrap_bounds,
|
||||
const GHOST_TAxisFlag wrap_axis,
|
||||
wl_surface *wl_surface,
|
||||
const int scale)
|
||||
const struct GWL_WindowScaleParams &scale_params)
|
||||
{
|
||||
/* Caller must lock `server_mutex`. */
|
||||
|
||||
|
@ -7115,10 +7196,14 @@ bool GHOST_SystemWayland::window_cursor_grab_set(const GHOST_TGrabCursorMode mod
|
|||
|
||||
GHOST_Rect bounds_scale;
|
||||
|
||||
bounds_scale.m_l = wl_fixed_from_int(wrap_bounds->m_l) / scale;
|
||||
bounds_scale.m_t = wl_fixed_from_int(wrap_bounds->m_t) / scale;
|
||||
bounds_scale.m_r = wl_fixed_from_int(wrap_bounds->m_r) / scale;
|
||||
bounds_scale.m_b = wl_fixed_from_int(wrap_bounds->m_b) / scale;
|
||||
bounds_scale.m_l = gwl_window_scale_wl_fixed_from(scale_params,
|
||||
wl_fixed_from_int(wrap_bounds->m_l));
|
||||
bounds_scale.m_t = gwl_window_scale_wl_fixed_from(scale_params,
|
||||
wl_fixed_from_int(wrap_bounds->m_t));
|
||||
bounds_scale.m_r = gwl_window_scale_wl_fixed_from(scale_params,
|
||||
wl_fixed_from_int(wrap_bounds->m_r));
|
||||
bounds_scale.m_b = gwl_window_scale_wl_fixed_from(scale_params,
|
||||
wl_fixed_from_int(wrap_bounds->m_b));
|
||||
|
||||
bounds_scale.wrapPoint(UNPACK2(xy_next), 0, wrap_axis);
|
||||
|
||||
|
@ -7138,8 +7223,8 @@ bool GHOST_SystemWayland::window_cursor_grab_set(const GHOST_TGrabCursorMode mod
|
|||
if ((init_grab_xy[0] != seat->grab_lock_xy[0]) ||
|
||||
(init_grab_xy[1] != seat->grab_lock_xy[1])) {
|
||||
const wl_fixed_t xy_next[2] = {
|
||||
wl_fixed_from_int(init_grab_xy[0]) / scale,
|
||||
wl_fixed_from_int(init_grab_xy[1]) / scale,
|
||||
gwl_window_scale_wl_fixed_from(scale_params, wl_fixed_from_int(init_grab_xy[0])),
|
||||
gwl_window_scale_wl_fixed_from(scale_params, wl_fixed_from_int(init_grab_xy[1])),
|
||||
};
|
||||
zwp_locked_pointer_v1_set_cursor_position_hint(seat->wp_locked_pointer,
|
||||
UNPACK2(xy_next));
|
||||
|
@ -7164,13 +7249,13 @@ bool GHOST_SystemWayland::window_cursor_grab_set(const GHOST_TGrabCursorMode mod
|
|||
#endif
|
||||
|
||||
if (xy_motion_create_event) {
|
||||
seat->system->pushEvent_maybe_pending(
|
||||
new GHOST_EventCursor(seat->system->getMilliSeconds(),
|
||||
GHOST_kEventCursorMove,
|
||||
ghost_wl_surface_user_data(wl_surface),
|
||||
wl_fixed_to_int(scale * xy_motion[0]),
|
||||
wl_fixed_to_int(scale * xy_motion[1]),
|
||||
GHOST_TABLET_DATA_NONE));
|
||||
seat->system->pushEvent_maybe_pending(new GHOST_EventCursor(
|
||||
seat->system->getMilliSeconds(),
|
||||
GHOST_kEventCursorMove,
|
||||
ghost_wl_surface_user_data(wl_surface),
|
||||
wl_fixed_to_int(gwl_window_scale_wl_fixed_to(scale_params, xy_motion[0])),
|
||||
wl_fixed_to_int(gwl_window_scale_wl_fixed_to(scale_params, xy_motion[1])),
|
||||
GHOST_TABLET_DATA_NONE));
|
||||
}
|
||||
|
||||
zwp_locked_pointer_v1_destroy(seat->wp_locked_pointer);
|
||||
|
@ -7206,8 +7291,10 @@ bool GHOST_SystemWayland::window_cursor_grab_set(const GHOST_TGrabCursorMode mod
|
|||
if (mode == GHOST_kGrabHide) {
|
||||
/* Set the initial position to detect any changes when un-grabbing,
|
||||
* otherwise the unlocked cursor defaults to un-locking in-place. */
|
||||
init_grab_xy[0] = wl_fixed_to_int(scale * seat->pointer.xy[0]);
|
||||
init_grab_xy[1] = wl_fixed_to_int(scale * seat->pointer.xy[1]);
|
||||
init_grab_xy[0] = wl_fixed_to_int(
|
||||
gwl_window_scale_wl_fixed_to(scale_params, seat->pointer.xy[0]));
|
||||
init_grab_xy[1] = wl_fixed_to_int(
|
||||
gwl_window_scale_wl_fixed_to(scale_params, seat->pointer.xy[1]));
|
||||
seat->grab_lock_xy[0] = init_grab_xy[0];
|
||||
seat->grab_lock_xy[1] = init_grab_xy[1];
|
||||
}
|
||||
|
|
|
@ -47,6 +47,26 @@ void ghost_wl_surface_tag_cursor_pointer(struct wl_surface *surface);
|
|||
bool ghost_wl_surface_own_cursor_tablet(const struct wl_surface *surface);
|
||||
void ghost_wl_surface_tag_cursor_tablet(struct wl_surface *surface);
|
||||
|
||||
/* Scaling to: translates from WAYLAND into GHOST (viewport local) coordinates.
|
||||
* Scaling from: performs the reverse translation.
|
||||
*
|
||||
* Scaling "to" is used to map WAYLAND location cursor coordinates to GHOST coordinates.
|
||||
* Scaling "from" is used to clamp cursor coordinates in WAYLAND local coordinates. */
|
||||
|
||||
struct GWL_WindowScaleParams;
|
||||
wl_fixed_t gwl_window_scale_wl_fixed_to(const GWL_WindowScaleParams &scale_params,
|
||||
wl_fixed_t value);
|
||||
wl_fixed_t gwl_window_scale_wl_fixed_from(const GWL_WindowScaleParams &scale_params,
|
||||
wl_fixed_t value);
|
||||
|
||||
/* Avoid this where possible as scaling integers often incurs rounding errors.
|
||||
* Scale #wl_fixed_t where possible.
|
||||
*
|
||||
* In general scale by large values where this is less likely to be a problem. */
|
||||
|
||||
int gwl_window_scale_int_to(const GWL_WindowScaleParams &scale_params, int value);
|
||||
int gwl_window_scale_int_from(const GWL_WindowScaleParams &scale_params, int value);
|
||||
|
||||
#ifdef WITH_GHOST_WAYLAND_DYNLOAD
|
||||
/**
|
||||
* Return true when all required WAYLAND libraries are present,
|
||||
|
@ -181,6 +201,10 @@ class GHOST_SystemWayland : public GHOST_System {
|
|||
struct zwp_primary_selection_device_manager_v1 *wp_primary_selection_manager();
|
||||
struct xdg_activation_v1 *xdg_activation_manager();
|
||||
struct zwp_pointer_gestures_v1 *wp_pointer_gestures();
|
||||
#ifdef WITH_GHOST_WAYLAND_FRACTIONAL_SCALE
|
||||
struct wp_fractional_scale_manager_v1 *wp_fractional_scale_manager();
|
||||
struct wp_viewporter *wp_viewporter();
|
||||
#endif
|
||||
|
||||
#ifdef WITH_GHOST_WAYLAND_LIBDECOR
|
||||
libdecor *libdecor_context();
|
||||
|
@ -234,7 +258,7 @@ class GHOST_SystemWayland : public GHOST_System {
|
|||
const GHOST_Rect *wrap_bounds,
|
||||
GHOST_TAxisFlag wrap_axis,
|
||||
wl_surface *wl_surface,
|
||||
int scale);
|
||||
const struct GWL_WindowScaleParams &scale_params);
|
||||
|
||||
#ifdef WITH_GHOST_WAYLAND_LIBDECOR
|
||||
static bool use_libdecor_runtime();
|
||||
|
|
|
@ -38,6 +38,11 @@
|
|||
#include <xdg-activation-v1-client-protocol.h>
|
||||
#include <xdg-decoration-unstable-v1-client-protocol.h>
|
||||
#include <xdg-shell-client-protocol.h>
|
||||
#ifdef WITH_GHOST_WAYLAND_FRACTIONAL_SCALE
|
||||
# include <fractional-scale-v1-client-protocol.h>
|
||||
# include <viewporter-client-protocol.h>
|
||||
# define FRACTIONAL_DENOMINATOR 120
|
||||
#endif
|
||||
|
||||
#include <atomic>
|
||||
|
||||
|
@ -83,6 +88,52 @@ static void gwl_xdg_decor_window_destroy(WGL_XDG_Decor_Window *decor)
|
|||
delete decor;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Window-Viewport/Wayland to/from Scale Conversion
|
||||
* \{ */
|
||||
|
||||
struct GWL_WindowScaleParams {
|
||||
bool is_fractional = false;
|
||||
/**
|
||||
* When fractional:
|
||||
* Scale is multiplied by #FRACTIONAL_DENOMINATOR.
|
||||
* Otherwise scale is an integer.
|
||||
*/
|
||||
wl_fixed_t scale = 0;
|
||||
};
|
||||
|
||||
wl_fixed_t gwl_window_scale_wl_fixed_to(const GWL_WindowScaleParams &scale_params,
|
||||
wl_fixed_t value)
|
||||
{
|
||||
#ifdef WITH_GHOST_WAYLAND_FRACTIONAL_SCALE
|
||||
if (scale_params.is_fractional) {
|
||||
return (value * scale_params.scale) / FRACTIONAL_DENOMINATOR;
|
||||
}
|
||||
#endif
|
||||
return value * scale_params.scale;
|
||||
}
|
||||
wl_fixed_t gwl_window_scale_wl_fixed_from(const GWL_WindowScaleParams &scale_params,
|
||||
wl_fixed_t value)
|
||||
{
|
||||
#ifdef WITH_GHOST_WAYLAND_FRACTIONAL_SCALE
|
||||
if (scale_params.is_fractional) {
|
||||
return (value * FRACTIONAL_DENOMINATOR) / scale_params.scale;
|
||||
}
|
||||
#endif
|
||||
return value / scale_params.scale;
|
||||
}
|
||||
|
||||
int gwl_window_scale_int_to(const GWL_WindowScaleParams &scale_params, int value)
|
||||
{
|
||||
return wl_fixed_to_int(gwl_window_scale_wl_fixed_to(scale_params, wl_fixed_from_int(value)));
|
||||
}
|
||||
int gwl_window_scale_int_from(const GWL_WindowScaleParams &scale_params, int value)
|
||||
{
|
||||
return wl_fixed_to_int(gwl_window_scale_wl_fixed_from(scale_params, wl_fixed_from_int(value)));
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Internal #GWL_Window
|
||||
* \{ */
|
||||
|
@ -111,10 +162,18 @@ enum eGWL_PendingWindowActions {
|
|||
#endif /* USE_EVENT_BACKGROUND_THREAD */
|
||||
|
||||
struct GWL_WindowFrame {
|
||||
/**
|
||||
* The frame size (in GHOST window coordinates).
|
||||
*
|
||||
* These must be converted to WAYLADN relative coordinates when the window is scaled
|
||||
* by Hi-DPI/fractional scaling.
|
||||
*/
|
||||
int32_t size[2] = {0, 0};
|
||||
bool is_maximised = false;
|
||||
bool is_fullscreen = false;
|
||||
bool is_active = false;
|
||||
/** Disable when the fractional scale is a whole number. */
|
||||
int fractional_scale = 0;
|
||||
};
|
||||
|
||||
struct GWL_Window {
|
||||
|
@ -132,14 +191,20 @@ struct GWL_Window {
|
|||
/** The scale value written to #wl_surface_set_buffer_scale. */
|
||||
int scale = 0;
|
||||
/**
|
||||
* The fractional scale used to calculate the DPI.
|
||||
* (always set, even when scaling is rounded to whole units).
|
||||
* The scale value to be used in the case fractional scale is disable.
|
||||
* In general this should only be used when changing states
|
||||
* (when disabling fractional scale).
|
||||
*/
|
||||
wl_fixed_t scale_fractional = 0;
|
||||
int scale_on_output = 0;
|
||||
|
||||
/** A temporary token used for the window to be notified of it's activation. */
|
||||
struct xdg_activation_token_v1 *xdg_activation_token = nullptr;
|
||||
|
||||
#ifdef WITH_GHOST_WAYLAND_FRACTIONAL_SCALE
|
||||
struct wp_viewport *viewport = nullptr;
|
||||
struct wp_fractional_scale_v1 *fractional_scale_handle = nullptr;
|
||||
#endif
|
||||
|
||||
#ifdef WITH_GHOST_WAYLAND_LIBDECOR
|
||||
WGL_LibDecor_Window *libdecor = nullptr;
|
||||
#endif
|
||||
|
@ -166,6 +231,9 @@ struct GWL_Window {
|
|||
|
||||
bool is_dialog = false;
|
||||
|
||||
/** Currently only initialized on access (avoids allocations & allows to keep private). */
|
||||
GWL_WindowScaleParams scale_params;
|
||||
|
||||
#ifdef USE_EVENT_BACKGROUND_THREAD
|
||||
/**
|
||||
* These pending actions can't be performed when WAYLAND handlers are running from a thread.
|
||||
|
@ -310,6 +378,124 @@ static bool gwl_window_state_set(GWL_Window *win, const GHOST_TWindowState state
|
|||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Internal #GWL_Window Viewport Setup/Teardown
|
||||
*
|
||||
* A viewport is needed to implement fractional scale,
|
||||
* as the outputs scale may change at runtime, support creating & clearing the viewport as needed.
|
||||
* \{ */
|
||||
|
||||
#ifdef WITH_GHOST_WAYLAND_FRACTIONAL_SCALE
|
||||
|
||||
/**
|
||||
* Scale a value from a viewport value to Wayland windowing.
|
||||
* Scale down or not at all.
|
||||
*/
|
||||
static int gwl_window_fractional_to_viewport(const GWL_WindowFrame &frame, int value)
|
||||
{
|
||||
GHOST_ASSERT(frame.fractional_scale != 0, "Not fractional or called before initiazlized!");
|
||||
return (value * frame.fractional_scale) / FRACTIONAL_DENOMINATOR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scale a value from a Wayland windowing value to the viewport.
|
||||
* Scales up or not at all.
|
||||
*/
|
||||
static int gwl_window_fractional_from_viewport(const GWL_WindowFrame &frame, int value)
|
||||
{
|
||||
GHOST_ASSERT(frame.fractional_scale != 0, "Not fractional or called before initiazlized!");
|
||||
return (value * FRACTIONAL_DENOMINATOR) / frame.fractional_scale;
|
||||
}
|
||||
|
||||
/* NOTE: rounded versions are needed for window-frame dimensions conversions.
|
||||
* (rounding is part of the WAYLAND spec). All other conversions such as cursor coordinates
|
||||
* can used simple integer division as rounding is not defined in this case. */
|
||||
|
||||
static int gwl_window_fractional_to_viewport_round(const GWL_WindowFrame &frame, int value)
|
||||
{
|
||||
GHOST_ASSERT(frame.fractional_scale != 0, "Not fractional or called before initiazlized!");
|
||||
return lroundf(double(value * frame.fractional_scale) / double(FRACTIONAL_DENOMINATOR));
|
||||
}
|
||||
|
||||
static int gwl_window_fractional_from_viewport_round(const GWL_WindowFrame &frame, int value)
|
||||
{
|
||||
GHOST_ASSERT(frame.fractional_scale != 0, "Not fractional or called before initiazlized!");
|
||||
return lroundf(double(value * FRACTIONAL_DENOMINATOR) / double(frame.fractional_scale));
|
||||
}
|
||||
|
||||
static bool gwl_window_viewport_set(GWL_Window *win, bool *r_surface_needs_commit)
|
||||
{
|
||||
if (win->viewport != nullptr) {
|
||||
return false;
|
||||
}
|
||||
struct wp_viewporter *viewporter = win->ghost_system->wp_viewporter();
|
||||
if (viewporter == nullptr) {
|
||||
return false;
|
||||
}
|
||||
win->viewport = wp_viewporter_get_viewport(viewporter, win->wl_surface);
|
||||
if (win->viewport == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Set the buffer scale to 1 since a viewport will be used. */
|
||||
if (win->scale != 1) {
|
||||
win->scale = 1;
|
||||
wl_surface_set_buffer_scale(win->wl_surface, win->scale);
|
||||
if (r_surface_needs_commit) {
|
||||
*r_surface_needs_commit = true;
|
||||
}
|
||||
else {
|
||||
wl_surface_commit(win->wl_surface);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool gwl_window_viewport_unset(GWL_Window *win, bool *r_surface_needs_commit)
|
||||
{
|
||||
if (win->viewport == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
wp_viewport_destroy(win->viewport);
|
||||
win->viewport = nullptr;
|
||||
|
||||
GHOST_ASSERT(win->scale == 1, "Unexpected scale!");
|
||||
if (win->scale != win->scale_on_output) {
|
||||
win->scale = win->scale_on_output;
|
||||
wl_surface_set_buffer_scale(win->wl_surface, win->scale);
|
||||
if (r_surface_needs_commit) {
|
||||
*r_surface_needs_commit = true;
|
||||
}
|
||||
else {
|
||||
wl_surface_commit(win->wl_surface);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool gwl_window_viewport_size_update(GWL_Window *win)
|
||||
{
|
||||
if (win->viewport == nullptr) {
|
||||
return false;
|
||||
}
|
||||
wp_viewport_set_source(win->viewport,
|
||||
wl_fixed_from_int(0),
|
||||
wl_fixed_from_int(0),
|
||||
wl_fixed_from_int(win->frame.size[0]),
|
||||
wl_fixed_from_int(win->frame.size[1]));
|
||||
wp_viewport_set_destination(
|
||||
win->viewport,
|
||||
gwl_window_fractional_from_viewport_round(win->frame, win->frame.size[0]),
|
||||
gwl_window_fractional_from_viewport_round(win->frame, win->frame.size[1]));
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif /* WITH_GHOST_WAYLAND_FRACTIONAL_SCALE */
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Internal #GWL_Window Activation
|
||||
* \{ */
|
||||
|
@ -369,7 +555,26 @@ static void gwl_window_activate(GWL_Window *win)
|
|||
/** \name Internal #GWL_Window Pending Actions
|
||||
* \{ */
|
||||
|
||||
static void gwl_window_frame_pending_size_set(GWL_Window *win)
|
||||
#ifdef WITH_GHOST_WAYLAND_FRACTIONAL_SCALE
|
||||
static void gwl_window_frame_pending_fractional_scale_set(GWL_Window *win,
|
||||
bool *r_surface_needs_commit)
|
||||
{
|
||||
if (win->frame_pending.fractional_scale == win->frame.fractional_scale) {
|
||||
return;
|
||||
}
|
||||
|
||||
win->frame.fractional_scale = win->frame_pending.fractional_scale;
|
||||
if (win->frame_pending.fractional_scale) {
|
||||
gwl_window_viewport_set(win, r_surface_needs_commit);
|
||||
gwl_window_viewport_size_update(win);
|
||||
}
|
||||
else {
|
||||
gwl_window_viewport_unset(win, r_surface_needs_commit);
|
||||
}
|
||||
}
|
||||
#endif /* WITH_GHOST_WAYLAND_FRACTIONAL_SCALE */
|
||||
|
||||
static void gwl_window_frame_pending_size_set(GWL_Window *win, bool *r_surface_needs_commit)
|
||||
{
|
||||
if (win->frame_pending.size[0] == 0 || win->frame_pending.size[1] == 0) {
|
||||
return;
|
||||
|
@ -378,7 +583,19 @@ static void gwl_window_frame_pending_size_set(GWL_Window *win)
|
|||
win->frame.size[0] = win->frame_pending.size[0];
|
||||
win->frame.size[1] = win->frame_pending.size[1];
|
||||
|
||||
#ifdef WITH_GHOST_WAYLAND_FRACTIONAL_SCALE
|
||||
if (win->frame_pending.fractional_scale != win->frame.fractional_scale) {
|
||||
gwl_window_frame_pending_fractional_scale_set(win, r_surface_needs_commit);
|
||||
}
|
||||
else {
|
||||
gwl_window_viewport_size_update(win);
|
||||
}
|
||||
#else
|
||||
(void)r_surface_needs_commit;
|
||||
#endif
|
||||
|
||||
wl_egl_window_resize(win->egl_window, UNPACK2(win->frame.size), 0, 0);
|
||||
|
||||
win->ghost_window->notify_size();
|
||||
|
||||
win->frame_pending.size[0] = 0;
|
||||
|
@ -401,6 +618,9 @@ static void gwl_window_pending_actions_handle(GWL_Window *win)
|
|||
gwl_window_frame_update_from_pending(win);
|
||||
}
|
||||
if (win->pending_actions[PENDING_EGL_WINDOW_RESIZE].exchange(false)) {
|
||||
# ifdef WITH_GHOST_WAYLAND_FRACTIONAL_SCALE
|
||||
gwl_window_viewport_size_update(win);
|
||||
# endif
|
||||
wl_egl_window_resize(win->egl_window, UNPACK2(win->frame.size), 0, 0);
|
||||
}
|
||||
# ifdef GHOST_OPENGL_ALPHA
|
||||
|
@ -427,13 +647,32 @@ static void gwl_window_frame_update_from_pending_no_lock(GWL_Window *win)
|
|||
|
||||
#endif
|
||||
|
||||
const bool dpi_changed = win->frame_pending.fractional_scale != win->frame.fractional_scale;
|
||||
bool surface_needs_commit = false;
|
||||
|
||||
if (win->frame_pending.size[0] != 0 && win->frame_pending.size[1] != 0) {
|
||||
if ((win->frame.size[0] != win->frame_pending.size[0]) ||
|
||||
(win->frame.size[1] != win->frame_pending.size[1])) {
|
||||
gwl_window_frame_pending_size_set(win);
|
||||
gwl_window_frame_pending_size_set(win, &surface_needs_commit);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef WITH_GHOST_WAYLAND_FRACTIONAL_SCALE
|
||||
if (win->frame_pending.fractional_scale != win->frame.fractional_scale) {
|
||||
gwl_window_frame_pending_fractional_scale_set(win, &surface_needs_commit);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (surface_needs_commit) {
|
||||
wl_surface_commit(win->wl_surface);
|
||||
}
|
||||
|
||||
if (dpi_changed) {
|
||||
GHOST_SystemWayland *system = win->ghost_system;
|
||||
system->pushEvent(new GHOST_Event(
|
||||
system->getMilliSeconds(), GHOST_kEventWindowDPIHintChanged, win->ghost_window));
|
||||
}
|
||||
|
||||
if (win->frame_pending.is_active) {
|
||||
win->ghost_window->activate();
|
||||
}
|
||||
|
@ -476,26 +715,11 @@ static int output_scale_cmp(const GWL_Output *output_a, const GWL_Output *output
|
|||
if (output_a->scale > output_b->scale) {
|
||||
return 1;
|
||||
}
|
||||
if (output_a->has_scale_fractional || output_b->has_scale_fractional) {
|
||||
const wl_fixed_t scale_fractional_a = output_a->has_scale_fractional ?
|
||||
output_a->scale_fractional :
|
||||
wl_fixed_from_int(output_a->scale);
|
||||
const wl_fixed_t scale_fractional_b = output_b->has_scale_fractional ?
|
||||
output_b->scale_fractional :
|
||||
wl_fixed_from_int(output_b->scale);
|
||||
if (scale_fractional_a < scale_fractional_b) {
|
||||
return -1;
|
||||
}
|
||||
if (scale_fractional_a > scale_fractional_b) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int outputs_max_scale_or_default(const std::vector<GWL_Output *> &outputs,
|
||||
const int32_t scale_default,
|
||||
wl_fixed_t *r_scale_fractional)
|
||||
const int32_t scale_default)
|
||||
{
|
||||
const GWL_Output *output_max = nullptr;
|
||||
for (const GWL_Output *reg_output : outputs) {
|
||||
|
@ -505,17 +729,9 @@ static int outputs_max_scale_or_default(const std::vector<GWL_Output *> &outputs
|
|||
}
|
||||
|
||||
if (output_max) {
|
||||
if (r_scale_fractional) {
|
||||
*r_scale_fractional = output_max->has_scale_fractional ?
|
||||
output_max->scale_fractional :
|
||||
wl_fixed_from_int(output_max->scale);
|
||||
}
|
||||
return output_max->scale;
|
||||
}
|
||||
|
||||
if (r_scale_fractional) {
|
||||
*r_scale_fractional = wl_fixed_from_int(scale_default);
|
||||
}
|
||||
return scale_default;
|
||||
}
|
||||
|
||||
|
@ -543,8 +759,17 @@ static void xdg_toplevel_handle_configure(void *data,
|
|||
std::lock_guard lock_frame_guard{win->frame_pending_mutex};
|
||||
#endif
|
||||
|
||||
win->frame_pending.size[0] = win->scale * width;
|
||||
win->frame_pending.size[1] = win->scale * height;
|
||||
#ifdef WITH_GHOST_WAYLAND_FRACTIONAL_SCALE
|
||||
if (win->frame.fractional_scale) {
|
||||
win->frame_pending.size[0] = gwl_window_fractional_to_viewport_round(win->frame, width);
|
||||
win->frame_pending.size[1] = gwl_window_fractional_to_viewport_round(win->frame, height);
|
||||
}
|
||||
else
|
||||
#endif /* WITH_GHOST_WAYLAND_FRACTIONAL_SCALE */
|
||||
{
|
||||
win->frame_pending.size[0] = width * win->scale;
|
||||
win->frame_pending.size[1] = height * win->scale;
|
||||
}
|
||||
|
||||
win->frame_pending.is_maximised = false;
|
||||
win->frame_pending.is_fullscreen = false;
|
||||
|
@ -619,6 +844,63 @@ static const struct xdg_activation_token_v1_listener *xdg_activation_listener_ge
|
|||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Listener (Fractional Scale), #wp_fractional_scale_manager_v1_interface
|
||||
*
|
||||
* Used by #gwl_window_activate.
|
||||
* \{ */
|
||||
|
||||
#ifdef WITH_GHOST_WAYLAND_FRACTIONAL_SCALE
|
||||
|
||||
static CLG_LogRef LOG_WL_FRACTIONAL_SCALE = {"ghost.wl.handle.fractional_scale"};
|
||||
# define LOG (&LOG_WL_FRACTIONAL_SCALE)
|
||||
|
||||
static void wp_fractional_scale_handle_preferred_scale(
|
||||
void *data, struct wp_fractional_scale_v1 * /*wp_fractional_scale_v1*/, uint preferred_scale)
|
||||
{
|
||||
CLOG_INFO(LOG,
|
||||
2,
|
||||
"preferred_scale (preferred_scale=%.6f)",
|
||||
double(preferred_scale) / FRACTIONAL_DENOMINATOR);
|
||||
GWL_Window *win = static_cast<GWL_Window *>(data);
|
||||
const bool is_fractional = (preferred_scale % FRACTIONAL_DENOMINATOR) != 0;
|
||||
/* When non-fractional, never use fractional scaling! */
|
||||
win->frame_pending.fractional_scale = is_fractional ? preferred_scale : 0;
|
||||
|
||||
if (win->frame.fractional_scale != win->frame_pending.fractional_scale) {
|
||||
|
||||
/* Resize the window failing to do so results in severe flickering with a
|
||||
* multi-monitor setup when multiple monitors have different scales.
|
||||
*
|
||||
* NOTE: some flickering is still possible even when resizing this
|
||||
* happens when dragging the right hand side of the title-bar in KDE
|
||||
* as expanding changed the size on the RHS, this may be up to the compositor to fix. */
|
||||
const int scale_prev = win->frame.fractional_scale ?
|
||||
win->frame.fractional_scale :
|
||||
win->scale_on_output * FRACTIONAL_DENOMINATOR;
|
||||
const int scale_next = win->frame_pending.fractional_scale ?
|
||||
win->frame_pending.fractional_scale :
|
||||
win->scale_on_output * FRACTIONAL_DENOMINATOR;
|
||||
for (size_t i = 0; i < ARRAY_SIZE(win->frame_pending.size); i++) {
|
||||
const int value = win->frame_pending.size[i] ? win->frame_pending.size[i] :
|
||||
win->frame.size[i];
|
||||
win->frame_pending.size[i] = lroundf(value * (double(scale_next) / double(scale_prev)));
|
||||
}
|
||||
|
||||
gwl_window_pending_actions_tag(win, PENDING_WINDOW_FRAME_CONFIGURE);
|
||||
}
|
||||
}
|
||||
|
||||
static const struct wp_fractional_scale_v1_listener wp_fractional_scale_listener = {
|
||||
/*preferred_scale*/ wp_fractional_scale_handle_preferred_scale,
|
||||
};
|
||||
|
||||
# undef LOG
|
||||
|
||||
#endif /* WITH_GHOST_WAYLAND_FRACTIONAL_SCALE */
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Listener (LibDecor Frame), #libdecor_frame_interface
|
||||
* \{ */
|
||||
|
@ -643,16 +925,27 @@ static void frame_handle_configure(struct libdecor_frame *frame,
|
|||
/* Set the size. */
|
||||
int size_next[2];
|
||||
{
|
||||
const int scale = static_cast<GWL_Window *>(data)->scale;
|
||||
GWL_Window *win = static_cast<GWL_Window *>(data);
|
||||
const int scale = win->scale;
|
||||
if (!libdecor_configuration_get_content_size(
|
||||
configuration, frame, &size_next[0], &size_next[1])) {
|
||||
GWL_Window *win = static_cast<GWL_Window *>(data);
|
||||
size_next[0] = win->frame.size[0] / scale;
|
||||
size_next[1] = win->frame.size[1] / scale;
|
||||
}
|
||||
|
||||
frame_pending->size[0] = scale * size_next[0];
|
||||
frame_pending->size[1] = scale * size_next[1];
|
||||
# ifdef WITH_GHOST_WAYLAND_FRACTIONAL_SCALE
|
||||
if (win->frame.fractional_scale) {
|
||||
win->frame_pending.size[0] = gwl_window_fractional_to_viewport_round(win->frame,
|
||||
size_next[0]);
|
||||
win->frame_pending.size[1] = gwl_window_fractional_to_viewport_round(win->frame,
|
||||
size_next[1]);
|
||||
}
|
||||
else
|
||||
# endif /* WITH_GHOST_WAYLAND_FRACTIONAL_SCALE */
|
||||
{
|
||||
frame_pending->size[0] = size_next[0] * scale;
|
||||
frame_pending->size[1] = size_next[1] * scale;
|
||||
}
|
||||
}
|
||||
|
||||
/* Set the state. */
|
||||
|
@ -892,7 +1185,8 @@ GHOST_WindowWayland::GHOST_WindowWayland(GHOST_SystemWayland *system,
|
|||
*
|
||||
* Using the maximum scale is best as it results in the window first being smaller,
|
||||
* avoiding a large window flashing before it's made smaller. */
|
||||
window_->scale = outputs_max_scale_or_default(system_->outputs(), 1, &window_->scale_fractional);
|
||||
window_->scale = outputs_max_scale_or_default(system_->outputs(), 1);
|
||||
window_->scale_on_output = window_->scale;
|
||||
|
||||
window_->frame.size[0] = int32_t(width);
|
||||
window_->frame.size[1] = int32_t(height);
|
||||
|
@ -915,6 +1209,17 @@ GHOST_WindowWayland::GHOST_WindowWayland(GHOST_SystemWayland *system,
|
|||
window_->egl_window = wl_egl_window_create(
|
||||
window_->wl_surface, int(window_->frame.size[0]), int(window_->frame.size[1]));
|
||||
|
||||
#ifdef WITH_GHOST_WAYLAND_FRACTIONAL_SCALE
|
||||
struct wp_fractional_scale_manager_v1 *fractional_scale_manager =
|
||||
system->wp_fractional_scale_manager();
|
||||
if (fractional_scale_manager) {
|
||||
window_->fractional_scale_handle = wp_fractional_scale_manager_v1_get_fractional_scale(
|
||||
fractional_scale_manager, window_->wl_surface);
|
||||
wp_fractional_scale_v1_add_listener(
|
||||
window_->fractional_scale_handle, &wp_fractional_scale_listener, window_);
|
||||
}
|
||||
#endif /* WITH_GHOST_WAYLAND_FRACTIONAL_SCALE */
|
||||
|
||||
/* NOTE: The limit is in points (not pixels) so Hi-DPI will limit to larger number of pixels.
|
||||
* This has the advantage that the size limit is the same when moving the window between monitors
|
||||
* with different scales set. If it was important to limit in pixels it could be re-calculated
|
||||
|
@ -1033,13 +1338,14 @@ GHOST_TSuccess GHOST_WindowWayland::setWindowCursorGrab(GHOST_TGrabCursorMode mo
|
|||
}
|
||||
bounds = &bounds_buf;
|
||||
}
|
||||
|
||||
if (system_->window_cursor_grab_set(mode,
|
||||
m_cursorGrab,
|
||||
m_cursorGrabInitPos,
|
||||
bounds,
|
||||
m_cursorGrabAxis,
|
||||
window_->wl_surface,
|
||||
window_->scale)) {
|
||||
this->scale_params())) {
|
||||
return GHOST_kSuccess;
|
||||
}
|
||||
return GHOST_kFailure;
|
||||
|
@ -1125,7 +1431,7 @@ GHOST_TSuccess GHOST_WindowWayland::setClientSize(const uint32_t width, const ui
|
|||
window_->frame_pending.size[0] = width;
|
||||
window_->frame_pending.size[1] = height;
|
||||
|
||||
gwl_window_frame_pending_size_set(window_);
|
||||
gwl_window_frame_pending_size_set(window_, nullptr);
|
||||
|
||||
return GHOST_kSuccess;
|
||||
}
|
||||
|
@ -1163,6 +1469,18 @@ GHOST_WindowWayland::~GHOST_WindowWayland()
|
|||
window_->xdg_activation_token = nullptr;
|
||||
}
|
||||
|
||||
#ifdef WITH_GHOST_WAYLAND_FRACTIONAL_SCALE
|
||||
if (window_->fractional_scale_handle) {
|
||||
wp_fractional_scale_v1_destroy(window_->fractional_scale_handle);
|
||||
window_->fractional_scale_handle = nullptr;
|
||||
}
|
||||
|
||||
if (window_->viewport) {
|
||||
wp_viewport_destroy(window_->viewport);
|
||||
window_->viewport = nullptr;
|
||||
}
|
||||
#endif /* WITH_GHOST_WAYLAND_FRACTIONAL_SCALE */
|
||||
|
||||
#ifdef WITH_GHOST_WAYLAND_LIBDECOR
|
||||
if (use_libdecor) {
|
||||
gwl_libdecor_window_destroy(window_->libdecor);
|
||||
|
@ -1192,25 +1510,13 @@ uint16_t GHOST_WindowWayland::getDPIHint()
|
|||
/* No need to lock `server_mutex`
|
||||
* (`outputs_changed_update_scale` never changes values in a non-main thread). */
|
||||
|
||||
const wl_fixed_t scale_fractional = window_->scale_fractional;
|
||||
GHOST_ASSERT(wl_fixed_from_int(window_->scale) >= scale_fractional,
|
||||
"Fractional scale should always be less than the fixed scale.");
|
||||
|
||||
/* Using the physical DPI will cause wrong scaling of the UI
|
||||
* use a multiplier for the default DPI as a workaround. */
|
||||
if (wl_fixed_from_int(window_->scale) == scale_fractional || window_->scale <= 1) {
|
||||
/* No fractional scaling. */
|
||||
return window_->scale * base_dpi;
|
||||
#ifdef WITH_GHOST_WAYLAND_FRACTIONAL_SCALE
|
||||
if (window_->frame.fractional_scale) {
|
||||
return gwl_window_fractional_to_viewport(window_->frame, base_dpi);
|
||||
}
|
||||
const int scale_ceil = window_->scale;
|
||||
const int scale_floor = scale_ceil - 1;
|
||||
const wl_fixed_t scale_fractional_final = wl_fixed_to_int(
|
||||
scale_fractional *
|
||||
/* Compensate for the buffer being rendered at `window_->scale`,
|
||||
* then scaled down fractionally. */
|
||||
(wl_fixed_from_int(1) + ((wl_fixed_from_int(scale_ceil) - scale_fractional) / scale_floor)));
|
||||
#endif /* WITH_GHOST_WAYLAND_FRACTIONAL_SCALE */
|
||||
|
||||
return wl_fixed_to_int(scale_fractional_final * base_dpi);
|
||||
return window_->scale * base_dpi;
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_WindowWayland::setWindowCursorVisibility(bool visible)
|
||||
|
@ -1383,18 +1689,34 @@ int GHOST_WindowWayland::scale() const
|
|||
return window_->scale;
|
||||
}
|
||||
|
||||
wl_fixed_t GHOST_WindowWayland::scale_fractional() const
|
||||
const struct GWL_WindowScaleParams &GHOST_WindowWayland::scale_params() const
|
||||
{
|
||||
return window_->scale_fractional;
|
||||
/* NOTE(@ideasman42): This could be kept initialized,
|
||||
* since it's such a small struct it's not so important. */
|
||||
GWL_WindowScaleParams *scale_params = &window_->scale_params;
|
||||
scale_params->is_fractional = (window_->frame.fractional_scale != 0);
|
||||
scale_params->scale = scale_params->is_fractional ? window_->frame.fractional_scale :
|
||||
window_->scale;
|
||||
return *scale_params;
|
||||
}
|
||||
|
||||
wl_fixed_t GHOST_WindowWayland::wl_fixed_from_window(wl_fixed_t value) const
|
||||
{
|
||||
#ifdef WITH_GHOST_WAYLAND_FRACTIONAL_SCALE
|
||||
if (window_->frame.fractional_scale) {
|
||||
return gwl_window_fractional_from_viewport(window_->frame, value);
|
||||
}
|
||||
#endif
|
||||
return value / window_->scale;
|
||||
}
|
||||
|
||||
wl_fixed_t GHOST_WindowWayland::wl_fixed_to_window(wl_fixed_t value) const
|
||||
{
|
||||
#ifdef WITH_GHOST_WAYLAND_FRACTIONAL_SCALE
|
||||
if (window_->frame.fractional_scale) {
|
||||
return gwl_window_fractional_to_viewport(window_->frame, value);
|
||||
}
|
||||
#endif
|
||||
return value * window_->scale;
|
||||
}
|
||||
|
||||
|
@ -1513,13 +1835,19 @@ bool GHOST_WindowWayland::outputs_changed_update_scale()
|
|||
}
|
||||
#endif
|
||||
|
||||
wl_fixed_t scale_fractional_next = 0;
|
||||
const int scale_next = outputs_max_scale_or_default(outputs(), 0, &scale_fractional_next);
|
||||
const int scale_next = outputs_max_scale_or_default(outputs(), 0);
|
||||
if (UNLIKELY(scale_next == 0)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const wl_fixed_t scale_fractional_curr = window_->scale_fractional;
|
||||
#ifdef WITH_GHOST_WAYLAND_FRACTIONAL_SCALE
|
||||
if (window_->frame.fractional_scale) {
|
||||
GHOST_ASSERT(window_->scale == 1, "Scale is expected to be 1 for fractional scaling!");
|
||||
window_->scale_on_output = scale_next;
|
||||
return false;
|
||||
}
|
||||
#endif /* WITH_GHOST_WAYLAND_FRACTIONAL_SCALE */
|
||||
|
||||
const int scale_curr = window_->scale;
|
||||
bool changed = false;
|
||||
|
||||
|
@ -1544,22 +1872,18 @@ bool GHOST_WindowWayland::outputs_changed_update_scale()
|
|||
window_->frame_pending.size[0] = (window_->frame.size[0] / scale_curr) * scale_next;
|
||||
window_->frame_pending.size[1] = (window_->frame.size[1] / scale_curr) * scale_next;
|
||||
|
||||
gwl_window_frame_pending_size_set(window_);
|
||||
gwl_window_frame_pending_size_set(window_, nullptr);
|
||||
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (scale_fractional_next != scale_fractional_curr) {
|
||||
window_->scale_fractional = scale_fractional_next;
|
||||
changed = true;
|
||||
|
||||
/* As this is a low-level function, we might want adding this event to be optional,
|
||||
* always add the event unless it causes issues. */
|
||||
GHOST_SystemWayland *system = window_->ghost_system;
|
||||
system->pushEvent(
|
||||
new GHOST_Event(system->getMilliSeconds(), GHOST_kEventWindowDPIHintChanged, this));
|
||||
|
||||
changed = true;
|
||||
}
|
||||
|
||||
/* Ensure both are always set. */
|
||||
window_->scale_on_output = window_->scale;
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
|
|
|
@ -140,7 +140,8 @@ class GHOST_WindowWayland : public GHOST_Window {
|
|||
/* WAYLAND direct-data access. */
|
||||
|
||||
int scale() const;
|
||||
wl_fixed_t scale_fractional() const;
|
||||
const struct GWL_WindowScaleParams &scale_params() const;
|
||||
|
||||
struct wl_surface *wl_surface() const;
|
||||
const std::vector<GWL_Output *> &outputs();
|
||||
|
||||
|
|
|
@ -330,7 +330,7 @@ external contributions to this project including patches, pull requests, etc.
|
|||
** libAOM; version 3.4.0 -- https://aomedia.googlesource.com/aom/
|
||||
Copyright (c) 2016, Alliance for Open Media. All rights reserved.
|
||||
** NASM; version 2.15.02 -- https://www.nasm.us/
|
||||
Contributions since 2008-12-15 are Copyright Intel Corporation.
|
||||
Copyright 1996-2010 the NASM Authors - All rights reserved.
|
||||
** OpenJPEG; version 2.5.0 -- https://github.com/uclouvain/openjpeg
|
||||
Copyright (c) 2002-2014, Universite catholique de Louvain (UCL), Belgium
|
||||
Copyright (c) 2002-2014, Professor Benoit Macq
|
||||
|
@ -369,7 +369,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
Ceres Solver - A fast non-linear least squares minimizer
|
||||
Copyright 2016 Google Inc. All rights reserved.
|
||||
** Curve-Fit-nD; version ddcd5bd -- https://github.com/ideasman42/curve-fit-nd
|
||||
Copyright (c) 2016, Blender Foundation.
|
||||
Copyright (c) 2016, DWANGO Co., Ltd.
|
||||
Copyright (c) 2016, Campbell Barton
|
||||
All rights reserved.
|
||||
** Google C++ Testing Framework; version 1.10.0 --
|
||||
https://github.com/google/googletest
|
||||
Copyright 2007, Google Inc.
|
||||
|
@ -381,11 +383,13 @@ All rights reserved.
|
|||
Copyright (c) 2006, Google Inc.
|
||||
All rights reserved.
|
||||
** Imath; version 3.1.5 -- https://github.com/AcademySoftwareFoundation/Imath
|
||||
Contributors to the OpenEXR Project.
|
||||
Copyright Contributors to the OpenEXR Project. All rights reserved.
|
||||
** ISPC; version 1.17.0 -- https://github.com/ispc/ispc
|
||||
Copyright Intel Corporation
|
||||
All rights reserved.
|
||||
** NumPy; version 1.23.5 -- https://numpy.org/
|
||||
Copyright (c) 2005-2021, NumPy Developers.
|
||||
Copyright (c) 2005-2022, NumPy Developers.
|
||||
All rights reserved.
|
||||
** Ogg; version 1.3.5 -- https://www.xiph.org/ogg/
|
||||
COPYRIGHT (C) 1994-2019 by the Xiph.Org Foundation https://www.xiph.org/
|
||||
** Open Shading Language; version
|
||||
|
|
|
@ -4410,6 +4410,8 @@ def km_face_mask(params):
|
|||
{"properties": [("deselect", False)]}),
|
||||
("paint.face_select_linked_pick", {"type": 'L', "value": 'PRESS', "shift": True},
|
||||
{"properties": [("deselect", True)]}),
|
||||
("paint.face_select_more", {"type": 'NUMPAD_PLUS', "value": 'PRESS', "ctrl": True}, None),
|
||||
("paint.face_select_less", {"type": 'NUMPAD_MINUS', "value": 'PRESS', "ctrl": True}, None),
|
||||
])
|
||||
|
||||
return keymap
|
||||
|
@ -4437,6 +4439,8 @@ def km_weight_paint_vertex_selection(params):
|
|||
{"properties": [("select", True)]}),
|
||||
("paint.vert_select_linked_pick", {"type": 'L', "value": 'PRESS', "shift": True},
|
||||
{"properties": [("select", False)]}),
|
||||
("paint.vert_select_more", {"type": 'NUMPAD_PLUS', "value": 'PRESS', "ctrl": True}, None),
|
||||
("paint.vert_select_less", {"type": 'NUMPAD_MINUS', "value": 'PRESS', "ctrl": True}, None),
|
||||
])
|
||||
|
||||
return keymap
|
||||
|
|
|
@ -2011,6 +2011,9 @@ class VIEW3D_MT_select_paint_mask(Menu):
|
|||
layout.operator("paint.face_select_all", text="None").action = 'DESELECT'
|
||||
layout.operator("paint.face_select_all", text="Invert").action = 'INVERT'
|
||||
|
||||
layout.operator("paint.face_select_more")
|
||||
layout.operator("paint.face_select_less")
|
||||
|
||||
layout.separator()
|
||||
|
||||
layout.operator("view3d.select_box")
|
||||
|
@ -2032,6 +2035,9 @@ class VIEW3D_MT_select_paint_mask_vertex(Menu):
|
|||
layout.operator("paint.vert_select_all", text="None").action = 'DESELECT'
|
||||
layout.operator("paint.vert_select_all", text="Invert").action = 'INVERT'
|
||||
|
||||
layout.operator("paint.vert_select_more"),
|
||||
layout.operator("paint.vert_select_less"),
|
||||
|
||||
layout.separator()
|
||||
|
||||
layout.operator("view3d.select_box")
|
||||
|
@ -7047,13 +7053,16 @@ class VIEW3D_PT_snapping(Panel):
|
|||
|
||||
col.prop(tool_settings, "use_snap_backface_culling")
|
||||
|
||||
if 'FACE' in snap_elements:
|
||||
col.prop(tool_settings, "use_snap_project")
|
||||
is_face_nearest_enabled = 'FACE_NEAREST' in snap_elements
|
||||
if is_face_nearest_enabled or 'FACE' in snap_elements:
|
||||
sub = col.column()
|
||||
sub.active = not is_face_nearest_enabled
|
||||
sub.prop(tool_settings, "use_snap_project")
|
||||
|
||||
if 'FACE_NEAREST' in snap_elements:
|
||||
col.prop(tool_settings, "use_snap_to_same_target")
|
||||
if object_mode == 'EDIT':
|
||||
col.prop(tool_settings, "snap_face_nearest_steps")
|
||||
if is_face_nearest_enabled:
|
||||
col.prop(tool_settings, "use_snap_to_same_target")
|
||||
if object_mode == 'EDIT':
|
||||
col.prop(tool_settings, "snap_face_nearest_steps")
|
||||
|
||||
if 'VOLUME' in snap_elements:
|
||||
col.prop(tool_settings, "use_snap_peel_object")
|
||||
|
|
|
@ -196,11 +196,14 @@ class AttributeFieldInput : public GeometryFieldInput {
|
|||
category_ = Category::NamedAttribute;
|
||||
}
|
||||
|
||||
static fn::GField Create(std::string name, const CPPType &type)
|
||||
{
|
||||
auto field_input = std::make_shared<AttributeFieldInput>(std::move(name), type);
|
||||
return fn::GField(field_input);
|
||||
}
|
||||
template<typename T> static fn::Field<T> Create(std::string name)
|
||||
{
|
||||
const CPPType &type = CPPType::get<T>();
|
||||
auto field_input = std::make_shared<AttributeFieldInput>(std::move(name), type);
|
||||
return fn::Field<T>{field_input};
|
||||
return fn::Field<T>(Create(std::move(name), CPPType::get<T>()));
|
||||
}
|
||||
|
||||
StringRefNull attribute_name() const
|
||||
|
|
|
@ -185,17 +185,17 @@ struct IDProperty *IDP_CopyProperty_ex(const struct IDProperty *prop,
|
|||
* Copy content from source #IDProperty into destination one,
|
||||
* freeing destination property's content first.
|
||||
*/
|
||||
void IDP_CopyPropertyContent(struct IDProperty *dst, struct IDProperty *src) ATTR_NONNULL();
|
||||
void IDP_CopyPropertyContent(struct IDProperty *dst, const struct IDProperty *src) ATTR_NONNULL();
|
||||
|
||||
/**
|
||||
* \param is_strict: When false treat missing items as a match.
|
||||
*/
|
||||
bool IDP_EqualsProperties_ex(struct IDProperty *prop1,
|
||||
struct IDProperty *prop2,
|
||||
bool IDP_EqualsProperties_ex(const struct IDProperty *prop1,
|
||||
const struct IDProperty *prop2,
|
||||
bool is_strict) ATTR_WARN_UNUSED_RESULT;
|
||||
|
||||
bool IDP_EqualsProperties(struct IDProperty *prop1,
|
||||
struct IDProperty *prop2) ATTR_WARN_UNUSED_RESULT;
|
||||
bool IDP_EqualsProperties(const struct IDProperty *prop1,
|
||||
const struct IDProperty *prop2) ATTR_WARN_UNUSED_RESULT;
|
||||
|
||||
/**
|
||||
* Allocate a new ID.
|
||||
|
|
|
@ -21,7 +21,7 @@ void ModifierComputeContext::print_current_in_line(std::ostream &stream) const
|
|||
|
||||
NodeGroupComputeContext::NodeGroupComputeContext(
|
||||
const ComputeContext *parent,
|
||||
const int node_id,
|
||||
const int32_t node_id,
|
||||
const std::optional<ComputeContextHash> &cached_hash)
|
||||
: ComputeContext(s_static_type, parent), node_id_(node_id)
|
||||
{
|
||||
|
|
|
@ -779,7 +779,7 @@ IDProperty *IDP_CopyProperty(const IDProperty *prop)
|
|||
return IDP_CopyProperty_ex(prop, 0);
|
||||
}
|
||||
|
||||
void IDP_CopyPropertyContent(IDProperty *dst, IDProperty *src)
|
||||
void IDP_CopyPropertyContent(IDProperty *dst, const IDProperty *src)
|
||||
{
|
||||
IDProperty *idprop_tmp = IDP_CopyProperty(src);
|
||||
idprop_tmp->prev = dst->prev;
|
||||
|
@ -805,7 +805,9 @@ IDProperty *IDP_GetProperties(ID *id, const bool create_if_needed)
|
|||
return id->properties;
|
||||
}
|
||||
|
||||
bool IDP_EqualsProperties_ex(IDProperty *prop1, IDProperty *prop2, const bool is_strict)
|
||||
bool IDP_EqualsProperties_ex(const IDProperty *prop1,
|
||||
const IDProperty *prop2,
|
||||
const bool is_strict)
|
||||
{
|
||||
if (prop1 == NULL && prop2 == NULL) {
|
||||
return true;
|
||||
|
@ -859,8 +861,8 @@ bool IDP_EqualsProperties_ex(IDProperty *prop1, IDProperty *prop2, const bool is
|
|||
return false;
|
||||
}
|
||||
|
||||
LISTBASE_FOREACH (IDProperty *, link1, &prop1->data.group) {
|
||||
IDProperty *link2 = IDP_GetPropertyFromGroup(prop2, link1->name);
|
||||
LISTBASE_FOREACH (const IDProperty *, link1, &prop1->data.group) {
|
||||
const IDProperty *link2 = IDP_GetPropertyFromGroup(prop2, link1->name);
|
||||
|
||||
if (!IDP_EqualsProperties_ex(link1, link2, is_strict)) {
|
||||
return false;
|
||||
|
@ -870,8 +872,8 @@ bool IDP_EqualsProperties_ex(IDProperty *prop1, IDProperty *prop2, const bool is
|
|||
return true;
|
||||
}
|
||||
case IDP_IDPARRAY: {
|
||||
IDProperty *array1 = IDP_IDPArray(prop1);
|
||||
IDProperty *array2 = IDP_IDPArray(prop2);
|
||||
const IDProperty *array1 = IDP_IDPArray(prop1);
|
||||
const IDProperty *array2 = IDP_IDPArray(prop2);
|
||||
|
||||
if (prop1->len != prop2->len) {
|
||||
return false;
|
||||
|
@ -894,7 +896,7 @@ bool IDP_EqualsProperties_ex(IDProperty *prop1, IDProperty *prop2, const bool is
|
|||
return true;
|
||||
}
|
||||
|
||||
bool IDP_EqualsProperties(IDProperty *prop1, IDProperty *prop2)
|
||||
bool IDP_EqualsProperties(const IDProperty *prop1, const IDProperty *prop2)
|
||||
{
|
||||
return IDP_EqualsProperties_ex(prop1, prop2, true);
|
||||
}
|
||||
|
|
|
@ -468,6 +468,40 @@ struct CartesianBasis {
|
|||
from_orthonormal_axes(b_forward, b_up));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
[[nodiscard]] inline VecBase<T, 3> transform_point(const CartesianBasis &basis,
|
||||
const VecBase<T, 3> &v)
|
||||
{
|
||||
VecBase<T, 3> result;
|
||||
result[basis.x().axis().as_int()] = basis.x().is_negative() ? -v[0] : v[0];
|
||||
result[basis.y().axis().as_int()] = basis.y().is_negative() ? -v[1] : v[1];
|
||||
result[basis.z().axis().as_int()] = basis.z().is_negative() ? -v[2] : v[2];
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the inverse transformation represented by the given basis.
|
||||
* This is conceptually the equivalent to a rotation matrix transpose, but much faster.
|
||||
*/
|
||||
[[nodiscard]] inline CartesianBasis invert(const CartesianBasis &basis)
|
||||
{
|
||||
/* Returns the column where the `axis` is found in. The sign is taken from the axis value. */
|
||||
auto search_axis = [](const CartesianBasis &basis, const Axis axis) {
|
||||
if (basis.x().axis() == axis) {
|
||||
return basis.x().is_negative() ? AxisSigned::X_NEG : AxisSigned::X_POS;
|
||||
}
|
||||
if (basis.y().axis() == axis) {
|
||||
return basis.y().is_negative() ? AxisSigned::Y_NEG : AxisSigned::Y_POS;
|
||||
}
|
||||
return basis.z().is_negative() ? AxisSigned::Z_NEG : AxisSigned::Z_POS;
|
||||
};
|
||||
CartesianBasis result;
|
||||
result.x() = search_axis(basis, Axis::X);
|
||||
result.y() = search_axis(basis, Axis::Y);
|
||||
result.z() = search_axis(basis, Axis::Z);
|
||||
return result;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender::math
|
||||
|
|
|
@ -1101,8 +1101,8 @@ double determinant_m3_array_db(const double m[3][3])
|
|||
|
||||
bool invert_m2_m2(float inverse[2][2], const float mat[2][2])
|
||||
{
|
||||
const float det = determinant_m2(mat[0][0], mat[1][0], mat[0][1], mat[1][1]);
|
||||
adjoint_m2_m2(inverse, mat);
|
||||
float det = determinant_m2(mat[0][0], mat[1][0], mat[0][1], mat[1][1]);
|
||||
|
||||
bool success = (det != 0.0f);
|
||||
if (success) {
|
||||
|
@ -1957,11 +1957,15 @@ void normalize_m4_m4(float rmat[4][4], const float mat[4][4])
|
|||
|
||||
void adjoint_m2_m2(float R[2][2], const float M[2][2])
|
||||
{
|
||||
BLI_assert(R != M);
|
||||
R[0][0] = M[1][1];
|
||||
R[0][1] = -M[0][1];
|
||||
R[1][0] = -M[1][0];
|
||||
R[1][1] = M[0][0];
|
||||
const float r00 = M[1][1];
|
||||
const float r01 = -M[0][1];
|
||||
const float r10 = -M[1][0];
|
||||
const float r11 = M[0][0];
|
||||
|
||||
R[0][0] = r00;
|
||||
R[0][1] = r01;
|
||||
R[1][0] = r10;
|
||||
R[1][1] = r11;
|
||||
}
|
||||
|
||||
void adjoint_m3_m3(float R[3][3], const float M[3][3])
|
||||
|
@ -2026,9 +2030,8 @@ void adjoint_m4_m4(float R[4][4], const float M[4][4]) /* out = ADJ(in) */
|
|||
R[3][3] = determinant_m3(a1, a2, a3, b1, b2, b3, c1, c2, c3);
|
||||
}
|
||||
|
||||
float determinant_m2(float a, float b, float c, float d)
|
||||
float determinant_m2(const float a, const float b, const float c, const float d)
|
||||
{
|
||||
|
||||
return a * d - b * c;
|
||||
}
|
||||
|
||||
|
|
|
@ -641,10 +641,9 @@ TEST(math_rotation, CartesianBasis)
|
|||
expect.ptr());
|
||||
}
|
||||
|
||||
EXPECT_EQ(from_rotation<float3x3>(
|
||||
rotation_between(from_orthonormal_axes(src_forward, src_up),
|
||||
from_orthonormal_axes(dst_forward, dst_up))),
|
||||
expect);
|
||||
CartesianBasis rotation = rotation_between(from_orthonormal_axes(src_forward, src_up),
|
||||
from_orthonormal_axes(dst_forward, dst_up));
|
||||
EXPECT_EQ(from_rotation<float3x3>(rotation), expect);
|
||||
|
||||
if (src_forward == dst_forward) {
|
||||
expect = float3x3::identity();
|
||||
|
@ -656,6 +655,11 @@ TEST(math_rotation, CartesianBasis)
|
|||
}
|
||||
|
||||
EXPECT_EQ(from_rotation<float3x3>(rotation_between(src_forward, dst_forward)), expect);
|
||||
|
||||
float3 point(1.0f, 2.0f, 3.0f);
|
||||
CartesianBasis rotation_inv = invert(rotation);
|
||||
/* Test inversion identity. */
|
||||
EXPECT_EQ(transform_point(rotation_inv, transform_point(rotation, point)), point);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -667,8 +671,23 @@ TEST(math_rotation, Transform)
|
|||
Quaternion q(0.927091f, 0.211322f, -0.124857f, 0.283295f);
|
||||
|
||||
float3 p(0.576f, -0.6546f, 46.354f);
|
||||
p = transform_point(q, p);
|
||||
EXPECT_V3_NEAR(p, float3(-4.33722f, -21.661f, 40.7608f), 1e-4f);
|
||||
float3 result = transform_point(q, p);
|
||||
EXPECT_V3_NEAR(result, float3(-4.33722f, -21.661f, 40.7608f), 1e-4f);
|
||||
|
||||
/* Validated using `to_quaternion` before doing the transform. */
|
||||
float3 p2(1.0f, 2.0f, 3.0f);
|
||||
result = transform_point(CartesianBasis(AxisSigned::X_POS, AxisSigned::Y_POS, AxisSigned::Z_POS),
|
||||
p2);
|
||||
EXPECT_EQ(result, float3(1.0f, 2.0f, 3.0f));
|
||||
result = transform_point(
|
||||
rotation_between(from_orthonormal_axes(AxisSigned::Y_POS, AxisSigned::Z_POS),
|
||||
from_orthonormal_axes(AxisSigned::X_POS, AxisSigned::Z_POS)),
|
||||
p2);
|
||||
EXPECT_EQ(result, float3(-2.0f, 1.0f, 3.0f));
|
||||
result = transform_point(from_orthonormal_axes(AxisSigned::Z_POS, AxisSigned::X_POS), p2);
|
||||
EXPECT_EQ(result, float3(3.0f, 1.0f, 2.0f));
|
||||
result = transform_point(from_orthonormal_axes(AxisSigned::X_NEG, AxisSigned::Y_POS), p2);
|
||||
EXPECT_EQ(result, float3(-2.0f, 3.0f, -1.0f));
|
||||
}
|
||||
|
||||
TEST(math_rotation, DualQuaternionNormalize)
|
||||
|
|
|
@ -2271,19 +2271,6 @@ static int lib_link_main_data_restore_cb(LibraryIDLinkCallbackData *cb_data)
|
|||
return IDWALK_RET_NOP;
|
||||
}
|
||||
|
||||
/* Special ugly case here, thanks again for those non-IDs IDs... */
|
||||
/* We probably need to add more cases here (hint: nodetrees),
|
||||
* but will wait for changes from D5559 to get in first. */
|
||||
if (GS((*id_pointer)->name) == ID_GR) {
|
||||
Collection *collection = reinterpret_cast<Collection *>(*id_pointer);
|
||||
if (collection->flag & COLLECTION_IS_MASTER) {
|
||||
/* We should never reach that point anymore, since master collection private ID should be
|
||||
* properly tagged with IDWALK_CB_EMBEDDED. */
|
||||
BLI_assert_unreachable();
|
||||
return IDWALK_RET_NOP;
|
||||
}
|
||||
}
|
||||
|
||||
IDNameLib_Map *id_map = static_cast<IDNameLib_Map *>(cb_data->user_data);
|
||||
|
||||
/* NOTE: Handling of user-count here is really bad, defining its own system...
|
||||
|
|
|
@ -60,6 +60,7 @@ void MaskNode::convert_to_operations(NodeConverter &converter,
|
|||
scale_operation->set_new_width(operation->get_mask_width());
|
||||
scale_operation->set_is_aspect(false);
|
||||
scale_operation->set_is_crop(false);
|
||||
scale_operation->set_offset(0.0f, 0.0f);
|
||||
scale_operation->set_scale_canvas_max_size({float(data->size_x), float(data->size_y)});
|
||||
|
||||
converter.add_operation(scale_operation);
|
||||
|
|
|
@ -57,7 +57,9 @@ void workbench_engine_init(void *ved)
|
|||
wpd->dummy_image_tx = txl->dummy_image_tx;
|
||||
|
||||
if (OBJECT_ID_PASS_ENABLED(wpd)) {
|
||||
wpd->object_id_tx = DRW_texture_pool_query_fullscreen(GPU_R16UI, &draw_engine_workbench);
|
||||
const eGPUTextureUsage usage = GPU_TEXTURE_USAGE_ATTACHMENT | GPU_TEXTURE_USAGE_SHADER_READ;
|
||||
wpd->object_id_tx = DRW_texture_pool_query_fullscreen_ex(
|
||||
GPU_R16UI, usage, &draw_engine_workbench);
|
||||
}
|
||||
else {
|
||||
/* Don't free because it's a pool texture. */
|
||||
|
|
|
@ -29,9 +29,10 @@ void workbench_opaque_engine_init(WORKBENCH_Data *data)
|
|||
/* Reused the same textures format for transparent pipeline to share the textures. */
|
||||
const eGPUTextureFormat col_tex_format = GPU_RGBA16F;
|
||||
const eGPUTextureFormat nor_tex_format = NORMAL_ENCODING_ENABLED() ? GPU_RG16F : GPU_RGBA16F;
|
||||
const eGPUTextureUsage usage = GPU_TEXTURE_USAGE_ATTACHMENT | GPU_TEXTURE_USAGE_SHADER_READ;
|
||||
|
||||
wpd->material_buffer_tx = DRW_texture_pool_query_fullscreen(col_tex_format, owner);
|
||||
wpd->normal_buffer_tx = DRW_texture_pool_query_fullscreen(nor_tex_format, owner);
|
||||
wpd->material_buffer_tx = DRW_texture_pool_query_fullscreen_ex(col_tex_format, usage, owner);
|
||||
wpd->normal_buffer_tx = DRW_texture_pool_query_fullscreen_ex(nor_tex_format, usage, owner);
|
||||
|
||||
GPU_framebuffer_ensure_config(&fbl->opaque_fb,
|
||||
{
|
||||
|
|
|
@ -352,7 +352,7 @@ void ShadowPass::init(const SceneState &scene_state, SceneResources &resources)
|
|||
float4x4 view_matrix;
|
||||
DRW_view_viewmat_get(nullptr, view_matrix.ptr(), false);
|
||||
resources.world_buf.shadow_direction_vs = float4(
|
||||
math::transform_direction(view_matrix, direction_ws));
|
||||
math::transform_direction(view_matrix, direction_ws), 0.0f);
|
||||
|
||||
/* Clamp to avoid overshadowing and shading errors. */
|
||||
float focus = clamp_f(scene.display.shadow_focus, 0.0001f, 0.99999f);
|
||||
|
|
|
@ -36,8 +36,9 @@ void workbench_transparent_engine_init(WORKBENCH_Data *data)
|
|||
const eGPUTextureFormat accum_tex_format = GPU_RGBA16F;
|
||||
const eGPUTextureFormat reveal_tex_format = NORMAL_ENCODING_ENABLED() ? GPU_RG16F : GPU_RGBA32F;
|
||||
|
||||
wpd->accum_buffer_tx = DRW_texture_pool_query_fullscreen(accum_tex_format, owner);
|
||||
wpd->reveal_buffer_tx = DRW_texture_pool_query_fullscreen(reveal_tex_format, owner);
|
||||
eGPUTextureUsage usage = GPU_TEXTURE_USAGE_ATTACHMENT | GPU_TEXTURE_USAGE_SHADER_READ;
|
||||
wpd->accum_buffer_tx = DRW_texture_pool_query_fullscreen_ex(accum_tex_format, usage, owner);
|
||||
wpd->reveal_buffer_tx = DRW_texture_pool_query_fullscreen_ex(reveal_tex_format, usage, owner);
|
||||
|
||||
GPU_framebuffer_ensure_config(&fbl->transp_accum_fb,
|
||||
{
|
||||
|
|
|
@ -23,15 +23,13 @@ namespace blender::ed::curves {
|
|||
|
||||
static bool has_surface_deformation_node(const bNodeTree &ntree)
|
||||
{
|
||||
LISTBASE_FOREACH (const bNode *, node, &ntree.nodes) {
|
||||
if (node->type == GEO_NODE_DEFORM_CURVES_ON_SURFACE) {
|
||||
return true;
|
||||
}
|
||||
if (node->type == NODE_GROUP) {
|
||||
if (node->id != nullptr) {
|
||||
if (has_surface_deformation_node(*reinterpret_cast<const bNodeTree *>(node->id))) {
|
||||
return true;
|
||||
}
|
||||
if (!ntree.nodes_by_type("GeometryNodeDeformCurvesOnSurface").is_empty()) {
|
||||
return true;
|
||||
}
|
||||
for (const bNode *node : ntree.group_nodes()) {
|
||||
if (const bNodeTree *sub_tree = reinterpret_cast<const bNodeTree *>(node->id)) {
|
||||
if (has_surface_deformation_node(*sub_tree)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -419,6 +419,11 @@ void paintface_select_linked(struct bContext *C,
|
|||
struct Object *ob,
|
||||
const int mval[2],
|
||||
bool select);
|
||||
/** Grow the selection of faces.
|
||||
* \param face_step If true will also select faces that only touch on the corner.
|
||||
*/
|
||||
void paintface_select_more(struct Mesh *mesh, bool face_step);
|
||||
void paintface_select_less(struct Mesh *mesh, bool face_step);
|
||||
bool paintface_minmax(struct Object *ob, float r_min[3], float r_max[3]);
|
||||
|
||||
void paintface_hide(struct bContext *C, struct Object *ob, bool unselected);
|
||||
|
@ -444,6 +449,8 @@ void paintvert_select_linked_pick(struct bContext *C,
|
|||
struct Object *ob,
|
||||
const int region_coordinates[2],
|
||||
bool select);
|
||||
void paintvert_select_more(struct Mesh *mesh, bool face_step);
|
||||
void paintvert_select_less(struct Mesh *mesh, bool face_step);
|
||||
void paintvert_hide(struct bContext *C, struct Object *ob, bool unselected);
|
||||
void paintvert_reveal(struct bContext *C, struct Object *ob, bool select);
|
||||
|
||||
|
|
|
@ -361,12 +361,6 @@ int bm_mesh_calc_uv_islands(const Scene *scene,
|
|||
const float aspect_y,
|
||||
BMUVOffsets offsets);
|
||||
|
||||
struct UVMapUDIM_Params {
|
||||
const struct Image *image;
|
||||
/** Copied from #SpaceImage.tile_grid_shape */
|
||||
int grid_shape[2];
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns true if UV coordinates lie on a valid tile in UDIM grid or tiled image.
|
||||
*/
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include "BKE_customdata.h"
|
||||
#include "BKE_global.h"
|
||||
#include "BKE_mesh.hh"
|
||||
#include "BKE_mesh_mapping.h"
|
||||
#include "BKE_object.h"
|
||||
|
||||
#include "ED_mesh.h"
|
||||
|
@ -350,6 +351,131 @@ void paintface_select_linked(bContext *C, Object *ob, const int mval[2], const b
|
|||
paintface_flush_flags(C, ob, true, false);
|
||||
}
|
||||
|
||||
static bool poly_has_selected_neighbor(blender::Span<int> poly_edges,
|
||||
blender::Span<MEdge> edges,
|
||||
blender::Span<bool> select_vert,
|
||||
const bool face_step)
|
||||
{
|
||||
for (const int edge_index : poly_edges) {
|
||||
const MEdge &edge = edges[edge_index];
|
||||
/* If a poly is selected, all of its verts are selected too, meaning that neighboring faces
|
||||
* will have some vertices selected. */
|
||||
if (face_step) {
|
||||
if (select_vert[edge.v1] || select_vert[edge.v2]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (select_vert[edge.v1] && select_vert[edge.v2]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void paintface_select_more(Mesh *mesh, const bool face_step)
|
||||
{
|
||||
using namespace blender;
|
||||
|
||||
bke::MutableAttributeAccessor attributes = mesh->attributes_for_write();
|
||||
bke::SpanAttributeWriter<bool> select_poly = attributes.lookup_or_add_for_write_span<bool>(
|
||||
".select_poly", ATTR_DOMAIN_FACE);
|
||||
bke::SpanAttributeWriter<bool> select_vert = attributes.lookup_or_add_for_write_span<bool>(
|
||||
".select_vert", ATTR_DOMAIN_POINT);
|
||||
const VArray<bool> hide_poly = attributes.lookup_or_default<bool>(
|
||||
".hide_poly", ATTR_DOMAIN_FACE, false);
|
||||
|
||||
const Span<MPoly> polys = mesh->polys();
|
||||
const Span<int> corner_edges = mesh->corner_edges();
|
||||
const Span<MEdge> edges = mesh->edges();
|
||||
|
||||
threading::parallel_for(select_poly.span.index_range(), 1024, [&](const IndexRange range) {
|
||||
for (const int i : range) {
|
||||
if (select_poly.span[i] || hide_poly[i]) {
|
||||
continue;
|
||||
}
|
||||
const MPoly &poly = polys[i];
|
||||
if (poly_has_selected_neighbor(corner_edges.slice(poly.loopstart, poly.totloop),
|
||||
edges,
|
||||
select_vert.span,
|
||||
face_step)) {
|
||||
select_poly.span[i] = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
select_poly.finish();
|
||||
select_vert.finish();
|
||||
}
|
||||
|
||||
static bool poly_has_unselected_neighbor(blender::Span<int> poly_edges,
|
||||
blender::Span<MEdge> edges,
|
||||
blender::BitSpan verts_of_unselected_faces,
|
||||
const bool face_step)
|
||||
{
|
||||
for (const int edge_index : poly_edges) {
|
||||
const MEdge &edge = edges[edge_index];
|
||||
if (face_step) {
|
||||
if (verts_of_unselected_faces[edge.v1] || verts_of_unselected_faces[edge.v2]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (verts_of_unselected_faces[edge.v1] && verts_of_unselected_faces[edge.v2]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void paintface_select_less(Mesh *mesh, const bool face_step)
|
||||
{
|
||||
using namespace blender;
|
||||
|
||||
bke::MutableAttributeAccessor attributes = mesh->attributes_for_write();
|
||||
bke::SpanAttributeWriter<bool> select_poly = attributes.lookup_or_add_for_write_span<bool>(
|
||||
".select_poly", ATTR_DOMAIN_FACE);
|
||||
const VArray<bool> hide_poly = attributes.lookup_or_default<bool>(
|
||||
".hide_poly", ATTR_DOMAIN_FACE, false);
|
||||
|
||||
const Span<MPoly> polys = mesh->polys();
|
||||
const Span<int> corner_verts = mesh->corner_verts();
|
||||
const Span<int> corner_edges = mesh->corner_edges();
|
||||
const Span<MEdge> edges = mesh->edges();
|
||||
|
||||
BitVector<> verts_of_unselected_faces(mesh->totvert);
|
||||
|
||||
/* Find all vertices of unselected faces to help find neighboring faces after. */
|
||||
for (const int i : polys.index_range()) {
|
||||
if (select_poly.span[i]) {
|
||||
continue;
|
||||
}
|
||||
const MPoly &poly = polys[i];
|
||||
for (const int vert : corner_verts.slice(poly.loopstart, poly.totloop)) {
|
||||
verts_of_unselected_faces[vert].set(true);
|
||||
}
|
||||
}
|
||||
|
||||
threading::parallel_for(polys.index_range(), 1024, [&](const IndexRange range) {
|
||||
for (const int i : range) {
|
||||
if (!select_poly.span[i] || hide_poly[i]) {
|
||||
continue;
|
||||
}
|
||||
const MPoly &poly = polys[i];
|
||||
if (poly_has_unselected_neighbor(corner_edges.slice(poly.loopstart, poly.totloop),
|
||||
edges,
|
||||
verts_of_unselected_faces,
|
||||
face_step)) {
|
||||
select_poly.span[i] = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
select_poly.finish();
|
||||
}
|
||||
|
||||
bool paintface_deselect_all_visible(bContext *C, Object *ob, int action, bool flush_flags)
|
||||
{
|
||||
using namespace blender;
|
||||
|
@ -660,6 +786,126 @@ void paintvert_select_linked(bContext *C, Object *ob)
|
|||
paintvert_select_linked_vertices(C, ob, indices, true);
|
||||
}
|
||||
|
||||
void paintvert_select_more(Mesh *mesh, const bool face_step)
|
||||
{
|
||||
using namespace blender;
|
||||
|
||||
bke::MutableAttributeAccessor attributes = mesh->attributes_for_write();
|
||||
bke::SpanAttributeWriter<bool> select_vert = attributes.lookup_or_add_for_write_span<bool>(
|
||||
".select_vert", ATTR_DOMAIN_POINT);
|
||||
const VArray<bool> hide_edge = attributes.lookup_or_default<bool>(
|
||||
".hide_edge", ATTR_DOMAIN_EDGE, false);
|
||||
const VArray<bool> hide_poly = attributes.lookup_or_default<bool>(
|
||||
".hide_poly", ATTR_DOMAIN_FACE, false);
|
||||
|
||||
const Span<MPoly> polys = mesh->polys();
|
||||
const Span<int> corner_edges = mesh->corner_edges();
|
||||
const Span<int> corner_verts = mesh->corner_verts();
|
||||
const Span<MEdge> edges = mesh->edges();
|
||||
|
||||
Array<Vector<int, 2>> edge_to_face_map;
|
||||
if (face_step) {
|
||||
edge_to_face_map = bke::mesh_topology::build_edge_to_poly_map(
|
||||
polys, corner_edges, mesh->totedge);
|
||||
}
|
||||
|
||||
/* Need a copy of the selected verts that we can read from and is not modified. */
|
||||
BitVector<> select_vert_original(mesh->totvert, false);
|
||||
for (int i = 0; i < mesh->totvert; i++) {
|
||||
select_vert_original[i].set(select_vert.span[i]);
|
||||
}
|
||||
|
||||
/* If we iterated over polys we wouldn't extend the selection through edges that have no face
|
||||
* attached to them. */
|
||||
for (const int i : edges.index_range()) {
|
||||
const MEdge &edge = edges[i];
|
||||
if ((!select_vert_original[edge.v1] && !select_vert_original[edge.v2]) || hide_edge[i]) {
|
||||
continue;
|
||||
}
|
||||
select_vert.span[edge.v1] = true;
|
||||
select_vert.span[edge.v2] = true;
|
||||
if (!face_step) {
|
||||
continue;
|
||||
}
|
||||
const Span<int> neighbor_polys = edge_to_face_map[i];
|
||||
for (const int poly_i : neighbor_polys) {
|
||||
if (hide_poly[poly_i]) {
|
||||
continue;
|
||||
}
|
||||
const MPoly &poly = polys[poly_i];
|
||||
for (const int vert : corner_verts.slice(poly.loopstart, poly.totloop)) {
|
||||
select_vert.span[vert] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
select_vert.finish();
|
||||
}
|
||||
|
||||
void paintvert_select_less(Mesh *mesh, const bool face_step)
|
||||
{
|
||||
using namespace blender;
|
||||
|
||||
bke::MutableAttributeAccessor attributes = mesh->attributes_for_write();
|
||||
bke::SpanAttributeWriter<bool> select_vert = attributes.lookup_or_add_for_write_span<bool>(
|
||||
".select_vert", ATTR_DOMAIN_POINT);
|
||||
const VArray<bool> hide_edge = attributes.lookup_or_default<bool>(
|
||||
".hide_edge", ATTR_DOMAIN_EDGE, false);
|
||||
const VArray<bool> hide_poly = attributes.lookup_or_default<bool>(
|
||||
".hide_poly", ATTR_DOMAIN_FACE, false);
|
||||
|
||||
const Span<MPoly> polys = mesh->polys();
|
||||
const Span<int> corner_edges = mesh->corner_edges();
|
||||
const Span<int> corner_verts = mesh->corner_verts();
|
||||
const Span<MEdge> edges = mesh->edges();
|
||||
|
||||
MeshElemMap *edge_poly_map;
|
||||
int *edge_poly_mem = nullptr;
|
||||
if (face_step) {
|
||||
BKE_mesh_edge_poly_map_create(&edge_poly_map,
|
||||
&edge_poly_mem,
|
||||
edges.size(),
|
||||
polys.data(),
|
||||
polys.size(),
|
||||
corner_edges.data(),
|
||||
corner_edges.size());
|
||||
}
|
||||
|
||||
/* Need a copy of the selected verts that we can read from and is not modified. */
|
||||
BitVector<> select_vert_original(mesh->totvert);
|
||||
for (int i = 0; i < mesh->totvert; i++) {
|
||||
select_vert_original[i].set(select_vert.span[i]);
|
||||
}
|
||||
|
||||
for (const int i : edges.index_range()) {
|
||||
const MEdge &edge = edges[i];
|
||||
if ((select_vert_original[edge.v1] && select_vert_original[edge.v2]) && !hide_edge[i]) {
|
||||
continue;
|
||||
}
|
||||
select_vert.span[edge.v1] = false;
|
||||
select_vert.span[edge.v2] = false;
|
||||
|
||||
if (!face_step) {
|
||||
continue;
|
||||
}
|
||||
const Span<int> neighbor_polys(edge_poly_map[i].indices, edge_poly_map[i].count);
|
||||
for (const int poly_i : neighbor_polys) {
|
||||
if (hide_poly[poly_i]) {
|
||||
continue;
|
||||
}
|
||||
const MPoly &poly = polys[poly_i];
|
||||
for (const int vert : corner_verts.slice(poly.loopstart, poly.totloop)) {
|
||||
select_vert.span[vert] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (edge_poly_mem) {
|
||||
MEM_freeN(edge_poly_map);
|
||||
MEM_freeN(edge_poly_mem);
|
||||
}
|
||||
select_vert.finish();
|
||||
}
|
||||
|
||||
void paintvert_tag_select_update(bContext *C, Object *ob)
|
||||
{
|
||||
DEG_id_tag_update(static_cast<ID *>(ob->data), ID_RECALC_COPY_ON_WRITE | ID_RECALC_SELECT);
|
||||
|
|
|
@ -81,7 +81,7 @@ void immDrawPixelsTexScaledFullSize(const IMMDrawPixelsTexState *state,
|
|||
const int mip_len = use_mipmap ? 9999 : 1;
|
||||
|
||||
GPUTexture *tex = GPU_texture_create_2d(
|
||||
"immDrawPixels", img_w, img_h, mip_len, gpu_format, GPU_TEXTURE_USAGE_GENERAL, NULL);
|
||||
"immDrawPixels", img_w, img_h, mip_len, gpu_format, GPU_TEXTURE_USAGE_SHADER_READ, NULL);
|
||||
|
||||
const bool use_float_data = ELEM(gpu_format, GPU_RGBA16F, GPU_RGB16F, GPU_R16F);
|
||||
eGPUDataFormat gpu_data_format = (use_float_data) ? GPU_DATA_FLOAT : GPU_DATA_UBYTE;
|
||||
|
@ -183,7 +183,7 @@ void immDrawPixelsTexTiled_scaling_clipping(IMMDrawPixelsTexState *state,
|
|||
size_t stride = components * ((use_float_data) ? sizeof(float) : sizeof(uchar));
|
||||
|
||||
GPUTexture *tex = GPU_texture_create_2d(
|
||||
"immDrawPixels", tex_w, tex_h, 1, gpu_format, GPU_TEXTURE_USAGE_GENERAL, NULL);
|
||||
"immDrawPixels", tex_w, tex_h, 1, gpu_format, GPU_TEXTURE_USAGE_SHADER_READ, NULL);
|
||||
|
||||
GPU_texture_filter_mode(tex, use_filter);
|
||||
GPU_texture_wrap_mode(tex, false, true);
|
||||
|
|
|
@ -4043,6 +4043,10 @@ static bool proj_paint_state_mesh_eval_init(const bContext *C, ProjPaintState *p
|
|||
CustomData_MeshMasks cddata_masks = scene_eval->customdata_mask;
|
||||
cddata_masks.fmask |= CD_MASK_MTFACE;
|
||||
cddata_masks.lmask |= CD_MASK_PROP_FLOAT2;
|
||||
cddata_masks.vmask |= CD_MASK_PROP_ALL | CD_MASK_CREASE;
|
||||
cddata_masks.emask |= CD_MASK_PROP_ALL | CD_MASK_CREASE;
|
||||
cddata_masks.pmask |= CD_MASK_PROP_ALL | CD_MASK_CREASE;
|
||||
cddata_masks.lmask |= CD_MASK_PROP_ALL | CD_MASK_CREASE;
|
||||
if (ps->do_face_sel) {
|
||||
cddata_masks.vmask |= CD_MASK_ORIGINDEX;
|
||||
cddata_masks.emask |= CD_MASK_ORIGINDEX;
|
||||
|
|
|
@ -378,6 +378,8 @@ void BRUSH_OT_sculpt_curves_falloff_preset(struct wmOperatorType *ot);
|
|||
void PAINT_OT_face_select_linked(struct wmOperatorType *ot);
|
||||
void PAINT_OT_face_select_linked_pick(struct wmOperatorType *ot);
|
||||
void PAINT_OT_face_select_all(struct wmOperatorType *ot);
|
||||
void PAINT_OT_face_select_more(struct wmOperatorType *ot);
|
||||
void PAINT_OT_face_select_less(struct wmOperatorType *ot);
|
||||
void PAINT_OT_face_select_hide(struct wmOperatorType *ot);
|
||||
|
||||
void PAINT_OT_face_vert_reveal(struct wmOperatorType *ot);
|
||||
|
@ -387,6 +389,8 @@ void PAINT_OT_vert_select_ungrouped(struct wmOperatorType *ot);
|
|||
void PAINT_OT_vert_select_hide(struct wmOperatorType *ot);
|
||||
void PAINT_OT_vert_select_linked(struct wmOperatorType *ot);
|
||||
void PAINT_OT_vert_select_linked_pick(struct wmOperatorType *ot);
|
||||
void PAINT_OT_vert_select_more(struct wmOperatorType *ot);
|
||||
void PAINT_OT_vert_select_less(struct wmOperatorType *ot);
|
||||
|
||||
bool vert_paint_poll(struct bContext *C);
|
||||
bool mask_paint_poll(struct bContext *C);
|
||||
|
|
|
@ -1501,6 +1501,8 @@ void ED_operatortypes_paint(void)
|
|||
WM_operatortype_append(PAINT_OT_vert_select_hide);
|
||||
WM_operatortype_append(PAINT_OT_vert_select_linked);
|
||||
WM_operatortype_append(PAINT_OT_vert_select_linked_pick);
|
||||
WM_operatortype_append(PAINT_OT_vert_select_more);
|
||||
WM_operatortype_append(PAINT_OT_vert_select_less);
|
||||
|
||||
/* vertex */
|
||||
WM_operatortype_append(PAINT_OT_vertex_paint_toggle);
|
||||
|
@ -1518,6 +1520,8 @@ void ED_operatortypes_paint(void)
|
|||
WM_operatortype_append(PAINT_OT_face_select_linked);
|
||||
WM_operatortype_append(PAINT_OT_face_select_linked_pick);
|
||||
WM_operatortype_append(PAINT_OT_face_select_all);
|
||||
WM_operatortype_append(PAINT_OT_face_select_more);
|
||||
WM_operatortype_append(PAINT_OT_face_select_less);
|
||||
WM_operatortype_append(PAINT_OT_face_select_hide);
|
||||
|
||||
WM_operatortype_append(PAINT_OT_face_vert_reveal);
|
||||
|
|
|
@ -693,6 +693,68 @@ void PAINT_OT_face_select_all(wmOperatorType *ot)
|
|||
WM_operator_properties_select_all(ot);
|
||||
}
|
||||
|
||||
static int paint_select_more_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
Object *ob = CTX_data_active_object(C);
|
||||
Mesh *mesh = BKE_mesh_from_object(ob);
|
||||
if (mesh == NULL || mesh->totpoly == 0) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
const bool face_step = RNA_boolean_get(op->ptr, "face_step");
|
||||
paintface_select_more(mesh, face_step);
|
||||
paintface_flush_flags(C, ob, true, false);
|
||||
|
||||
ED_region_tag_redraw(CTX_wm_region(C));
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
void PAINT_OT_face_select_more(wmOperatorType *ot)
|
||||
{
|
||||
ot->name = "Select More";
|
||||
ot->description = "Select Faces connected to existing selection";
|
||||
ot->idname = "PAINT_OT_face_select_more";
|
||||
|
||||
ot->exec = paint_select_more_exec;
|
||||
ot->poll = facemask_paint_poll;
|
||||
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
|
||||
RNA_def_boolean(
|
||||
ot->srna, "face_step", true, "Face Step", "Also select faces that only touch on a corner");
|
||||
}
|
||||
|
||||
static int paint_select_less_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
Object *ob = CTX_data_active_object(C);
|
||||
Mesh *mesh = BKE_mesh_from_object(ob);
|
||||
if (mesh == NULL || mesh->totpoly == 0) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
const bool face_step = RNA_boolean_get(op->ptr, "face_step");
|
||||
paintface_select_less(mesh, face_step);
|
||||
paintface_flush_flags(C, ob, true, false);
|
||||
|
||||
ED_region_tag_redraw(CTX_wm_region(C));
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
void PAINT_OT_face_select_less(wmOperatorType *ot)
|
||||
{
|
||||
ot->name = "Select Less";
|
||||
ot->description = "Deselect Faces connected to existing selection";
|
||||
ot->idname = "PAINT_OT_face_select_less";
|
||||
|
||||
ot->exec = paint_select_less_exec;
|
||||
ot->poll = facemask_paint_poll;
|
||||
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
|
||||
RNA_def_boolean(
|
||||
ot->srna, "face_step", true, "Face Step", "Also deselect faces that only touch on a corner");
|
||||
}
|
||||
|
||||
static int vert_select_all_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
Object *ob = CTX_data_active_object(C);
|
||||
|
@ -796,6 +858,72 @@ void PAINT_OT_vert_select_linked_pick(wmOperatorType *ot)
|
|||
"Whether to select or deselect linked vertices under the cursor");
|
||||
}
|
||||
|
||||
static int paintvert_select_more_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
Object *ob = CTX_data_active_object(C);
|
||||
Mesh *mesh = BKE_mesh_from_object(ob);
|
||||
if (mesh == NULL || mesh->totpoly == 0) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
const bool face_step = RNA_boolean_get(op->ptr, "face_step");
|
||||
paintvert_select_more(mesh, face_step);
|
||||
|
||||
paintvert_flush_flags(ob);
|
||||
paintvert_tag_select_update(C, ob);
|
||||
ED_region_tag_redraw(CTX_wm_region(C));
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
void PAINT_OT_vert_select_more(wmOperatorType *ot)
|
||||
{
|
||||
ot->name = "Select More";
|
||||
ot->description = "Select Vertices connected to existing selection";
|
||||
ot->idname = "PAINT_OT_vert_select_more";
|
||||
|
||||
ot->exec = paintvert_select_more_exec;
|
||||
ot->poll = vert_paint_poll;
|
||||
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
|
||||
RNA_def_boolean(
|
||||
ot->srna, "face_step", true, "Face Step", "Also select faces that only touch on a corner");
|
||||
}
|
||||
|
||||
static int paintvert_select_less_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
Object *ob = CTX_data_active_object(C);
|
||||
Mesh *mesh = BKE_mesh_from_object(ob);
|
||||
if (mesh == NULL || mesh->totpoly == 0) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
const bool face_step = RNA_boolean_get(op->ptr, "face_step");
|
||||
paintvert_select_less(mesh, face_step);
|
||||
|
||||
paintvert_flush_flags(ob);
|
||||
paintvert_tag_select_update(C, ob);
|
||||
ED_region_tag_redraw(CTX_wm_region(C));
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
void PAINT_OT_vert_select_less(wmOperatorType *ot)
|
||||
{
|
||||
ot->name = "Select Less";
|
||||
ot->description = "Deselect Vertices connected to existing selection";
|
||||
ot->idname = "PAINT_OT_vert_select_less";
|
||||
|
||||
ot->exec = paintvert_select_less_exec;
|
||||
ot->poll = vert_paint_poll;
|
||||
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
|
||||
RNA_def_boolean(
|
||||
ot->srna, "face_step", true, "Face Step", "Also deselect faces that only touch on a corner");
|
||||
}
|
||||
|
||||
static int face_select_hide_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
const bool unselected = RNA_boolean_get(op->ptr, "unselected");
|
||||
|
|
|
@ -182,6 +182,28 @@ static void graph_main_region_init(wmWindowManager *wm, ARegion *region)
|
|||
WM_event_add_keymap_handler(®ion->handlers, keymap);
|
||||
}
|
||||
|
||||
/* Draw a darker area above 1 and below -1. */
|
||||
static void draw_normalization_borders(Scene *scene, View2D *v2d)
|
||||
{
|
||||
GPU_blend(GPU_BLEND_ALPHA);
|
||||
|
||||
GPUVertFormat *format = immVertexFormat();
|
||||
const uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
|
||||
|
||||
immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
|
||||
immUniformThemeColorShadeAlpha(TH_BACK, -25, -180);
|
||||
|
||||
if (v2d->cur.ymax >= 1) {
|
||||
immRectf(pos, scene->r.sfra, 1, scene->r.efra, v2d->cur.ymax);
|
||||
}
|
||||
if (v2d->cur.ymin <= -1) {
|
||||
immRectf(pos, scene->r.sfra, v2d->cur.ymin, scene->r.efra, -1);
|
||||
}
|
||||
|
||||
GPU_blend(GPU_BLEND_NONE);
|
||||
immUnbindProgram();
|
||||
}
|
||||
|
||||
static void graph_main_region_draw(const bContext *C, ARegion *region)
|
||||
{
|
||||
/* draw entirely, view changes should be handled here */
|
||||
|
@ -207,6 +229,10 @@ static void graph_main_region_draw(const bContext *C, ARegion *region)
|
|||
ANIM_draw_framerange(scene, v2d);
|
||||
}
|
||||
|
||||
if (sipo->mode == SIPO_MODE_ANIMATION && (sipo->flag & SIPO_NORMALIZE)) {
|
||||
draw_normalization_borders(scene, v2d);
|
||||
}
|
||||
|
||||
/* draw data */
|
||||
if (ANIM_animdata_get_context(C, &ac)) {
|
||||
/* draw ghost curves */
|
||||
|
|
|
@ -433,8 +433,9 @@ static void nla_draw_strip(SpaceNla *snla,
|
|||
float yminc,
|
||||
float ymaxc)
|
||||
{
|
||||
const bool non_solo = ((adt && (adt->flag & ADT_NLA_SOLO_TRACK)) &&
|
||||
(nlt->flag & NLATRACK_SOLO) == 0);
|
||||
const bool solo = !((adt && (adt->flag & ADT_NLA_SOLO_TRACK)) &&
|
||||
(nlt->flag & NLATRACK_SOLO) == 0);
|
||||
|
||||
const bool muted = ((nlt->flag & NLATRACK_MUTED) || (strip->flag & NLASTRIP_FLAG_MUTED));
|
||||
float color[4] = {1.0f, 1.0f, 1.0f, 1.0f};
|
||||
uint shdr_pos;
|
||||
|
@ -448,7 +449,7 @@ static void nla_draw_strip(SpaceNla *snla,
|
|||
/* draw extrapolation info first (as backdrop)
|
||||
* - but this should only be drawn if track has some contribution
|
||||
*/
|
||||
if ((strip->extendmode != NLASTRIP_EXTEND_NOTHING) && (non_solo == 0)) {
|
||||
if ((strip->extendmode != NLASTRIP_EXTEND_NOTHING) && solo) {
|
||||
/* enable transparency... */
|
||||
GPU_blend(GPU_BLEND_ALPHA);
|
||||
|
||||
|
@ -486,7 +487,7 @@ static void nla_draw_strip(SpaceNla *snla,
|
|||
}
|
||||
|
||||
/* draw 'inside' of strip itself */
|
||||
if (non_solo == 0 && is_nlastrip_enabled(adt, nlt, strip)) {
|
||||
if (solo && is_nlastrip_enabled(adt, nlt, strip)) {
|
||||
immUnbindProgram();
|
||||
|
||||
/* strip is in normal track */
|
||||
|
@ -629,8 +630,9 @@ static void nla_draw_strip_text(AnimData *adt,
|
|||
float yminc,
|
||||
float ymaxc)
|
||||
{
|
||||
const bool non_solo = ((adt && (adt->flag & ADT_NLA_SOLO_TRACK)) &&
|
||||
(nlt->flag & NLATRACK_SOLO) == 0);
|
||||
const bool solo = !((adt && (adt->flag & ADT_NLA_SOLO_TRACK)) &&
|
||||
(nlt->flag & NLATRACK_SOLO) == 0);
|
||||
|
||||
char str[256];
|
||||
size_t str_len;
|
||||
uchar col[4];
|
||||
|
@ -650,12 +652,12 @@ static void nla_draw_strip_text(AnimData *adt,
|
|||
else {
|
||||
col[0] = col[1] = col[2] = 255;
|
||||
}
|
||||
// Default strip to 100% opacity.
|
||||
col[3] = 255;
|
||||
|
||||
/* text opacity depends on whether if there's a solo'd track, this isn't it */
|
||||
if (non_solo == 0) {
|
||||
col[3] = 255;
|
||||
}
|
||||
else {
|
||||
/* Reduce text opacity if a track is soloed,
|
||||
* and if target track isn't the soloed track. */
|
||||
if (!solo) {
|
||||
col[3] = 128;
|
||||
}
|
||||
|
||||
|
|
|
@ -3480,7 +3480,7 @@ static void do_nurbs_box_select__doSelect(void *userData,
|
|||
}
|
||||
}
|
||||
}
|
||||
static bool do_nurbs_box_select(ViewContext *vc, rcti *rect, const eSelectOp sel_op)
|
||||
static bool do_nurbs_box_select(ViewContext *vc, const rcti *rect, const eSelectOp sel_op)
|
||||
{
|
||||
const bool deselect_all = (sel_op == SEL_OP_SET);
|
||||
BoxSelectUserData data;
|
||||
|
@ -3520,7 +3520,7 @@ static void do_lattice_box_select__doSelect(void *userData, BPoint *bp, const fl
|
|||
data->is_changed = true;
|
||||
}
|
||||
}
|
||||
static bool do_lattice_box_select(ViewContext *vc, rcti *rect, const eSelectOp sel_op)
|
||||
static bool do_lattice_box_select(ViewContext *vc, const rcti *rect, const eSelectOp sel_op)
|
||||
{
|
||||
BoxSelectUserData data;
|
||||
|
||||
|
@ -3855,7 +3855,10 @@ static int opengl_bone_select_buffer_cmp(const void *sel_a_p, const void *sel_b_
|
|||
return 0;
|
||||
}
|
||||
|
||||
static bool do_object_box_select(bContext *C, ViewContext *vc, rcti *rect, const eSelectOp sel_op)
|
||||
static bool do_object_box_select(bContext *C,
|
||||
ViewContext *vc,
|
||||
const rcti *rect,
|
||||
const eSelectOp sel_op)
|
||||
{
|
||||
View3D *v3d = vc->v3d;
|
||||
int totobj = MAXPICKELEMS; /* XXX solve later */
|
||||
|
@ -3928,7 +3931,10 @@ finally:
|
|||
return changed;
|
||||
}
|
||||
|
||||
static bool do_pose_box_select(bContext *C, ViewContext *vc, rcti *rect, const eSelectOp sel_op)
|
||||
static bool do_pose_box_select(bContext *C,
|
||||
ViewContext *vc,
|
||||
const rcti *rect,
|
||||
const eSelectOp sel_op)
|
||||
{
|
||||
blender::Vector<Base *> bases = do_pose_tag_select_op_prepare(vc);
|
||||
|
||||
|
|
|
@ -731,16 +731,27 @@ static eSnapTargetOP snap_target_select_from_spacetype(TransInfo *t)
|
|||
|
||||
static void initSnappingMode(TransInfo *t)
|
||||
{
|
||||
if (!transformModeUseSnap(t)) {
|
||||
/* In this case, snapping is always disabled by default. */
|
||||
t->modifiers &= ~MOD_SNAP;
|
||||
}
|
||||
|
||||
if (doForceIncrementSnap(t)) {
|
||||
t->tsnap.mode = SCE_SNAP_MODE_INCREMENT;
|
||||
}
|
||||
|
||||
if ((t->spacetype != SPACE_VIEW3D) || !(t->tsnap.mode & SCE_SNAP_MODE_FACE_RAYCAST) ||
|
||||
(t->tsnap.mode & SCE_SNAP_MODE_FACE_NEAREST) || (t->flag & T_NO_PROJECT)) {
|
||||
if ((t->spacetype != SPACE_VIEW3D) ||
|
||||
!(t->tsnap.mode & (SCE_SNAP_MODE_FACE_RAYCAST | SCE_SNAP_MODE_FACE_NEAREST)) ||
|
||||
(t->flag & T_NO_PROJECT)) {
|
||||
/* Force project off when not supported. */
|
||||
t->tsnap.flag &= ~SCE_SNAP_PROJECT;
|
||||
}
|
||||
|
||||
if (t->tsnap.mode & SCE_SNAP_MODE_FACE_NEAREST) {
|
||||
/* This mode only works with individual projection. */
|
||||
t->tsnap.flag |= SCE_SNAP_PROJECT;
|
||||
}
|
||||
|
||||
setSnappingCallback(t);
|
||||
|
||||
if (t->spacetype == SPACE_VIEW3D) {
|
||||
|
@ -881,9 +892,9 @@ void initSnapping(TransInfo *t, wmOperator *op)
|
|||
|
||||
t->tsnap.source_operation = snap_source;
|
||||
|
||||
transform_snap_flag_from_modifiers_set(t);
|
||||
|
||||
initSnappingMode(t);
|
||||
|
||||
transform_snap_flag_from_modifiers_set(t);
|
||||
}
|
||||
|
||||
void freeSnapping(TransInfo *t)
|
||||
|
|
|
@ -1287,15 +1287,17 @@ static bool island_has_pins(const Scene *scene,
|
|||
* \param bmesh_override: BMesh array aligned with `objects`.
|
||||
* Optional, when non-null this overrides object's BMesh.
|
||||
* This is needed to perform UV packing on objects that aren't in edit-mode.
|
||||
* \param udim_params: Parameters to specify UDIM target and UDIM source image.
|
||||
* \param udim_source_closest: UDIM source SpaceImage.
|
||||
* \param original_selection: Pack to original selection.
|
||||
* \param params: Parameters and options to pass to the packing engine.
|
||||
*/
|
||||
static void uvedit_pack_islands_multi(const Scene *scene,
|
||||
Object **objects,
|
||||
const int objects_len,
|
||||
BMesh **bmesh_override,
|
||||
const UVMapUDIM_Params *closest_udim,
|
||||
const blender::geometry::UVPackIsland_Params *params)
|
||||
const SpaceImage *udim_source_closest,
|
||||
const bool original_selection,
|
||||
blender::geometry::UVPackIsland_Params *params)
|
||||
{
|
||||
blender::Vector<FaceIsland *> island_vector;
|
||||
|
||||
|
@ -1357,12 +1359,10 @@ static void uvedit_pack_islands_multi(const Scene *scene,
|
|||
|
||||
for (int index = 0; index < island_vector.size(); index++) {
|
||||
FaceIsland *island = island_vector[index];
|
||||
if (closest_udim) {
|
||||
/* Only calculate selection bounding box if using closest_udim. */
|
||||
for (int i = 0; i < island->faces_len; i++) {
|
||||
BMFace *f = island->faces[i];
|
||||
BM_face_uv_minmax(f, selection_min_co, selection_max_co, island->offsets.uv);
|
||||
}
|
||||
|
||||
for (int i = 0; i < island->faces_len; i++) {
|
||||
BMFace *f = island->faces[i];
|
||||
BM_face_uv_minmax(f, selection_min_co, selection_max_co, island->offsets.uv);
|
||||
}
|
||||
|
||||
if (params->rotate) {
|
||||
|
@ -1375,9 +1375,15 @@ static void uvedit_pack_islands_multi(const Scene *scene,
|
|||
|
||||
/* Center of bounding box containing all selected UVs. */
|
||||
float selection_center[2];
|
||||
if (closest_udim) {
|
||||
selection_center[0] = (selection_min_co[0] + selection_max_co[0]) / 2.0f;
|
||||
selection_center[1] = (selection_min_co[1] + selection_max_co[1]) / 2.0f;
|
||||
mid_v2_v2v2(selection_center, selection_min_co, selection_max_co);
|
||||
|
||||
if (original_selection) {
|
||||
/* Protect against degenerate source AABB. */
|
||||
if ((selection_max_co[0] - selection_min_co[0]) * (selection_max_co[1] - selection_min_co[1]) >
|
||||
1e-40f) {
|
||||
params->target_aspect_y = (selection_max_co[0] - selection_min_co[0]) /
|
||||
(selection_max_co[1] - selection_min_co[1]);
|
||||
}
|
||||
}
|
||||
|
||||
MemArena *arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__);
|
||||
|
@ -1415,14 +1421,15 @@ static void uvedit_pack_islands_multi(const Scene *scene,
|
|||
}
|
||||
BLI_heap_free(heap, nullptr);
|
||||
BLI_memarena_free(arena);
|
||||
|
||||
pack_islands(pack_island_vector, *params, scale);
|
||||
|
||||
float base_offset[2] = {0.0f, 0.0f};
|
||||
copy_v2_v2(base_offset, params->udim_base_offset);
|
||||
|
||||
if (closest_udim) {
|
||||
const Image *image = closest_udim->image;
|
||||
const int *udim_grid = closest_udim->grid_shape;
|
||||
if (udim_source_closest) {
|
||||
const Image *image = udim_source_closest->image;
|
||||
const int *udim_grid = udim_source_closest->tile_grid_shape;
|
||||
/* Check if selection lies on a valid UDIM grid tile. */
|
||||
bool is_valid_udim = uv_coords_isect_udim(image, udim_grid, selection_center);
|
||||
if (is_valid_udim) {
|
||||
|
@ -1457,12 +1464,7 @@ static void uvedit_pack_islands_multi(const Scene *scene,
|
|||
for (int64_t i : pack_island_vector.index_range()) {
|
||||
blender::geometry::PackIsland *pack_island = pack_island_vector[i];
|
||||
FaceIsland *island = island_vector[pack_island->caller_index];
|
||||
const float cos_angle = cosf(pack_island->angle);
|
||||
const float sin_angle = sinf(pack_island->angle);
|
||||
matrix[0][0] = cos_angle * scale[0];
|
||||
matrix[0][1] = -sin_angle * scale[0] * pack_island->aspect_y;
|
||||
matrix[1][0] = sin_angle * scale[1] / pack_island->aspect_y;
|
||||
matrix[1][1] = cos_angle * scale[1];
|
||||
pack_island->build_transformation(scale[0], pack_island->angle, matrix);
|
||||
invert_m2_m2(matrix_inverse, matrix);
|
||||
|
||||
/* Add base_offset, post transform. */
|
||||
|
@ -1475,6 +1477,20 @@ static void uvedit_pack_islands_multi(const Scene *scene,
|
|||
/* Perform the transformation. */
|
||||
island_uv_transform(island, matrix, pre_translate);
|
||||
|
||||
if (original_selection) {
|
||||
const float rescale_x = (selection_max_co[0] - selection_min_co[0]) /
|
||||
params->target_aspect_y;
|
||||
const float rescale_y = (selection_max_co[1] - selection_min_co[1]);
|
||||
const float rescale = std::min(rescale_x, rescale_y);
|
||||
matrix[0][0] = rescale;
|
||||
matrix[0][1] = 0.0f;
|
||||
matrix[1][0] = 0.0f;
|
||||
matrix[1][1] = rescale;
|
||||
pre_translate[0] = selection_min_co[0] / rescale;
|
||||
pre_translate[1] = selection_min_co[1] / rescale;
|
||||
island_uv_transform(island, matrix, pre_translate);
|
||||
}
|
||||
|
||||
/* Cleanup memory. */
|
||||
pack_island_vector[i] = nullptr;
|
||||
delete pack_island;
|
||||
|
@ -1499,7 +1515,8 @@ static void uvedit_pack_islands_multi(const Scene *scene,
|
|||
/* Packing targets. */
|
||||
enum {
|
||||
PACK_UDIM_SRC_CLOSEST = 0,
|
||||
PACK_UDIM_SRC_ACTIVE = 1,
|
||||
PACK_UDIM_SRC_ACTIVE,
|
||||
PACK_ORIGINAL_AABB,
|
||||
};
|
||||
|
||||
static int pack_islands_exec(bContext *C, wmOperator *op)
|
||||
|
@ -1544,21 +1561,17 @@ static int pack_islands_exec(bContext *C, wmOperator *op)
|
|||
pack_island_params.shape_method = eUVPackIsland_ShapeMethod(
|
||||
RNA_enum_get(op->ptr, "shape_method"));
|
||||
|
||||
UVMapUDIM_Params closest_udim_buf;
|
||||
UVMapUDIM_Params *closest_udim = nullptr;
|
||||
if (udim_source == PACK_UDIM_SRC_ACTIVE) {
|
||||
pack_island_params.setUDIMOffsetFromSpaceImage(sima);
|
||||
}
|
||||
else if (sima) {
|
||||
BLI_assert(udim_source == PACK_UDIM_SRC_CLOSEST);
|
||||
closest_udim = &closest_udim_buf;
|
||||
closest_udim->image = sima->image;
|
||||
closest_udim->grid_shape[0] = sima->tile_grid_shape[0];
|
||||
closest_udim->grid_shape[1] = sima->tile_grid_shape[1];
|
||||
}
|
||||
|
||||
uvedit_pack_islands_multi(
|
||||
scene, objects, objects_len, nullptr, closest_udim, &pack_island_params);
|
||||
uvedit_pack_islands_multi(scene,
|
||||
objects,
|
||||
objects_len,
|
||||
nullptr,
|
||||
(udim_source == PACK_UDIM_SRC_CLOSEST) ? sima : nullptr,
|
||||
(udim_source == PACK_ORIGINAL_AABB),
|
||||
&pack_island_params);
|
||||
|
||||
MEM_freeN(objects);
|
||||
return OPERATOR_FINISHED;
|
||||
|
@ -1596,6 +1609,11 @@ void UV_OT_pack_islands(wmOperatorType *ot)
|
|||
0,
|
||||
"Active UDIM",
|
||||
"Pack islands to active UDIM image tile or UDIM grid tile where 2D cursor is located"},
|
||||
{PACK_ORIGINAL_AABB,
|
||||
"ORIGINAL_AABB",
|
||||
0,
|
||||
"Original bounding box",
|
||||
"Pack to starting bounding box of islands"},
|
||||
{0, nullptr, 0, nullptr, nullptr},
|
||||
};
|
||||
/* identifiers */
|
||||
|
@ -2341,7 +2359,8 @@ void ED_uvedit_live_unwrap(const Scene *scene, Object **objects, int objects_len
|
|||
pack_island_params.margin_method = ED_UVPACK_MARGIN_SCALED;
|
||||
pack_island_params.margin = scene->toolsettings->uvcalc_margin;
|
||||
|
||||
uvedit_pack_islands_multi(scene, objects, objects_len, nullptr, nullptr, &pack_island_params);
|
||||
uvedit_pack_islands_multi(
|
||||
scene, objects, objects_len, nullptr, nullptr, false, &pack_island_params);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2483,7 +2502,8 @@ static int unwrap_exec(bContext *C, wmOperator *op)
|
|||
RNA_enum_get(op->ptr, "margin_method"));
|
||||
pack_island_params.margin = RNA_float_get(op->ptr, "margin");
|
||||
|
||||
uvedit_pack_islands_multi(scene, objects, objects_len, nullptr, nullptr, &pack_island_params);
|
||||
uvedit_pack_islands_multi(
|
||||
scene, objects, objects_len, nullptr, nullptr, false, &pack_island_params);
|
||||
|
||||
MEM_freeN(objects);
|
||||
|
||||
|
@ -2864,7 +2884,7 @@ static int smart_project_exec(bContext *C, wmOperator *op)
|
|||
params.margin = RNA_float_get(op->ptr, "island_margin");
|
||||
|
||||
uvedit_pack_islands_multi(
|
||||
scene, objects_changed, object_changed_len, nullptr, nullptr, ¶ms);
|
||||
scene, objects_changed, object_changed_len, nullptr, nullptr, false, ¶ms);
|
||||
|
||||
/* #uvedit_pack_islands_multi only supports `per_face_aspect = false`. */
|
||||
const bool per_face_aspect = false;
|
||||
|
@ -3852,7 +3872,7 @@ void ED_uvedit_add_simple_uvs(Main *bmain, const Scene *scene, Object *ob)
|
|||
params.margin_method = ED_UVPACK_MARGIN_SCALED;
|
||||
params.margin = 0.001f;
|
||||
|
||||
uvedit_pack_islands_multi(scene, &ob, 1, &bm, nullptr, ¶ms);
|
||||
uvedit_pack_islands_multi(scene, &ob, 1, &bm, nullptr, false, ¶ms);
|
||||
|
||||
/* Write back from BMesh to Mesh. */
|
||||
BMeshToMeshParams bm_to_me_params{};
|
||||
|
|
|
@ -250,7 +250,7 @@ struct Output {
|
|||
*/
|
||||
class LazyFunction {
|
||||
protected:
|
||||
const char *debug_name_ = "<unknown>";
|
||||
const char *debug_name_ = "unknown";
|
||||
Vector<Input> inputs_;
|
||||
Vector<Output> outputs_;
|
||||
/**
|
||||
|
|
|
@ -18,6 +18,7 @@ set(SRC
|
|||
intern/add_curves_on_mesh.cc
|
||||
intern/curve_constraints.cc
|
||||
intern/fillet_curves.cc
|
||||
intern/mesh_flip_faces.cc
|
||||
intern/mesh_merge_by_distance.cc
|
||||
intern/mesh_primitive_cuboid.cc
|
||||
intern/mesh_split_edges.cc
|
||||
|
@ -37,6 +38,7 @@ set(SRC
|
|||
GEO_add_curves_on_mesh.hh
|
||||
GEO_curve_constraints.hh
|
||||
GEO_fillet_curves.hh
|
||||
GEO_mesh_flip_faces.hh
|
||||
GEO_mesh_merge_by_distance.hh
|
||||
GEO_mesh_primitive_cuboid.hh
|
||||
GEO_mesh_split_edges.hh
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_index_mask.hh"
|
||||
|
||||
struct Mesh;
|
||||
|
||||
namespace blender::geometry {
|
||||
|
||||
/**
|
||||
* Reverse the order of the vertices in the selected faces. This effectively changes the
|
||||
* direction of the face normal.
|
||||
*/
|
||||
void flip_faces(Mesh &mesh, const IndexMask &selection);
|
||||
|
||||
} // namespace blender::geometry
|
|
@ -66,14 +66,14 @@ class UVPackIsland_Params {
|
|||
eUVPackIsland_MarginMethod margin_method;
|
||||
/** Additional translation for bottom left corner. */
|
||||
float udim_base_offset[2];
|
||||
/** Target aspect ratio. */
|
||||
float target_aspect_y;
|
||||
/** Which shape to use when packing. */
|
||||
eUVPackIsland_ShapeMethod shape_method;
|
||||
};
|
||||
|
||||
class PackIsland {
|
||||
public:
|
||||
/** Calculated automatically. */
|
||||
rctf bounds_rect;
|
||||
/** Aspect ratio, required for rotation. */
|
||||
float aspect_y;
|
||||
/** Output pre-translation. */
|
||||
|
@ -87,8 +87,16 @@ class PackIsland {
|
|||
void add_polygon(const blender::Span<float2> uvs, MemArena *arena, Heap *heap);
|
||||
void finalize_geometry(const UVPackIsland_Params ¶ms, MemArena *arena, Heap *heap);
|
||||
|
||||
void build_transformation(const float scale, const float angle, float r_matrix[2][2]);
|
||||
void build_inverse_transformation(const float scale, const float angle, float r_matrix[2][2]);
|
||||
|
||||
/** Center of AABB and inside-or-touching the convex hull. */
|
||||
float2 pivot_;
|
||||
/** Half of the diagonal of the AABB. */
|
||||
float2 half_diagonal_;
|
||||
|
||||
private:
|
||||
void calculate_pivot(); /* Choose a pivot based on triangles. */
|
||||
void calculate_pivot(); /* Calculate `pivot_` and `half_diagonal_` based on added triangles. */
|
||||
blender::Vector<float2> triangle_vertices_;
|
||||
friend class Occupancy;
|
||||
};
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "GEO_mesh_flip_faces.hh"
|
||||
|
||||
#include "DNA_mesh_types.h"
|
||||
|
||||
#include "BLI_task.hh"
|
||||
|
||||
#include "BKE_attribute.hh"
|
||||
#include "BKE_attribute_math.hh"
|
||||
#include "BKE_mesh.hh"
|
||||
|
||||
namespace blender::geometry {
|
||||
|
||||
void flip_faces(Mesh &mesh, const IndexMask &selection)
|
||||
{
|
||||
if (mesh.totpoly == 0 || selection.is_empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const Span<MPoly> polys = mesh.polys();
|
||||
MutableSpan<int> corner_verts = mesh.corner_verts_for_write();
|
||||
MutableSpan<int> corner_edges = mesh.corner_edges_for_write();
|
||||
|
||||
threading::parallel_for(selection.index_range(), 1024, [&](const IndexRange range) {
|
||||
for (const int i : selection.slice(range)) {
|
||||
const IndexRange poly(polys[i].loopstart, polys[i].totloop);
|
||||
for (const int j : IndexRange(poly.size() / 2)) {
|
||||
const int a = poly[j + 1];
|
||||
const int b = poly.last(j);
|
||||
std::swap(corner_verts[a], corner_verts[b]);
|
||||
std::swap(corner_edges[a - 1], corner_edges[b]);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
bke::MutableAttributeAccessor attributes = mesh.attributes_for_write();
|
||||
attributes.for_all(
|
||||
[&](const bke::AttributeIDRef &attribute_id, const bke::AttributeMetaData &meta_data) {
|
||||
if (meta_data.data_type == CD_PROP_STRING) {
|
||||
return true;
|
||||
}
|
||||
if (meta_data.domain != ATTR_DOMAIN_CORNER) {
|
||||
return true;
|
||||
}
|
||||
if (ELEM(attribute_id.name(), ".corner_vert", ".corner_edge")) {
|
||||
return true;
|
||||
}
|
||||
bke::GSpanAttributeWriter attribute = attributes.lookup_for_write_span(attribute_id);
|
||||
attribute_math::convert_to_static_type(meta_data.data_type, [&](auto dummy) {
|
||||
using T = decltype(dummy);
|
||||
MutableSpan<T> dst_span = attribute.span.typed<T>();
|
||||
threading::parallel_for(selection.index_range(), 1024, [&](const IndexRange range) {
|
||||
for (const int i : selection.slice(range)) {
|
||||
const IndexRange poly(polys[i].loopstart, polys[i].totloop);
|
||||
dst_span.slice(poly.drop_front(1)).reverse();
|
||||
}
|
||||
});
|
||||
});
|
||||
attribute.finish();
|
||||
return true;
|
||||
});
|
||||
|
||||
BKE_mesh_tag_topology_changed(&mesh);
|
||||
}
|
||||
|
||||
} // namespace blender::geometry
|
|
@ -163,15 +163,8 @@ void PackIsland::finalize_geometry(const UVPackIsland_Params ¶ms, MemArena *
|
|||
void PackIsland::calculate_pivot()
|
||||
{
|
||||
Bounds<float2> triangle_bounds = *bounds::min_max(triangle_vertices_.as_span());
|
||||
float2 aabb_min = triangle_bounds.min;
|
||||
float2 aabb_max = triangle_bounds.max;
|
||||
float2 pivot = (aabb_min + aabb_max) * 0.5f;
|
||||
float2 half_diagonal = (aabb_max - aabb_min) * 0.5f;
|
||||
|
||||
bounds_rect.xmin = pivot.x - half_diagonal.x;
|
||||
bounds_rect.ymin = pivot.y - half_diagonal.y;
|
||||
bounds_rect.xmax = pivot.x + half_diagonal.x;
|
||||
bounds_rect.ymax = pivot.y + half_diagonal.y;
|
||||
pivot_ = (triangle_bounds.min + triangle_bounds.max) * 0.5f;
|
||||
half_diagonal_ = (triangle_bounds.max - triangle_bounds.min) * 0.5f;
|
||||
}
|
||||
|
||||
UVPackIsland_Params::UVPackIsland_Params()
|
||||
|
@ -188,6 +181,7 @@ UVPackIsland_Params::UVPackIsland_Params()
|
|||
margin_method = ED_UVPACK_MARGIN_SCALED;
|
||||
udim_base_offset[0] = 0.0f;
|
||||
udim_base_offset[1] = 0.0f;
|
||||
target_aspect_y = 1.0f;
|
||||
shape_method = ED_UVPACK_SHAPE_AABB;
|
||||
}
|
||||
|
||||
|
@ -212,21 +206,22 @@ class UVAABBIsland {
|
|||
* the input must be pre-sorted, which costs an additional `O(nlogn)` time complexity.
|
||||
*/
|
||||
static void pack_islands_alpaca_turbo(const Span<UVAABBIsland *> islands,
|
||||
const float target_aspect_y,
|
||||
float *r_max_u,
|
||||
float *r_max_v)
|
||||
{
|
||||
/* Exclude an initial AABB near the origin. */
|
||||
float next_u1 = *r_max_u;
|
||||
float next_v1 = *r_max_v;
|
||||
bool zigzag = next_u1 < next_v1; /* Horizontal or Vertical strip? */
|
||||
bool zigzag = next_u1 / target_aspect_y < next_v1; /* Horizontal or Vertical strip? */
|
||||
|
||||
float u0 = zigzag ? next_u1 : 0.0f;
|
||||
float v0 = zigzag ? 0.0f : next_v1;
|
||||
|
||||
/* Visit every island in order. */
|
||||
for (UVAABBIsland *island : islands) {
|
||||
float dsm_u = island->uv_diagonal.x;
|
||||
float dsm_v = island->uv_diagonal.y;
|
||||
const float dsm_u = island->uv_diagonal.x;
|
||||
const float dsm_v = island->uv_diagonal.y;
|
||||
|
||||
bool restart = false;
|
||||
if (zigzag) {
|
||||
|
@ -237,14 +232,14 @@ static void pack_islands_alpaca_turbo(const Span<UVAABBIsland *> islands,
|
|||
}
|
||||
if (restart) {
|
||||
/* We're at the end of a strip. Restart from U axis or V axis. */
|
||||
zigzag = next_u1 < next_v1;
|
||||
zigzag = next_u1 / target_aspect_y < next_v1;
|
||||
u0 = zigzag ? next_u1 : 0.0f;
|
||||
v0 = zigzag ? 0.0f : next_v1;
|
||||
}
|
||||
|
||||
/* Place the island. */
|
||||
island->uv_placement.x = u0;
|
||||
island->uv_placement.y = v0;
|
||||
island->uv_placement.x = u0 + dsm_u * 0.5f;
|
||||
island->uv_placement.y = v0 + dsm_v * 0.5f;
|
||||
if (zigzag) {
|
||||
/* Move upwards. */
|
||||
v0 += dsm_v;
|
||||
|
@ -264,6 +259,48 @@ static void pack_islands_alpaca_turbo(const Span<UVAABBIsland *> islands,
|
|||
*r_max_v = next_v1;
|
||||
}
|
||||
|
||||
/* Wrapper around #BLI_box_pack_2d. */
|
||||
static void pack_island_box_pack_2d(const Span<UVAABBIsland *> aabbs,
|
||||
const Span<PackIsland *> islands,
|
||||
const float scale,
|
||||
const float margin,
|
||||
const float target_aspect_y,
|
||||
float *r_max_u,
|
||||
float *r_max_v)
|
||||
{
|
||||
/* Allocate storage. */
|
||||
BoxPack *box_array = static_cast<BoxPack *>(
|
||||
MEM_mallocN(sizeof(*box_array) * islands.size(), __func__));
|
||||
|
||||
/* Prepare for box_pack_2d. */
|
||||
for (const int64_t i : aabbs.index_range()) {
|
||||
PackIsland *island = islands[aabbs[i]->index];
|
||||
BoxPack *box = box_array + i;
|
||||
box->w = (island->half_diagonal_.x * 2 * scale + 2 * margin) / target_aspect_y;
|
||||
box->h = island->half_diagonal_.y * 2 * scale + 2 * margin;
|
||||
}
|
||||
|
||||
const bool sort_boxes = false; /* Use existing ordering from `aabbs`. */
|
||||
|
||||
/* \note Writes to `*r_max_u` and `*r_max_v`. */
|
||||
BLI_box_pack_2d(box_array, int(aabbs.size()), sort_boxes, r_max_u, r_max_v);
|
||||
|
||||
*r_max_u *= target_aspect_y;
|
||||
|
||||
/* Write back box_pack UVs. */
|
||||
for (const int64_t i : aabbs.index_range()) {
|
||||
PackIsland *island = islands[aabbs[i]->index];
|
||||
BoxPack *box = box_array + i;
|
||||
island->angle = 0.0f; /* #BLI_box_pack_2d never rotates. */
|
||||
island->pre_translate.x = (box->x + box->w * 0.5f) * target_aspect_y / scale -
|
||||
island->pivot_.x;
|
||||
island->pre_translate.y = (box->y + box->h * 0.5f) / scale - island->pivot_.y;
|
||||
}
|
||||
|
||||
/* Housekeeping. */
|
||||
MEM_freeN(box_array);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper class for the `xatlas` strategy.
|
||||
* Accelerates geometry queries by approximating exact queries with a bitmap.
|
||||
|
@ -410,13 +447,13 @@ float Occupancy::trace_island(PackIsland *island,
|
|||
{
|
||||
|
||||
if (!write) {
|
||||
if (uv.x <= 0.0f || uv.y <= 0.0f) {
|
||||
if (uv.x < island->half_diagonal_.x * scale + margin ||
|
||||
uv.y < island->half_diagonal_.y * scale + margin) {
|
||||
return terminal; /* Occupied. */
|
||||
}
|
||||
}
|
||||
const float2 origin(island->bounds_rect.xmin, island->bounds_rect.ymin);
|
||||
const float2 delta = uv - origin * scale;
|
||||
uint vert_count = uint(island->triangle_vertices_.size());
|
||||
const float2 delta = uv - island->pivot_ * scale;
|
||||
uint vert_count = uint(island->triangle_vertices_.size()); /* `uint` is faster than `int`. */
|
||||
for (uint i = 0; i < vert_count; i += 3) {
|
||||
uint j = (i + triangle_hint_) % vert_count;
|
||||
float extent = trace_triangle(delta + island->triangle_vertices_[j] * scale,
|
||||
|
@ -433,39 +470,46 @@ float Occupancy::trace_island(PackIsland *island,
|
|||
return -1.0f; /* Available. */
|
||||
}
|
||||
|
||||
static float2 find_best_fit_for_island(
|
||||
PackIsland *island, int scan_line, Occupancy &occupancy, const float scale, const float margin)
|
||||
static float2 find_best_fit_for_island(PackIsland *island,
|
||||
int scan_line,
|
||||
Occupancy &occupancy,
|
||||
const float scale,
|
||||
const float margin,
|
||||
const float target_aspect_y)
|
||||
{
|
||||
const float scan_line_bscaled = scan_line / occupancy.bitmap_scale_reciprocal;
|
||||
const float size_x_scaled = BLI_rctf_size_x(&island->bounds_rect) * scale;
|
||||
const float size_y_scaled = BLI_rctf_size_y(&island->bounds_rect) * scale;
|
||||
const float bitmap_scale = 1.0f / occupancy.bitmap_scale_reciprocal;
|
||||
const float half_x_scaled = island->half_diagonal_.x * scale;
|
||||
const float half_y_scaled = island->half_diagonal_.y * scale;
|
||||
|
||||
const float sqrt_target_aspect_y = sqrtf(target_aspect_y);
|
||||
int scan_line_x = int(scan_line * sqrt_target_aspect_y);
|
||||
int scan_line_y = int(scan_line / sqrt_target_aspect_y);
|
||||
|
||||
/* Scan using an "Alpaca"-style search, first horizontally using "less-than". */
|
||||
|
||||
int t = int(ceilf(size_x_scaled * occupancy.bitmap_scale_reciprocal));
|
||||
while (t < scan_line) {
|
||||
const float t_bscaled = t / occupancy.bitmap_scale_reciprocal;
|
||||
const float2 probe(t_bscaled - size_x_scaled, scan_line_bscaled - size_y_scaled);
|
||||
int t = int(ceilf((2 * half_x_scaled + margin) * occupancy.bitmap_scale_reciprocal));
|
||||
while (t < scan_line_x) {
|
||||
const float2 probe(t * bitmap_scale - half_x_scaled,
|
||||
scan_line_y * bitmap_scale - half_y_scaled);
|
||||
const float extent = occupancy.trace_island(island, scale, margin, probe, false);
|
||||
if (extent < 0.0f) {
|
||||
return probe;
|
||||
return probe; /* Success. */
|
||||
}
|
||||
t = t + std::max(1, int(extent));
|
||||
}
|
||||
|
||||
/* Then scan vertically using "less-than-or-equal" */
|
||||
t = int(ceilf(size_y_scaled * occupancy.bitmap_scale_reciprocal));
|
||||
while (t <= scan_line) {
|
||||
const float t_bscaled = t / occupancy.bitmap_scale_reciprocal;
|
||||
const float2 probe(scan_line_bscaled - size_x_scaled, t_bscaled - size_y_scaled);
|
||||
t = int(ceilf((2 * half_y_scaled + margin) * occupancy.bitmap_scale_reciprocal));
|
||||
while (t <= scan_line_y) {
|
||||
const float2 probe(scan_line_x * bitmap_scale - half_x_scaled,
|
||||
t * bitmap_scale - half_y_scaled);
|
||||
const float extent = occupancy.trace_island(island, scale, margin, probe, false);
|
||||
if (extent < 0.0f) {
|
||||
return probe;
|
||||
return probe; /* Success. */
|
||||
}
|
||||
t = t + std::max(1, int(extent));
|
||||
}
|
||||
|
||||
return float2(-1, -1);
|
||||
return float2(-1, -1); /* Unable to find a place to fit. */
|
||||
}
|
||||
|
||||
static float guess_initial_scale(const Span<PackIsland *> islands,
|
||||
|
@ -475,8 +519,8 @@ static float guess_initial_scale(const Span<PackIsland *> islands,
|
|||
float sum = 1e-40f;
|
||||
for (int64_t i : islands.index_range()) {
|
||||
PackIsland *island = islands[i];
|
||||
sum += BLI_rctf_size_x(&island->bounds_rect) * scale + 2 * margin;
|
||||
sum += BLI_rctf_size_y(&island->bounds_rect) * scale + 2 * margin;
|
||||
sum += island->half_diagonal_.x * 2 * scale + 2 * margin;
|
||||
sum += island->half_diagonal_.y * 2 * scale + 2 * margin;
|
||||
}
|
||||
return sqrtf(sum) / 6.0f;
|
||||
}
|
||||
|
@ -498,9 +542,9 @@ static float guess_initial_scale(const Span<PackIsland *> islands,
|
|||
*/
|
||||
static void pack_island_xatlas(const Span<UVAABBIsland *> island_indices,
|
||||
const Span<PackIsland *> islands,
|
||||
BoxPack *box_array,
|
||||
const float scale,
|
||||
const float margin,
|
||||
const float target_aspect_y,
|
||||
float *r_max_u,
|
||||
float *r_max_v)
|
||||
{
|
||||
|
@ -519,7 +563,8 @@ static void pack_island_xatlas(const Span<UVAABBIsland *> island_indices,
|
|||
|
||||
while (i < island_indices.size()) {
|
||||
PackIsland *island = islands[island_indices[i]->index];
|
||||
const float2 best = find_best_fit_for_island(island, scan_line, occupancy, scale, margin);
|
||||
const float2 best = find_best_fit_for_island(
|
||||
island, scan_line, occupancy, scale, margin, target_aspect_y);
|
||||
|
||||
if (best.x <= -1.0f) {
|
||||
/* Unable to find a fit on this scan_line. */
|
||||
|
@ -535,7 +580,8 @@ static void pack_island_xatlas(const Span<UVAABBIsland *> island_indices,
|
|||
* choice is 2. */
|
||||
scan_line += 2;
|
||||
}
|
||||
if (scan_line < occupancy.bitmap_radix) {
|
||||
if (scan_line <
|
||||
occupancy.bitmap_radix * sqrtf(std::min(target_aspect_y, 1.0f / target_aspect_y))) {
|
||||
continue; /* Try again on next scan_line. */
|
||||
}
|
||||
|
||||
|
@ -545,22 +591,22 @@ static void pack_island_xatlas(const Span<UVAABBIsland *> island_indices,
|
|||
|
||||
/* Redraw already placed islands. (Greedy.) */
|
||||
for (int j = 0; j < i; j++) {
|
||||
BoxPack *box = box_array + j;
|
||||
occupancy.trace_island(
|
||||
islands[island_indices[j]->index], scale, margin, float2(box->x, box->y), true);
|
||||
PackIsland *other = islands[island_indices[j]->index];
|
||||
float2 where = (other->pre_translate + other->pivot_) * scale;
|
||||
occupancy.trace_island(islands[island_indices[j]->index], scale, margin, where, true);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Place island. */
|
||||
BoxPack *box = box_array + i;
|
||||
box->x = best.x;
|
||||
box->y = best.y;
|
||||
max_u = std::max(box->x + BLI_rctf_size_x(&island->bounds_rect) * scale + 2 * margin, max_u);
|
||||
max_v = std::max(box->y + BLI_rctf_size_y(&island->bounds_rect) * scale + 2 * margin, max_v);
|
||||
occupancy.trace_island(island, scale, margin, float2(box->x, box->y), true);
|
||||
island->angle = 0.0f;
|
||||
island->pre_translate = best / scale - island->pivot_;
|
||||
occupancy.trace_island(island, scale, margin, best, true);
|
||||
i++; /* Next island. */
|
||||
|
||||
max_u = std::max(best.x + island->half_diagonal_.x * scale + margin, max_u);
|
||||
max_v = std::max(best.y + island->half_diagonal_.y * scale + margin, max_v);
|
||||
|
||||
if (i < 128 || (i & 31) == 16) {
|
||||
scan_line = 0; /* Restart completely. */
|
||||
}
|
||||
|
@ -627,13 +673,14 @@ static void update_hole_rotate(float2 &hole,
|
|||
* Tracking the "Hole" has a slight performance cost, while improving packing efficiency.
|
||||
*/
|
||||
static void pack_islands_alpaca_rotate(const Span<UVAABBIsland *> islands,
|
||||
const float target_aspect_y,
|
||||
float *r_max_u,
|
||||
float *r_max_v)
|
||||
{
|
||||
/* Exclude an initial AABB near the origin. */
|
||||
float next_u1 = *r_max_u;
|
||||
float next_v1 = *r_max_v;
|
||||
bool zigzag = next_u1 < next_v1; /* Horizontal or Vertical strip? */
|
||||
bool zigzag = next_u1 / target_aspect_y < next_v1; /* Horizontal or Vertical strip? */
|
||||
|
||||
/* Track an AABB "hole" which may be filled at any time. */
|
||||
float2 hole(0.0f);
|
||||
|
@ -654,9 +701,13 @@ static void pack_islands_alpaca_rotate(const Span<UVAABBIsland *> islands,
|
|||
island->uv_placement.y = hole[1];
|
||||
if (hole_rotate == (min_dsm == island->uv_diagonal.x)) {
|
||||
island->angle = DEG2RADF(90.0f);
|
||||
island->uv_placement.x += max_dsm * 0.5f;
|
||||
island->uv_placement.y += min_dsm * 0.5f;
|
||||
}
|
||||
else {
|
||||
island->angle = 0.0f;
|
||||
island->uv_placement.x += min_dsm * 0.5f;
|
||||
island->uv_placement.y += max_dsm * 0.5f;
|
||||
}
|
||||
|
||||
/* Update space left in the hole. */
|
||||
|
@ -685,20 +736,24 @@ static void pack_islands_alpaca_rotate(const Span<UVAABBIsland *> islands,
|
|||
if (restart) {
|
||||
update_hole_rotate(hole, hole_diagonal, hole_rotate, u0, v0, next_u1, next_v1);
|
||||
/* We're at the end of a strip. Restart from U axis or V axis. */
|
||||
zigzag = next_u1 < next_v1;
|
||||
zigzag = next_u1 / target_aspect_y < next_v1;
|
||||
u0 = zigzag ? next_u1 : 0.0f;
|
||||
v0 = zigzag ? 0.0f : next_v1;
|
||||
}
|
||||
|
||||
/* Place the island. */
|
||||
island->uv_placement.x = u0;
|
||||
island->uv_placement.y = v0;
|
||||
if (zigzag == (min_dsm == island->uv_diagonal.x)) {
|
||||
island->angle = DEG2RADF(90.0f);
|
||||
island->uv_placement.x += max_dsm * 0.5f;
|
||||
island->uv_placement.y += min_dsm * 0.5f;
|
||||
}
|
||||
else {
|
||||
island->angle = 0.0f;
|
||||
island->uv_placement.x += min_dsm * 0.5f;
|
||||
island->uv_placement.y += max_dsm * 0.5f;
|
||||
}
|
||||
island->uv_placement.x = u0;
|
||||
island->uv_placement.y = v0;
|
||||
|
||||
/* Move according to the "Alpaca rules", with rotation. */
|
||||
if (zigzag) {
|
||||
|
@ -720,8 +775,15 @@ static void pack_islands_alpaca_rotate(const Span<UVAABBIsland *> islands,
|
|||
*r_max_v = next_v1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pack islands using a mix of other strategies.
|
||||
* \param islands: The islands to be packed. Will be modified with results.
|
||||
* \param scale: Scale islands by `scale` before packing.
|
||||
* \param margin: Add `margin` units around islands before packing.
|
||||
* \param params: Additional parameters. Scale and margin information is ignored.
|
||||
* \return Size of square covering the resulting packed UVs. The maximum `u` or `v` co-ordinate.
|
||||
*/
|
||||
static float pack_islands_scale_margin(const Span<PackIsland *> islands,
|
||||
BoxPack *box_array,
|
||||
const float scale,
|
||||
const float margin,
|
||||
const UVPackIsland_Params ¶ms)
|
||||
|
@ -751,8 +813,8 @@ static float pack_islands_scale_margin(const Span<PackIsland *> islands,
|
|||
PackIsland *pack_island = islands[i];
|
||||
UVAABBIsland *aabb = new UVAABBIsland();
|
||||
aabb->index = i;
|
||||
aabb->uv_diagonal.x = BLI_rctf_size_x(&pack_island->bounds_rect) * scale + 2 * margin;
|
||||
aabb->uv_diagonal.y = BLI_rctf_size_y(&pack_island->bounds_rect) * scale + 2 * margin;
|
||||
aabb->uv_diagonal.x = pack_island->half_diagonal_.x * 2 * scale + 2 * margin;
|
||||
aabb->uv_diagonal.y = pack_island->half_diagonal_.y * 2 * scale + 2 * margin;
|
||||
aabbs[i] = aabb;
|
||||
if (pack_island->aspect_y != 1.0f) {
|
||||
contains_non_square_islands = true;
|
||||
|
@ -802,15 +864,6 @@ static float pack_islands_scale_margin(const Span<PackIsland *> islands,
|
|||
}
|
||||
const int64_t max_box_pack = std::min(alpaca_cutoff, islands.size());
|
||||
|
||||
/* Prepare for box_pack_2d. */
|
||||
for (const int64_t i : islands.index_range()) {
|
||||
UVAABBIsland *aabb = aabbs[i];
|
||||
BoxPack *box = &box_array[i];
|
||||
box->index = int(aabb->index);
|
||||
box->w = aabb->uv_diagonal.x;
|
||||
box->h = aabb->uv_diagonal.y;
|
||||
}
|
||||
|
||||
/* Call box_pack_2d (slow for large N.) */
|
||||
float max_u = 0.0f;
|
||||
float max_v = 0.0f;
|
||||
|
@ -819,36 +872,44 @@ static float pack_islands_scale_margin(const Span<PackIsland *> islands,
|
|||
case ED_UVPACK_SHAPE_CONCAVE:
|
||||
pack_island_xatlas(aabbs.as_span().take_front(max_box_pack),
|
||||
islands,
|
||||
box_array,
|
||||
scale,
|
||||
margin,
|
||||
params.target_aspect_y,
|
||||
&max_u,
|
||||
&max_v);
|
||||
break;
|
||||
default:
|
||||
BLI_box_pack_2d(box_array, int(max_box_pack), false, &max_u, &max_v);
|
||||
pack_island_box_pack_2d(aabbs.as_span().take_front(max_box_pack),
|
||||
islands,
|
||||
scale,
|
||||
margin,
|
||||
params.target_aspect_y,
|
||||
&max_u,
|
||||
&max_v);
|
||||
break;
|
||||
}
|
||||
|
||||
/* At this stage, `max_u` and `max_v` contain the box_pack UVs. */
|
||||
/* At this stage, `max_u` and `max_v` contain the box_pack/xatlas UVs. */
|
||||
|
||||
/* Call Alpaca. */
|
||||
if (params.rotate && !contains_non_square_islands) {
|
||||
pack_islands_alpaca_rotate(aabbs.as_mutable_span().drop_front(max_box_pack), &max_u, &max_v);
|
||||
pack_islands_alpaca_rotate(
|
||||
aabbs.as_mutable_span().drop_front(max_box_pack), params.target_aspect_y, &max_u, &max_v);
|
||||
}
|
||||
else {
|
||||
pack_islands_alpaca_turbo(aabbs.as_mutable_span().drop_front(max_box_pack), &max_u, &max_v);
|
||||
pack_islands_alpaca_turbo(
|
||||
aabbs.as_mutable_span().drop_front(max_box_pack), params.target_aspect_y, &max_u, &max_v);
|
||||
}
|
||||
|
||||
/* Write back Alpaca UVs. */
|
||||
for (int64_t i = max_box_pack; i < aabbs.size(); i++) {
|
||||
UVAABBIsland *aabb = aabbs[i];
|
||||
BoxPack *box = &box_array[i];
|
||||
box->x = aabb->uv_placement.x;
|
||||
box->y = aabb->uv_placement.y;
|
||||
|
||||
PackIsland *pack_island = islands[aabb->index];
|
||||
pack_island->angle = aabb->angle;
|
||||
float matrix_inverse[2][2];
|
||||
pack_island->build_inverse_transformation(scale, pack_island->angle, matrix_inverse);
|
||||
mul_v2_m2v2(pack_island->pre_translate, matrix_inverse, aabb->uv_placement);
|
||||
pack_island->pre_translate -= pack_island->pivot_;
|
||||
}
|
||||
|
||||
/* Memory management. */
|
||||
|
@ -858,11 +919,13 @@ static float pack_islands_scale_margin(const Span<PackIsland *> islands,
|
|||
delete aabb;
|
||||
}
|
||||
|
||||
return std::max(max_u, max_v);
|
||||
return std::max(max_u / params.target_aspect_y, max_v);
|
||||
}
|
||||
|
||||
/** Find the optimal scale to pack islands into the unit square.
|
||||
* returns largest scale that will pack `islands` into the unit square.
|
||||
*/
|
||||
static float pack_islands_margin_fraction(const Span<PackIsland *> &island_vector,
|
||||
BoxPack *box_array,
|
||||
const float margin_fraction,
|
||||
const UVPackIsland_Params ¶ms)
|
||||
{
|
||||
|
@ -932,7 +995,7 @@ static float pack_islands_margin_fraction(const Span<PackIsland *> &island_vecto
|
|||
/* Evaluate our `f`. */
|
||||
scale_last = scale;
|
||||
const float max_uv = pack_islands_scale_margin(
|
||||
island_vector, box_array, scale_last, margin_fraction, params);
|
||||
island_vector, scale_last, margin_fraction, params);
|
||||
const float value = sqrtf(max_uv) - 1.0f;
|
||||
|
||||
if (value <= 0.0f) {
|
||||
|
@ -952,25 +1015,15 @@ static float pack_islands_margin_fraction(const Span<PackIsland *> &island_vecto
|
|||
|
||||
const bool flush = true;
|
||||
if (flush) {
|
||||
/* Write back best pack as a side-effect. First get best pack. */
|
||||
/* Write back best pack as a side-effect. */
|
||||
if (scale_last != scale_low) {
|
||||
scale_last = scale_low;
|
||||
const float max_uv = pack_islands_scale_margin(
|
||||
island_vector, box_array, scale_last, margin_fraction, params);
|
||||
island_vector, scale_last, margin_fraction, params);
|
||||
BLI_assert(max_uv == value_low);
|
||||
UNUSED_VARS(max_uv);
|
||||
/* TODO (?): `if (max_uv < 1.0f) { scale_last /= max_uv; }` */
|
||||
}
|
||||
|
||||
/* Then expand PackIslands by the correct amount. */
|
||||
for (const int64_t index : island_vector.index_range()) {
|
||||
BoxPack *box = &box_array[index];
|
||||
box->x /= scale_last;
|
||||
box->y /= scale_last;
|
||||
PackIsland *island = island_vector[index];
|
||||
BLI_rctf_pad(
|
||||
&island->bounds_rect, margin_fraction / scale_last, margin_fraction / scale_last);
|
||||
}
|
||||
}
|
||||
return scale_last;
|
||||
}
|
||||
|
@ -983,8 +1036,8 @@ static float calc_margin_from_aabb_length_sum(const Span<PackIsland *> &island_v
|
|||
* `aabb_length_sum` (was "`area`") to multiply the margin by the length (was "area"). */
|
||||
double aabb_length_sum = 0.0f;
|
||||
for (PackIsland *island : island_vector) {
|
||||
float w = BLI_rctf_size_x(&island->bounds_rect);
|
||||
float h = BLI_rctf_size_y(&island->bounds_rect);
|
||||
float w = island->half_diagonal_.x * 2.0f;
|
||||
float h = island->half_diagonal_.y * 2.0f;
|
||||
aabb_length_sum += sqrtf(w * h);
|
||||
}
|
||||
return params.margin * aabb_length_sum * 0.1f;
|
||||
|
@ -993,31 +1046,26 @@ static float calc_margin_from_aabb_length_sum(const Span<PackIsland *> &island_v
|
|||
/* -------------------------------------------------------------------- */
|
||||
/** \name Implement `pack_islands`
|
||||
*
|
||||
* Smooth differences between old API and new API by converting between storage representations.
|
||||
* \{ */
|
||||
|
||||
static BoxPack *pack_islands_box_array(const Span<PackIsland *> &islands,
|
||||
const UVPackIsland_Params ¶ms,
|
||||
float r_scale[2])
|
||||
void pack_islands(const Span<PackIsland *> &islands,
|
||||
const UVPackIsland_Params ¶ms,
|
||||
float r_scale[2])
|
||||
{
|
||||
BoxPack *box_array = static_cast<BoxPack *>(
|
||||
MEM_mallocN(sizeof(*box_array) * islands.size(), __func__));
|
||||
|
||||
if (params.margin == 0.0f) {
|
||||
/* Special case for zero margin. Margin_method is ignored as all formulas give same result. */
|
||||
const float max_uv = pack_islands_scale_margin(islands, box_array, 1.0f, 0.0f, params);
|
||||
const float max_uv = pack_islands_scale_margin(islands, 1.0f, 0.0f, params);
|
||||
r_scale[0] = 1.0f / max_uv;
|
||||
r_scale[1] = r_scale[0];
|
||||
return box_array;
|
||||
return;
|
||||
}
|
||||
|
||||
if (params.margin_method == ED_UVPACK_MARGIN_FRACTION) {
|
||||
/* Uses a line search on scale. ~10x slower than other method. */
|
||||
const float scale = pack_islands_margin_fraction(islands, box_array, params.margin, params);
|
||||
const float scale = pack_islands_margin_fraction(islands, params.margin, params);
|
||||
r_scale[0] = scale;
|
||||
r_scale[1] = scale;
|
||||
/* pack_islands_margin_fraction will pad PackIslands, return early. */
|
||||
return box_array;
|
||||
return;
|
||||
}
|
||||
|
||||
float margin = params.margin;
|
||||
|
@ -1034,40 +1082,30 @@ static BoxPack *pack_islands_box_array(const Span<PackIsland *> &islands,
|
|||
BLI_assert_unreachable();
|
||||
}
|
||||
|
||||
const float max_uv = pack_islands_scale_margin(islands, box_array, 1.0f, margin, params);
|
||||
const float max_uv = pack_islands_scale_margin(islands, 1.0f, margin, params);
|
||||
r_scale[0] = 1.0f / max_uv;
|
||||
r_scale[1] = r_scale[0];
|
||||
|
||||
for (int index = 0; index < islands.size(); index++) {
|
||||
PackIsland *island = islands[index];
|
||||
BLI_rctf_pad(&island->bounds_rect, margin, margin);
|
||||
}
|
||||
return box_array;
|
||||
}
|
||||
|
||||
void pack_islands(const Span<PackIsland *> &islands,
|
||||
const UVPackIsland_Params ¶ms,
|
||||
float r_scale[2])
|
||||
{
|
||||
BoxPack *box_array = pack_islands_box_array(islands, params, r_scale);
|
||||
|
||||
for (int64_t i : islands.index_range()) {
|
||||
BoxPack *box = box_array + i;
|
||||
PackIsland *island = islands[box->index];
|
||||
if (island->angle) {
|
||||
/* TODO: Apply proper rotation. */
|
||||
island->pre_translate.x = (-box->y / island->aspect_y) - island->bounds_rect.xmax;
|
||||
island->pre_translate.y = (box->x * island->aspect_y) - island->bounds_rect.ymin;
|
||||
}
|
||||
else {
|
||||
island->pre_translate.x = box->x - island->bounds_rect.xmin;
|
||||
island->pre_translate.y = box->y - island->bounds_rect.ymin;
|
||||
}
|
||||
}
|
||||
|
||||
MEM_freeN(box_array);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
void PackIsland::build_transformation(const float scale, const float angle, float (*r_matrix)[2])
|
||||
{
|
||||
const float cos_angle = cosf(angle);
|
||||
const float sin_angle = sinf(angle);
|
||||
r_matrix[0][0] = cos_angle * scale;
|
||||
r_matrix[0][1] = -sin_angle * scale * aspect_y;
|
||||
r_matrix[1][0] = sin_angle * scale / aspect_y;
|
||||
r_matrix[1][1] = cos_angle * scale;
|
||||
}
|
||||
|
||||
void PackIsland::build_inverse_transformation(const float scale,
|
||||
const float angle,
|
||||
float (*r_matrix)[2])
|
||||
{
|
||||
/* TODO: Generate inverse transform directly. */
|
||||
build_transformation(scale, angle, r_matrix);
|
||||
invert_m2_m2(r_matrix, r_matrix);
|
||||
}
|
||||
|
||||
} // namespace blender::geometry
|
||||
|
|
|
@ -444,10 +444,11 @@ static void uv_parametrizer_scale_x(ParamHandle *phandle, const float scale_x)
|
|||
return; /* Identity transform. */
|
||||
}
|
||||
|
||||
/* Scale every chart. */
|
||||
for (int i = 0; i < phandle->ncharts; i++) {
|
||||
PChart *chart = phandle->charts[i];
|
||||
for (PVert *v = chart->verts; v; v = v->nextlink) {
|
||||
v->uv[0] *= scale_x;
|
||||
v->uv[0] *= scale_x; /* Only scale x axis. */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4146,15 +4147,11 @@ void uv_parametrizer_pack(ParamHandle *handle, float margin, bool do_rotate, boo
|
|||
MemArena *arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__);
|
||||
Heap *heap = BLI_heap_new();
|
||||
|
||||
int unpacked = 0;
|
||||
for (int i = 0; i < handle->ncharts; i++) {
|
||||
PChart *chart = handle->charts[i];
|
||||
|
||||
if (ignore_pinned && chart->has_pins) {
|
||||
unpacked++;
|
||||
continue;
|
||||
}
|
||||
(void)unpacked; /* Quiet set-but-unused warning (may be removed). */
|
||||
|
||||
geometry::PackIsland *pack_island = new geometry::PackIsland();
|
||||
pack_island->caller_index = i;
|
||||
|
@ -4182,17 +4179,9 @@ void uv_parametrizer_pack(ParamHandle *handle, float margin, bool do_rotate, boo
|
|||
PChart *chart = handle->charts[pack_island->caller_index];
|
||||
|
||||
float matrix[2][2];
|
||||
float b[2];
|
||||
const float cos_angle = cosf(pack_island->angle);
|
||||
const float sin_angle = sinf(pack_island->angle);
|
||||
matrix[0][0] = cos_angle * scale[0];
|
||||
matrix[0][1] = -sin_angle * scale[0] * pack_island->aspect_y;
|
||||
matrix[1][0] = sin_angle * scale[1] / pack_island->aspect_y;
|
||||
matrix[1][1] = cos_angle * scale[1];
|
||||
b[0] = pack_island->pre_translate.x;
|
||||
b[1] = pack_island->pre_translate.y;
|
||||
pack_island->build_transformation(scale[0], pack_island->angle, matrix);
|
||||
for (PVert *v = chart->verts; v; v = v->nextlink) {
|
||||
blender::geometry::mul_v2_m2_add_v2v2(v->uv, matrix, v->uv, b);
|
||||
blender::geometry::mul_v2_m2_add_v2v2(v->uv, matrix, v->uv, pack_island->pre_translate);
|
||||
}
|
||||
|
||||
pack_island_vector[i] = nullptr;
|
||||
|
|
|
@ -378,7 +378,7 @@ void GPU_framebuffer_config_array(GPUFrameBuffer *gpu_fb,
|
|||
|
||||
void GPU_framebuffer_default_size(GPUFrameBuffer *gpu_fb, int width, int height)
|
||||
{
|
||||
unwrap(gpu_fb)->size_set(width, height);
|
||||
unwrap(gpu_fb)->default_size_set(width, height);
|
||||
}
|
||||
|
||||
/* ---------- Viewport & Scissor Region ----------- */
|
||||
|
|
|
@ -137,6 +137,7 @@ class FrameBuffer {
|
|||
void *userData);
|
||||
uint get_bits_per_pixel();
|
||||
|
||||
/* Sets the size after creation. */
|
||||
inline void size_set(int width, int height)
|
||||
{
|
||||
width_ = width;
|
||||
|
@ -144,6 +145,15 @@ class FrameBuffer {
|
|||
dirty_state_ = true;
|
||||
}
|
||||
|
||||
/* Sets the size for frame-buffer with no attachments. */
|
||||
inline void default_size_set(int width, int height)
|
||||
{
|
||||
width_ = width;
|
||||
height_ = height;
|
||||
dirty_attachments_ = true;
|
||||
dirty_state_ = true;
|
||||
}
|
||||
|
||||
inline void viewport_set(const int viewport[4])
|
||||
{
|
||||
if (!equals_v4v4_int(viewport_, viewport)) {
|
||||
|
|
|
@ -164,30 +164,30 @@ void BKE_material_defaults_free_gpu()
|
|||
/** \name Stubs of BKE_customdata.h
|
||||
* \{ */
|
||||
|
||||
int CustomData_get_offset(const struct CustomData * /*data*/, int /*type*/)
|
||||
int CustomData_get_offset(const struct CustomData * /*data*/, eCustomDataType /*type*/)
|
||||
{
|
||||
BLI_assert_unreachable();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int CustomData_get_named_layer_index(const struct CustomData * /*data*/,
|
||||
int /*type*/,
|
||||
eCustomDataType /*type*/,
|
||||
const char * /*name*/)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int CustomData_get_active_layer_index(const struct CustomData * /*data*/, int /*type*/)
|
||||
int CustomData_get_active_layer_index(const struct CustomData * /*data*/, eCustomDataType /*type*/)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int CustomData_get_render_layer_index(const struct CustomData * /*data*/, int /*type*/)
|
||||
int CustomData_get_render_layer_index(const struct CustomData * /*data*/, eCustomDataType /*type*/)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool CustomData_has_layer(const struct CustomData * /*data*/, int /*type*/)
|
||||
bool CustomData_has_layer(const struct CustomData * /*data*/, eCustomDataType /*type*/)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -1041,14 +1041,17 @@ void gpu::MTLTexture::update_sub(
|
|||
if (texture_.storageMode == MTLStorageModeManaged) {
|
||||
[blit_encoder synchronizeResource:texture_];
|
||||
}
|
||||
[blit_encoder optimizeContentsForGPUAccess:texture_];
|
||||
}
|
||||
else {
|
||||
/* Textures which use MTLStorageModeManaged need to have updated contents
|
||||
* synced back to CPU to avoid an automatic flush overwriting contents. */
|
||||
blit_encoder = ctx->main_command_buffer.ensure_begin_blit_encoder();
|
||||
if (texture_.storageMode == MTLStorageModeManaged) {
|
||||
blit_encoder = ctx->main_command_buffer.ensure_begin_blit_encoder();
|
||||
|
||||
[blit_encoder synchronizeResource:texture_];
|
||||
}
|
||||
[blit_encoder optimizeContentsForGPUAccess:texture_];
|
||||
}
|
||||
|
||||
/* Decrement texture reference counts. This ensures temporary texture views are released. */
|
||||
|
@ -1110,6 +1113,7 @@ void MTLTexture::update_sub(int offset[3],
|
|||
if (texture_.storageMode == MTLStorageModeManaged) {
|
||||
[blit_encoder synchronizeResource:texture_];
|
||||
}
|
||||
[blit_encoder optimizeContentsForGPUAccess:texture_];
|
||||
}
|
||||
else {
|
||||
BLI_assert(false);
|
||||
|
@ -1230,6 +1234,7 @@ void gpu::MTLTexture::copy_to(Texture *dst)
|
|||
BLI_assert(mt_dst->d_ == d_);
|
||||
[blit_encoder copyFromTexture:this->get_metal_handle_base()
|
||||
toTexture:mt_dst->get_metal_handle_base()];
|
||||
[blit_encoder optimizeContentsForGPUAccess:mt_dst->get_metal_handle_base()];
|
||||
} break;
|
||||
default: {
|
||||
int slice = 0;
|
||||
|
|
|
@ -140,11 +140,9 @@ TEST(obj_import_string_utils, parse_float_invalid)
|
|||
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. */
|
||||
/* Out of float range. */
|
||||
EXPECT_STRREF_EQ(" a", parse_float("9.0e500 a", -3.0f, val));
|
||||
EXPECT_EQ(val, std::numeric_limits<float>::infinity());
|
||||
EXPECT_EQ(val, -3.0f);
|
||||
/* 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);
|
||||
|
|
|
@ -2307,7 +2307,7 @@ typedef enum eSnapMode {
|
|||
/* Due to dependency conflicts with Cycles, header cannot directly include `BLI_utildefines.h`. */
|
||||
/* TODO: move this macro to a more general place. */
|
||||
#ifdef ENUM_OPERATORS
|
||||
ENUM_OPERATORS(eSnapMode, SCE_SNAP_MODE_GRID)
|
||||
ENUM_OPERATORS(eSnapMode, SCE_SNAP_MODE_FACE_NEAREST)
|
||||
#endif
|
||||
|
||||
#define SCE_SNAP_MODE_GEOM \
|
||||
|
|
|
@ -3414,7 +3414,8 @@ static void rna_def_tool_settings(BlenderRNA *brna)
|
|||
RNA_def_property_boolean_sdna(prop, NULL, "snap_flag", SCE_SNAP_PROJECT);
|
||||
RNA_def_property_ui_text(prop,
|
||||
"Project Individual Elements",
|
||||
"Project individual elements on the surface of other objects");
|
||||
"Project individual elements on the surface of other objects (Always "
|
||||
"enabled with Face Nearest)");
|
||||
RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); /* header redraw */
|
||||
|
||||
prop = RNA_def_property(srna, "use_snap_backface_culling", PROP_BOOLEAN, PROP_NONE);
|
||||
|
|
|
@ -94,47 +94,9 @@
|
|||
#include "FN_multi_function.hh"
|
||||
|
||||
namespace lf = blender::fn::lazy_function;
|
||||
namespace geo_log = blender::nodes::geo_eval_log;
|
||||
|
||||
using blender::Array;
|
||||
using blender::ColorGeometry4f;
|
||||
using blender::CPPType;
|
||||
using blender::destruct_ptr;
|
||||
using blender::float3;
|
||||
using blender::FunctionRef;
|
||||
using blender::GMutablePointer;
|
||||
using blender::GMutableSpan;
|
||||
using blender::GPointer;
|
||||
using blender::GVArray;
|
||||
using blender::IndexRange;
|
||||
using blender::Map;
|
||||
using blender::MultiValueMap;
|
||||
using blender::MutableSpan;
|
||||
using blender::Set;
|
||||
using blender::Span;
|
||||
using blender::Stack;
|
||||
using blender::StringRef;
|
||||
using blender::StringRefNull;
|
||||
using blender::Vector;
|
||||
using blender::bke::AttributeMetaData;
|
||||
using blender::bke::AttributeValidator;
|
||||
using blender::fn::Field;
|
||||
using blender::fn::FieldOperation;
|
||||
using blender::fn::GField;
|
||||
using blender::fn::ValueOrField;
|
||||
using blender::fn::ValueOrFieldCPPType;
|
||||
using blender::nodes::FieldInferencingInterface;
|
||||
using blender::nodes::GeoNodeExecParams;
|
||||
using blender::nodes::InputSocketFieldType;
|
||||
using blender::nodes::geo_eval_log::GeometryAttributeInfo;
|
||||
using blender::nodes::geo_eval_log::GeometryInfoLog;
|
||||
using blender::nodes::geo_eval_log::GeoModifierLog;
|
||||
using blender::nodes::geo_eval_log::GeoNodeLog;
|
||||
using blender::nodes::geo_eval_log::GeoTreeLog;
|
||||
using blender::nodes::geo_eval_log::NamedAttributeUsage;
|
||||
using blender::nodes::geo_eval_log::NodeWarning;
|
||||
using blender::nodes::geo_eval_log::NodeWarningType;
|
||||
using blender::nodes::geo_eval_log::ValueLog;
|
||||
using blender::threading::EnumerableThreadSpecific;
|
||||
namespace blender {
|
||||
|
||||
static void initData(ModifierData *md)
|
||||
{
|
||||
|
@ -216,22 +178,25 @@ static bool node_needs_own_transform_relation(const bNode &node)
|
|||
|
||||
static void process_nodes_for_depsgraph(const bNodeTree &tree,
|
||||
Set<ID *> &ids,
|
||||
bool &r_needs_own_transform_relation)
|
||||
bool &r_needs_own_transform_relation,
|
||||
Set<const bNodeTree *> &checked_groups)
|
||||
{
|
||||
Set<const bNodeTree *> handled_groups;
|
||||
if (!checked_groups.add(&tree)) {
|
||||
return;
|
||||
}
|
||||
|
||||
LISTBASE_FOREACH (const bNode *, node, &tree.nodes) {
|
||||
tree.ensure_topology_cache();
|
||||
for (const bNode *node : tree.all_nodes()) {
|
||||
add_used_ids_from_sockets(node->inputs, ids);
|
||||
add_used_ids_from_sockets(node->outputs, ids);
|
||||
|
||||
if (ELEM(node->type, NODE_GROUP, NODE_CUSTOM_GROUP)) {
|
||||
const bNodeTree *group = (bNodeTree *)node->id;
|
||||
if (group != nullptr && handled_groups.add(group)) {
|
||||
process_nodes_for_depsgraph(*group, ids, r_needs_own_transform_relation);
|
||||
}
|
||||
}
|
||||
r_needs_own_transform_relation |= node_needs_own_transform_relation(*node);
|
||||
}
|
||||
|
||||
for (const bNode *node : tree.group_nodes()) {
|
||||
if (const bNodeTree *sub_tree = reinterpret_cast<const bNodeTree *>(node->id)) {
|
||||
process_nodes_for_depsgraph(*sub_tree, ids, r_needs_own_transform_relation, checked_groups);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void find_used_ids_from_settings(const NodesModifierSettings &settings, Set<ID *> &ids)
|
||||
|
@ -289,7 +254,9 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte
|
|||
bool needs_own_transform_relation = false;
|
||||
Set<ID *> used_ids;
|
||||
find_used_ids_from_settings(nmd->settings, used_ids);
|
||||
process_nodes_for_depsgraph(*nmd->node_group, used_ids, needs_own_transform_relation);
|
||||
Set<const bNodeTree *> checked_groups;
|
||||
process_nodes_for_depsgraph(
|
||||
*nmd->node_group, used_ids, needs_own_transform_relation, checked_groups);
|
||||
|
||||
if (ctx->object->type == OB_CURVES) {
|
||||
Curves *curves_id = static_cast<Curves *>(ctx->object->data);
|
||||
|
@ -328,19 +295,18 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte
|
|||
}
|
||||
}
|
||||
|
||||
static bool check_tree_for_time_node(const bNodeTree &tree,
|
||||
Set<const bNodeTree *> &r_checked_trees)
|
||||
static bool check_tree_for_time_node(const bNodeTree &tree, Set<const bNodeTree *> &checked_groups)
|
||||
{
|
||||
if (!r_checked_trees.add(&tree)) {
|
||||
if (!checked_groups.add(&tree)) {
|
||||
return false;
|
||||
}
|
||||
LISTBASE_FOREACH (const bNode *, node, &tree.nodes) {
|
||||
if (node->type == GEO_NODE_INPUT_SCENE_TIME) {
|
||||
return true;
|
||||
}
|
||||
if (node->type == NODE_GROUP) {
|
||||
const bNodeTree *sub_tree = reinterpret_cast<const bNodeTree *>(node->id);
|
||||
if (sub_tree && check_tree_for_time_node(*sub_tree, r_checked_trees)) {
|
||||
tree.ensure_topology_cache();
|
||||
if (!tree.nodes_by_type("GeometryNodeInputSceneTime").is_empty()) {
|
||||
return true;
|
||||
}
|
||||
for (const bNode *node : tree.group_nodes()) {
|
||||
if (const bNodeTree *sub_tree = reinterpret_cast<const bNodeTree *>(node->id)) {
|
||||
if (check_tree_for_time_node(*sub_tree, checked_groups)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -355,8 +321,8 @@ static bool dependsOnTime(struct Scene * /*scene*/, ModifierData *md)
|
|||
if (tree == nullptr) {
|
||||
return false;
|
||||
}
|
||||
Set<const bNodeTree *> checked_trees;
|
||||
return check_tree_for_time_node(*tree, checked_trees);
|
||||
Set<const bNodeTree *> checked_groups;
|
||||
return check_tree_for_time_node(*tree, checked_groups);
|
||||
}
|
||||
|
||||
static void foreachIDLink(ModifierData *md, Object *ob, IDWalkFunc walk, void *userData)
|
||||
|
@ -426,15 +392,14 @@ static bool socket_type_has_attribute_toggle(const bNodeSocket &socket)
|
|||
static bool input_has_attribute_toggle(const bNodeTree &node_tree, const int socket_index)
|
||||
{
|
||||
BLI_assert(node_tree.runtime->field_inferencing_interface);
|
||||
const FieldInferencingInterface &field_interface =
|
||||
const nodes::FieldInferencingInterface &field_interface =
|
||||
*node_tree.runtime->field_inferencing_interface;
|
||||
return field_interface.inputs[socket_index] != InputSocketFieldType::None;
|
||||
return field_interface.inputs[socket_index] != nodes::InputSocketFieldType::None;
|
||||
}
|
||||
|
||||
static std::unique_ptr<IDProperty, blender::bke::idprop::IDPropertyDeleter>
|
||||
id_property_create_from_socket(const bNodeSocket &socket)
|
||||
static std::unique_ptr<IDProperty, bke::idprop::IDPropertyDeleter> id_property_create_from_socket(
|
||||
const bNodeSocket &socket)
|
||||
{
|
||||
using namespace blender;
|
||||
switch (socket.type) {
|
||||
case SOCK_FLOAT: {
|
||||
const bNodeSocketValueFloat *value = static_cast<const bNodeSocketValueFloat *>(
|
||||
|
@ -581,34 +546,34 @@ static void init_socket_cpp_value_from_property(const IDProperty &property,
|
|||
else if (property.type == IDP_DOUBLE) {
|
||||
value = float(IDP_Double(&property));
|
||||
}
|
||||
new (r_value) ValueOrField<float>(value);
|
||||
new (r_value) fn::ValueOrField<float>(value);
|
||||
break;
|
||||
}
|
||||
case SOCK_INT: {
|
||||
int value = IDP_Int(&property);
|
||||
new (r_value) ValueOrField<int>(value);
|
||||
new (r_value) fn::ValueOrField<int>(value);
|
||||
break;
|
||||
}
|
||||
case SOCK_VECTOR: {
|
||||
float3 value;
|
||||
copy_v3_v3(value, (const float *)IDP_Array(&property));
|
||||
new (r_value) ValueOrField<float3>(value);
|
||||
new (r_value) fn::ValueOrField<float3>(value);
|
||||
break;
|
||||
}
|
||||
case SOCK_RGBA: {
|
||||
blender::ColorGeometry4f value;
|
||||
ColorGeometry4f value;
|
||||
copy_v4_v4((float *)value, (const float *)IDP_Array(&property));
|
||||
new (r_value) ValueOrField<ColorGeometry4f>(value);
|
||||
new (r_value) fn::ValueOrField<ColorGeometry4f>(value);
|
||||
break;
|
||||
}
|
||||
case SOCK_BOOLEAN: {
|
||||
const bool value = IDP_Bool(&property);
|
||||
new (r_value) ValueOrField<bool>(value);
|
||||
new (r_value) fn::ValueOrField<bool>(value);
|
||||
break;
|
||||
}
|
||||
case SOCK_STRING: {
|
||||
std::string value = IDP_String(&property);
|
||||
new (r_value) ValueOrField<std::string>(std::move(value));
|
||||
new (r_value) fn::ValueOrField<std::string>(std::move(value));
|
||||
break;
|
||||
}
|
||||
case SOCK_OBJECT: {
|
||||
|
@ -648,43 +613,33 @@ static void init_socket_cpp_value_from_property(const IDProperty &property,
|
|||
}
|
||||
}
|
||||
|
||||
void MOD_nodes_update_interface(Object *object, NodesModifierData *nmd)
|
||||
static void update_input_properties_from_node_tree(const bNodeTree &tree,
|
||||
const IDProperty *old_properties,
|
||||
IDProperty &properties)
|
||||
{
|
||||
if (nmd->node_group == nullptr) {
|
||||
if (nmd->settings.properties) {
|
||||
IDP_FreeProperty(nmd->settings.properties);
|
||||
nmd->settings.properties = nullptr;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
IDProperty *old_properties = nmd->settings.properties;
|
||||
{
|
||||
IDPropertyTemplate idprop = {0};
|
||||
nmd->settings.properties = IDP_New(IDP_GROUP, &idprop, "Nodes Modifier Settings");
|
||||
}
|
||||
|
||||
int socket_index;
|
||||
LISTBASE_FOREACH_INDEX (bNodeSocket *, socket, &nmd->node_group->inputs, socket_index) {
|
||||
IDProperty *new_prop = id_property_create_from_socket(*socket).release();
|
||||
tree.ensure_topology_cache();
|
||||
const Span<const bNodeSocket *> tree_inputs = tree.interface_inputs();
|
||||
for (const int i : tree_inputs.index_range()) {
|
||||
const bNodeSocket &socket = *tree_inputs[i];
|
||||
IDProperty *new_prop = id_property_create_from_socket(socket).release();
|
||||
if (new_prop == nullptr) {
|
||||
/* Out of the set of supported input sockets, only
|
||||
* geometry sockets aren't added to the modifier. */
|
||||
BLI_assert(socket->type == SOCK_GEOMETRY);
|
||||
BLI_assert(socket.type == SOCK_GEOMETRY);
|
||||
continue;
|
||||
}
|
||||
|
||||
new_prop->flag |= IDP_FLAG_OVERRIDABLE_LIBRARY;
|
||||
if (socket->description[0] != '\0') {
|
||||
if (socket.description[0] != '\0') {
|
||||
IDPropertyUIData *ui_data = IDP_ui_data_ensure(new_prop);
|
||||
ui_data->description = BLI_strdup(socket->description);
|
||||
ui_data->description = BLI_strdup(socket.description);
|
||||
}
|
||||
IDP_AddToGroup(nmd->settings.properties, new_prop);
|
||||
IDP_AddToGroup(&properties, new_prop);
|
||||
|
||||
if (old_properties != nullptr) {
|
||||
IDProperty *old_prop = IDP_GetPropertyFromGroup(old_properties, socket->identifier);
|
||||
const IDProperty *old_prop = IDP_GetPropertyFromGroup(old_properties, socket.identifier);
|
||||
if (old_prop != nullptr) {
|
||||
if (id_property_type_matches_socket(*socket, *old_prop)) {
|
||||
if (id_property_type_matches_socket(socket, *old_prop)) {
|
||||
/* #IDP_CopyPropertyContent replaces the UI data as well, which we don't (we only
|
||||
* want to replace the values). So release it temporarily and replace it after. */
|
||||
IDPropertyUIData *ui_data = new_prop->ui_data;
|
||||
|
@ -703,20 +658,20 @@ void MOD_nodes_update_interface(Object *object, NodesModifierData *nmd)
|
|||
}
|
||||
}
|
||||
|
||||
if (socket_type_has_attribute_toggle(*socket)) {
|
||||
const std::string use_attribute_id = socket->identifier + use_attribute_suffix;
|
||||
const std::string attribute_name_id = socket->identifier + attribute_name_suffix;
|
||||
if (socket_type_has_attribute_toggle(socket)) {
|
||||
const std::string use_attribute_id = socket.identifier + use_attribute_suffix;
|
||||
const std::string attribute_name_id = socket.identifier + attribute_name_suffix;
|
||||
|
||||
IDPropertyTemplate idprop = {0};
|
||||
IDProperty *use_attribute_prop = IDP_New(IDP_INT, &idprop, use_attribute_id.c_str());
|
||||
IDP_AddToGroup(nmd->settings.properties, use_attribute_prop);
|
||||
IDP_AddToGroup(&properties, use_attribute_prop);
|
||||
|
||||
IDProperty *attribute_prop = IDP_New(IDP_STRING, &idprop, attribute_name_id.c_str());
|
||||
IDP_AddToGroup(nmd->settings.properties, attribute_prop);
|
||||
IDP_AddToGroup(&properties, attribute_prop);
|
||||
|
||||
if (old_properties == nullptr) {
|
||||
if (socket->default_attribute_name && socket->default_attribute_name[0] != '\0') {
|
||||
IDP_AssignString(attribute_prop, socket->default_attribute_name, MAX_NAME);
|
||||
if (socket.default_attribute_name && socket.default_attribute_name[0] != '\0') {
|
||||
IDP_AssignString(attribute_prop, socket.default_attribute_name, MAX_NAME);
|
||||
IDP_Int(use_attribute_prop) = 1;
|
||||
}
|
||||
}
|
||||
|
@ -735,23 +690,31 @@ void MOD_nodes_update_interface(Object *object, NodesModifierData *nmd)
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LISTBASE_FOREACH (bNodeSocket *, socket, &nmd->node_group->outputs) {
|
||||
if (!socket_type_has_attribute_toggle(*socket)) {
|
||||
static void update_output_properties_from_node_tree(const bNodeTree &tree,
|
||||
const IDProperty *old_properties,
|
||||
IDProperty &properties)
|
||||
{
|
||||
tree.ensure_topology_cache();
|
||||
const Span<const bNodeSocket *> tree_outputs = tree.interface_outputs();
|
||||
for (const int i : tree_outputs.index_range()) {
|
||||
const bNodeSocket &socket = *tree_outputs[i];
|
||||
if (!socket_type_has_attribute_toggle(socket)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const std::string idprop_name = socket->identifier + attribute_name_suffix;
|
||||
const std::string idprop_name = socket.identifier + attribute_name_suffix;
|
||||
IDProperty *new_prop = IDP_NewString("", idprop_name.c_str(), MAX_NAME);
|
||||
if (socket->description[0] != '\0') {
|
||||
if (socket.description[0] != '\0') {
|
||||
IDPropertyUIData *ui_data = IDP_ui_data_ensure(new_prop);
|
||||
ui_data->description = BLI_strdup(socket->description);
|
||||
ui_data->description = BLI_strdup(socket.description);
|
||||
}
|
||||
IDP_AddToGroup(nmd->settings.properties, new_prop);
|
||||
IDP_AddToGroup(&properties, new_prop);
|
||||
|
||||
if (old_properties == nullptr) {
|
||||
if (socket->default_attribute_name && socket->default_attribute_name[0] != '\0') {
|
||||
IDP_AssignString(new_prop, socket->default_attribute_name, MAX_NAME);
|
||||
if (socket.default_attribute_name && socket.default_attribute_name[0] != '\0') {
|
||||
IDP_AssignString(new_prop, socket.default_attribute_name, MAX_NAME);
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
@ -769,6 +732,30 @@ void MOD_nodes_update_interface(Object *object, NodesModifierData *nmd)
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
|
||||
void MOD_nodes_update_interface(Object *object, NodesModifierData *nmd)
|
||||
{
|
||||
using namespace blender;
|
||||
if (nmd->node_group == nullptr) {
|
||||
if (nmd->settings.properties) {
|
||||
IDP_FreeProperty(nmd->settings.properties);
|
||||
nmd->settings.properties = nullptr;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
IDProperty *old_properties = nmd->settings.properties;
|
||||
{
|
||||
IDPropertyTemplate idprop = {0};
|
||||
nmd->settings.properties = IDP_New(IDP_GROUP, &idprop, "Nodes Modifier Settings");
|
||||
}
|
||||
IDProperty *new_properties = nmd->settings.properties;
|
||||
|
||||
update_input_properties_from_node_tree(*nmd->node_group, old_properties, *new_properties);
|
||||
update_output_properties_from_node_tree(*nmd->node_group, old_properties, *new_properties);
|
||||
|
||||
if (old_properties != nullptr) {
|
||||
IDP_FreeProperty(old_properties);
|
||||
|
@ -777,38 +764,39 @@ void MOD_nodes_update_interface(Object *object, NodesModifierData *nmd)
|
|||
DEG_id_tag_update(&object->id, ID_RECALC_GEOMETRY);
|
||||
}
|
||||
|
||||
static void initialize_group_input(NodesModifierData &nmd,
|
||||
const bNodeSocket &interface_socket,
|
||||
namespace blender {
|
||||
|
||||
static void initialize_group_input(const bNodeTree &tree,
|
||||
const IDProperty *properties,
|
||||
const int input_index,
|
||||
void *r_value)
|
||||
{
|
||||
const bNodeSocketType &socket_type = *interface_socket.typeinfo;
|
||||
const eNodeSocketDatatype socket_data_type = static_cast<eNodeSocketDatatype>(
|
||||
interface_socket.type);
|
||||
if (nmd.settings.properties == nullptr) {
|
||||
socket_type.get_geometry_nodes_cpp_value(interface_socket, r_value);
|
||||
const bNodeSocket &io_input = *tree.interface_inputs()[input_index];
|
||||
const bNodeSocketType &socket_type = *io_input.typeinfo;
|
||||
const eNodeSocketDatatype socket_data_type = static_cast<eNodeSocketDatatype>(io_input.type);
|
||||
if (properties == nullptr) {
|
||||
socket_type.get_geometry_nodes_cpp_value(io_input, r_value);
|
||||
return;
|
||||
}
|
||||
const IDProperty *property = IDP_GetPropertyFromGroup(nmd.settings.properties,
|
||||
interface_socket.identifier);
|
||||
const IDProperty *property = IDP_GetPropertyFromGroup(properties, io_input.identifier);
|
||||
if (property == nullptr) {
|
||||
socket_type.get_geometry_nodes_cpp_value(interface_socket, r_value);
|
||||
socket_type.get_geometry_nodes_cpp_value(io_input, r_value);
|
||||
return;
|
||||
}
|
||||
if (!id_property_type_matches_socket(interface_socket, *property)) {
|
||||
socket_type.get_geometry_nodes_cpp_value(interface_socket, r_value);
|
||||
if (!id_property_type_matches_socket(io_input, *property)) {
|
||||
socket_type.get_geometry_nodes_cpp_value(io_input, r_value);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!input_has_attribute_toggle(*nmd.node_group, input_index)) {
|
||||
if (!input_has_attribute_toggle(tree, input_index)) {
|
||||
init_socket_cpp_value_from_property(*property, socket_data_type, r_value);
|
||||
return;
|
||||
}
|
||||
|
||||
const IDProperty *property_use_attribute = IDP_GetPropertyFromGroup(
|
||||
nmd.settings.properties, (interface_socket.identifier + use_attribute_suffix).c_str());
|
||||
properties, (io_input.identifier + use_attribute_suffix).c_str());
|
||||
const IDProperty *property_attribute_name = IDP_GetPropertyFromGroup(
|
||||
nmd.settings.properties, (interface_socket.identifier + attribute_name_suffix).c_str());
|
||||
properties, (io_input.identifier + attribute_name_suffix).c_str());
|
||||
if (property_use_attribute == nullptr || property_attribute_name == nullptr) {
|
||||
init_socket_cpp_value_from_property(*property, socket_data_type, r_value);
|
||||
return;
|
||||
|
@ -817,14 +805,13 @@ static void initialize_group_input(NodesModifierData &nmd,
|
|||
const bool use_attribute = IDP_Int(property_use_attribute) != 0;
|
||||
if (use_attribute) {
|
||||
const StringRef attribute_name{IDP_String(property_attribute_name)};
|
||||
if (!blender::bke::allow_procedural_attribute_access(attribute_name)) {
|
||||
if (!bke::allow_procedural_attribute_access(attribute_name)) {
|
||||
init_socket_cpp_value_from_property(*property, socket_data_type, r_value);
|
||||
return;
|
||||
}
|
||||
auto attribute_input = std::make_shared<blender::bke::AttributeFieldInput>(
|
||||
attribute_name, *socket_type.base_cpp_type);
|
||||
GField attribute_field{std::move(attribute_input), 0};
|
||||
const auto *value_or_field_cpp_type = ValueOrFieldCPPType::get_from_self(
|
||||
fn::GField attribute_field = bke::AttributeFieldInput::Create(attribute_name,
|
||||
*socket_type.base_cpp_type);
|
||||
const auto *value_or_field_cpp_type = fn::ValueOrFieldCPPType::get_from_self(
|
||||
*socket_type.geometry_nodes_cpp_type);
|
||||
BLI_assert(value_or_field_cpp_type != nullptr);
|
||||
value_or_field_cpp_type->construct_from_field(r_value, std::move(attribute_field));
|
||||
|
@ -836,16 +823,16 @@ static void initialize_group_input(NodesModifierData &nmd,
|
|||
|
||||
static const lf::FunctionNode *find_viewer_lf_node(const bNode &viewer_bnode)
|
||||
{
|
||||
if (const blender::nodes::GeometryNodesLazyFunctionGraphInfo *lf_graph_info =
|
||||
blender::nodes::ensure_geometry_nodes_lazy_function_graph(viewer_bnode.owner_tree())) {
|
||||
if (const nodes::GeometryNodesLazyFunctionGraphInfo *lf_graph_info =
|
||||
nodes::ensure_geometry_nodes_lazy_function_graph(viewer_bnode.owner_tree())) {
|
||||
return lf_graph_info->mapping.viewer_node_map.lookup_default(&viewer_bnode, nullptr);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
static const lf::FunctionNode *find_group_lf_node(const bNode &group_bnode)
|
||||
{
|
||||
if (const blender::nodes::GeometryNodesLazyFunctionGraphInfo *lf_graph_info =
|
||||
blender::nodes::ensure_geometry_nodes_lazy_function_graph(group_bnode.owner_tree())) {
|
||||
if (const nodes::GeometryNodesLazyFunctionGraphInfo *lf_graph_info =
|
||||
nodes::ensure_geometry_nodes_lazy_function_graph(group_bnode.owner_tree())) {
|
||||
return lf_graph_info->mapping.group_node_map.lookup_default(&group_bnode, nullptr);
|
||||
}
|
||||
return nullptr;
|
||||
|
@ -855,10 +842,10 @@ static void find_side_effect_nodes_for_viewer_path(
|
|||
const ViewerPath &viewer_path,
|
||||
const NodesModifierData &nmd,
|
||||
const ModifierEvalContext &ctx,
|
||||
MultiValueMap<blender::ComputeContextHash, const lf::FunctionNode *> &r_side_effect_nodes)
|
||||
MultiValueMap<ComputeContextHash, const lf::FunctionNode *> &r_side_effect_nodes)
|
||||
{
|
||||
const std::optional<blender::ed::viewer_path::ViewerPathForGeometryNodesViewer> parsed_path =
|
||||
blender::ed::viewer_path::parse_geometry_nodes_viewer(viewer_path);
|
||||
const std::optional<ed::viewer_path::ViewerPathForGeometryNodesViewer> parsed_path =
|
||||
ed::viewer_path::parse_geometry_nodes_viewer(viewer_path);
|
||||
if (!parsed_path.has_value()) {
|
||||
return;
|
||||
}
|
||||
|
@ -869,8 +856,8 @@ static void find_side_effect_nodes_for_viewer_path(
|
|||
return;
|
||||
}
|
||||
|
||||
blender::ComputeContextBuilder compute_context_builder;
|
||||
compute_context_builder.push<blender::bke::ModifierComputeContext>(parsed_path->modifier_name);
|
||||
ComputeContextBuilder compute_context_builder;
|
||||
compute_context_builder.push<bke::ModifierComputeContext>(parsed_path->modifier_name);
|
||||
|
||||
const bNodeTree *group = nmd.node_group;
|
||||
Stack<const bNode *> group_node_stack;
|
||||
|
@ -887,7 +874,7 @@ static void find_side_effect_nodes_for_viewer_path(
|
|||
}
|
||||
group_node_stack.push(found_node);
|
||||
group = reinterpret_cast<bNodeTree *>(found_node->id);
|
||||
compute_context_builder.push<blender::bke::NodeGroupComputeContext>(*found_node);
|
||||
compute_context_builder.push<bke::NodeGroupComputeContext>(*found_node);
|
||||
}
|
||||
|
||||
const bNode *found_viewer_node = group->node_by_id(parsed_path->viewer_node_id);
|
||||
|
@ -917,7 +904,7 @@ static void find_side_effect_nodes_for_viewer_path(
|
|||
static void find_side_effect_nodes(
|
||||
const NodesModifierData &nmd,
|
||||
const ModifierEvalContext &ctx,
|
||||
MultiValueMap<blender::ComputeContextHash, const lf::FunctionNode *> &r_side_effect_nodes)
|
||||
MultiValueMap<ComputeContextHash, const lf::FunctionNode *> &r_side_effect_nodes)
|
||||
{
|
||||
Main *bmain = DEG_get_bmain(ctx.depsgraph);
|
||||
wmWindowManager *wm = (wmWindowManager *)bmain->wm.first;
|
||||
|
@ -945,7 +932,7 @@ static void find_side_effect_nodes(
|
|||
|
||||
static void find_socket_log_contexts(const NodesModifierData &nmd,
|
||||
const ModifierEvalContext &ctx,
|
||||
Set<blender::ComputeContextHash> &r_socket_log_contexts)
|
||||
Set<ComputeContextHash> &r_socket_log_contexts)
|
||||
{
|
||||
Main *bmain = DEG_get_bmain(ctx.depsgraph);
|
||||
wmWindowManager *wm = (wmWindowManager *)bmain->wm.first;
|
||||
|
@ -958,9 +945,9 @@ static void find_socket_log_contexts(const NodesModifierData &nmd,
|
|||
const SpaceLink *sl = static_cast<SpaceLink *>(area->spacedata.first);
|
||||
if (sl->spacetype == SPACE_NODE) {
|
||||
const SpaceNode &snode = *reinterpret_cast<const SpaceNode *>(sl);
|
||||
if (const std::optional<blender::ComputeContextHash> hash = blender::nodes::geo_eval_log::
|
||||
GeoModifierLog::get_compute_context_hash_for_node_editor(snode,
|
||||
nmd.modifier.name)) {
|
||||
if (const std::optional<ComputeContextHash> hash =
|
||||
geo_log::GeoModifierLog::get_compute_context_hash_for_node_editor(
|
||||
snode, nmd.modifier.name)) {
|
||||
r_socket_log_contexts.add(*hash);
|
||||
}
|
||||
}
|
||||
|
@ -971,13 +958,13 @@ static void find_socket_log_contexts(const NodesModifierData &nmd,
|
|||
static void clear_runtime_data(NodesModifierData *nmd)
|
||||
{
|
||||
if (nmd->runtime_eval_log != nullptr) {
|
||||
delete static_cast<GeoModifierLog *>(nmd->runtime_eval_log);
|
||||
delete static_cast<geo_log::GeoModifierLog *>(nmd->runtime_eval_log);
|
||||
nmd->runtime_eval_log = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
struct OutputAttributeInfo {
|
||||
GField field;
|
||||
fn::GField field;
|
||||
StringRefNull name;
|
||||
};
|
||||
|
||||
|
@ -993,7 +980,10 @@ struct OutputAttributeToStore {
|
|||
* can be evaluated together.
|
||||
*/
|
||||
static MultiValueMap<eAttrDomain, OutputAttributeInfo> find_output_attributes_to_store(
|
||||
const NodesModifierData &nmd, const bNode &output_node, Span<GMutablePointer> output_values)
|
||||
const bNodeTree &tree,
|
||||
const IDProperty *properties,
|
||||
const bNode &output_node,
|
||||
Span<GMutablePointer> output_values)
|
||||
{
|
||||
MultiValueMap<eAttrDomain, OutputAttributeInfo> outputs_by_domain;
|
||||
for (const bNodeSocket *socket : output_node.input_sockets().drop_front(1).drop_back(1)) {
|
||||
|
@ -1002,7 +992,7 @@ static MultiValueMap<eAttrDomain, OutputAttributeInfo> find_output_attributes_to
|
|||
}
|
||||
|
||||
const std::string prop_name = socket->identifier + attribute_name_suffix;
|
||||
const IDProperty *prop = IDP_GetPropertyFromGroup(nmd.settings.properties, prop_name.c_str());
|
||||
const IDProperty *prop = IDP_GetPropertyFromGroup(properties, prop_name.c_str());
|
||||
if (prop == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
@ -1010,18 +1000,17 @@ static MultiValueMap<eAttrDomain, OutputAttributeInfo> find_output_attributes_to
|
|||
if (attribute_name.is_empty()) {
|
||||
continue;
|
||||
}
|
||||
if (!blender::bke::allow_procedural_attribute_access(attribute_name)) {
|
||||
if (!bke::allow_procedural_attribute_access(attribute_name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const int index = socket->index();
|
||||
const GPointer value = output_values[index];
|
||||
const auto *value_or_field_type = ValueOrFieldCPPType::get_from_self(*value.type());
|
||||
const auto *value_or_field_type = fn::ValueOrFieldCPPType::get_from_self(*value.type());
|
||||
BLI_assert(value_or_field_type != nullptr);
|
||||
const GField field = value_or_field_type->as_field(value.get());
|
||||
const fn::GField field = value_or_field_type->as_field(value.get());
|
||||
|
||||
const bNodeSocket *interface_socket = (const bNodeSocket *)BLI_findlink(
|
||||
&nmd.node_group->outputs, index);
|
||||
const bNodeSocket *interface_socket = (const bNodeSocket *)BLI_findlink(&tree.outputs, index);
|
||||
const eAttrDomain domain = (eAttrDomain)interface_socket->attribute_domain;
|
||||
OutputAttributeInfo output_info;
|
||||
output_info.field = std::move(field);
|
||||
|
@ -1048,7 +1037,7 @@ static Vector<OutputAttributeToStore> compute_attributes_to_store(
|
|||
continue;
|
||||
}
|
||||
const GeometryComponent &component = *geometry.get_component_for_read(component_type);
|
||||
const blender::bke::AttributeAccessor attributes = *component.attributes();
|
||||
const bke::AttributeAccessor attributes = *component.attributes();
|
||||
for (const auto item : outputs_by_domain.items()) {
|
||||
const eAttrDomain domain = item.key;
|
||||
const Span<OutputAttributeInfo> outputs_info = item.value;
|
||||
|
@ -1056,18 +1045,18 @@ static Vector<OutputAttributeToStore> compute_attributes_to_store(
|
|||
continue;
|
||||
}
|
||||
const int domain_size = attributes.domain_size(domain);
|
||||
blender::bke::GeometryFieldContext field_context{component, domain};
|
||||
blender::fn::FieldEvaluator field_evaluator{field_context, domain_size};
|
||||
bke::GeometryFieldContext field_context{component, domain};
|
||||
fn::FieldEvaluator field_evaluator{field_context, domain_size};
|
||||
for (const OutputAttributeInfo &output_info : outputs_info) {
|
||||
const CPPType &type = output_info.field.cpp_type();
|
||||
const AttributeValidator validator = attributes.lookup_validator(output_info.name);
|
||||
const bke::AttributeValidator validator = attributes.lookup_validator(output_info.name);
|
||||
OutputAttributeToStore store{
|
||||
component_type,
|
||||
domain,
|
||||
output_info.name,
|
||||
GMutableSpan{
|
||||
type, MEM_malloc_arrayN(domain_size, type.size(), __func__), domain_size}};
|
||||
GField field = validator.validate_field_if_necessary(output_info.field);
|
||||
fn::GField field = validator.validate_field_if_necessary(output_info.field);
|
||||
field_evaluator.add_with_destination(std::move(field), store.data);
|
||||
attributes_to_store.append(store);
|
||||
}
|
||||
|
@ -1082,11 +1071,11 @@ static void store_computed_output_attributes(
|
|||
{
|
||||
for (const OutputAttributeToStore &store : attributes_to_store) {
|
||||
GeometryComponent &component = geometry.get_component_for_write(store.component_type);
|
||||
blender::bke::MutableAttributeAccessor attributes = *component.attributes_for_write();
|
||||
bke::MutableAttributeAccessor attributes = *component.attributes_for_write();
|
||||
|
||||
const eCustomDataType data_type = blender::bke::cpp_type_to_custom_data_type(
|
||||
store.data.type());
|
||||
const std::optional<AttributeMetaData> meta_data = attributes.lookup_meta_data(store.name);
|
||||
const eCustomDataType data_type = bke::cpp_type_to_custom_data_type(store.data.type());
|
||||
const std::optional<bke::AttributeMetaData> meta_data = attributes.lookup_meta_data(
|
||||
store.name);
|
||||
|
||||
/* Attempt to remove the attribute if it already exists but the domain and type don't match.
|
||||
* Removing the attribute won't succeed if it is built in and non-removable. */
|
||||
|
@ -1099,12 +1088,12 @@ static void store_computed_output_attributes(
|
|||
* attribute didn't exist before, or if it existed but was removed above. */
|
||||
if (attributes.add(store.name,
|
||||
store.domain,
|
||||
blender::bke::cpp_type_to_custom_data_type(store.data.type()),
|
||||
blender::bke::AttributeInitMoveArray(store.data.data()))) {
|
||||
bke::cpp_type_to_custom_data_type(store.data.type()),
|
||||
bke::AttributeInitMoveArray(store.data.data()))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
blender::bke::GAttributeWriter attribute = attributes.lookup_or_add_for_write(
|
||||
bke::GAttributeWriter attribute = attributes.lookup_or_add_for_write(
|
||||
store.name, store.domain, data_type);
|
||||
if (attribute) {
|
||||
attribute.varray.set_all(store.data.data());
|
||||
|
@ -1118,14 +1107,15 @@ static void store_computed_output_attributes(
|
|||
}
|
||||
|
||||
static void store_output_attributes(GeometrySet &geometry,
|
||||
const NodesModifierData &nmd,
|
||||
const bNodeTree &tree,
|
||||
const IDProperty *properties,
|
||||
const bNode &output_node,
|
||||
Span<GMutablePointer> output_values)
|
||||
{
|
||||
/* All new attribute values have to be computed before the geometry is actually changed. This is
|
||||
* necessary because some fields might depend on attributes that are overwritten. */
|
||||
MultiValueMap<eAttrDomain, OutputAttributeInfo> outputs_by_domain =
|
||||
find_output_attributes_to_store(nmd, output_node, output_values);
|
||||
find_output_attributes_to_store(tree, properties, output_node, output_values);
|
||||
Vector<OutputAttributeToStore> attributes_to_store = compute_attributes_to_store(
|
||||
geometry, outputs_by_domain);
|
||||
store_computed_output_attributes(geometry, attributes_to_store);
|
||||
|
@ -1134,15 +1124,14 @@ static void store_output_attributes(GeometrySet &geometry,
|
|||
/**
|
||||
* Evaluate a node group to compute the output geometry.
|
||||
*/
|
||||
static GeometrySet compute_geometry(
|
||||
const bNodeTree &btree,
|
||||
const blender::nodes::GeometryNodesLazyFunctionGraphInfo &lf_graph_info,
|
||||
const bNode &output_node,
|
||||
GeometrySet input_geometry_set,
|
||||
NodesModifierData *nmd,
|
||||
const ModifierEvalContext *ctx)
|
||||
static GeometrySet compute_geometry(const bNodeTree &btree,
|
||||
const nodes::GeometryNodesLazyFunctionGraphInfo &lf_graph_info,
|
||||
const bNode &output_node,
|
||||
GeometrySet input_geometry_set,
|
||||
NodesModifierData *nmd,
|
||||
const ModifierEvalContext *ctx)
|
||||
{
|
||||
const blender::nodes::GeometryNodeLazyFunctionGraphMapping &mapping = lf_graph_info.mapping;
|
||||
const nodes::GeometryNodeLazyFunctionGraphMapping &mapping = lf_graph_info.mapping;
|
||||
|
||||
Vector<const lf::OutputSocket *> graph_inputs = mapping.group_input_sockets;
|
||||
graph_inputs.extend(mapping.group_output_used_sockets);
|
||||
|
@ -1156,35 +1145,36 @@ static GeometrySet compute_geometry(
|
|||
Array<lf::ValueUsage> param_output_usages(graph_outputs.size(), lf::ValueUsage::Used);
|
||||
Array<bool> param_set_outputs(graph_outputs.size(), false);
|
||||
|
||||
blender::nodes::GeometryNodesLazyFunctionLogger lf_logger(lf_graph_info);
|
||||
blender::nodes::GeometryNodesLazyFunctionSideEffectProvider lf_side_effect_provider;
|
||||
nodes::GeometryNodesLazyFunctionLogger lf_logger(lf_graph_info);
|
||||
nodes::GeometryNodesLazyFunctionSideEffectProvider lf_side_effect_provider;
|
||||
|
||||
lf::GraphExecutor graph_executor{
|
||||
lf_graph_info.graph, graph_inputs, graph_outputs, &lf_logger, &lf_side_effect_provider};
|
||||
|
||||
blender::nodes::GeoNodesModifierData geo_nodes_modifier_data;
|
||||
nodes::GeoNodesModifierData geo_nodes_modifier_data;
|
||||
geo_nodes_modifier_data.depsgraph = ctx->depsgraph;
|
||||
geo_nodes_modifier_data.self_object = ctx->object;
|
||||
auto eval_log = std::make_unique<GeoModifierLog>();
|
||||
auto eval_log = std::make_unique<geo_log::GeoModifierLog>();
|
||||
|
||||
Set<blender::ComputeContextHash> socket_log_contexts;
|
||||
Set<ComputeContextHash> socket_log_contexts;
|
||||
if (logging_enabled(ctx)) {
|
||||
geo_nodes_modifier_data.eval_log = eval_log.get();
|
||||
|
||||
find_socket_log_contexts(*nmd, *ctx, socket_log_contexts);
|
||||
geo_nodes_modifier_data.socket_log_contexts = &socket_log_contexts;
|
||||
}
|
||||
MultiValueMap<blender::ComputeContextHash, const lf::FunctionNode *> r_side_effect_nodes;
|
||||
MultiValueMap<ComputeContextHash, const lf::FunctionNode *> r_side_effect_nodes;
|
||||
find_side_effect_nodes(*nmd, *ctx, r_side_effect_nodes);
|
||||
geo_nodes_modifier_data.side_effect_nodes = &r_side_effect_nodes;
|
||||
blender::nodes::GeoNodesLFUserData user_data;
|
||||
nodes::GeoNodesLFUserData user_data;
|
||||
user_data.modifier_data = &geo_nodes_modifier_data;
|
||||
blender::bke::ModifierComputeContext modifier_compute_context{nullptr, nmd->modifier.name};
|
||||
bke::ModifierComputeContext modifier_compute_context{nullptr, nmd->modifier.name};
|
||||
user_data.compute_context = &modifier_compute_context;
|
||||
|
||||
blender::LinearAllocator<> allocator;
|
||||
LinearAllocator<> allocator;
|
||||
Vector<GMutablePointer> inputs_to_destruct;
|
||||
|
||||
const IDProperty *properties = nmd->settings.properties;
|
||||
int input_index = -1;
|
||||
for (const int i : btree.interface_inputs().index_range()) {
|
||||
input_index++;
|
||||
|
@ -1197,7 +1187,7 @@ static GeometrySet compute_geometry(
|
|||
const CPPType *type = interface_socket.typeinfo->geometry_nodes_cpp_type;
|
||||
BLI_assert(type != nullptr);
|
||||
void *value = allocator.allocate(type->size(), type->alignment());
|
||||
initialize_group_input(*nmd, interface_socket, i, value);
|
||||
initialize_group_input(btree, properties, i, value);
|
||||
param_inputs[input_index] = {type, value};
|
||||
inputs_to_destruct.append({type, value});
|
||||
}
|
||||
|
@ -1208,7 +1198,7 @@ static GeometrySet compute_geometry(
|
|||
param_inputs[input_index] = &output_used_inputs[i];
|
||||
}
|
||||
|
||||
Array<blender::bke::AnonymousAttributeSet> attributes_to_propagate(
|
||||
Array<bke::AnonymousAttributeSet> attributes_to_propagate(
|
||||
mapping.attribute_set_by_geometry_output.size());
|
||||
for (const int i : attributes_to_propagate.index_range()) {
|
||||
input_index++;
|
||||
|
@ -1239,7 +1229,7 @@ static GeometrySet compute_geometry(
|
|||
}
|
||||
|
||||
GeometrySet output_geometry_set = std::move(*static_cast<GeometrySet *>(param_outputs[0].get()));
|
||||
store_output_attributes(output_geometry_set, *nmd, output_node, param_outputs);
|
||||
store_output_attributes(output_geometry_set, btree, properties, output_node, param_outputs);
|
||||
|
||||
for (GMutablePointer &ptr : param_outputs) {
|
||||
ptr.destruct();
|
||||
|
@ -1248,7 +1238,7 @@ static GeometrySet compute_geometry(
|
|||
if (logging_enabled(ctx)) {
|
||||
NodesModifierData *nmd_orig = reinterpret_cast<NodesModifierData *>(
|
||||
BKE_modifier_get_original(ctx->object, &nmd->modifier));
|
||||
delete static_cast<GeoModifierLog *>(nmd_orig->runtime_eval_log);
|
||||
delete static_cast<geo_log::GeoModifierLog *>(nmd_orig->runtime_eval_log);
|
||||
nmd_orig->runtime_eval_log = eval_log.release();
|
||||
}
|
||||
|
||||
|
@ -1302,6 +1292,7 @@ static void modifyGeometry(ModifierData *md,
|
|||
const ModifierEvalContext *ctx,
|
||||
GeometrySet &geometry_set)
|
||||
{
|
||||
using namespace blender;
|
||||
NodesModifierData *nmd = reinterpret_cast<NodesModifierData *>(md);
|
||||
if (nmd->node_group == nullptr) {
|
||||
return;
|
||||
|
@ -1332,8 +1323,8 @@ static void modifyGeometry(ModifierData *md,
|
|||
return;
|
||||
}
|
||||
|
||||
const blender::nodes::GeometryNodesLazyFunctionGraphInfo *lf_graph_info =
|
||||
blender::nodes::ensure_geometry_nodes_lazy_function_graph(tree);
|
||||
const nodes::GeometryNodesLazyFunctionGraphInfo *lf_graph_info =
|
||||
nodes::ensure_geometry_nodes_lazy_function_graph(tree);
|
||||
if (lf_graph_info == nullptr) {
|
||||
BKE_modifier_set_error(ctx->object, md, "Cannot evaluate node group");
|
||||
geometry_set.clear();
|
||||
|
@ -1423,13 +1414,13 @@ static NodesModifierData *get_modifier_data(Main &bmain,
|
|||
return reinterpret_cast<NodesModifierData *>(md);
|
||||
}
|
||||
|
||||
static GeoTreeLog *get_root_tree_log(const NodesModifierData &nmd)
|
||||
static geo_log::GeoTreeLog *get_root_tree_log(const NodesModifierData &nmd)
|
||||
{
|
||||
if (nmd.runtime_eval_log == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
GeoModifierLog &modifier_log = *static_cast<GeoModifierLog *>(nmd.runtime_eval_log);
|
||||
blender::bke::ModifierComputeContext compute_context{nullptr, nmd.modifier.name};
|
||||
auto &modifier_log = *static_cast<geo_log::GeoModifierLog *>(nmd.runtime_eval_log);
|
||||
bke::ModifierComputeContext compute_context{nullptr, nmd.modifier.name};
|
||||
return &modifier_log.get_tree_log(compute_context.hash());
|
||||
}
|
||||
|
||||
|
@ -1444,7 +1435,7 @@ static void attribute_search_update_fn(
|
|||
if (nmd->node_group == nullptr) {
|
||||
return;
|
||||
}
|
||||
GeoTreeLog *tree_log = get_root_tree_log(*nmd);
|
||||
geo_log::GeoTreeLog *tree_log = get_root_tree_log(*nmd);
|
||||
if (tree_log == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
@ -1471,22 +1462,21 @@ static void attribute_search_update_fn(
|
|||
}
|
||||
}
|
||||
Set<StringRef> names;
|
||||
Vector<const GeometryAttributeInfo *> attributes;
|
||||
Vector<const geo_log::GeometryAttributeInfo *> attributes;
|
||||
for (const bNodeSocket *socket : sockets_to_check) {
|
||||
const ValueLog *value_log = tree_log->find_socket_value_log(*socket);
|
||||
const geo_log::ValueLog *value_log = tree_log->find_socket_value_log(*socket);
|
||||
if (value_log == nullptr) {
|
||||
continue;
|
||||
}
|
||||
if (const GeometryInfoLog *geo_log = dynamic_cast<const GeometryInfoLog *>(value_log)) {
|
||||
for (const GeometryAttributeInfo &attribute : geo_log->attributes) {
|
||||
if (const auto *geo_log = dynamic_cast<const geo_log::GeometryInfoLog *>(value_log)) {
|
||||
for (const geo_log::GeometryAttributeInfo &attribute : geo_log->attributes) {
|
||||
if (names.add(attribute.name)) {
|
||||
attributes.append(&attribute);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
blender::ui::attribute_search_add_items(
|
||||
str, data.is_output, attributes.as_span(), items, is_first);
|
||||
ui::attribute_search_add_items(str, data.is_output, attributes.as_span(), items, is_first);
|
||||
}
|
||||
|
||||
static void attribute_search_exec_fn(bContext *C, void *data_v, void *item_v)
|
||||
|
@ -1495,7 +1485,7 @@ static void attribute_search_exec_fn(bContext *C, void *data_v, void *item_v)
|
|||
return;
|
||||
}
|
||||
AttributeSearchData &data = *static_cast<AttributeSearchData *>(data_v);
|
||||
const GeometryAttributeInfo &item = *static_cast<const GeometryAttributeInfo *>(item_v);
|
||||
const auto &item = *static_cast<const geo_log::GeometryAttributeInfo *>(item_v);
|
||||
const NodesModifierData *nmd = get_modifier_data(*CTX_data_main(C), *CTX_wm_manager(C), data);
|
||||
if (nmd == nullptr) {
|
||||
return;
|
||||
|
@ -1566,7 +1556,7 @@ static void add_attribute_search_button(const bContext &C,
|
|||
|
||||
char *attribute_name = RNA_string_get_alloc(
|
||||
md_ptr, rna_path_attribute_name.c_str(), nullptr, 0, nullptr);
|
||||
const bool access_allowed = blender::bke::allow_procedural_attribute_access(attribute_name);
|
||||
const bool access_allowed = bke::allow_procedural_attribute_access(attribute_name);
|
||||
MEM_freeN(attribute_name);
|
||||
if (!access_allowed) {
|
||||
UI_but_flag_enable(but, UI_BUT_REDALERT);
|
||||
|
@ -1756,11 +1746,11 @@ static void panel_draw(const bContext *C, Panel *panel)
|
|||
}
|
||||
|
||||
/* Draw node warnings. */
|
||||
GeoTreeLog *tree_log = get_root_tree_log(*nmd);
|
||||
geo_log::GeoTreeLog *tree_log = get_root_tree_log(*nmd);
|
||||
if (tree_log != nullptr) {
|
||||
tree_log->ensure_node_warnings();
|
||||
for (const NodeWarning &warning : tree_log->all_warnings) {
|
||||
if (warning.type != NodeWarningType::Info) {
|
||||
for (const geo_log::NodeWarning &warning : tree_log->all_warnings) {
|
||||
if (warning.type != geo_log::NodeWarningType::Info) {
|
||||
uiItemL(layout, warning.message.c_str(), ICON_ERROR);
|
||||
}
|
||||
}
|
||||
|
@ -1800,13 +1790,13 @@ static void internal_dependencies_panel_draw(const bContext * /*C*/, Panel *pane
|
|||
PointerRNA *ptr = modifier_panel_get_property_pointers(panel, nullptr);
|
||||
NodesModifierData *nmd = static_cast<NodesModifierData *>(ptr->data);
|
||||
|
||||
GeoTreeLog *tree_log = get_root_tree_log(*nmd);
|
||||
geo_log::GeoTreeLog *tree_log = get_root_tree_log(*nmd);
|
||||
if (tree_log == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
tree_log->ensure_used_named_attributes();
|
||||
const Map<StringRefNull, NamedAttributeUsage> &usage_by_attribute =
|
||||
const Map<StringRefNull, geo_log::NamedAttributeUsage> &usage_by_attribute =
|
||||
tree_log->used_named_attributes;
|
||||
|
||||
if (usage_by_attribute.is_empty()) {
|
||||
|
@ -1816,7 +1806,7 @@ static void internal_dependencies_panel_draw(const bContext * /*C*/, Panel *pane
|
|||
|
||||
struct NameWithUsage {
|
||||
StringRefNull name;
|
||||
NamedAttributeUsage usage;
|
||||
geo_log::NamedAttributeUsage usage;
|
||||
};
|
||||
|
||||
Vector<NameWithUsage> sorted_used_attribute;
|
||||
|
@ -1831,20 +1821,20 @@ static void internal_dependencies_panel_draw(const bContext * /*C*/, Panel *pane
|
|||
|
||||
for (const NameWithUsage &attribute : sorted_used_attribute) {
|
||||
const StringRefNull attribute_name = attribute.name;
|
||||
const NamedAttributeUsage usage = attribute.usage;
|
||||
const geo_log::NamedAttributeUsage usage = attribute.usage;
|
||||
|
||||
/* #uiLayoutRowWithHeading doesn't seem to work in this case. */
|
||||
uiLayout *split = uiLayoutSplit(layout, 0.4f, false);
|
||||
|
||||
std::stringstream ss;
|
||||
Vector<std::string> usages;
|
||||
if ((usage & NamedAttributeUsage::Read) != NamedAttributeUsage::None) {
|
||||
if ((usage & geo_log::NamedAttributeUsage::Read) != geo_log::NamedAttributeUsage::None) {
|
||||
usages.append(TIP_("Read"));
|
||||
}
|
||||
if ((usage & NamedAttributeUsage::Write) != NamedAttributeUsage::None) {
|
||||
if ((usage & geo_log::NamedAttributeUsage::Write) != geo_log::NamedAttributeUsage::None) {
|
||||
usages.append(TIP_("Write"));
|
||||
}
|
||||
if ((usage & NamedAttributeUsage::Remove) != NamedAttributeUsage::None) {
|
||||
if ((usage & geo_log::NamedAttributeUsage::Remove) != geo_log::NamedAttributeUsage::None) {
|
||||
usages.append(TIP_("Remove"));
|
||||
}
|
||||
for (const int i : usages.index_range()) {
|
||||
|
@ -1866,6 +1856,7 @@ static void internal_dependencies_panel_draw(const bContext * /*C*/, Panel *pane
|
|||
|
||||
static void panelRegister(ARegionType *region_type)
|
||||
{
|
||||
using namespace blender;
|
||||
PanelType *panel_type = modifier_panel_register(region_type, eModifierType_Nodes, panel_draw);
|
||||
modifier_subpanel_register(region_type,
|
||||
"output_attributes",
|
||||
|
@ -1968,6 +1959,8 @@ static void requiredDataMask(ModifierData * /*md*/, CustomData_MeshMasks *r_cdda
|
|||
r_cddata_masks->vmask |= CD_MASK_PROP_ALL;
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
|
||||
ModifierTypeInfo modifierType_Nodes = {
|
||||
/*name*/ N_("GeometryNodes"),
|
||||
/*structName*/ "NodesModifierData",
|
||||
|
@ -1981,26 +1974,26 @@ ModifierTypeInfo modifierType_Nodes = {
|
|||
eModifierTypeFlag_SupportsMapping),
|
||||
/*icon*/ ICON_GEOMETRY_NODES,
|
||||
|
||||
/*copyData*/ copyData,
|
||||
/*copyData*/ blender::copyData,
|
||||
|
||||
/*deformVerts*/ nullptr,
|
||||
/*deformMatrices*/ nullptr,
|
||||
/*deformVertsEM*/ nullptr,
|
||||
/*deformMatricesEM*/ nullptr,
|
||||
/*modifyMesh*/ modifyMesh,
|
||||
/*modifyGeometrySet*/ modifyGeometrySet,
|
||||
/*modifyMesh*/ blender::modifyMesh,
|
||||
/*modifyGeometrySet*/ blender::modifyGeometrySet,
|
||||
|
||||
/*initData*/ initData,
|
||||
/*requiredDataMask*/ requiredDataMask,
|
||||
/*freeData*/ freeData,
|
||||
/*isDisabled*/ isDisabled,
|
||||
/*updateDepsgraph*/ updateDepsgraph,
|
||||
/*dependsOnTime*/ dependsOnTime,
|
||||
/*initData*/ blender::initData,
|
||||
/*requiredDataMask*/ blender::requiredDataMask,
|
||||
/*freeData*/ blender::freeData,
|
||||
/*isDisabled*/ blender::isDisabled,
|
||||
/*updateDepsgraph*/ blender::updateDepsgraph,
|
||||
/*dependsOnTime*/ blender::dependsOnTime,
|
||||
/*dependsOnNormals*/ nullptr,
|
||||
/*foreachIDLink*/ foreachIDLink,
|
||||
/*foreachTexLink*/ foreachTexLink,
|
||||
/*foreachIDLink*/ blender::foreachIDLink,
|
||||
/*foreachTexLink*/ blender::foreachTexLink,
|
||||
/*freeRuntimeData*/ nullptr,
|
||||
/*panelRegister*/ panelRegister,
|
||||
/*blendWrite*/ blendWrite,
|
||||
/*blendRead*/ blendRead,
|
||||
/*panelRegister*/ blender::panelRegister,
|
||||
/*blendWrite*/ blender::blendWrite,
|
||||
/*blendRead*/ blender::blendRead,
|
||||
};
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
|
||||
#include "BKE_attribute_math.hh"
|
||||
|
||||
#include "GEO_mesh_flip_faces.hh"
|
||||
|
||||
#include "node_geometry_util.hh"
|
||||
|
||||
namespace blender::nodes::node_geo_flip_faces_cc {
|
||||
|
@ -29,48 +31,7 @@ static void mesh_flip_faces(Mesh &mesh, const Field<bool> &selection_field)
|
|||
evaluator.evaluate();
|
||||
const IndexMask selection = evaluator.get_evaluated_as_mask(0);
|
||||
|
||||
const Span<MPoly> polys = mesh.polys();
|
||||
MutableSpan<int> corner_verts = mesh.corner_verts_for_write();
|
||||
MutableSpan<int> corner_edges = mesh.corner_edges_for_write();
|
||||
|
||||
threading::parallel_for(selection.index_range(), 1024, [&](const IndexRange range) {
|
||||
for (const int i : selection.slice(range)) {
|
||||
const IndexRange poly(polys[i].loopstart, polys[i].totloop);
|
||||
for (const int j : IndexRange(poly.size() / 2)) {
|
||||
const int a = poly[j + 1];
|
||||
const int b = poly.last(j);
|
||||
std::swap(corner_verts[a], corner_verts[b]);
|
||||
std::swap(corner_edges[a - 1], corner_edges[b]);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
MutableAttributeAccessor attributes = mesh.attributes_for_write();
|
||||
attributes.for_all(
|
||||
[&](const bke::AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) {
|
||||
if (meta_data.data_type == CD_PROP_STRING) {
|
||||
return true;
|
||||
}
|
||||
if (meta_data.domain != ATTR_DOMAIN_CORNER) {
|
||||
return true;
|
||||
}
|
||||
if (ELEM(attribute_id.name(), ".corner_vert", ".corner_edge")) {
|
||||
return true;
|
||||
}
|
||||
GSpanAttributeWriter attribute = attributes.lookup_for_write_span(attribute_id);
|
||||
attribute_math::convert_to_static_type(meta_data.data_type, [&](auto dummy) {
|
||||
using T = decltype(dummy);
|
||||
MutableSpan<T> dst_span = attribute.span.typed<T>();
|
||||
threading::parallel_for(selection.index_range(), 1024, [&](const IndexRange range) {
|
||||
for (const int i : selection.slice(range)) {
|
||||
const IndexRange poly(polys[i].loopstart, polys[i].totloop);
|
||||
dst_span.slice(poly.drop_front(1)).reverse();
|
||||
}
|
||||
});
|
||||
});
|
||||
attribute.finish();
|
||||
return true;
|
||||
});
|
||||
geometry::flip_faces(mesh, selection);
|
||||
}
|
||||
|
||||
static void node_geo_exec(GeoNodeExecParams params)
|
||||
|
|
|
@ -93,6 +93,9 @@ static void window_manager_foreach_id(ID *id, LibraryForeachIDData *data)
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
BKE_LIB_FOREACHID_PROCESS_IDSUPER(
|
||||
data, wm->xr.session_settings.base_pose_object, IDWALK_CB_USER_ONE);
|
||||
}
|
||||
|
||||
static void write_wm_xr_data(BlendWriter *writer, wmXrData *xr_data)
|
||||
|
|
|
@ -736,8 +736,9 @@ if(WITH_CYCLES OR WITH_OPENGL_RENDER_TESTS)
|
|||
set(_cycles_render_tests bake;${render_tests};osl)
|
||||
|
||||
foreach(render_test ${_cycles_render_tests})
|
||||
set(_cycles_test_name "cycles_${render_test}_${_cycles_device_lower}")
|
||||
add_python_test(
|
||||
cycles_${render_test}_${_cycles_device_lower}
|
||||
${_cycles_test_name}
|
||||
${CMAKE_CURRENT_LIST_DIR}/cycles_render_tests.py
|
||||
-blender "${TEST_BLENDER_EXE}"
|
||||
-testdir "${TEST_SRC_DIR}/render/${render_test}"
|
||||
|
@ -746,6 +747,11 @@ if(WITH_CYCLES OR WITH_OPENGL_RENDER_TESTS)
|
|||
-device ${_cycles_device}
|
||||
-blacklist ${_cycles_blacklist}
|
||||
)
|
||||
|
||||
if(NOT("${_cycles_device_lower}" STREQUAL "cpu"))
|
||||
set_tests_properties(${_cycles_test_name} PROPERTIES RUN_SERIAL TRUE)
|
||||
endif()
|
||||
unset(_cycles_test_name)
|
||||
endforeach()
|
||||
endforeach()
|
||||
unset(_cycles_blacklist)
|
||||
|
|
Loading…
Reference in New Issue