Image Editor: Remove drawing artifacts #106173

Closed
Jeroen Bakker wants to merge 13 commits from Jeroen-Bakker:image-editor-drawing-artifacts into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
36 changed files with 2231 additions and 1300 deletions
Showing only changes of commit 3c66b822cf - Show all commits

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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")

View File

@ -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

View File

@ -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)
{

View File

@ -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

View File

@ -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;
}

View File

@ -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)

View File

@ -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...

View File

@ -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);

View File

@ -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);

View File

@ -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.
*/

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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);

View File

@ -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");

View File

@ -182,6 +182,28 @@ static void graph_main_region_init(wmWindowManager *wm, ARegion *region)
WM_event_add_keymap_handler(&region->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 */

View File

@ -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;
}

View File

@ -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)

View File

@ -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, &params);
scene, objects_changed, object_changed_len, nullptr, nullptr, false, &params);
/* #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, &params);
uvedit_pack_islands_multi(scene, &ob, 1, &bm, nullptr, false, &params);
/* Write back from BMesh to Mesh. */
BMeshToMeshParams bm_to_me_params{};

View File

@ -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 &params, 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;
};

View File

@ -163,15 +163,8 @@ void PackIsland::finalize_geometry(const UVPackIsland_Params &params, 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

View File

@ -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;

View File

@ -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 ----------- */

View File

@ -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)) {

View File

@ -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;
}

View File

@ -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);

View File

@ -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 \

View File

@ -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);

View File

@ -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,
};

View File

@ -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)