2022-05-09 17:33:30 +02:00
|
|
|
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
|
|
|
|
|
|
#include "BLI_length_parameterize.hh"
|
|
|
|
|
#include "BLI_task.hh"
|
|
|
|
|
|
|
|
|
|
#include "FN_field.hh"
|
|
|
|
|
#include "FN_multi_function_builder.hh"
|
|
|
|
|
|
|
|
|
|
#include "BKE_attribute_math.hh"
|
|
|
|
|
#include "BKE_curves.hh"
|
2022-05-10 18:27:24 +02:00
|
|
|
#include "BKE_curves_utils.hh"
|
2022-05-09 17:33:30 +02:00
|
|
|
#include "BKE_geometry_fields.hh"
|
|
|
|
|
|
|
|
|
|
#include "GEO_resample_curves.hh"
|
|
|
|
|
|
|
|
|
|
namespace blender::geometry {
|
|
|
|
|
|
|
|
|
|
static fn::Field<int> get_count_input_max_one(const fn::Field<int> &count_field)
|
|
|
|
|
{
|
|
|
|
|
static fn::CustomMF_SI_SO<int, int> max_one_fn(
|
|
|
|
|
"Clamp Above One",
|
|
|
|
|
[](int value) { return std::max(1, value); },
|
|
|
|
|
fn::CustomMF_presets::AllSpanOrSingle());
|
|
|
|
|
auto clamp_op = std::make_shared<fn::FieldOperation>(
|
|
|
|
|
fn::FieldOperation(max_one_fn, {count_field}));
|
|
|
|
|
|
|
|
|
|
return fn::Field<int>(std::move(clamp_op));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static fn::Field<int> get_count_input_from_length(const fn::Field<float> &length_field)
|
|
|
|
|
{
|
|
|
|
|
static fn::CustomMF_SI_SI_SO<float, float, int> get_count_fn(
|
|
|
|
|
"Length Input to Count",
|
|
|
|
|
[](const float curve_length, const float sample_length) {
|
|
|
|
|
/* Find the number of sampled segments by dividing the total length by
|
|
|
|
|
* the sample length. Then there is one more sampled point than segment. */
|
|
|
|
|
const int count = int(curve_length / sample_length) + 1;
|
|
|
|
|
return std::max(1, count);
|
|
|
|
|
},
|
|
|
|
|
fn::CustomMF_presets::AllSpanOrSingle());
|
|
|
|
|
|
|
|
|
|
auto get_count_op = std::make_shared<fn::FieldOperation>(fn::FieldOperation(
|
|
|
|
|
get_count_fn,
|
|
|
|
|
{fn::Field<float>(std::make_shared<bke::CurveLengthFieldInput>()), length_field}));
|
|
|
|
|
|
|
|
|
|
return fn::Field<int>(std::move(get_count_op));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Return true if the attribute should be copied/interpolated to the result curves.
|
|
|
|
|
* Don't output attributes that correspond to curve types that have no curves in the result.
|
|
|
|
|
*/
|
|
|
|
|
static bool interpolate_attribute_to_curves(const bke::AttributeIDRef &attribute_id,
|
|
|
|
|
const std::array<int, CURVE_TYPES_NUM> &type_counts)
|
|
|
|
|
{
|
|
|
|
|
if (!attribute_id.is_named()) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
if (ELEM(attribute_id.name(),
|
|
|
|
|
"handle_type_left",
|
|
|
|
|
"handle_type_right",
|
|
|
|
|
"handle_left",
|
|
|
|
|
"handle_right")) {
|
|
|
|
|
return type_counts[CURVE_TYPE_BEZIER] != 0;
|
|
|
|
|
}
|
|
|
|
|
if (ELEM(attribute_id.name(), "nurbs_weight")) {
|
|
|
|
|
return type_counts[CURVE_TYPE_NURBS] != 0;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Return true if the attribute should be copied to poly curves.
|
|
|
|
|
*/
|
|
|
|
|
static bool interpolate_attribute_to_poly_curve(const bke::AttributeIDRef &attribute_id)
|
|
|
|
|
{
|
|
|
|
|
static const Set<StringRef> no_interpolation{{
|
|
|
|
|
"handle_type_left",
|
|
|
|
|
"handle_type_right",
|
2022-06-30 19:30:06 -05:00
|
|
|
"handle_right",
|
|
|
|
|
"handle_left",
|
2022-05-09 17:33:30 +02:00
|
|
|
"nurbs_weight",
|
|
|
|
|
}};
|
|
|
|
|
return !(attribute_id.is_named() && no_interpolation.contains(attribute_id.name()));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Retrieve spans from source and result attributes.
|
|
|
|
|
*/
|
|
|
|
|
static void retrieve_attribute_spans(const Span<bke::AttributeIDRef> ids,
|
Geometry Nodes: Use separate field context for each geometry type
Using the same `GeometryComponentFieldContext` for all situations,
even when only one geometry type is supported is misleading, and mixes
too many different abstraction levels into code that could be simpler.
With the attribute API moved out of geometry components recently,
the "component" system is just getting in the way here.
This commit adds specific field contexts for geometry types: meshes,
curves, point clouds, and instances. There are also separate field input
helper classes, to help reduce boilerplate for fields that only support
specific geometry types.
Another benefit of this change is that it separates geometry components
from fields, which makes it easier to see the purpose of the two concepts,
and how they relate.
Because we want to be able to evaluate a field on just `CurvesGeometry`
rather than the full `Curves` data-block, the generic "geometry context"
had to be changed to avoid using `GeometryComponent`, since there is
no corresponding geometry component type. The resulting void pointer
is ugly, but only turns up in three places in practice. When Apple clang
supports `std::variant`, that could be used instead.
Differential Revision: https://developer.blender.org/D15519
2022-08-30 11:08:27 -05:00
|
|
|
const CurvesGeometry &src_curves,
|
|
|
|
|
CurvesGeometry &dst_curves,
|
2022-05-09 17:33:30 +02:00
|
|
|
Vector<GSpan> &src,
|
|
|
|
|
Vector<GMutableSpan> &dst,
|
Geometry Nodes: new geometry attribute API
Currently, there are two attribute API. The first, defined in `BKE_attribute.h` is
accessible from RNA and C code. The second is implemented with `GeometryComponent`
and is only accessible in C++ code. The second is widely used, but only being
accessible through the `GeometrySet` API makes it awkward to use, and even impossible
for types that don't correspond directly to a geometry component like `CurvesGeometry`.
This patch adds a new attribute API, designed to replace the `GeometryComponent`
attribute API now, and to eventually replace or be the basis of the other one.
The basic idea is that there is an `AttributeAccessor` class that allows code to
interact with a set of attributes owned by some geometry. The accessor itself has
no ownership. `AttributeAccessor` is a simple type that can be passed around by
value. That makes it easy to return it from functions and to store it in containers.
For const-correctness, there is also a `MutableAttributeAccessor` that allows
changing individual and can add or remove attributes.
Currently, `AttributeAccessor` is composed of two pointers. The first is a pointer
to the owner of the attribute data. The second is a pointer to a struct with
function pointers, that is similar to a virtual function table. The functions
know how to access attributes on the owner.
The actual attribute access for geometries is still implemented with the `AttributeProvider`
pattern, which makes it easy to support different sources of attributes on a
geometry and simplifies dealing with built-in attributes.
There are different ways to get an attribute accessor for a geometry:
* `GeometryComponent.attributes()`
* `CurvesGeometry.attributes()`
* `bke::mesh_attributes(const Mesh &)`
* `bke::pointcloud_attributes(const PointCloud &)`
All of these also have a `_for_write` variant that returns a `MutabelAttributeAccessor`.
Differential Revision: https://developer.blender.org/D15280
2022-07-08 16:16:56 +02:00
|
|
|
Vector<bke::GSpanAttributeWriter> &dst_attributes)
|
2022-05-09 17:33:30 +02:00
|
|
|
{
|
|
|
|
|
for (const int i : ids.index_range()) {
|
Geometry Nodes: Use separate field context for each geometry type
Using the same `GeometryComponentFieldContext` for all situations,
even when only one geometry type is supported is misleading, and mixes
too many different abstraction levels into code that could be simpler.
With the attribute API moved out of geometry components recently,
the "component" system is just getting in the way here.
This commit adds specific field contexts for geometry types: meshes,
curves, point clouds, and instances. There are also separate field input
helper classes, to help reduce boilerplate for fields that only support
specific geometry types.
Another benefit of this change is that it separates geometry components
from fields, which makes it easier to see the purpose of the two concepts,
and how they relate.
Because we want to be able to evaluate a field on just `CurvesGeometry`
rather than the full `Curves` data-block, the generic "geometry context"
had to be changed to avoid using `GeometryComponent`, since there is
no corresponding geometry component type. The resulting void pointer
is ugly, but only turns up in three places in practice. When Apple clang
supports `std::variant`, that could be used instead.
Differential Revision: https://developer.blender.org/D15519
2022-08-30 11:08:27 -05:00
|
|
|
GVArray src_attribute = src_curves.attributes().lookup(ids[i], ATTR_DOMAIN_POINT);
|
2022-05-09 17:33:30 +02:00
|
|
|
BLI_assert(src_attribute);
|
|
|
|
|
src.append(src_attribute.get_internal_span());
|
|
|
|
|
|
2022-06-01 14:38:06 +10:00
|
|
|
const eCustomDataType data_type = bke::cpp_type_to_custom_data_type(src_attribute.type());
|
Geometry Nodes: new geometry attribute API
Currently, there are two attribute API. The first, defined in `BKE_attribute.h` is
accessible from RNA and C code. The second is implemented with `GeometryComponent`
and is only accessible in C++ code. The second is widely used, but only being
accessible through the `GeometrySet` API makes it awkward to use, and even impossible
for types that don't correspond directly to a geometry component like `CurvesGeometry`.
This patch adds a new attribute API, designed to replace the `GeometryComponent`
attribute API now, and to eventually replace or be the basis of the other one.
The basic idea is that there is an `AttributeAccessor` class that allows code to
interact with a set of attributes owned by some geometry. The accessor itself has
no ownership. `AttributeAccessor` is a simple type that can be passed around by
value. That makes it easy to return it from functions and to store it in containers.
For const-correctness, there is also a `MutableAttributeAccessor` that allows
changing individual and can add or remove attributes.
Currently, `AttributeAccessor` is composed of two pointers. The first is a pointer
to the owner of the attribute data. The second is a pointer to a struct with
function pointers, that is similar to a virtual function table. The functions
know how to access attributes on the owner.
The actual attribute access for geometries is still implemented with the `AttributeProvider`
pattern, which makes it easy to support different sources of attributes on a
geometry and simplifies dealing with built-in attributes.
There are different ways to get an attribute accessor for a geometry:
* `GeometryComponent.attributes()`
* `CurvesGeometry.attributes()`
* `bke::mesh_attributes(const Mesh &)`
* `bke::pointcloud_attributes(const PointCloud &)`
All of these also have a `_for_write` variant that returns a `MutabelAttributeAccessor`.
Differential Revision: https://developer.blender.org/D15280
2022-07-08 16:16:56 +02:00
|
|
|
bke::GSpanAttributeWriter dst_attribute =
|
Geometry Nodes: Use separate field context for each geometry type
Using the same `GeometryComponentFieldContext` for all situations,
even when only one geometry type is supported is misleading, and mixes
too many different abstraction levels into code that could be simpler.
With the attribute API moved out of geometry components recently,
the "component" system is just getting in the way here.
This commit adds specific field contexts for geometry types: meshes,
curves, point clouds, and instances. There are also separate field input
helper classes, to help reduce boilerplate for fields that only support
specific geometry types.
Another benefit of this change is that it separates geometry components
from fields, which makes it easier to see the purpose of the two concepts,
and how they relate.
Because we want to be able to evaluate a field on just `CurvesGeometry`
rather than the full `Curves` data-block, the generic "geometry context"
had to be changed to avoid using `GeometryComponent`, since there is
no corresponding geometry component type. The resulting void pointer
is ugly, but only turns up in three places in practice. When Apple clang
supports `std::variant`, that could be used instead.
Differential Revision: https://developer.blender.org/D15519
2022-08-30 11:08:27 -05:00
|
|
|
dst_curves.attributes_for_write().lookup_or_add_for_write_only_span(
|
Geometry Nodes: new geometry attribute API
Currently, there are two attribute API. The first, defined in `BKE_attribute.h` is
accessible from RNA and C code. The second is implemented with `GeometryComponent`
and is only accessible in C++ code. The second is widely used, but only being
accessible through the `GeometrySet` API makes it awkward to use, and even impossible
for types that don't correspond directly to a geometry component like `CurvesGeometry`.
This patch adds a new attribute API, designed to replace the `GeometryComponent`
attribute API now, and to eventually replace or be the basis of the other one.
The basic idea is that there is an `AttributeAccessor` class that allows code to
interact with a set of attributes owned by some geometry. The accessor itself has
no ownership. `AttributeAccessor` is a simple type that can be passed around by
value. That makes it easy to return it from functions and to store it in containers.
For const-correctness, there is also a `MutableAttributeAccessor` that allows
changing individual and can add or remove attributes.
Currently, `AttributeAccessor` is composed of two pointers. The first is a pointer
to the owner of the attribute data. The second is a pointer to a struct with
function pointers, that is similar to a virtual function table. The functions
know how to access attributes on the owner.
The actual attribute access for geometries is still implemented with the `AttributeProvider`
pattern, which makes it easy to support different sources of attributes on a
geometry and simplifies dealing with built-in attributes.
There are different ways to get an attribute accessor for a geometry:
* `GeometryComponent.attributes()`
* `CurvesGeometry.attributes()`
* `bke::mesh_attributes(const Mesh &)`
* `bke::pointcloud_attributes(const PointCloud &)`
All of these also have a `_for_write` variant that returns a `MutabelAttributeAccessor`.
Differential Revision: https://developer.blender.org/D15280
2022-07-08 16:16:56 +02:00
|
|
|
ids[i], ATTR_DOMAIN_POINT, data_type);
|
|
|
|
|
dst.append(dst_attribute.span);
|
2022-05-09 17:33:30 +02:00
|
|
|
dst_attributes.append(std::move(dst_attribute));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct AttributesForInterpolation : NonCopyable, NonMovable {
|
|
|
|
|
Vector<GSpan> src;
|
|
|
|
|
Vector<GMutableSpan> dst;
|
|
|
|
|
|
Geometry Nodes: new geometry attribute API
Currently, there are two attribute API. The first, defined in `BKE_attribute.h` is
accessible from RNA and C code. The second is implemented with `GeometryComponent`
and is only accessible in C++ code. The second is widely used, but only being
accessible through the `GeometrySet` API makes it awkward to use, and even impossible
for types that don't correspond directly to a geometry component like `CurvesGeometry`.
This patch adds a new attribute API, designed to replace the `GeometryComponent`
attribute API now, and to eventually replace or be the basis of the other one.
The basic idea is that there is an `AttributeAccessor` class that allows code to
interact with a set of attributes owned by some geometry. The accessor itself has
no ownership. `AttributeAccessor` is a simple type that can be passed around by
value. That makes it easy to return it from functions and to store it in containers.
For const-correctness, there is also a `MutableAttributeAccessor` that allows
changing individual and can add or remove attributes.
Currently, `AttributeAccessor` is composed of two pointers. The first is a pointer
to the owner of the attribute data. The second is a pointer to a struct with
function pointers, that is similar to a virtual function table. The functions
know how to access attributes on the owner.
The actual attribute access for geometries is still implemented with the `AttributeProvider`
pattern, which makes it easy to support different sources of attributes on a
geometry and simplifies dealing with built-in attributes.
There are different ways to get an attribute accessor for a geometry:
* `GeometryComponent.attributes()`
* `CurvesGeometry.attributes()`
* `bke::mesh_attributes(const Mesh &)`
* `bke::pointcloud_attributes(const PointCloud &)`
All of these also have a `_for_write` variant that returns a `MutabelAttributeAccessor`.
Differential Revision: https://developer.blender.org/D15280
2022-07-08 16:16:56 +02:00
|
|
|
Vector<bke::GSpanAttributeWriter> dst_attributes;
|
2022-05-09 17:33:30 +02:00
|
|
|
|
|
|
|
|
Vector<GSpan> src_no_interpolation;
|
|
|
|
|
Vector<GMutableSpan> dst_no_interpolation;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Gather a set of all generic attribute IDs to copy to the result curves.
|
|
|
|
|
*/
|
Geometry Nodes: Use separate field context for each geometry type
Using the same `GeometryComponentFieldContext` for all situations,
even when only one geometry type is supported is misleading, and mixes
too many different abstraction levels into code that could be simpler.
With the attribute API moved out of geometry components recently,
the "component" system is just getting in the way here.
This commit adds specific field contexts for geometry types: meshes,
curves, point clouds, and instances. There are also separate field input
helper classes, to help reduce boilerplate for fields that only support
specific geometry types.
Another benefit of this change is that it separates geometry components
from fields, which makes it easier to see the purpose of the two concepts,
and how they relate.
Because we want to be able to evaluate a field on just `CurvesGeometry`
rather than the full `Curves` data-block, the generic "geometry context"
had to be changed to avoid using `GeometryComponent`, since there is
no corresponding geometry component type. The resulting void pointer
is ugly, but only turns up in three places in practice. When Apple clang
supports `std::variant`, that could be used instead.
Differential Revision: https://developer.blender.org/D15519
2022-08-30 11:08:27 -05:00
|
|
|
static void gather_point_attributes_to_interpolate(const CurvesGeometry &src_curves,
|
|
|
|
|
CurvesGeometry &dst_curves,
|
2022-05-09 17:33:30 +02:00
|
|
|
AttributesForInterpolation &result)
|
|
|
|
|
{
|
|
|
|
|
VectorSet<bke::AttributeIDRef> ids;
|
|
|
|
|
VectorSet<bke::AttributeIDRef> ids_no_interpolation;
|
Geometry Nodes: Use separate field context for each geometry type
Using the same `GeometryComponentFieldContext` for all situations,
even when only one geometry type is supported is misleading, and mixes
too many different abstraction levels into code that could be simpler.
With the attribute API moved out of geometry components recently,
the "component" system is just getting in the way here.
This commit adds specific field contexts for geometry types: meshes,
curves, point clouds, and instances. There are also separate field input
helper classes, to help reduce boilerplate for fields that only support
specific geometry types.
Another benefit of this change is that it separates geometry components
from fields, which makes it easier to see the purpose of the two concepts,
and how they relate.
Because we want to be able to evaluate a field on just `CurvesGeometry`
rather than the full `Curves` data-block, the generic "geometry context"
had to be changed to avoid using `GeometryComponent`, since there is
no corresponding geometry component type. The resulting void pointer
is ugly, but only turns up in three places in practice. When Apple clang
supports `std::variant`, that could be used instead.
Differential Revision: https://developer.blender.org/D15519
2022-08-30 11:08:27 -05:00
|
|
|
src_curves.attributes().for_all(
|
Geometry Nodes: new geometry attribute API
Currently, there are two attribute API. The first, defined in `BKE_attribute.h` is
accessible from RNA and C code. The second is implemented with `GeometryComponent`
and is only accessible in C++ code. The second is widely used, but only being
accessible through the `GeometrySet` API makes it awkward to use, and even impossible
for types that don't correspond directly to a geometry component like `CurvesGeometry`.
This patch adds a new attribute API, designed to replace the `GeometryComponent`
attribute API now, and to eventually replace or be the basis of the other one.
The basic idea is that there is an `AttributeAccessor` class that allows code to
interact with a set of attributes owned by some geometry. The accessor itself has
no ownership. `AttributeAccessor` is a simple type that can be passed around by
value. That makes it easy to return it from functions and to store it in containers.
For const-correctness, there is also a `MutableAttributeAccessor` that allows
changing individual and can add or remove attributes.
Currently, `AttributeAccessor` is composed of two pointers. The first is a pointer
to the owner of the attribute data. The second is a pointer to a struct with
function pointers, that is similar to a virtual function table. The functions
know how to access attributes on the owner.
The actual attribute access for geometries is still implemented with the `AttributeProvider`
pattern, which makes it easy to support different sources of attributes on a
geometry and simplifies dealing with built-in attributes.
There are different ways to get an attribute accessor for a geometry:
* `GeometryComponent.attributes()`
* `CurvesGeometry.attributes()`
* `bke::mesh_attributes(const Mesh &)`
* `bke::pointcloud_attributes(const PointCloud &)`
All of these also have a `_for_write` variant that returns a `MutabelAttributeAccessor`.
Differential Revision: https://developer.blender.org/D15280
2022-07-08 16:16:56 +02:00
|
|
|
[&](const bke::AttributeIDRef &id, const bke::AttributeMetaData meta_data) {
|
2022-05-09 17:33:30 +02:00
|
|
|
if (meta_data.domain != ATTR_DOMAIN_POINT) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
if (!interpolate_attribute_to_curves(id, dst_curves.curve_type_counts())) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
if (interpolate_attribute_to_poly_curve(id)) {
|
|
|
|
|
ids.add_new(id);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
ids_no_interpolation.add_new(id);
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
/* Position is handled differently since it has non-generic interpolation for Bezier
|
|
|
|
|
* curves and because the evaluated positions are cached for each evaluated point. */
|
|
|
|
|
ids.remove_contained("position");
|
|
|
|
|
|
|
|
|
|
retrieve_attribute_spans(
|
Geometry Nodes: Use separate field context for each geometry type
Using the same `GeometryComponentFieldContext` for all situations,
even when only one geometry type is supported is misleading, and mixes
too many different abstraction levels into code that could be simpler.
With the attribute API moved out of geometry components recently,
the "component" system is just getting in the way here.
This commit adds specific field contexts for geometry types: meshes,
curves, point clouds, and instances. There are also separate field input
helper classes, to help reduce boilerplate for fields that only support
specific geometry types.
Another benefit of this change is that it separates geometry components
from fields, which makes it easier to see the purpose of the two concepts,
and how they relate.
Because we want to be able to evaluate a field on just `CurvesGeometry`
rather than the full `Curves` data-block, the generic "geometry context"
had to be changed to avoid using `GeometryComponent`, since there is
no corresponding geometry component type. The resulting void pointer
is ugly, but only turns up in three places in practice. When Apple clang
supports `std::variant`, that could be used instead.
Differential Revision: https://developer.blender.org/D15519
2022-08-30 11:08:27 -05:00
|
|
|
ids, src_curves, dst_curves, result.src, result.dst, result.dst_attributes);
|
2022-05-09 17:33:30 +02:00
|
|
|
|
2022-08-12 12:38:54 +10:00
|
|
|
/* Attributes that aren't interpolated like Bezier handles still have to be copied
|
2022-05-09 17:33:30 +02:00
|
|
|
* to the result when there are any unselected curves of the corresponding type. */
|
|
|
|
|
retrieve_attribute_spans(ids_no_interpolation,
|
Geometry Nodes: Use separate field context for each geometry type
Using the same `GeometryComponentFieldContext` for all situations,
even when only one geometry type is supported is misleading, and mixes
too many different abstraction levels into code that could be simpler.
With the attribute API moved out of geometry components recently,
the "component" system is just getting in the way here.
This commit adds specific field contexts for geometry types: meshes,
curves, point clouds, and instances. There are also separate field input
helper classes, to help reduce boilerplate for fields that only support
specific geometry types.
Another benefit of this change is that it separates geometry components
from fields, which makes it easier to see the purpose of the two concepts,
and how they relate.
Because we want to be able to evaluate a field on just `CurvesGeometry`
rather than the full `Curves` data-block, the generic "geometry context"
had to be changed to avoid using `GeometryComponent`, since there is
no corresponding geometry component type. The resulting void pointer
is ugly, but only turns up in three places in practice. When Apple clang
supports `std::variant`, that could be used instead.
Differential Revision: https://developer.blender.org/D15519
2022-08-30 11:08:27 -05:00
|
|
|
src_curves,
|
|
|
|
|
dst_curves,
|
2022-05-09 17:33:30 +02:00
|
|
|
result.src_no_interpolation,
|
|
|
|
|
result.dst_no_interpolation,
|
|
|
|
|
result.dst_attributes);
|
|
|
|
|
}
|
|
|
|
|
|
Geometry Nodes: Use separate field context for each geometry type
Using the same `GeometryComponentFieldContext` for all situations,
even when only one geometry type is supported is misleading, and mixes
too many different abstraction levels into code that could be simpler.
With the attribute API moved out of geometry components recently,
the "component" system is just getting in the way here.
This commit adds specific field contexts for geometry types: meshes,
curves, point clouds, and instances. There are also separate field input
helper classes, to help reduce boilerplate for fields that only support
specific geometry types.
Another benefit of this change is that it separates geometry components
from fields, which makes it easier to see the purpose of the two concepts,
and how they relate.
Because we want to be able to evaluate a field on just `CurvesGeometry`
rather than the full `Curves` data-block, the generic "geometry context"
had to be changed to avoid using `GeometryComponent`, since there is
no corresponding geometry component type. The resulting void pointer
is ugly, but only turns up in three places in practice. When Apple clang
supports `std::variant`, that could be used instead.
Differential Revision: https://developer.blender.org/D15519
2022-08-30 11:08:27 -05:00
|
|
|
static CurvesGeometry resample_to_uniform(const CurvesGeometry &src_curves,
|
|
|
|
|
const fn::Field<bool> &selection_field,
|
|
|
|
|
const fn::Field<int> &count_field)
|
2022-05-09 17:33:30 +02:00
|
|
|
{
|
|
|
|
|
/* Create the new curves without any points and evaluate the final count directly
|
|
|
|
|
* into the offsets array, in order to be accumulated into offsets later. */
|
Geometry Nodes: Use separate field context for each geometry type
Using the same `GeometryComponentFieldContext` for all situations,
even when only one geometry type is supported is misleading, and mixes
too many different abstraction levels into code that could be simpler.
With the attribute API moved out of geometry components recently,
the "component" system is just getting in the way here.
This commit adds specific field contexts for geometry types: meshes,
curves, point clouds, and instances. There are also separate field input
helper classes, to help reduce boilerplate for fields that only support
specific geometry types.
Another benefit of this change is that it separates geometry components
from fields, which makes it easier to see the purpose of the two concepts,
and how they relate.
Because we want to be able to evaluate a field on just `CurvesGeometry`
rather than the full `Curves` data-block, the generic "geometry context"
had to be changed to avoid using `GeometryComponent`, since there is
no corresponding geometry component type. The resulting void pointer
is ugly, but only turns up in three places in practice. When Apple clang
supports `std::variant`, that could be used instead.
Differential Revision: https://developer.blender.org/D15519
2022-08-30 11:08:27 -05:00
|
|
|
CurvesGeometry dst_curves = CurvesGeometry(0, src_curves.curves_num());
|
2022-05-09 17:33:30 +02:00
|
|
|
|
|
|
|
|
/* Directly copy curve attributes, since they stay the same (except for curve types). */
|
|
|
|
|
CustomData_copy(&src_curves.curve_data,
|
|
|
|
|
&dst_curves.curve_data,
|
|
|
|
|
CD_MASK_ALL,
|
|
|
|
|
CD_DUPLICATE,
|
|
|
|
|
src_curves.curves_num());
|
|
|
|
|
MutableSpan<int> dst_offsets = dst_curves.offsets_for_write();
|
|
|
|
|
|
Geometry Nodes: Use separate field context for each geometry type
Using the same `GeometryComponentFieldContext` for all situations,
even when only one geometry type is supported is misleading, and mixes
too many different abstraction levels into code that could be simpler.
With the attribute API moved out of geometry components recently,
the "component" system is just getting in the way here.
This commit adds specific field contexts for geometry types: meshes,
curves, point clouds, and instances. There are also separate field input
helper classes, to help reduce boilerplate for fields that only support
specific geometry types.
Another benefit of this change is that it separates geometry components
from fields, which makes it easier to see the purpose of the two concepts,
and how they relate.
Because we want to be able to evaluate a field on just `CurvesGeometry`
rather than the full `Curves` data-block, the generic "geometry context"
had to be changed to avoid using `GeometryComponent`, since there is
no corresponding geometry component type. The resulting void pointer
is ugly, but only turns up in three places in practice. When Apple clang
supports `std::variant`, that could be used instead.
Differential Revision: https://developer.blender.org/D15519
2022-08-30 11:08:27 -05:00
|
|
|
bke::CurvesFieldContext field_context{src_curves, ATTR_DOMAIN_CURVE};
|
2022-05-09 17:33:30 +02:00
|
|
|
fn::FieldEvaluator evaluator{field_context, src_curves.curves_num()};
|
|
|
|
|
evaluator.set_selection(selection_field);
|
|
|
|
|
evaluator.add_with_destination(count_field, dst_offsets);
|
|
|
|
|
evaluator.evaluate();
|
|
|
|
|
const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
|
|
|
|
|
const Vector<IndexRange> unselected_ranges = selection.extract_ranges_invert(
|
|
|
|
|
src_curves.curves_range(), nullptr);
|
|
|
|
|
|
|
|
|
|
/* Fill the counts for the curves that aren't selected and accumulate the counts into offsets. */
|
2022-05-10 18:27:24 +02:00
|
|
|
bke::curves::fill_curve_counts(src_curves, unselected_ranges, dst_offsets);
|
|
|
|
|
bke::curves::accumulate_counts_to_offsets(dst_offsets);
|
2022-05-09 17:33:30 +02:00
|
|
|
dst_curves.resize(dst_offsets.last(), dst_curves.curves_num());
|
|
|
|
|
|
|
|
|
|
/* All resampled curves are poly curves. */
|
|
|
|
|
dst_curves.fill_curve_types(selection, CURVE_TYPE_POLY);
|
|
|
|
|
|
|
|
|
|
VArray<bool> curves_cyclic = src_curves.cyclic();
|
|
|
|
|
VArray<int8_t> curve_types = src_curves.curve_types();
|
|
|
|
|
Span<float3> evaluated_positions = src_curves.evaluated_positions();
|
|
|
|
|
MutableSpan<float3> dst_positions = dst_curves.positions_for_write();
|
|
|
|
|
|
|
|
|
|
AttributesForInterpolation attributes;
|
Geometry Nodes: Use separate field context for each geometry type
Using the same `GeometryComponentFieldContext` for all situations,
even when only one geometry type is supported is misleading, and mixes
too many different abstraction levels into code that could be simpler.
With the attribute API moved out of geometry components recently,
the "component" system is just getting in the way here.
This commit adds specific field contexts for geometry types: meshes,
curves, point clouds, and instances. There are also separate field input
helper classes, to help reduce boilerplate for fields that only support
specific geometry types.
Another benefit of this change is that it separates geometry components
from fields, which makes it easier to see the purpose of the two concepts,
and how they relate.
Because we want to be able to evaluate a field on just `CurvesGeometry`
rather than the full `Curves` data-block, the generic "geometry context"
had to be changed to avoid using `GeometryComponent`, since there is
no corresponding geometry component type. The resulting void pointer
is ugly, but only turns up in three places in practice. When Apple clang
supports `std::variant`, that could be used instead.
Differential Revision: https://developer.blender.org/D15519
2022-08-30 11:08:27 -05:00
|
|
|
gather_point_attributes_to_interpolate(src_curves, dst_curves, attributes);
|
2022-05-09 17:33:30 +02:00
|
|
|
|
|
|
|
|
src_curves.ensure_evaluated_lengths();
|
|
|
|
|
|
|
|
|
|
/* Sampling arbitrary attributes works by first interpolating them to the curve's standard
|
|
|
|
|
* "evaluated points" and then interpolating that result with the uniform samples. This is
|
|
|
|
|
* potentially wasteful when down-sampling a curve to many fewer points. There are two possible
|
|
|
|
|
* solutions: only sample the necessary points for interpolation, or first sample curve
|
|
|
|
|
* parameter/segment indices and evaluate the curve directly. */
|
|
|
|
|
Array<int> sample_indices(dst_curves.points_num());
|
|
|
|
|
Array<float> sample_factors(dst_curves.points_num());
|
|
|
|
|
|
|
|
|
|
/* Use a "for each group of curves: for each attribute: for each curve" pattern to work on
|
|
|
|
|
* smaller sections of data that ideally fit into CPU cache better than simply one attribute at a
|
|
|
|
|
* time or one curve at a time. */
|
|
|
|
|
threading::parallel_for(selection.index_range(), 512, [&](IndexRange selection_range) {
|
|
|
|
|
const IndexMask sliced_selection = selection.slice(selection_range);
|
|
|
|
|
|
|
|
|
|
Vector<std::byte> evaluated_buffer;
|
|
|
|
|
|
|
|
|
|
/* Gather uniform samples based on the accumulated lengths of the original curve. */
|
|
|
|
|
for (const int i_curve : sliced_selection) {
|
|
|
|
|
const bool cyclic = curves_cyclic[i_curve];
|
|
|
|
|
const IndexRange dst_points = dst_curves.points_for_curve(i_curve);
|
2022-07-25 11:53:06 -05:00
|
|
|
const Span<float> lengths = src_curves.evaluated_lengths_for_curve(i_curve, cyclic);
|
|
|
|
|
if (lengths.is_empty()) {
|
|
|
|
|
/* Handle curves with only one evaluated point. */
|
|
|
|
|
sample_indices.as_mutable_span().slice(dst_points).fill(0);
|
|
|
|
|
sample_factors.as_mutable_span().slice(dst_points).fill(0.0f);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
length_parameterize::sample_uniform(lengths,
|
|
|
|
|
!curves_cyclic[i_curve],
|
|
|
|
|
sample_indices.as_mutable_span().slice(dst_points),
|
|
|
|
|
sample_factors.as_mutable_span().slice(dst_points));
|
|
|
|
|
}
|
2022-05-09 17:33:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* For every attribute, evaluate attributes from every curve in the range in the original
|
|
|
|
|
* curve's "evaluated points", then use linear interpolation to sample to the result. */
|
|
|
|
|
for (const int i_attribute : attributes.dst.index_range()) {
|
|
|
|
|
attribute_math::convert_to_static_type(attributes.src[i_attribute].type(), [&](auto dummy) {
|
|
|
|
|
using T = decltype(dummy);
|
|
|
|
|
Span<T> src = attributes.src[i_attribute].typed<T>();
|
|
|
|
|
MutableSpan<T> dst = attributes.dst[i_attribute].typed<T>();
|
|
|
|
|
|
|
|
|
|
for (const int i_curve : sliced_selection) {
|
|
|
|
|
const IndexRange src_points = src_curves.points_for_curve(i_curve);
|
|
|
|
|
const IndexRange dst_points = dst_curves.points_for_curve(i_curve);
|
|
|
|
|
|
|
|
|
|
if (curve_types[i_curve] == CURVE_TYPE_POLY) {
|
2022-07-21 08:15:06 -05:00
|
|
|
length_parameterize::interpolate(src.slice(src_points),
|
|
|
|
|
sample_indices.as_span().slice(dst_points),
|
|
|
|
|
sample_factors.as_span().slice(dst_points),
|
|
|
|
|
dst.slice(dst_points));
|
2022-05-09 17:33:30 +02:00
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
const int evaluated_size = src_curves.evaluated_points_for_curve(i_curve).size();
|
|
|
|
|
evaluated_buffer.clear();
|
|
|
|
|
evaluated_buffer.resize(sizeof(T) * evaluated_size);
|
|
|
|
|
MutableSpan<T> evaluated = evaluated_buffer.as_mutable_span().cast<T>();
|
|
|
|
|
src_curves.interpolate_to_evaluated(i_curve, src.slice(src_points), evaluated);
|
|
|
|
|
|
2022-07-21 08:15:06 -05:00
|
|
|
length_parameterize::interpolate(evaluated.as_span(),
|
|
|
|
|
sample_indices.as_span().slice(dst_points),
|
|
|
|
|
sample_factors.as_span().slice(dst_points),
|
|
|
|
|
dst.slice(dst_points));
|
2022-05-09 17:33:30 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Interpolate the evaluated positions to the resampled curves. */
|
|
|
|
|
for (const int i_curve : sliced_selection) {
|
|
|
|
|
const IndexRange src_points = src_curves.evaluated_points_for_curve(i_curve);
|
|
|
|
|
const IndexRange dst_points = dst_curves.points_for_curve(i_curve);
|
2022-07-21 08:15:06 -05:00
|
|
|
length_parameterize::interpolate(evaluated_positions.slice(src_points),
|
|
|
|
|
sample_indices.as_span().slice(dst_points),
|
|
|
|
|
sample_factors.as_span().slice(dst_points),
|
|
|
|
|
dst_positions.slice(dst_points));
|
2022-05-09 17:33:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Fill the default value for non-interpolating attributes that still must be copied. */
|
|
|
|
|
for (GMutableSpan dst : attributes.dst_no_interpolation) {
|
|
|
|
|
for (const int i_curve : sliced_selection) {
|
|
|
|
|
const IndexRange dst_points = dst_curves.points_for_curve(i_curve);
|
|
|
|
|
dst.type().value_initialize_n(dst.slice(dst_points).data(), dst_points.size());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
/* Any attribute data from unselected curve points can be directly copied. */
|
|
|
|
|
for (const int i : attributes.src.index_range()) {
|
2022-06-08 15:37:46 +02:00
|
|
|
bke::curves::copy_point_data(
|
2022-05-09 17:33:30 +02:00
|
|
|
src_curves, dst_curves, unselected_ranges, attributes.src[i], attributes.dst[i]);
|
|
|
|
|
}
|
|
|
|
|
for (const int i : attributes.src_no_interpolation.index_range()) {
|
2022-06-08 15:37:46 +02:00
|
|
|
bke::curves::copy_point_data(src_curves,
|
|
|
|
|
dst_curves,
|
|
|
|
|
unselected_ranges,
|
|
|
|
|
attributes.src_no_interpolation[i],
|
|
|
|
|
attributes.dst_no_interpolation[i]);
|
2022-05-09 17:33:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Copy positions for unselected curves. */
|
|
|
|
|
Span<float3> src_positions = src_curves.positions();
|
2022-06-08 15:37:46 +02:00
|
|
|
bke::curves::copy_point_data(
|
|
|
|
|
src_curves, dst_curves, unselected_ranges, src_positions, dst_positions);
|
2022-05-09 17:33:30 +02:00
|
|
|
|
Geometry Nodes: new geometry attribute API
Currently, there are two attribute API. The first, defined in `BKE_attribute.h` is
accessible from RNA and C code. The second is implemented with `GeometryComponent`
and is only accessible in C++ code. The second is widely used, but only being
accessible through the `GeometrySet` API makes it awkward to use, and even impossible
for types that don't correspond directly to a geometry component like `CurvesGeometry`.
This patch adds a new attribute API, designed to replace the `GeometryComponent`
attribute API now, and to eventually replace or be the basis of the other one.
The basic idea is that there is an `AttributeAccessor` class that allows code to
interact with a set of attributes owned by some geometry. The accessor itself has
no ownership. `AttributeAccessor` is a simple type that can be passed around by
value. That makes it easy to return it from functions and to store it in containers.
For const-correctness, there is also a `MutableAttributeAccessor` that allows
changing individual and can add or remove attributes.
Currently, `AttributeAccessor` is composed of two pointers. The first is a pointer
to the owner of the attribute data. The second is a pointer to a struct with
function pointers, that is similar to a virtual function table. The functions
know how to access attributes on the owner.
The actual attribute access for geometries is still implemented with the `AttributeProvider`
pattern, which makes it easy to support different sources of attributes on a
geometry and simplifies dealing with built-in attributes.
There are different ways to get an attribute accessor for a geometry:
* `GeometryComponent.attributes()`
* `CurvesGeometry.attributes()`
* `bke::mesh_attributes(const Mesh &)`
* `bke::pointcloud_attributes(const PointCloud &)`
All of these also have a `_for_write` variant that returns a `MutabelAttributeAccessor`.
Differential Revision: https://developer.blender.org/D15280
2022-07-08 16:16:56 +02:00
|
|
|
for (bke::GSpanAttributeWriter &attribute : attributes.dst_attributes) {
|
|
|
|
|
attribute.finish();
|
2022-05-09 17:33:30 +02:00
|
|
|
}
|
|
|
|
|
|
Geometry Nodes: Use separate field context for each geometry type
Using the same `GeometryComponentFieldContext` for all situations,
even when only one geometry type is supported is misleading, and mixes
too many different abstraction levels into code that could be simpler.
With the attribute API moved out of geometry components recently,
the "component" system is just getting in the way here.
This commit adds specific field contexts for geometry types: meshes,
curves, point clouds, and instances. There are also separate field input
helper classes, to help reduce boilerplate for fields that only support
specific geometry types.
Another benefit of this change is that it separates geometry components
from fields, which makes it easier to see the purpose of the two concepts,
and how they relate.
Because we want to be able to evaluate a field on just `CurvesGeometry`
rather than the full `Curves` data-block, the generic "geometry context"
had to be changed to avoid using `GeometryComponent`, since there is
no corresponding geometry component type. The resulting void pointer
is ugly, but only turns up in three places in practice. When Apple clang
supports `std::variant`, that could be used instead.
Differential Revision: https://developer.blender.org/D15519
2022-08-30 11:08:27 -05:00
|
|
|
return dst_curves;
|
2022-05-09 17:33:30 +02:00
|
|
|
}
|
|
|
|
|
|
Geometry Nodes: Use separate field context for each geometry type
Using the same `GeometryComponentFieldContext` for all situations,
even when only one geometry type is supported is misleading, and mixes
too many different abstraction levels into code that could be simpler.
With the attribute API moved out of geometry components recently,
the "component" system is just getting in the way here.
This commit adds specific field contexts for geometry types: meshes,
curves, point clouds, and instances. There are also separate field input
helper classes, to help reduce boilerplate for fields that only support
specific geometry types.
Another benefit of this change is that it separates geometry components
from fields, which makes it easier to see the purpose of the two concepts,
and how they relate.
Because we want to be able to evaluate a field on just `CurvesGeometry`
rather than the full `Curves` data-block, the generic "geometry context"
had to be changed to avoid using `GeometryComponent`, since there is
no corresponding geometry component type. The resulting void pointer
is ugly, but only turns up in three places in practice. When Apple clang
supports `std::variant`, that could be used instead.
Differential Revision: https://developer.blender.org/D15519
2022-08-30 11:08:27 -05:00
|
|
|
CurvesGeometry resample_to_count(const CurvesGeometry &src_curves,
|
|
|
|
|
const fn::Field<bool> &selection_field,
|
|
|
|
|
const fn::Field<int> &count_field)
|
2022-05-09 17:33:30 +02:00
|
|
|
{
|
Geometry Nodes: Use separate field context for each geometry type
Using the same `GeometryComponentFieldContext` for all situations,
even when only one geometry type is supported is misleading, and mixes
too many different abstraction levels into code that could be simpler.
With the attribute API moved out of geometry components recently,
the "component" system is just getting in the way here.
This commit adds specific field contexts for geometry types: meshes,
curves, point clouds, and instances. There are also separate field input
helper classes, to help reduce boilerplate for fields that only support
specific geometry types.
Another benefit of this change is that it separates geometry components
from fields, which makes it easier to see the purpose of the two concepts,
and how they relate.
Because we want to be able to evaluate a field on just `CurvesGeometry`
rather than the full `Curves` data-block, the generic "geometry context"
had to be changed to avoid using `GeometryComponent`, since there is
no corresponding geometry component type. The resulting void pointer
is ugly, but only turns up in three places in practice. When Apple clang
supports `std::variant`, that could be used instead.
Differential Revision: https://developer.blender.org/D15519
2022-08-30 11:08:27 -05:00
|
|
|
return resample_to_uniform(src_curves, selection_field, get_count_input_max_one(count_field));
|
2022-05-09 17:33:30 +02:00
|
|
|
}
|
|
|
|
|
|
Geometry Nodes: Use separate field context for each geometry type
Using the same `GeometryComponentFieldContext` for all situations,
even when only one geometry type is supported is misleading, and mixes
too many different abstraction levels into code that could be simpler.
With the attribute API moved out of geometry components recently,
the "component" system is just getting in the way here.
This commit adds specific field contexts for geometry types: meshes,
curves, point clouds, and instances. There are also separate field input
helper classes, to help reduce boilerplate for fields that only support
specific geometry types.
Another benefit of this change is that it separates geometry components
from fields, which makes it easier to see the purpose of the two concepts,
and how they relate.
Because we want to be able to evaluate a field on just `CurvesGeometry`
rather than the full `Curves` data-block, the generic "geometry context"
had to be changed to avoid using `GeometryComponent`, since there is
no corresponding geometry component type. The resulting void pointer
is ugly, but only turns up in three places in practice. When Apple clang
supports `std::variant`, that could be used instead.
Differential Revision: https://developer.blender.org/D15519
2022-08-30 11:08:27 -05:00
|
|
|
CurvesGeometry resample_to_length(const CurvesGeometry &src_curves,
|
|
|
|
|
const fn::Field<bool> &selection_field,
|
|
|
|
|
const fn::Field<float> &segment_length_field)
|
2022-05-09 17:33:30 +02:00
|
|
|
{
|
|
|
|
|
return resample_to_uniform(
|
Geometry Nodes: Use separate field context for each geometry type
Using the same `GeometryComponentFieldContext` for all situations,
even when only one geometry type is supported is misleading, and mixes
too many different abstraction levels into code that could be simpler.
With the attribute API moved out of geometry components recently,
the "component" system is just getting in the way here.
This commit adds specific field contexts for geometry types: meshes,
curves, point clouds, and instances. There are also separate field input
helper classes, to help reduce boilerplate for fields that only support
specific geometry types.
Another benefit of this change is that it separates geometry components
from fields, which makes it easier to see the purpose of the two concepts,
and how they relate.
Because we want to be able to evaluate a field on just `CurvesGeometry`
rather than the full `Curves` data-block, the generic "geometry context"
had to be changed to avoid using `GeometryComponent`, since there is
no corresponding geometry component type. The resulting void pointer
is ugly, but only turns up in three places in practice. When Apple clang
supports `std::variant`, that could be used instead.
Differential Revision: https://developer.blender.org/D15519
2022-08-30 11:08:27 -05:00
|
|
|
src_curves, selection_field, get_count_input_from_length(segment_length_field));
|
2022-05-09 17:33:30 +02:00
|
|
|
}
|
|
|
|
|
|
Geometry Nodes: Use separate field context for each geometry type
Using the same `GeometryComponentFieldContext` for all situations,
even when only one geometry type is supported is misleading, and mixes
too many different abstraction levels into code that could be simpler.
With the attribute API moved out of geometry components recently,
the "component" system is just getting in the way here.
This commit adds specific field contexts for geometry types: meshes,
curves, point clouds, and instances. There are also separate field input
helper classes, to help reduce boilerplate for fields that only support
specific geometry types.
Another benefit of this change is that it separates geometry components
from fields, which makes it easier to see the purpose of the two concepts,
and how they relate.
Because we want to be able to evaluate a field on just `CurvesGeometry`
rather than the full `Curves` data-block, the generic "geometry context"
had to be changed to avoid using `GeometryComponent`, since there is
no corresponding geometry component type. The resulting void pointer
is ugly, but only turns up in three places in practice. When Apple clang
supports `std::variant`, that could be used instead.
Differential Revision: https://developer.blender.org/D15519
2022-08-30 11:08:27 -05:00
|
|
|
CurvesGeometry resample_to_evaluated(const CurvesGeometry &src_curves,
|
|
|
|
|
const fn::Field<bool> &selection_field)
|
2022-05-09 17:33:30 +02:00
|
|
|
{
|
2022-07-27 18:05:31 +02:00
|
|
|
src_curves.ensure_evaluated_offsets();
|
2022-05-09 17:33:30 +02:00
|
|
|
|
Geometry Nodes: Use separate field context for each geometry type
Using the same `GeometryComponentFieldContext` for all situations,
even when only one geometry type is supported is misleading, and mixes
too many different abstraction levels into code that could be simpler.
With the attribute API moved out of geometry components recently,
the "component" system is just getting in the way here.
This commit adds specific field contexts for geometry types: meshes,
curves, point clouds, and instances. There are also separate field input
helper classes, to help reduce boilerplate for fields that only support
specific geometry types.
Another benefit of this change is that it separates geometry components
from fields, which makes it easier to see the purpose of the two concepts,
and how they relate.
Because we want to be able to evaluate a field on just `CurvesGeometry`
rather than the full `Curves` data-block, the generic "geometry context"
had to be changed to avoid using `GeometryComponent`, since there is
no corresponding geometry component type. The resulting void pointer
is ugly, but only turns up in three places in practice. When Apple clang
supports `std::variant`, that could be used instead.
Differential Revision: https://developer.blender.org/D15519
2022-08-30 11:08:27 -05:00
|
|
|
bke::CurvesFieldContext field_context{src_curves, ATTR_DOMAIN_CURVE};
|
2022-05-09 17:33:30 +02:00
|
|
|
fn::FieldEvaluator evaluator{field_context, src_curves.curves_num()};
|
|
|
|
|
evaluator.set_selection(selection_field);
|
|
|
|
|
evaluator.evaluate();
|
|
|
|
|
const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
|
|
|
|
|
const Vector<IndexRange> unselected_ranges = selection.extract_ranges_invert(
|
|
|
|
|
src_curves.curves_range(), nullptr);
|
|
|
|
|
|
Geometry Nodes: Use separate field context for each geometry type
Using the same `GeometryComponentFieldContext` for all situations,
even when only one geometry type is supported is misleading, and mixes
too many different abstraction levels into code that could be simpler.
With the attribute API moved out of geometry components recently,
the "component" system is just getting in the way here.
This commit adds specific field contexts for geometry types: meshes,
curves, point clouds, and instances. There are also separate field input
helper classes, to help reduce boilerplate for fields that only support
specific geometry types.
Another benefit of this change is that it separates geometry components
from fields, which makes it easier to see the purpose of the two concepts,
and how they relate.
Because we want to be able to evaluate a field on just `CurvesGeometry`
rather than the full `Curves` data-block, the generic "geometry context"
had to be changed to avoid using `GeometryComponent`, since there is
no corresponding geometry component type. The resulting void pointer
is ugly, but only turns up in three places in practice. When Apple clang
supports `std::variant`, that could be used instead.
Differential Revision: https://developer.blender.org/D15519
2022-08-30 11:08:27 -05:00
|
|
|
CurvesGeometry dst_curves(0, src_curves.curves_num());
|
2022-05-09 17:33:30 +02:00
|
|
|
|
|
|
|
|
/* Directly copy curve attributes, since they stay the same (except for curve types). */
|
|
|
|
|
CustomData_copy(&src_curves.curve_data,
|
|
|
|
|
&dst_curves.curve_data,
|
|
|
|
|
CD_MASK_ALL,
|
|
|
|
|
CD_DUPLICATE,
|
|
|
|
|
src_curves.curves_num());
|
|
|
|
|
/* All resampled curves are poly curves. */
|
|
|
|
|
dst_curves.fill_curve_types(selection, CURVE_TYPE_POLY);
|
|
|
|
|
MutableSpan<int> dst_offsets = dst_curves.offsets_for_write();
|
|
|
|
|
|
2022-08-19 16:24:24 +02:00
|
|
|
src_curves.ensure_can_interpolate_to_evaluated();
|
2022-05-09 17:33:30 +02:00
|
|
|
threading::parallel_for(selection.index_range(), 4096, [&](IndexRange range) {
|
|
|
|
|
for (const int i : selection.slice(range)) {
|
|
|
|
|
dst_offsets[i] = src_curves.evaluated_points_for_curve(i).size();
|
|
|
|
|
}
|
|
|
|
|
});
|
2022-05-10 18:27:24 +02:00
|
|
|
bke::curves::fill_curve_counts(src_curves, unselected_ranges, dst_offsets);
|
|
|
|
|
bke::curves::accumulate_counts_to_offsets(dst_offsets);
|
2022-05-09 17:33:30 +02:00
|
|
|
|
|
|
|
|
dst_curves.resize(dst_offsets.last(), dst_curves.curves_num());
|
|
|
|
|
|
|
|
|
|
/* Create the correct number of uniform-length samples for every selected curve. */
|
|
|
|
|
Span<float3> evaluated_positions = src_curves.evaluated_positions();
|
|
|
|
|
MutableSpan<float3> dst_positions = dst_curves.positions_for_write();
|
|
|
|
|
|
|
|
|
|
AttributesForInterpolation attributes;
|
Geometry Nodes: Use separate field context for each geometry type
Using the same `GeometryComponentFieldContext` for all situations,
even when only one geometry type is supported is misleading, and mixes
too many different abstraction levels into code that could be simpler.
With the attribute API moved out of geometry components recently,
the "component" system is just getting in the way here.
This commit adds specific field contexts for geometry types: meshes,
curves, point clouds, and instances. There are also separate field input
helper classes, to help reduce boilerplate for fields that only support
specific geometry types.
Another benefit of this change is that it separates geometry components
from fields, which makes it easier to see the purpose of the two concepts,
and how they relate.
Because we want to be able to evaluate a field on just `CurvesGeometry`
rather than the full `Curves` data-block, the generic "geometry context"
had to be changed to avoid using `GeometryComponent`, since there is
no corresponding geometry component type. The resulting void pointer
is ugly, but only turns up in three places in practice. When Apple clang
supports `std::variant`, that could be used instead.
Differential Revision: https://developer.blender.org/D15519
2022-08-30 11:08:27 -05:00
|
|
|
gather_point_attributes_to_interpolate(src_curves, dst_curves, attributes);
|
2022-05-09 17:33:30 +02:00
|
|
|
|
|
|
|
|
threading::parallel_for(selection.index_range(), 512, [&](IndexRange selection_range) {
|
|
|
|
|
const IndexMask sliced_selection = selection.slice(selection_range);
|
|
|
|
|
|
|
|
|
|
/* Evaluate generic point attributes directly to the result attributes. */
|
|
|
|
|
for (const int i_attribute : attributes.dst.index_range()) {
|
|
|
|
|
attribute_math::convert_to_static_type(attributes.src[i_attribute].type(), [&](auto dummy) {
|
|
|
|
|
using T = decltype(dummy);
|
|
|
|
|
Span<T> src = attributes.src[i_attribute].typed<T>();
|
|
|
|
|
MutableSpan<T> dst = attributes.dst[i_attribute].typed<T>();
|
|
|
|
|
|
|
|
|
|
for (const int i_curve : sliced_selection) {
|
|
|
|
|
const IndexRange src_points = src_curves.points_for_curve(i_curve);
|
|
|
|
|
const IndexRange dst_points = dst_curves.points_for_curve(i_curve);
|
|
|
|
|
src_curves.interpolate_to_evaluated(
|
|
|
|
|
i_curve, src.slice(src_points), dst.slice(dst_points));
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Copy the evaluated positions to the selected curves. */
|
|
|
|
|
for (const int i_curve : sliced_selection) {
|
|
|
|
|
const IndexRange src_points = src_curves.evaluated_points_for_curve(i_curve);
|
|
|
|
|
const IndexRange dst_points = dst_curves.points_for_curve(i_curve);
|
|
|
|
|
dst_positions.slice(dst_points).copy_from(evaluated_positions.slice(src_points));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Fill the default value for non-interpolating attributes that still must be copied. */
|
|
|
|
|
for (GMutableSpan dst : attributes.dst_no_interpolation) {
|
|
|
|
|
for (const int i_curve : sliced_selection) {
|
|
|
|
|
const IndexRange dst_points = dst_curves.points_for_curve(i_curve);
|
|
|
|
|
dst.type().value_initialize_n(dst.slice(dst_points).data(), dst_points.size());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
/* Any attribute data from unselected curve points can be directly copied. */
|
|
|
|
|
for (const int i : attributes.src.index_range()) {
|
2022-06-08 15:37:46 +02:00
|
|
|
bke::curves::copy_point_data(
|
2022-05-09 17:33:30 +02:00
|
|
|
src_curves, dst_curves, unselected_ranges, attributes.src[i], attributes.dst[i]);
|
|
|
|
|
}
|
|
|
|
|
for (const int i : attributes.src_no_interpolation.index_range()) {
|
2022-06-08 15:37:46 +02:00
|
|
|
bke::curves::copy_point_data(src_curves,
|
|
|
|
|
dst_curves,
|
|
|
|
|
unselected_ranges,
|
|
|
|
|
attributes.src_no_interpolation[i],
|
|
|
|
|
attributes.dst_no_interpolation[i]);
|
2022-05-09 17:33:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Copy positions for unselected curves. */
|
|
|
|
|
Span<float3> src_positions = src_curves.positions();
|
2022-06-08 15:37:46 +02:00
|
|
|
bke::curves::copy_point_data(
|
|
|
|
|
src_curves, dst_curves, unselected_ranges, src_positions, dst_positions);
|
2022-05-09 17:33:30 +02:00
|
|
|
|
Geometry Nodes: new geometry attribute API
Currently, there are two attribute API. The first, defined in `BKE_attribute.h` is
accessible from RNA and C code. The second is implemented with `GeometryComponent`
and is only accessible in C++ code. The second is widely used, but only being
accessible through the `GeometrySet` API makes it awkward to use, and even impossible
for types that don't correspond directly to a geometry component like `CurvesGeometry`.
This patch adds a new attribute API, designed to replace the `GeometryComponent`
attribute API now, and to eventually replace or be the basis of the other one.
The basic idea is that there is an `AttributeAccessor` class that allows code to
interact with a set of attributes owned by some geometry. The accessor itself has
no ownership. `AttributeAccessor` is a simple type that can be passed around by
value. That makes it easy to return it from functions and to store it in containers.
For const-correctness, there is also a `MutableAttributeAccessor` that allows
changing individual and can add or remove attributes.
Currently, `AttributeAccessor` is composed of two pointers. The first is a pointer
to the owner of the attribute data. The second is a pointer to a struct with
function pointers, that is similar to a virtual function table. The functions
know how to access attributes on the owner.
The actual attribute access for geometries is still implemented with the `AttributeProvider`
pattern, which makes it easy to support different sources of attributes on a
geometry and simplifies dealing with built-in attributes.
There are different ways to get an attribute accessor for a geometry:
* `GeometryComponent.attributes()`
* `CurvesGeometry.attributes()`
* `bke::mesh_attributes(const Mesh &)`
* `bke::pointcloud_attributes(const PointCloud &)`
All of these also have a `_for_write` variant that returns a `MutabelAttributeAccessor`.
Differential Revision: https://developer.blender.org/D15280
2022-07-08 16:16:56 +02:00
|
|
|
for (bke::GSpanAttributeWriter &attribute : attributes.dst_attributes) {
|
|
|
|
|
attribute.finish();
|
2022-05-09 17:33:30 +02:00
|
|
|
}
|
|
|
|
|
|
Geometry Nodes: Use separate field context for each geometry type
Using the same `GeometryComponentFieldContext` for all situations,
even when only one geometry type is supported is misleading, and mixes
too many different abstraction levels into code that could be simpler.
With the attribute API moved out of geometry components recently,
the "component" system is just getting in the way here.
This commit adds specific field contexts for geometry types: meshes,
curves, point clouds, and instances. There are also separate field input
helper classes, to help reduce boilerplate for fields that only support
specific geometry types.
Another benefit of this change is that it separates geometry components
from fields, which makes it easier to see the purpose of the two concepts,
and how they relate.
Because we want to be able to evaluate a field on just `CurvesGeometry`
rather than the full `Curves` data-block, the generic "geometry context"
had to be changed to avoid using `GeometryComponent`, since there is
no corresponding geometry component type. The resulting void pointer
is ugly, but only turns up in three places in practice. When Apple clang
supports `std::variant`, that could be used instead.
Differential Revision: https://developer.blender.org/D15519
2022-08-30 11:08:27 -05:00
|
|
|
return dst_curves;
|
2022-05-09 17:33:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace blender::geometry
|