Image Editor: Remove drawing artifacts #106173
|
@ -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
|
@ -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
|
||||
|
|
|
@ -287,6 +287,8 @@ class GPENCIL_MT_material_active(Menu):
|
|||
|
||||
for slot in ob.material_slots:
|
||||
mat = slot.material
|
||||
if not mat:
|
||||
continue
|
||||
mat.id_data.preview_ensure()
|
||||
if mat and mat.id_data and mat.id_data.preview:
|
||||
icon = mat.id_data.preview.icon_id
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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...
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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{};
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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,13 +206,14 @@ 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;
|
||||
|
@ -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;
|
||||
|
@ -269,6 +264,7 @@ 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)
|
||||
{
|
||||
|
@ -277,25 +273,28 @@ static void pack_island_box_pack_2d(const Span<UVAABBIsland *> aabbs,
|
|||
MEM_mallocN(sizeof(*box_array) * islands.size(), __func__));
|
||||
|
||||
/* Prepare for box_pack_2d. */
|
||||
for (const int64_t i : islands.index_range()) {
|
||||
for (const int64_t i : aabbs.index_range()) {
|
||||
PackIsland *island = islands[aabbs[i]->index];
|
||||
BoxPack *box = box_array + i;
|
||||
box->w = BLI_rctf_size_x(&island->bounds_rect) * scale + 2 * margin;
|
||||
box->h = BLI_rctf_size_y(&island->bounds_rect) * scale + 2 * margin;
|
||||
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(islands.size()), sort_boxes, r_max_u, 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 : islands.index_range()) {
|
||||
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 + margin) / scale - island->bounds_rect.xmin;
|
||||
island->pre_translate.y = (box->y + margin) / scale - island->bounds_rect.ymin;
|
||||
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. */
|
||||
|
@ -448,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,
|
||||
|
@ -471,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,
|
||||
|
@ -513,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;
|
||||
}
|
||||
|
@ -538,6 +544,7 @@ static void pack_island_xatlas(const Span<UVAABBIsland *> island_indices,
|
|||
const Span<PackIsland *> islands,
|
||||
const float scale,
|
||||
const float margin,
|
||||
const float target_aspect_y,
|
||||
float *r_max_u,
|
||||
float *r_max_v)
|
||||
{
|
||||
|
@ -556,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. */
|
||||
|
@ -572,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. */
|
||||
}
|
||||
|
||||
|
@ -583,8 +592,7 @@ static void pack_island_xatlas(const Span<UVAABBIsland *> island_indices,
|
|||
/* Redraw already placed islands. (Greedy.) */
|
||||
for (int j = 0; j < i; j++) {
|
||||
PackIsland *other = islands[island_indices[j]->index];
|
||||
float2 where(other->bounds_rect.xmin * scale - margin,
|
||||
other->bounds_rect.ymin * scale - margin);
|
||||
float2 where = (other->pre_translate + other->pivot_) * scale;
|
||||
occupancy.trace_island(islands[island_indices[j]->index], scale, margin, where, true);
|
||||
}
|
||||
continue;
|
||||
|
@ -592,13 +600,12 @@ static void pack_island_xatlas(const Span<UVAABBIsland *> island_indices,
|
|||
|
||||
/* Place island. */
|
||||
island->angle = 0.0f;
|
||||
island->pre_translate.x = (best.x + margin) / scale - island->bounds_rect.xmin;
|
||||
island->pre_translate.y = (best.y + margin) / scale - island->bounds_rect.ymin;
|
||||
island->pre_translate = best / scale - island->pivot_;
|
||||
occupancy.trace_island(island, scale, margin, best, true);
|
||||
i++; /* Next island. */
|
||||
|
||||
max_u = std::max(best.x + BLI_rctf_size_x(&island->bounds_rect) * scale + 2 * margin, max_u);
|
||||
max_v = std::max(best.y + BLI_rctf_size_y(&island->bounds_rect) * scale + 2 * margin, max_v);
|
||||
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. */
|
||||
|
@ -666,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);
|
||||
|
@ -693,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. */
|
||||
|
@ -724,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) {
|
||||
|
@ -797,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;
|
||||
|
@ -854,23 +870,35 @@ static float pack_islands_scale_margin(const Span<PackIsland *> islands,
|
|||
switch (params.shape_method) {
|
||||
case ED_UVPACK_SHAPE_CONVEX:
|
||||
case ED_UVPACK_SHAPE_CONCAVE:
|
||||
pack_island_xatlas(
|
||||
aabbs.as_span().take_front(max_box_pack), islands, scale, margin, &max_u, &max_v);
|
||||
pack_island_xatlas(aabbs.as_span().take_front(max_box_pack),
|
||||
islands,
|
||||
scale,
|
||||
margin,
|
||||
params.target_aspect_y,
|
||||
&max_u,
|
||||
&max_v);
|
||||
break;
|
||||
default:
|
||||
pack_island_box_pack_2d(
|
||||
aabbs.as_span().take_front(max_box_pack), islands, scale, margin, &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. */
|
||||
|
@ -878,20 +906,10 @@ static float pack_islands_scale_margin(const Span<PackIsland *> islands,
|
|||
UVAABBIsland *aabb = aabbs[i];
|
||||
PackIsland *pack_island = islands[aabb->index];
|
||||
pack_island->angle = aabb->angle;
|
||||
if (aabb->angle) {
|
||||
pack_island->pre_translate.x = (aabb->uv_placement.y + margin) / scale /
|
||||
-pack_island->aspect_y -
|
||||
pack_island->bounds_rect.xmax;
|
||||
pack_island->pre_translate.y = (aabb->uv_placement.x + margin) / scale *
|
||||
pack_island->aspect_y -
|
||||
pack_island->bounds_rect.ymin;
|
||||
}
|
||||
else {
|
||||
pack_island->pre_translate.x = (aabb->uv_placement.x + margin) / scale -
|
||||
pack_island->bounds_rect.xmin;
|
||||
pack_island->pre_translate.y = (aabb->uv_placement.y + margin) / scale -
|
||||
pack_island->bounds_rect.ymin;
|
||||
}
|
||||
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. */
|
||||
|
@ -901,7 +919,7 @@ 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.
|
||||
|
@ -1018,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;
|
||||
|
@ -1071,4 +1089,23 @@ void pack_islands(const Span<PackIsland *> &islands,
|
|||
|
||||
/** \} */
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
@ -430,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 *>(
|
||||
|
@ -585,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: {
|
||||
|
@ -773,8 +734,11 @@ static void update_output_properties_from_node_tree(const bNodeTree &tree,
|
|||
}
|
||||
}
|
||||
|
||||
} // 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);
|
||||
|
@ -800,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;
|
||||
|
@ -840,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));
|
||||
|
@ -859,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;
|
||||
|
@ -878,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;
|
||||
}
|
||||
|
@ -892,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;
|
||||
|
@ -910,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);
|
||||
|
@ -940,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;
|
||||
|
@ -968,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;
|
||||
|
@ -981,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);
|
||||
}
|
||||
}
|
||||
|
@ -994,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;
|
||||
};
|
||||
|
||||
|
@ -1016,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)) {
|
||||
|
@ -1025,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;
|
||||
}
|
||||
|
@ -1033,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);
|
||||
|
@ -1071,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;
|
||||
|
@ -1079,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);
|
||||
}
|
||||
|
@ -1105,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. */
|
||||
|
@ -1122,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());
|
||||
|
@ -1141,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);
|
||||
|
@ -1157,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);
|
||||
|
@ -1179,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++;
|
||||
|
@ -1220,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});
|
||||
}
|
||||
|
@ -1231,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++;
|
||||
|
@ -1262,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();
|
||||
|
@ -1271,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();
|
||||
}
|
||||
|
||||
|
@ -1325,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;
|
||||
|
@ -1355,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();
|
||||
|
@ -1446,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());
|
||||
}
|
||||
|
||||
|
@ -1467,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;
|
||||
}
|
||||
|
@ -1494,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)
|
||||
|
@ -1518,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;
|
||||
|
@ -1589,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);
|
||||
|
@ -1779,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);
|
||||
}
|
||||
}
|
||||
|
@ -1823,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()) {
|
||||
|
@ -1839,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;
|
||||
|
@ -1854,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()) {
|
||||
|
@ -1889,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",
|
||||
|
@ -1991,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",
|
||||
|
@ -2004,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,
|
||||
};
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue