2023-08-16 00:20:26 +10:00
|
|
|
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
2023-05-31 16:19:06 +02:00
|
|
|
*
|
|
|
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
2022-07-22 15:39:41 +02:00
|
|
|
|
|
|
|
|
#include "BKE_curves.hh"
|
Geometry Nodes: support attaching gizmos to input values
This adds support for attaching gizmos for input values. The goal is to make it
easier for users to set input values intuitively in the 3D viewport.
We went through multiple different possible designs until we settled on the one
implemented here. We picked it for it's flexibility and ease of use when using
geometry node assets. The core principle in the design is that **gizmos are
attached to existing input values instead of being the input value themselves**.
This actually fits the existing concept of gizmos in Blender well, but may be a
bit unintutitive in a node setup at first. The attachment is done using links in
the node editor.
The most basic usage of the node is to link a Value node to the new Linear Gizmo
node. This attaches the gizmo to the input value and allows you to change it
from the 3D view. The attachment is indicated by the gizmo icon in the sockets
which are controlled by a gizmo as well as the back-link (notice the double
link) when the gizmo is active.
The core principle makes it straight forward to control the same node setup from
the 3D view with gizmos, or by manually changing input values, or by driving the
input values procedurally.
If the input value is controlled indirectly by other inputs, it's often possible
to **automatically propagate** the gizmo to the actual input.
Backpropagation does not work for all nodes, although more nodes can be
supported over time.
This patch adds the first three gizmo nodes which cover common use cases:
* **Linear Gizmo**: Creates a gizmo that controls a float or integer value using
a linear movement of e.g. an arrow in the 3D viewport.
* **Dial Gizmo**: Creates a circular gizmo in the 3D viewport that can be
rotated to change the attached angle input.
* **Transform Gizmo**: Creates a simple gizmo for location, rotation and scale.
In the future, more built-in gizmos and potentially the ability for custom
gizmos could be added.
All gizmo nodes have a **Transform** geometry output. Using it is optional but
it is recommended when the gizmo is used to control inputs that affect a
geometry. When it is used, Blender will automatically transform the gizmos
together with the geometry that they control. To achieve this, the output should
be merged with the generated geometry using the *Join Geometry* node. The data
contained in *Transform* output is not visible geometry, but just internal
information that helps Blender to give a better user experience when using
gizmos.
The gizmo nodes have a multi-input socket. This allows **controlling multiple
values** with the same gizmo.
Only a small set of **gizmo shapes** is supported initially. It might be
extended in the future but one goal is to give the gizmos used by different node
group assets a familiar look and feel. A similar constraint exists for
**colors**. Currently, one can choose from a fixed set of colors which can be
modified in the theme settings.
The set of **visible gizmos** is determined by a multiple factors because it's
not really feasible to show all possible gizmos at all times. To see any of the
geometry nodes gizmos, the "Active Modifier" option has to be enabled in the
"Viewport Gizmos" popover. Then all gizmos are drawn for which at least one of
the following is true:
* The gizmo controls an input of the active modifier of the active object.
* The gizmo controls a value in a selected node in an open node editor.
* The gizmo controls a pinned value in an open node editor. Pinning works by
clicking the gizmo icon next to the value.
Pull Request: https://projects.blender.org/blender/blender/pulls/112677
2024-07-10 16:18:47 +02:00
|
|
|
#include "BKE_geometry_nodes_gizmos_transforms.hh"
|
2022-07-22 15:39:41 +02:00
|
|
|
#include "BKE_geometry_set.hh"
|
2023-10-12 15:42:04 +02:00
|
|
|
#include "BKE_grease_pencil.hh"
|
2022-07-22 15:39:41 +02:00
|
|
|
|
2023-06-15 22:18:28 +02:00
|
|
|
namespace blender::bke {
|
2022-07-22 15:39:41 +02:00
|
|
|
|
2023-06-15 22:18:28 +02:00
|
|
|
GeometryComponentEditData::GeometryComponentEditData() : GeometryComponent(Type::Edit) {}
|
2022-07-22 15:39:41 +02:00
|
|
|
|
2023-12-01 11:23:00 +01:00
|
|
|
GeometryComponentPtr GeometryComponentEditData::copy() const
|
2022-07-22 15:39:41 +02:00
|
|
|
{
|
|
|
|
|
GeometryComponentEditData *new_component = new GeometryComponentEditData();
|
Geometry Nodes: support attaching gizmos to input values
This adds support for attaching gizmos for input values. The goal is to make it
easier for users to set input values intuitively in the 3D viewport.
We went through multiple different possible designs until we settled on the one
implemented here. We picked it for it's flexibility and ease of use when using
geometry node assets. The core principle in the design is that **gizmos are
attached to existing input values instead of being the input value themselves**.
This actually fits the existing concept of gizmos in Blender well, but may be a
bit unintutitive in a node setup at first. The attachment is done using links in
the node editor.
The most basic usage of the node is to link a Value node to the new Linear Gizmo
node. This attaches the gizmo to the input value and allows you to change it
from the 3D view. The attachment is indicated by the gizmo icon in the sockets
which are controlled by a gizmo as well as the back-link (notice the double
link) when the gizmo is active.
The core principle makes it straight forward to control the same node setup from
the 3D view with gizmos, or by manually changing input values, or by driving the
input values procedurally.
If the input value is controlled indirectly by other inputs, it's often possible
to **automatically propagate** the gizmo to the actual input.
Backpropagation does not work for all nodes, although more nodes can be
supported over time.
This patch adds the first three gizmo nodes which cover common use cases:
* **Linear Gizmo**: Creates a gizmo that controls a float or integer value using
a linear movement of e.g. an arrow in the 3D viewport.
* **Dial Gizmo**: Creates a circular gizmo in the 3D viewport that can be
rotated to change the attached angle input.
* **Transform Gizmo**: Creates a simple gizmo for location, rotation and scale.
In the future, more built-in gizmos and potentially the ability for custom
gizmos could be added.
All gizmo nodes have a **Transform** geometry output. Using it is optional but
it is recommended when the gizmo is used to control inputs that affect a
geometry. When it is used, Blender will automatically transform the gizmos
together with the geometry that they control. To achieve this, the output should
be merged with the generated geometry using the *Join Geometry* node. The data
contained in *Transform* output is not visible geometry, but just internal
information that helps Blender to give a better user experience when using
gizmos.
The gizmo nodes have a multi-input socket. This allows **controlling multiple
values** with the same gizmo.
Only a small set of **gizmo shapes** is supported initially. It might be
extended in the future but one goal is to give the gizmos used by different node
group assets a familiar look and feel. A similar constraint exists for
**colors**. Currently, one can choose from a fixed set of colors which can be
modified in the theme settings.
The set of **visible gizmos** is determined by a multiple factors because it's
not really feasible to show all possible gizmos at all times. To see any of the
geometry nodes gizmos, the "Active Modifier" option has to be enabled in the
"Viewport Gizmos" popover. Then all gizmos are drawn for which at least one of
the following is true:
* The gizmo controls an input of the active modifier of the active object.
* The gizmo controls a value in a selected node in an open node editor.
* The gizmo controls a pinned value in an open node editor. Pinning works by
clicking the gizmo icon next to the value.
Pull Request: https://projects.blender.org/blender/blender/pulls/112677
2024-07-10 16:18:47 +02:00
|
|
|
if (gizmo_edit_hints_) {
|
|
|
|
|
new_component->gizmo_edit_hints_ = std::make_unique<GizmoEditHints>(*gizmo_edit_hints_);
|
|
|
|
|
}
|
2022-07-22 15:39:41 +02:00
|
|
|
if (curves_edit_hints_) {
|
|
|
|
|
new_component->curves_edit_hints_ = std::make_unique<CurvesEditHints>(*curves_edit_hints_);
|
|
|
|
|
}
|
2023-10-12 15:42:04 +02:00
|
|
|
if (grease_pencil_edit_hints_) {
|
|
|
|
|
new_component->grease_pencil_edit_hints_ = std::make_unique<GreasePencilEditHints>(
|
|
|
|
|
*grease_pencil_edit_hints_);
|
|
|
|
|
}
|
2023-12-01 11:23:00 +01:00
|
|
|
return GeometryComponentPtr(new_component);
|
2022-07-22 15:39:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool GeometryComponentEditData::owns_direct_data() const
|
|
|
|
|
{
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GeometryComponentEditData::ensure_owns_direct_data()
|
|
|
|
|
{
|
|
|
|
|
/* Nothing to do. */
|
|
|
|
|
}
|
BLI: support weak users and version in implicit sharing info
The main goal of these changes is to support checking if some data has
been changed over time. This is used by the WIP simulation nodes during
baking to detect which attributes have to be stored in every frame because
they have changed.
By using a combination of a weak user count and a version counter, it is
possible to detect that an attribute (or any data controlled by implicit
sharing) has not been changed with O(1) memory and time. It's still
possible that the data has been changed multiple times and is the same
in the end and beginning of course. That wouldn't be detected using this
mechanism.
The `ImplicitSharingInfo` struct has a new weak user count. A weak
reference is one that does not keep the referenced data alive, but makes sure
that the `ImplicitSharingInfo` itself is not deleted. If some piece of
data has one strong and multiple weak users, it is still mutable. If the
strong user count goes down to zero, the referenced data is freed.
Remaining weak users can check for this condition using `is_expired`.
This is a bit similar to `std::weak_ptr` but there is an important difference:
a weak user can not become a strong user while one can create a `shared_ptr`
from a `weak_ptr`. This restriction is necessary, because some code might
be changing the referenced data assuming that it is the only owner. If
another thread suddenly adds a new owner, the data would be shared again
and the first thread would not have been allowed to modify the data in
the first place.
There is also a new integer version counter in `ImplicitSharingInfo`.
It is incremented whenever some code wants to modify the referenced data.
Obviously, this can only be done when the data is not shared because then
it would be immutable. By comparing an old and new version number of the
same sharing info, one can check if the data has been modified. One has
to keep a weak reference to the sharing info together with the old version
number to ensure that the new sharing info is still the same as the old one.
Without this, it can happen that the sharing info was freed and a new
one was allocated at the same pointer address. Using a strong reference
for this purpose does not work, because then the data would never be
modified because it's shared.
2023-04-28 12:03:42 +02:00
|
|
|
|
|
|
|
|
void GeometryComponentEditData::clear()
|
|
|
|
|
{
|
|
|
|
|
BLI_assert(this->is_mutable() || this->is_expired());
|
|
|
|
|
curves_edit_hints_.reset();
|
2023-10-12 15:42:04 +02:00
|
|
|
grease_pencil_edit_hints_.reset();
|
Geometry Nodes: support attaching gizmos to input values
This adds support for attaching gizmos for input values. The goal is to make it
easier for users to set input values intuitively in the 3D viewport.
We went through multiple different possible designs until we settled on the one
implemented here. We picked it for it's flexibility and ease of use when using
geometry node assets. The core principle in the design is that **gizmos are
attached to existing input values instead of being the input value themselves**.
This actually fits the existing concept of gizmos in Blender well, but may be a
bit unintutitive in a node setup at first. The attachment is done using links in
the node editor.
The most basic usage of the node is to link a Value node to the new Linear Gizmo
node. This attaches the gizmo to the input value and allows you to change it
from the 3D view. The attachment is indicated by the gizmo icon in the sockets
which are controlled by a gizmo as well as the back-link (notice the double
link) when the gizmo is active.
The core principle makes it straight forward to control the same node setup from
the 3D view with gizmos, or by manually changing input values, or by driving the
input values procedurally.
If the input value is controlled indirectly by other inputs, it's often possible
to **automatically propagate** the gizmo to the actual input.
Backpropagation does not work for all nodes, although more nodes can be
supported over time.
This patch adds the first three gizmo nodes which cover common use cases:
* **Linear Gizmo**: Creates a gizmo that controls a float or integer value using
a linear movement of e.g. an arrow in the 3D viewport.
* **Dial Gizmo**: Creates a circular gizmo in the 3D viewport that can be
rotated to change the attached angle input.
* **Transform Gizmo**: Creates a simple gizmo for location, rotation and scale.
In the future, more built-in gizmos and potentially the ability for custom
gizmos could be added.
All gizmo nodes have a **Transform** geometry output. Using it is optional but
it is recommended when the gizmo is used to control inputs that affect a
geometry. When it is used, Blender will automatically transform the gizmos
together with the geometry that they control. To achieve this, the output should
be merged with the generated geometry using the *Join Geometry* node. The data
contained in *Transform* output is not visible geometry, but just internal
information that helps Blender to give a better user experience when using
gizmos.
The gizmo nodes have a multi-input socket. This allows **controlling multiple
values** with the same gizmo.
Only a small set of **gizmo shapes** is supported initially. It might be
extended in the future but one goal is to give the gizmos used by different node
group assets a familiar look and feel. A similar constraint exists for
**colors**. Currently, one can choose from a fixed set of colors which can be
modified in the theme settings.
The set of **visible gizmos** is determined by a multiple factors because it's
not really feasible to show all possible gizmos at all times. To see any of the
geometry nodes gizmos, the "Active Modifier" option has to be enabled in the
"Viewport Gizmos" popover. Then all gizmos are drawn for which at least one of
the following is true:
* The gizmo controls an input of the active modifier of the active object.
* The gizmo controls a value in a selected node in an open node editor.
* The gizmo controls a pinned value in an open node editor. Pinning works by
clicking the gizmo icon next to the value.
Pull Request: https://projects.blender.org/blender/blender/pulls/112677
2024-07-10 16:18:47 +02:00
|
|
|
gizmo_edit_hints_.reset();
|
BLI: support weak users and version in implicit sharing info
The main goal of these changes is to support checking if some data has
been changed over time. This is used by the WIP simulation nodes during
baking to detect which attributes have to be stored in every frame because
they have changed.
By using a combination of a weak user count and a version counter, it is
possible to detect that an attribute (or any data controlled by implicit
sharing) has not been changed with O(1) memory and time. It's still
possible that the data has been changed multiple times and is the same
in the end and beginning of course. That wouldn't be detected using this
mechanism.
The `ImplicitSharingInfo` struct has a new weak user count. A weak
reference is one that does not keep the referenced data alive, but makes sure
that the `ImplicitSharingInfo` itself is not deleted. If some piece of
data has one strong and multiple weak users, it is still mutable. If the
strong user count goes down to zero, the referenced data is freed.
Remaining weak users can check for this condition using `is_expired`.
This is a bit similar to `std::weak_ptr` but there is an important difference:
a weak user can not become a strong user while one can create a `shared_ptr`
from a `weak_ptr`. This restriction is necessary, because some code might
be changing the referenced data assuming that it is the only owner. If
another thread suddenly adds a new owner, the data would be shared again
and the first thread would not have been allowed to modify the data in
the first place.
There is also a new integer version counter in `ImplicitSharingInfo`.
It is incremented whenever some code wants to modify the referenced data.
Obviously, this can only be done when the data is not shared because then
it would be immutable. By comparing an old and new version number of the
same sharing info, one can check if the data has been modified. One has
to keep a weak reference to the sharing info together with the old version
number to ensure that the new sharing info is still the same as the old one.
Without this, it can happen that the sharing info was freed and a new
one was allocated at the same pointer address. Using a strong reference
for this purpose does not work, because then the data would never be
modified because it's shared.
2023-04-28 12:03:42 +02:00
|
|
|
}
|
2022-07-22 15:39:41 +02:00
|
|
|
|
2024-04-03 14:14:34 +02:00
|
|
|
static ImplicitSharingPtrAndData save_shared_attribute(const GAttributeReader &attribute)
|
|
|
|
|
{
|
|
|
|
|
if (attribute.sharing_info && attribute.varray.is_span()) {
|
|
|
|
|
const void *data = attribute.varray.get_internal_span().data();
|
|
|
|
|
attribute.sharing_info->add_user();
|
|
|
|
|
return {ImplicitSharingPtr(attribute.sharing_info), data};
|
|
|
|
|
}
|
|
|
|
|
auto *data = new ImplicitSharedValue<GArray<>>(attribute.varray.type(), attribute.varray.size());
|
|
|
|
|
attribute.varray.materialize(data->data.data());
|
2024-08-16 17:36:29 +02:00
|
|
|
return {ImplicitSharingPtr<>(data), data->data.data()};
|
2024-04-03 14:14:34 +02:00
|
|
|
}
|
|
|
|
|
|
2023-10-12 15:42:04 +02:00
|
|
|
static void remember_deformed_curve_positions_if_necessary(
|
|
|
|
|
const Curves *curves_id, GeometryComponentEditData &edit_component)
|
2022-07-22 15:39:41 +02:00
|
|
|
{
|
|
|
|
|
if (!edit_component.curves_edit_hints_) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2024-04-03 14:14:34 +02:00
|
|
|
if (curves_id == nullptr) {
|
2022-07-22 15:39:41 +02:00
|
|
|
return;
|
|
|
|
|
}
|
2024-04-03 14:14:34 +02:00
|
|
|
CurvesEditHints &edit_hints = *edit_component.curves_edit_hints_;
|
|
|
|
|
if (edit_hints.positions().has_value()) {
|
2022-07-22 15:39:41 +02:00
|
|
|
return;
|
|
|
|
|
}
|
2023-06-15 22:18:28 +02:00
|
|
|
const CurvesGeometry &curves = curves_id->geometry.wrap();
|
2022-07-22 15:39:41 +02:00
|
|
|
const int points_num = curves.points_num();
|
2024-04-03 14:14:34 +02:00
|
|
|
if (points_num != edit_hints.curves_id_orig.geometry.point_num) {
|
2022-07-22 15:39:41 +02:00
|
|
|
return;
|
|
|
|
|
}
|
2024-04-03 14:14:34 +02:00
|
|
|
edit_hints.positions_data = save_shared_attribute(curves.attributes().lookup("position"));
|
2022-07-22 15:39:41 +02:00
|
|
|
}
|
2023-06-15 22:18:28 +02:00
|
|
|
|
2023-10-12 15:42:04 +02:00
|
|
|
static void remember_deformed_grease_pencil_if_necessary(const GreasePencil *grease_pencil,
|
|
|
|
|
GeometryComponentEditData &edit_component)
|
|
|
|
|
{
|
|
|
|
|
if (!edit_component.grease_pencil_edit_hints_) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (edit_component.grease_pencil_edit_hints_->drawing_hints.has_value()) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (grease_pencil == nullptr) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
const GreasePencil &orig_grease_pencil =
|
|
|
|
|
edit_component.grease_pencil_edit_hints_->grease_pencil_id_orig;
|
2023-12-28 11:23:40 -05:00
|
|
|
const Span<const greasepencil::Layer *> layers = grease_pencil->layers();
|
|
|
|
|
const Span<const greasepencil::Layer *> orig_layers = orig_grease_pencil.layers();
|
2023-10-12 15:42:04 +02:00
|
|
|
const int layers_num = layers.size();
|
|
|
|
|
if (layers_num != orig_layers.size()) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
edit_component.grease_pencil_edit_hints_->drawing_hints.emplace(layers_num);
|
|
|
|
|
MutableSpan<GreasePencilDrawingEditHints> all_hints =
|
|
|
|
|
*edit_component.grease_pencil_edit_hints_->drawing_hints;
|
|
|
|
|
for (const int layer_index : layers.index_range()) {
|
2024-05-08 12:27:40 +02:00
|
|
|
const greasepencil::Drawing *drawing = grease_pencil->get_eval_drawing(
|
2024-09-23 13:54:02 +02:00
|
|
|
grease_pencil->layer(layer_index));
|
2023-12-28 11:23:40 -05:00
|
|
|
const greasepencil::Layer &orig_layer = *orig_layers[layer_index];
|
|
|
|
|
const greasepencil::Drawing *orig_drawing = orig_grease_pencil.get_drawing_at(
|
2024-01-09 12:40:21 +01:00
|
|
|
orig_layer, grease_pencil->runtime->eval_frame);
|
2023-10-12 15:42:04 +02:00
|
|
|
GreasePencilDrawingEditHints &drawing_hints = all_hints[layer_index];
|
|
|
|
|
if (!drawing || !orig_drawing) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2024-04-03 14:14:34 +02:00
|
|
|
drawing_hints.drawing_orig = orig_drawing;
|
|
|
|
|
const CurvesGeometry &curves = drawing->strokes();
|
|
|
|
|
if (curves.points_num() != orig_drawing->strokes().points_num()) {
|
2023-10-12 15:42:04 +02:00
|
|
|
continue;
|
|
|
|
|
}
|
2024-04-03 14:14:34 +02:00
|
|
|
drawing_hints.positions_data = save_shared_attribute(curves.attributes().lookup("position"));
|
2023-10-12 15:42:04 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GeometryComponentEditData::remember_deformed_positions_if_necessary(GeometrySet &geometry)
|
|
|
|
|
{
|
|
|
|
|
/* This component should be created at the start of object evaluation if it's necessary. */
|
|
|
|
|
if (!geometry.has<GeometryComponentEditData>()) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
GeometryComponentEditData &edit_component =
|
|
|
|
|
geometry.get_component_for_write<GeometryComponentEditData>();
|
|
|
|
|
remember_deformed_curve_positions_if_necessary(geometry.get_curves(), edit_component);
|
|
|
|
|
remember_deformed_grease_pencil_if_necessary(geometry.get_grease_pencil(), edit_component);
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-15 22:18:28 +02:00
|
|
|
} // namespace blender::bke
|