Geometry Nodes: Begin conversion to new curves

This commit changes `CurveComponent` to store the new curve
type by adding conversions to and from `CurveEval` in most nodes.
This will temporarily make performance of curves in geometry nodes
much worse, but as functionality is implemented for the new type
and it is used in more places, performance will become better than
before.

We still use `CurveEval` for drawing curves, because the new `Curves`
data-block has no evaluated points yet. So the `Curve` ID is still
generated for rendering in the same way as before. It's also still
needed for drawing curve object edit mode overlays.

The old curve component isn't removed yet, because it is still used
to implement the conversions to and from `CurveEval`.

A few more attributes are added to make this possible:
- `nurbs_weight`: The weight for each control point on NURBS curves.
- `nurbs_order`: The order of the NURBS curve
- `knots_mode`: Necessary for conversion, not defined yet.
- `handle_type_{left/right}`: An 8 bit integer attribute.

Differential Revision: https://developer.blender.org/D14145
This commit is contained in:
Hans Goudey 2022-02-28 10:46:34 -05:00
parent 81bb86c750
commit 9ec12c26f1
Notes: blender-bot 2023-02-14 11:21:40 +01:00
Referenced by commit 52af51708f, Fix: Copy resolution when creating CurveEval from Curves
Referenced by commit 887ccb8537, Fix T96152: Crash realizing curve instances
Referenced by issue #96487, Crash when dynamically resampling splines using Spline Length (Point Count) + 1
Referenced by issue #95941, Change curve component to store Curves data
Referenced by issue #95443, Refactor curve nodes to use new data structure
Referenced by issue #95355, New Curves data block
53 changed files with 1004 additions and 327 deletions

View File

@ -24,6 +24,7 @@
#include "FN_field.hh"
struct Curves;
struct Collection;
struct Curve;
struct CurveEval;
@ -415,7 +416,7 @@ struct GeometrySet {
* Create a new geometry set that only contains the given curve.
*/
static GeometrySet create_with_curve(
CurveEval *curve, GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
Curves *curves, GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
/* Utility methods for access. */
/**
@ -462,7 +463,7 @@ struct GeometrySet {
/**
* Returns a read-only curve or null.
*/
const CurveEval *get_curve_for_read() const;
const Curves *get_curve_for_read() const;
/**
* Returns a mutable mesh or null. No ownership is transferred.
@ -479,7 +480,7 @@ struct GeometrySet {
/**
* Returns a mutable curve or null. No ownership is transferred.
*/
CurveEval *get_curve_for_write();
Curves *get_curve_for_write();
/* Utility methods for replacement. */
/**
@ -499,7 +500,7 @@ struct GeometrySet {
/**
* Clear the existing curve and replace it with the given one.
*/
void replace_curve(CurveEval *curve,
void replace_curve(Curves *curves,
GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
private:
@ -632,15 +633,57 @@ class PointCloudComponent : public GeometryComponent {
};
/**
* A geometry component that stores curve data, in other words, a group of splines.
* Curves are stored differently than other geometry components, because the data structure used
* here does not correspond exactly to the #Curve DNA data structure. A #CurveEval is stored here
* instead, though the component does give access to a #Curve for interfacing with render engines
* and other areas of Blender that expect to use a data-block with an #ID.
* Legacy runtime-only curves type.
* These curves are stored differently than other geometry components, because the data structure
* used here does not correspond exactly to the #Curve DNA data structure. A #CurveEval is stored
* here instead, though the component does give access to a #Curve for interfacing with render
* engines and other areas of Blender that expect to use a data-block with an #ID.
*/
class CurveComponentLegacy : public GeometryComponent {
private:
CurveEval *curve_ = nullptr;
GeometryOwnershipType ownership_ = GeometryOwnershipType::Owned;
public:
CurveComponentLegacy();
~CurveComponentLegacy();
GeometryComponent *copy() const override;
void clear();
bool has_curve() const;
/**
* Clear the component and replace it with the new curve.
*/
void replace(CurveEval *curve, GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
CurveEval *release();
const CurveEval *get_for_read() const;
CurveEval *get_for_write();
int attribute_domain_size(AttributeDomain domain) const final;
bool is_empty() const final;
bool owns_direct_data() const override;
void ensure_owns_direct_data() override;
static constexpr inline GeometryComponentType static_type = GEO_COMPONENT_TYPE_CURVE;
private:
const blender::bke::ComponentAttributeProviders *get_attribute_providers() const final;
blender::fn::GVArray attribute_try_adapt_domain_impl(const blender::fn::GVArray &varray,
AttributeDomain from_domain,
AttributeDomain to_domain) const final;
};
/**
* A geometry component that stores a group of curves, corresponding the the #Curves and
* #CurvesGeometry types.
*/
class CurveComponent : public GeometryComponent {
private:
CurveEval *curve_ = nullptr;
Curves *curves_ = nullptr;
GeometryOwnershipType ownership_ = GeometryOwnershipType::Owned;
/**
@ -658,15 +701,15 @@ class CurveComponent : public GeometryComponent {
GeometryComponent *copy() const override;
void clear();
bool has_curve() const;
bool has_curves() const;
/**
* Clear the component and replace it with the new curve.
*/
void replace(CurveEval *curve, GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
CurveEval *release();
void replace(Curves *curve, GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
Curves *release();
const CurveEval *get_for_read() const;
CurveEval *get_for_write();
const Curves *get_for_read() const;
Curves *get_for_write();
int attribute_domain_size(AttributeDomain domain) const final;

View File

@ -20,6 +20,7 @@
#include "BKE_attribute_math.hh"
struct Curve;
struct Curves;
struct ListBase;
class Spline;
@ -691,3 +692,5 @@ struct CurveEval {
std::unique_ptr<CurveEval> curve_eval_from_dna_curve(const Curve &curve,
const ListBase &nurbs_list);
std::unique_ptr<CurveEval> curve_eval_from_dna_curve(const Curve &dna_curve);
std::unique_ptr<CurveEval> curves_to_curve_eval(const Curves &curves);
Curves *curve_eval_to_curves(const CurveEval &curve_eval);

View File

@ -131,6 +131,7 @@ set(SRC
intern/fmodifier.c
intern/freestyle.c
intern/geometry_component_curve.cc
intern/geometry_component_curves.cc
intern/geometry_component_instances.cc
intern/geometry_component_mesh.cc
intern/geometry_component_pointcloud.cc

View File

@ -115,6 +115,8 @@ static void curve_free_data(ID *id)
MEM_SAFE_FREE(curve->str);
MEM_SAFE_FREE(curve->strinfo);
MEM_SAFE_FREE(curve->tb);
delete curve->curve_eval;
}
static void curve_foreach_id(ID *id, LibraryForeachIDData *data)

View File

@ -13,6 +13,8 @@
#include "BKE_anonymous_attribute.hh"
#include "BKE_curve.h"
#include "BKE_curves.hh"
#include "BKE_geometry_set.hh"
#include "BKE_spline.hh"
using blender::Array;
@ -23,8 +25,15 @@ using blender::Map;
using blender::MutableSpan;
using blender::Span;
using blender::StringRefNull;
using blender::VArray;
using blender::VArray_Span;
using blender::Vector;
using blender::bke::AttributeIDRef;
using blender::bke::OutputAttribute;
using blender::bke::OutputAttribute_Typed;
using blender::bke::ReadAttributeLookup;
using blender::fn::GVArray;
using blender::fn::GVArray_GSpan;
blender::Span<SplinePtr> CurveEval::splines() const
{
@ -336,6 +345,186 @@ std::unique_ptr<CurveEval> curve_eval_from_dna_curve(const Curve &dna_curve)
return curve_eval_from_dna_curve(dna_curve, *BKE_curve_nurbs_get_for_read(&dna_curve));
}
static void copy_attributes_between_components(const GeometryComponent &src_component,
GeometryComponent &dst_component,
Span<std::string> skip)
{
src_component.attribute_foreach(
[&](const AttributeIDRef &id, const AttributeMetaData meta_data) {
if (id.is_named() && skip.contains(id.name())) {
return true;
}
GVArray src_attribute = src_component.attribute_try_get_for_read(
id, meta_data.domain, meta_data.data_type);
if (!src_attribute) {
return true;
}
GVArray_GSpan src_attribute_data{src_attribute};
OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only(
id, meta_data.domain, meta_data.data_type);
if (!dst_attribute) {
return true;
}
dst_attribute.varray().set_all(src_attribute_data.data());
dst_attribute.save();
return true;
});
}
std::unique_ptr<CurveEval> curves_to_curve_eval(const Curves &curves)
{
CurveComponent src_component;
src_component.replace(&const_cast<Curves &>(curves), GeometryOwnershipType::ReadOnly);
const blender::bke::CurvesGeometry &geometry = blender::bke::CurvesGeometry::wrap(
curves.geometry);
VArray_Span<float> nurbs_weights{
src_component.attribute_get_for_read<float>("nurbs_weight", ATTR_DOMAIN_POINT, 0.0f)};
VArray_Span<int> nurbs_orders{
src_component.attribute_get_for_read<int>("nurbs_order", ATTR_DOMAIN_CURVE, 4)};
VArray_Span<int8_t> nurbs_knots_modes{
src_component.attribute_get_for_read<int8_t>("knots_mode", ATTR_DOMAIN_CURVE, 0)};
VArray_Span<int8_t> handle_types_right{
src_component.attribute_get_for_read<int8_t>("handle_type_right", ATTR_DOMAIN_POINT, 0)};
VArray_Span<int8_t> handle_types_left{
src_component.attribute_get_for_read<int8_t>("handle_type_left", ATTR_DOMAIN_POINT, 0)};
/* Create splines with the correct size and type. */
VArray<int8_t> curve_types = geometry.curve_types();
std::unique_ptr<CurveEval> curve_eval = std::make_unique<CurveEval>();
for (const int curve_index : curve_types.index_range()) {
const IndexRange point_range = geometry.range_for_curve(curve_index);
std::unique_ptr<Spline> spline;
switch (curve_types[curve_index]) {
case CURVE_TYPE_POLY: {
spline = std::make_unique<PolySpline>();
spline->resize(point_range.size());
break;
}
case CURVE_TYPE_BEZIER: {
std::unique_ptr<BezierSpline> bezier_spline = std::make_unique<BezierSpline>();
bezier_spline->resize(point_range.size());
bezier_spline->handle_types_left().copy_from(handle_types_left.slice(point_range));
bezier_spline->handle_types_right().copy_from(handle_types_right.slice(point_range));
spline = std::move(bezier_spline);
break;
}
case CURVE_TYPE_NURBS: {
std::unique_ptr<NURBSpline> nurb_spline = std::make_unique<NURBSpline>();
nurb_spline->resize(point_range.size());
nurb_spline->weights().copy_from(nurbs_weights.slice(point_range));
nurb_spline->set_order(nurbs_orders[curve_index]);
nurb_spline->knots_mode = static_cast<NURBSpline::KnotsMode>(
nurbs_knots_modes[curve_index]);
spline = std::move(nurb_spline);
break;
}
case CURVE_TYPE_CATMULL_ROM:
/* Not supported yet. */
BLI_assert_unreachable();
continue;
}
spline->positions().fill(float3(0));
spline->tilts().fill(0.0f);
spline->radii().fill(0.0f);
curve_eval->add_spline(std::move(spline));
}
CurveComponentLegacy dst_component;
dst_component.replace(curve_eval.get(), GeometryOwnershipType::Editable);
copy_attributes_between_components(src_component,
dst_component,
{"curve_type",
"nurbs_weight",
"nurbs_order",
"knots_mode",
"handle_type_right",
"handle_type_left"});
return curve_eval;
}
Curves *curve_eval_to_curves(const CurveEval &curve_eval)
{
Curves *curves = blender::bke::curves_new_nomain(curve_eval.total_control_point_size(),
curve_eval.splines().size());
CurveComponent dst_component;
dst_component.replace(curves, GeometryOwnershipType::Editable);
blender::bke::CurvesGeometry &geometry = blender::bke::CurvesGeometry::wrap(curves->geometry);
geometry.offsets().copy_from(curve_eval.control_point_offsets());
MutableSpan<int8_t> curve_types = geometry.curve_types();
OutputAttribute_Typed<float> nurbs_weight;
OutputAttribute_Typed<int> nurbs_order;
OutputAttribute_Typed<int8_t> nurbs_knots_mode;
if (curve_eval.has_spline_with_type(CURVE_TYPE_NURBS)) {
nurbs_weight = dst_component.attribute_try_get_for_output_only<float>("nurbs_weight",
ATTR_DOMAIN_POINT);
nurbs_order = dst_component.attribute_try_get_for_output_only<int>("nurbs_order",
ATTR_DOMAIN_CURVE);
nurbs_knots_mode = dst_component.attribute_try_get_for_output_only<int8_t>("knots_mode",
ATTR_DOMAIN_CURVE);
}
OutputAttribute_Typed<int8_t> handle_type_right;
OutputAttribute_Typed<int8_t> handle_type_left;
if (curve_eval.has_spline_with_type(CURVE_TYPE_BEZIER)) {
handle_type_right = dst_component.attribute_try_get_for_output_only<int8_t>(
"handle_type_right", ATTR_DOMAIN_POINT);
handle_type_left = dst_component.attribute_try_get_for_output_only<int8_t>("handle_type_left",
ATTR_DOMAIN_POINT);
}
for (const int curve_index : curve_eval.splines().index_range()) {
const Spline &spline = *curve_eval.splines()[curve_index];
curve_types[curve_index] = curve_eval.splines()[curve_index]->type();
const IndexRange point_range = geometry.range_for_curve(curve_index);
switch (spline.type()) {
case CURVE_TYPE_POLY:
break;
case CURVE_TYPE_BEZIER: {
const BezierSpline &src = static_cast<const BezierSpline &>(spline);
handle_type_right.as_span().slice(point_range).copy_from(src.handle_types_right());
handle_type_left.as_span().slice(point_range).copy_from(src.handle_types_left());
break;
}
case CURVE_TYPE_NURBS: {
const NURBSpline &src = static_cast<const NURBSpline &>(spline);
nurbs_knots_mode.as_span()[curve_index] = static_cast<int8_t>(src.knots_mode);
nurbs_order.as_span()[curve_index] = src.order();
nurbs_weight.as_span().slice(point_range).copy_from(src.weights());
break;
}
case CURVE_TYPE_CATMULL_ROM: {
BLI_assert_unreachable();
break;
}
}
}
nurbs_weight.save();
nurbs_order.save();
nurbs_knots_mode.save();
handle_type_right.save();
handle_type_left.save();
CurveComponentLegacy src_component;
src_component.replace(&const_cast<CurveEval &>(curve_eval), GeometryOwnershipType::ReadOnly);
copy_attributes_between_components(src_component, dst_component, {});
return curves;
}
void CurveEval::assert_valid_point_attributes() const
{
#ifdef DEBUG

View File

@ -865,7 +865,7 @@ static GeometrySet curve_calc_modifiers_post(Depsgraph *depsgraph,
else {
std::unique_ptr<CurveEval> curve_eval = curve_eval_from_dna_curve(
*cu, ob->runtime.curve_cache->deformed_nurbs);
geometry_set.replace_curve(curve_eval.release());
geometry_set.replace_curve(curve_eval_to_curves(*curve_eval));
}
for (; md; md = md->next) {
@ -1497,7 +1497,7 @@ void BKE_displist_make_curveTypes(Depsgraph *depsgraph,
* the CurveEval data type was introduced, when an evaluated object's curve data was just a
* copy of the original curve and everything else ended up in #CurveCache. */
CurveComponent &curve_component = geometry.get_component_for_write<CurveComponent>();
cow_curve.curve_eval = curve_component.get_for_write();
cow_curve.curve_eval = curves_to_curve_eval(*curve_component.get_for_read()).release();
BKE_object_eval_assign_data(ob, &cow_curve.id, false);
}

View File

@ -23,18 +23,18 @@ using blender::fn::GVArray_GSpan;
/** \name Geometry Component Implementation
* \{ */
CurveComponent::CurveComponent() : GeometryComponent(GEO_COMPONENT_TYPE_CURVE)
CurveComponentLegacy::CurveComponentLegacy() : GeometryComponent(GEO_COMPONENT_TYPE_CURVE)
{
}
CurveComponent::~CurveComponent()
CurveComponentLegacy::~CurveComponentLegacy()
{
this->clear();
}
GeometryComponent *CurveComponent::copy() const
GeometryComponent *CurveComponentLegacy::copy() const
{
CurveComponent *new_component = new CurveComponent();
CurveComponentLegacy *new_component = new CurveComponentLegacy();
if (curve_ != nullptr) {
new_component->curve_ = new CurveEval(*curve_);
new_component->ownership_ = GeometryOwnershipType::Owned;
@ -42,30 +42,23 @@ GeometryComponent *CurveComponent::copy() const
return new_component;
}
void CurveComponent::clear()
void CurveComponentLegacy::clear()
{
BLI_assert(this->is_mutable());
if (curve_ != nullptr) {
if (ownership_ == GeometryOwnershipType::Owned) {
delete curve_;
}
if (curve_for_render_ != nullptr) {
/* The curve created by this component should not have any edit mode data. */
BLI_assert(curve_for_render_->editfont == nullptr && curve_for_render_->editnurb == nullptr);
BKE_id_free(nullptr, curve_for_render_);
curve_for_render_ = nullptr;
}
curve_ = nullptr;
}
}
bool CurveComponent::has_curve() const
bool CurveComponentLegacy::has_curve() const
{
return curve_ != nullptr;
}
void CurveComponent::replace(CurveEval *curve, GeometryOwnershipType ownership)
void CurveComponentLegacy::replace(CurveEval *curve, GeometryOwnershipType ownership)
{
BLI_assert(this->is_mutable());
this->clear();
@ -73,7 +66,7 @@ void CurveComponent::replace(CurveEval *curve, GeometryOwnershipType ownership)
ownership_ = ownership;
}
CurveEval *CurveComponent::release()
CurveEval *CurveComponentLegacy::release()
{
BLI_assert(this->is_mutable());
CurveEval *curve = curve_;
@ -81,12 +74,12 @@ CurveEval *CurveComponent::release()
return curve;
}
const CurveEval *CurveComponent::get_for_read() const
const CurveEval *CurveComponentLegacy::get_for_read() const
{
return curve_;
}
CurveEval *CurveComponent::get_for_write()
CurveEval *CurveComponentLegacy::get_for_write()
{
BLI_assert(this->is_mutable());
if (ownership_ == GeometryOwnershipType::ReadOnly) {
@ -96,17 +89,17 @@ CurveEval *CurveComponent::get_for_write()
return curve_;
}
bool CurveComponent::is_empty() const
bool CurveComponentLegacy::is_empty() const
{
return curve_ == nullptr;
}
bool CurveComponent::owns_direct_data() const
bool CurveComponentLegacy::owns_direct_data() const
{
return ownership_ == GeometryOwnershipType::Owned;
}
void CurveComponent::ensure_owns_direct_data()
void CurveComponentLegacy::ensure_owns_direct_data()
{
BLI_assert(this->is_mutable());
if (ownership_ != GeometryOwnershipType::Owned) {
@ -115,32 +108,13 @@ void CurveComponent::ensure_owns_direct_data()
}
}
const Curve *CurveComponent::get_curve_for_render() const
{
if (curve_ == nullptr) {
return nullptr;
}
if (curve_for_render_ != nullptr) {
return curve_for_render_;
}
std::lock_guard lock{curve_for_render_mutex_};
if (curve_for_render_ != nullptr) {
return curve_for_render_;
}
curve_for_render_ = (Curve *)BKE_id_new_nomain(ID_CU_LEGACY, nullptr);
curve_for_render_->curve_eval = curve_;
return curve_for_render_;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Attribute Access Helper Functions
* \{ */
int CurveComponent::attribute_domain_size(const AttributeDomain domain) const
int CurveComponentLegacy::attribute_domain_size(const AttributeDomain domain) const
{
if (curve_ == nullptr) {
return 0;
@ -334,9 +308,10 @@ static GVArray adapt_curve_domain_spline_to_point(const CurveEval &curve, GVArra
} // namespace blender::bke
GVArray CurveComponent::attribute_try_adapt_domain_impl(const GVArray &varray,
const AttributeDomain from_domain,
const AttributeDomain to_domain) const
GVArray CurveComponentLegacy::attribute_try_adapt_domain_impl(
const GVArray &varray,
const AttributeDomain from_domain,
const AttributeDomain to_domain) const
{
if (!varray) {
return {};
@ -361,14 +336,15 @@ GVArray CurveComponent::attribute_try_adapt_domain_impl(const GVArray &varray,
static CurveEval *get_curve_from_component_for_write(GeometryComponent &component)
{
BLI_assert(component.type() == GEO_COMPONENT_TYPE_CURVE);
CurveComponent &curve_component = static_cast<CurveComponent &>(component);
CurveComponentLegacy &curve_component = static_cast<CurveComponentLegacy &>(component);
return curve_component.get_for_write();
}
static const CurveEval *get_curve_from_component_for_read(const GeometryComponent &component)
{
BLI_assert(component.type() == GEO_COMPONENT_TYPE_CURVE);
const CurveComponent &curve_component = static_cast<const CurveComponent &>(component);
const CurveComponentLegacy &curve_component = static_cast<const CurveComponentLegacy &>(
component);
return curve_component.get_for_read();
}
@ -376,101 +352,6 @@ static const CurveEval *get_curve_from_component_for_read(const GeometryComponen
namespace blender::bke {
/* -------------------------------------------------------------------- */
/** \name Curve Normals Access
* \{ */
static void calculate_bezier_normals(const BezierSpline &spline, MutableSpan<float3> normals)
{
Span<int> offsets = spline.control_point_offsets();
Span<float3> evaluated_normals = spline.evaluated_normals();
for (const int i : IndexRange(spline.size())) {
normals[i] = evaluated_normals[offsets[i]];
}
}
static void calculate_poly_normals(const PolySpline &spline, MutableSpan<float3> normals)
{
normals.copy_from(spline.evaluated_normals());
}
/**
* Because NURBS control points are not necessarily on the path, the normal at the control points
* is not well defined, so create a temporary poly spline to find the normals. This requires extra
* copying currently, but may be more efficient in the future if attributes have some form of CoW.
*/
static void calculate_nurbs_normals(const NURBSpline &spline, MutableSpan<float3> normals)
{
PolySpline poly_spline;
poly_spline.resize(spline.size());
poly_spline.positions().copy_from(spline.positions());
poly_spline.tilts().copy_from(spline.tilts());
normals.copy_from(poly_spline.evaluated_normals());
}
static Array<float3> curve_normal_point_domain(const CurveEval &curve)
{
Span<SplinePtr> splines = curve.splines();
Array<int> offsets = curve.control_point_offsets();
const int total_size = offsets.last();
Array<float3> normals(total_size);
threading::parallel_for(splines.index_range(), 128, [&](IndexRange range) {
for (const int i : range) {
const Spline &spline = *splines[i];
MutableSpan spline_normals{normals.as_mutable_span().slice(offsets[i], spline.size())};
switch (splines[i]->type()) {
case CURVE_TYPE_BEZIER:
calculate_bezier_normals(static_cast<const BezierSpline &>(spline), spline_normals);
break;
case CURVE_TYPE_POLY:
calculate_poly_normals(static_cast<const PolySpline &>(spline), spline_normals);
break;
case CURVE_TYPE_NURBS:
calculate_nurbs_normals(static_cast<const NURBSpline &>(spline), spline_normals);
break;
case CURVE_TYPE_CATMULL_ROM:
BLI_assert_unreachable();
break;
}
}
});
return normals;
}
VArray<float3> curve_normals_varray(const CurveComponent &component, const AttributeDomain domain)
{
const CurveEval *curve = component.get_for_read();
if (curve == nullptr) {
return nullptr;
}
if (domain == ATTR_DOMAIN_POINT) {
const Span<SplinePtr> splines = curve->splines();
/* Use a reference to evaluated normals if possible to avoid an allocation and a copy.
* This is only possible when there is only one poly spline. */
if (splines.size() == 1 && splines.first()->type() == CURVE_TYPE_POLY) {
const PolySpline &spline = static_cast<PolySpline &>(*splines.first());
return VArray<float3>::ForSpan(spline.evaluated_normals());
}
Array<float3> normals = curve_normal_point_domain(*curve);
return VArray<float3>::ForContainer(std::move(normals));
}
if (domain == ATTR_DOMAIN_CURVE) {
Array<float3> point_normals = curve_normal_point_domain(*curve);
VArray<float3> varray = VArray<float3>::ForContainer(std::move(point_normals));
return component.attribute_try_adapt_domain<float3>(
std::move(varray), ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE);
}
return nullptr;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Builtin Spline Attributes
*
@ -1306,7 +1187,8 @@ class DynamicPointAttributeProvider final : public DynamicAttributesProvider {
private:
static constexpr uint64_t supported_types_mask = CD_MASK_PROP_FLOAT | CD_MASK_PROP_FLOAT2 |
CD_MASK_PROP_FLOAT3 | CD_MASK_PROP_INT32 |
CD_MASK_PROP_COLOR | CD_MASK_PROP_BOOL;
CD_MASK_PROP_COLOR | CD_MASK_PROP_BOOL |
CD_MASK_PROP_INT8;
public:
ReadAttributeLookup try_get_for_read(const GeometryComponent &component,
@ -1551,7 +1433,8 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
} // namespace blender::bke
const blender::bke::ComponentAttributeProviders *CurveComponent::get_attribute_providers() const
const blender::bke::ComponentAttributeProviders *CurveComponentLegacy::get_attribute_providers()
const
{
static blender::bke::ComponentAttributeProviders providers =
blender::bke::create_attribute_providers_for_curve();

View File

@ -0,0 +1,521 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BLI_task.hh"
#include "DNA_ID_enums.h"
#include "DNA_curve_types.h"
#include "BKE_attribute_access.hh"
#include "BKE_attribute_math.hh"
#include "BKE_curve.h"
#include "BKE_curves.hh"
#include "BKE_geometry_set.hh"
#include "BKE_lib_id.h"
#include "BKE_spline.hh"
#include "attribute_access_intern.hh"
using blender::fn::GVArray;
/* -------------------------------------------------------------------- */
/** \name Geometry Component Implementation
* \{ */
CurveComponent::CurveComponent() : GeometryComponent(GEO_COMPONENT_TYPE_CURVE)
{
}
CurveComponent::~CurveComponent()
{
this->clear();
}
GeometryComponent *CurveComponent::copy() const
{
CurveComponent *new_component = new CurveComponent();
if (curves_ != nullptr) {
new_component->curves_ = BKE_curves_copy_for_eval(curves_, false);
new_component->ownership_ = GeometryOwnershipType::Owned;
}
return new_component;
}
void CurveComponent::clear()
{
BLI_assert(this->is_mutable());
if (curves_ != nullptr) {
if (ownership_ == GeometryOwnershipType::Owned) {
BKE_id_free(nullptr, curves_);
}
if (curve_for_render_ != nullptr) {
/* The curve created by this component should not have any edit mode data. */
BLI_assert(curve_for_render_->editfont == nullptr && curve_for_render_->editnurb == nullptr);
BKE_id_free(nullptr, curve_for_render_);
curve_for_render_ = nullptr;
}
curves_ = nullptr;
}
}
bool CurveComponent::has_curves() const
{
return curves_ != nullptr;
}
void CurveComponent::replace(Curves *curves, GeometryOwnershipType ownership)
{
BLI_assert(this->is_mutable());
this->clear();
curves_ = curves;
ownership_ = ownership;
}
Curves *CurveComponent::release()
{
BLI_assert(this->is_mutable());
Curves *curves = curves_;
curves_ = nullptr;
return curves;
}
const Curves *CurveComponent::get_for_read() const
{
return curves_;
}
Curves *CurveComponent::get_for_write()
{
BLI_assert(this->is_mutable());
if (ownership_ == GeometryOwnershipType::ReadOnly) {
curves_ = BKE_curves_copy_for_eval(curves_, false);
ownership_ = GeometryOwnershipType::Owned;
}
return curves_;
}
bool CurveComponent::is_empty() const
{
return curves_ == nullptr;
}
bool CurveComponent::owns_direct_data() const
{
return ownership_ == GeometryOwnershipType::Owned;
}
void CurveComponent::ensure_owns_direct_data()
{
BLI_assert(this->is_mutable());
if (ownership_ != GeometryOwnershipType::Owned) {
curves_ = BKE_curves_copy_for_eval(curves_, false);
ownership_ = GeometryOwnershipType::Owned;
}
}
const Curve *CurveComponent::get_curve_for_render() const
{
if (curves_ == nullptr) {
return nullptr;
}
if (curve_for_render_ != nullptr) {
return curve_for_render_;
}
std::lock_guard lock{curve_for_render_mutex_};
if (curve_for_render_ != nullptr) {
return curve_for_render_;
}
curve_for_render_ = (Curve *)BKE_id_new_nomain(ID_CU_LEGACY, nullptr);
curve_for_render_->curve_eval = curves_to_curve_eval(*curves_).release();
return curve_for_render_;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Curve Normals Access
* \{ */
namespace blender::bke {
static void calculate_bezier_normals(const BezierSpline &spline, MutableSpan<float3> normals)
{
Span<int> offsets = spline.control_point_offsets();
Span<float3> evaluated_normals = spline.evaluated_normals();
for (const int i : IndexRange(spline.size())) {
normals[i] = evaluated_normals[offsets[i]];
}
}
static void calculate_poly_normals(const PolySpline &spline, MutableSpan<float3> normals)
{
normals.copy_from(spline.evaluated_normals());
}
/**
* Because NURBS control points are not necessarily on the path, the normal at the control points
* is not well defined, so create a temporary poly spline to find the normals. This requires extra
* copying currently, but may be more efficient in the future if attributes have some form of CoW.
*/
static void calculate_nurbs_normals(const NURBSpline &spline, MutableSpan<float3> normals)
{
PolySpline poly_spline;
poly_spline.resize(spline.size());
poly_spline.positions().copy_from(spline.positions());
poly_spline.tilts().copy_from(spline.tilts());
normals.copy_from(poly_spline.evaluated_normals());
}
static Array<float3> curve_normal_point_domain(const CurveEval &curve)
{
Span<SplinePtr> splines = curve.splines();
Array<int> offsets = curve.control_point_offsets();
const int total_size = offsets.last();
Array<float3> normals(total_size);
threading::parallel_for(splines.index_range(), 128, [&](IndexRange range) {
for (const int i : range) {
const Spline &spline = *splines[i];
MutableSpan spline_normals{normals.as_mutable_span().slice(offsets[i], spline.size())};
switch (splines[i]->type()) {
case CURVE_TYPE_BEZIER:
calculate_bezier_normals(static_cast<const BezierSpline &>(spline), spline_normals);
break;
case CURVE_TYPE_POLY:
calculate_poly_normals(static_cast<const PolySpline &>(spline), spline_normals);
break;
case CURVE_TYPE_NURBS:
calculate_nurbs_normals(static_cast<const NURBSpline &>(spline), spline_normals);
break;
case CURVE_TYPE_CATMULL_ROM:
BLI_assert_unreachable();
break;
}
}
});
return normals;
}
VArray<float3> curve_normals_varray(const CurveComponent &component, const AttributeDomain domain)
{
if (component.is_empty()) {
return nullptr;
}
const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*component.get_for_read());
if (domain == ATTR_DOMAIN_POINT) {
Array<float3> normals = curve_normal_point_domain(*curve);
return VArray<float3>::ForContainer(std::move(normals));
}
if (domain == ATTR_DOMAIN_CURVE) {
Array<float3> point_normals = curve_normal_point_domain(*curve);
VArray<float3> varray = VArray<float3>::ForContainer(std::move(point_normals));
return component.attribute_try_adapt_domain<float3>(
std::move(varray), ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE);
}
return nullptr;
}
} // namespace blender::bke
/** \} */
/* -------------------------------------------------------------------- */
/** \name Attribute Access Helper Functions
* \{ */
int CurveComponent::attribute_domain_size(const AttributeDomain domain) const
{
if (curves_ == nullptr) {
return 0;
}
const blender::bke::CurvesGeometry &geometry = blender::bke::CurvesGeometry::wrap(
curves_->geometry);
if (domain == ATTR_DOMAIN_POINT) {
return geometry.points_size();
}
if (domain == ATTR_DOMAIN_CURVE) {
return geometry.curves_size();
}
return 0;
}
GVArray CurveComponent::attribute_try_adapt_domain_impl(const GVArray &varray,
const AttributeDomain from_domain,
const AttributeDomain to_domain) const
{
return blender::bke::CurvesGeometry::wrap(curves_->geometry)
.adapt_domain(varray, from_domain, to_domain);
}
static Curves *get_curves_from_component_for_write(GeometryComponent &component)
{
BLI_assert(component.type() == GEO_COMPONENT_TYPE_CURVE);
CurveComponent &curve_component = static_cast<CurveComponent &>(component);
return curve_component.get_for_write();
}
static const Curves *get_curves_from_component_for_read(const GeometryComponent &component)
{
BLI_assert(component.type() == GEO_COMPONENT_TYPE_CURVE);
const CurveComponent &curve_component = static_cast<const CurveComponent &>(component);
return curve_component.get_for_read();
}
static void tag_component_topology_changed(GeometryComponent &component)
{
Curves *curves = get_curves_from_component_for_write(component);
if (curves) {
blender::bke::CurvesGeometry::wrap(curves->geometry).tag_topology_changed();
}
}
static void tag_component_positions_changed(GeometryComponent &component)
{
Curves *curves = get_curves_from_component_for_write(component);
if (curves) {
blender::bke::CurvesGeometry::wrap(curves->geometry).tag_positions_changed();
}
}
static void tag_component_normals_changed(GeometryComponent &component)
{
Curves *curves = get_curves_from_component_for_write(component);
if (curves) {
blender::bke::CurvesGeometry::wrap(curves->geometry).tag_normals_changed();
}
}
/** \} */
namespace blender::bke {
/* -------------------------------------------------------------------- */
/** \name Attribute Provider Declaration
* \{ */
/**
* In this function all the attribute providers for a curves component are created.
* Most data in this function is statically allocated, because it does not change over time.
*/
static ComponentAttributeProviders create_attribute_providers_for_curve()
{
static CustomDataAccessInfo curve_access = {
[](GeometryComponent &component) -> CustomData * {
Curves *curves = get_curves_from_component_for_write(component);
return curves ? &curves->geometry.curve_data : nullptr;
},
[](const GeometryComponent &component) -> const CustomData * {
const Curves *curves = get_curves_from_component_for_read(component);
return curves ? &curves->geometry.curve_data : nullptr;
},
[](GeometryComponent &component) {
Curves *curves = get_curves_from_component_for_write(component);
if (curves) {
blender::bke::CurvesGeometry::wrap(curves->geometry).update_customdata_pointers();
}
}};
static CustomDataAccessInfo point_access = {
[](GeometryComponent &component) -> CustomData * {
Curves *curves = get_curves_from_component_for_write(component);
return curves ? &curves->geometry.point_data : nullptr;
},
[](const GeometryComponent &component) -> const CustomData * {
const Curves *curves = get_curves_from_component_for_read(component);
return curves ? &curves->geometry.point_data : nullptr;
},
[](GeometryComponent &component) {
Curves *curves = get_curves_from_component_for_write(component);
if (curves) {
blender::bke::CurvesGeometry::wrap(curves->geometry).update_customdata_pointers();
}
}};
static BuiltinCustomDataLayerProvider position("position",
ATTR_DOMAIN_POINT,
CD_PROP_FLOAT3,
CD_PROP_FLOAT3,
BuiltinAttributeProvider::NonCreatable,
BuiltinAttributeProvider::Writable,
BuiltinAttributeProvider::NonDeletable,
point_access,
make_array_read_attribute<float3>,
make_array_write_attribute<float3>,
tag_component_positions_changed);
static BuiltinCustomDataLayerProvider radius("radius",
ATTR_DOMAIN_POINT,
CD_PROP_FLOAT,
CD_PROP_FLOAT,
BuiltinAttributeProvider::Creatable,
BuiltinAttributeProvider::Writable,
BuiltinAttributeProvider::Deletable,
point_access,
make_array_read_attribute<float>,
make_array_write_attribute<float>,
tag_component_normals_changed);
static BuiltinCustomDataLayerProvider id("id",
ATTR_DOMAIN_POINT,
CD_PROP_INT32,
CD_PROP_INT32,
BuiltinAttributeProvider::Creatable,
BuiltinAttributeProvider::Writable,
BuiltinAttributeProvider::Deletable,
point_access,
make_array_read_attribute<int>,
make_array_write_attribute<int>,
nullptr);
static BuiltinCustomDataLayerProvider tilt("tilt",
ATTR_DOMAIN_POINT,
CD_PROP_FLOAT,
CD_PROP_FLOAT,
BuiltinAttributeProvider::Creatable,
BuiltinAttributeProvider::Writable,
BuiltinAttributeProvider::Deletable,
point_access,
make_array_read_attribute<float>,
make_array_write_attribute<float>,
tag_component_normals_changed);
static BuiltinCustomDataLayerProvider handle_right("handle_right",
ATTR_DOMAIN_POINT,
CD_PROP_FLOAT3,
CD_PROP_FLOAT3,
BuiltinAttributeProvider::Creatable,
BuiltinAttributeProvider::Writable,
BuiltinAttributeProvider::Deletable,
point_access,
make_array_read_attribute<float3>,
make_array_write_attribute<float3>,
tag_component_positions_changed);
static BuiltinCustomDataLayerProvider handle_left("handle_left",
ATTR_DOMAIN_POINT,
CD_PROP_FLOAT3,
CD_PROP_FLOAT3,
BuiltinAttributeProvider::Creatable,
BuiltinAttributeProvider::Writable,
BuiltinAttributeProvider::Deletable,
point_access,
make_array_read_attribute<float3>,
make_array_write_attribute<float3>,
tag_component_positions_changed);
static BuiltinCustomDataLayerProvider handle_type_right("handle_type_right",
ATTR_DOMAIN_POINT,
CD_PROP_INT8,
CD_PROP_INT8,
BuiltinAttributeProvider::Creatable,
BuiltinAttributeProvider::Writable,
BuiltinAttributeProvider::Deletable,
point_access,
make_array_read_attribute<int8_t>,
make_array_write_attribute<int8_t>,
tag_component_topology_changed);
static BuiltinCustomDataLayerProvider handle_type_left("handle_type_left",
ATTR_DOMAIN_POINT,
CD_PROP_INT8,
CD_PROP_INT8,
BuiltinAttributeProvider::Creatable,
BuiltinAttributeProvider::Writable,
BuiltinAttributeProvider::Deletable,
point_access,
make_array_read_attribute<int8_t>,
make_array_write_attribute<int8_t>,
tag_component_topology_changed);
static BuiltinCustomDataLayerProvider nurbs_weight("nurbs_weight",
ATTR_DOMAIN_POINT,
CD_PROP_FLOAT,
CD_PROP_FLOAT,
BuiltinAttributeProvider::Creatable,
BuiltinAttributeProvider::Writable,
BuiltinAttributeProvider::Deletable,
point_access,
make_array_read_attribute<float>,
make_array_write_attribute<float>,
tag_component_positions_changed);
static BuiltinCustomDataLayerProvider nurbs_order("nurbs_order",
ATTR_DOMAIN_CURVE,
CD_PROP_INT32,
CD_PROP_INT32,
BuiltinAttributeProvider::Creatable,
BuiltinAttributeProvider::Writable,
BuiltinAttributeProvider::Deletable,
curve_access,
make_array_read_attribute<int>,
make_array_write_attribute<int>,
tag_component_topology_changed);
static BuiltinCustomDataLayerProvider nurbs_knots_mode("knots_mode",
ATTR_DOMAIN_CURVE,
CD_PROP_INT8,
CD_PROP_INT8,
BuiltinAttributeProvider::Creatable,
BuiltinAttributeProvider::Writable,
BuiltinAttributeProvider::Deletable,
curve_access,
make_array_read_attribute<int8_t>,
make_array_write_attribute<int8_t>,
tag_component_topology_changed);
static BuiltinCustomDataLayerProvider resolution("resolution",
ATTR_DOMAIN_CURVE,
CD_PROP_INT32,
CD_PROP_INT32,
BuiltinAttributeProvider::Creatable,
BuiltinAttributeProvider::Writable,
BuiltinAttributeProvider::Deletable,
curve_access,
make_array_read_attribute<int>,
make_array_write_attribute<int>,
tag_component_positions_changed);
static BuiltinCustomDataLayerProvider cyclic("cyclic",
ATTR_DOMAIN_CURVE,
CD_PROP_BOOL,
CD_PROP_BOOL,
BuiltinAttributeProvider::Creatable,
BuiltinAttributeProvider::Writable,
BuiltinAttributeProvider::Deletable,
curve_access,
make_array_read_attribute<bool>,
make_array_write_attribute<bool>,
tag_component_topology_changed);
static CustomDataAttributeProvider curve_custom_data(ATTR_DOMAIN_CURVE, curve_access);
static CustomDataAttributeProvider point_custom_data(ATTR_DOMAIN_POINT, point_access);
return ComponentAttributeProviders({&position,
&radius,
&id,
&tilt,
&handle_right,
&handle_left,
&handle_type_right,
&handle_type_left,
&nurbs_order,
&nurbs_weight,
&resolution,
&cyclic},
{&curve_custom_data, &point_custom_data});
}
/** \} */
} // namespace blender::bke
const blender::bke::ComponentAttributeProviders *CurveComponent::get_attribute_providers() const
{
static blender::bke::ComponentAttributeProviders providers =
blender::bke::create_attribute_providers_for_curve();
return &providers;
}

View File

@ -7,6 +7,7 @@
#include "BKE_attribute.h"
#include "BKE_attribute_access.hh"
#include "BKE_curves.hh"
#include "BKE_geometry_set.hh"
#include "BKE_lib_id.h"
#include "BKE_mesh.h"
@ -186,8 +187,9 @@ bool GeometrySet::compute_boundbox_without_instances(float3 *r_min, float3 *r_ma
if (volume != nullptr) {
have_minmax |= BKE_volume_min_max(volume, *r_min, *r_max);
}
const CurveEval *curve = this->get_curve_for_read();
if (curve != nullptr) {
const Curves *curves = this->get_curve_for_read();
if (curves != nullptr) {
std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*curves);
/* Using the evaluated positions is somewhat arbitrary, but it is probably expected. */
have_minmax |= curve->bounds_min_max(*r_min, *r_max, true);
}
@ -258,7 +260,7 @@ const Volume *GeometrySet::get_volume_for_read() const
return (component == nullptr) ? nullptr : component->get_for_read();
}
const CurveEval *GeometrySet::get_curve_for_read() const
const Curves *GeometrySet::get_curve_for_read() const
{
const CurveComponent *component = this->get_component_for_read<CurveComponent>();
return (component == nullptr) ? nullptr : component->get_for_read();
@ -285,7 +287,7 @@ bool GeometrySet::has_volume() const
bool GeometrySet::has_curve() const
{
const CurveComponent *component = this->get_component_for_read<CurveComponent>();
return component != nullptr && component->has_curve();
return component != nullptr && component->has_curves();
}
bool GeometrySet::has_realized_data() const
@ -327,12 +329,12 @@ GeometrySet GeometrySet::create_with_pointcloud(PointCloud *pointcloud,
return geometry_set;
}
GeometrySet GeometrySet::create_with_curve(CurveEval *curve, GeometryOwnershipType ownership)
GeometrySet GeometrySet::create_with_curve(Curves *curves, GeometryOwnershipType ownership)
{
GeometrySet geometry_set;
if (curve != nullptr) {
if (curves != nullptr) {
CurveComponent &component = geometry_set.get_component_for_write<CurveComponent>();
component.replace(curve, ownership);
component.replace(curves, ownership);
}
return geometry_set;
}
@ -351,18 +353,18 @@ void GeometrySet::replace_mesh(Mesh *mesh, GeometryOwnershipType ownership)
component.replace(mesh, ownership);
}
void GeometrySet::replace_curve(CurveEval *curve, GeometryOwnershipType ownership)
void GeometrySet::replace_curve(Curves *curves, GeometryOwnershipType ownership)
{
if (curve == nullptr) {
if (curves == nullptr) {
this->remove<CurveComponent>();
return;
}
if (curve == this->get_curve_for_read()) {
if (curves == this->get_curve_for_read()) {
return;
}
this->remove<CurveComponent>();
CurveComponent &component = this->get_component_for_write<CurveComponent>();
component.replace(curve, ownership);
component.replace(curves, ownership);
}
void GeometrySet::replace_pointcloud(PointCloud *pointcloud, GeometryOwnershipType ownership)
@ -411,7 +413,7 @@ Volume *GeometrySet::get_volume_for_write()
return component == nullptr ? nullptr : component->get_for_write();
}
CurveEval *GeometrySet::get_curve_for_write()
Curves *GeometrySet::get_curve_for_write()
{
CurveComponent *component = this->get_component_ptr<CurveComponent>();
return component == nullptr ? nullptr : component->get_for_write();

View File

@ -953,7 +953,7 @@ static const Mesh *get_evaluated_mesh_from_object(const Object *object)
return nullptr;
}
static const CurveEval *get_evaluated_curve_from_object(const Object *object)
static const Curves *get_evaluated_curves_from_object(const Object *object)
{
GeometrySet *geometry_set_eval = object->runtime.geometry_set_eval;
if (geometry_set_eval) {
@ -968,8 +968,9 @@ static Mesh *mesh_new_from_evaluated_curve_type_object(const Object *evaluated_o
if (mesh) {
return BKE_mesh_copy_for_eval(mesh, false);
}
const CurveEval *curve = get_evaluated_curve_from_object(evaluated_object);
if (curve) {
const Curves *curves = get_evaluated_curves_from_object(evaluated_object);
if (curves) {
std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*curves);
return blender::bke::curve_to_wire_mesh(*curve);
}
return nullptr;

View File

@ -13,6 +13,7 @@
#include "BLI_task.hh"
#include "BKE_collection.h"
#include "BKE_curves.hh"
#include "BKE_geometry_set_instances.hh"
#include "BKE_material.h"
#include "BKE_mesh.h"
@ -118,7 +119,7 @@ struct RealizeMeshTask {
};
struct RealizeCurveInfo {
const CurveEval *curve = nullptr;
const Curves *curves;
/**
* Matches the order in #AllCurvesInfo.attributes. For point attributes, the `std::optional`
* will be empty.
@ -163,7 +164,7 @@ struct AllCurvesInfo {
/** Ordering of all attributes that are propagated to the output curve generically. */
OrderedAttributes attributes;
/** Ordering of the original curves that are joined. */
VectorSet<const CurveEval *> order;
VectorSet<const Curves *> order;
/** Preprocessed data about every original curve. This is ordered by #order. */
Array<RealizeCurveInfo> realize_info;
bool create_id_attribute = false;
@ -443,16 +444,16 @@ static void gather_realize_tasks_recursive(GatherTasksInfo &gather_info,
}
case GEO_COMPONENT_TYPE_CURVE: {
const CurveComponent &curve_component = *static_cast<const CurveComponent *>(component);
const CurveEval *curve = curve_component.get_for_read();
if (curve != nullptr && !curve->splines().is_empty()) {
const int curve_index = gather_info.curves.order.index_of(curve);
const Curves *curves = curve_component.get_for_read();
if (curves != nullptr && curves->geometry.curve_size > 0) {
const int curve_index = gather_info.curves.order.index_of(curves);
const RealizeCurveInfo &curve_info = gather_info.curves.realize_info[curve_index];
gather_info.r_tasks.curve_tasks.append({gather_info.r_offsets.spline_offset,
&curve_info,
base_transform,
base_instance_context.curves,
base_instance_context.id});
gather_info.r_offsets.spline_offset += curve->splines().size();
gather_info.r_offsets.spline_offset += curves->geometry.curve_size;
}
break;
}
@ -1038,11 +1039,11 @@ static OrderedAttributes gather_generic_curve_attributes_to_propagate(
}
static void gather_curves_to_realize(const GeometrySet &geometry_set,
VectorSet<const CurveEval *> &r_curves)
VectorSet<const Curves *> &r_curves)
{
if (const CurveEval *curve = geometry_set.get_curve_for_read()) {
if (!curve->splines().is_empty()) {
r_curves.add(curve);
if (const Curves *curves = geometry_set.get_curve_for_read()) {
if (curves->geometry.curve_size != 0) {
r_curves.add(curves);
}
}
if (const InstancesComponent *instances =
@ -1064,12 +1065,12 @@ static AllCurvesInfo preprocess_curves(const GeometrySet &geometry_set,
info.realize_info.reinitialize(info.order.size());
for (const int curve_index : info.realize_info.index_range()) {
RealizeCurveInfo &curve_info = info.realize_info[curve_index];
const CurveEval *curve = info.order[curve_index];
curve_info.curve = curve;
const Curves *curves = info.order[curve_index];
curve_info.curves = curves;
/* Access attributes. */
CurveComponent component;
component.replace(const_cast<CurveEval *>(curve), GeometryOwnershipType::ReadOnly);
component.replace(const_cast<Curves *>(curves), GeometryOwnershipType::ReadOnly);
curve_info.spline_attributes.reinitialize(info.attributes.size());
for (const int attribute_index : info.attributes.index_range()) {
const AttributeDomain domain = info.attributes.kinds[attribute_index].domain;
@ -1095,9 +1096,9 @@ static void execute_realize_curve_task(const RealizeInstancesOptions &options,
MutableSpan<GMutableSpan> dst_spline_attributes)
{
const RealizeCurveInfo &curve_info = *task.curve_info;
const CurveEval &curve = *curve_info.curve;
const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*curve_info.curves);
const Span<SplinePtr> src_splines = curve.splines();
const Span<SplinePtr> src_splines = curve->splines();
/* Initialize point attributes. */
threading::parallel_for(src_splines.index_range(), 100, [&](const IndexRange src_spline_range) {
@ -1206,12 +1207,12 @@ static void execute_realize_curve_tasks(const RealizeInstancesOptions &options,
}
const RealizeCurveTask &last_task = tasks.last();
const CurveEval &last_curve = *last_task.curve_info->curve;
const int tot_splines = last_task.start_spline_index + last_curve.splines().size();
const Curves &last_curves = *last_task.curve_info->curves;
const int tot_splines = last_task.start_spline_index + last_curves.geometry.curve_size;
Array<SplinePtr> dst_splines(tot_splines);
CurveEval *dst_curve = new CurveEval();
std::unique_ptr<CurveEval> dst_curve = std::make_unique<CurveEval>();
dst_curve->attributes.reallocate(tot_splines);
CustomDataAttributes &spline_attributes = dst_curve->attributes;
@ -1242,7 +1243,7 @@ static void execute_realize_curve_tasks(const RealizeInstancesOptions &options,
dst_curve->add_splines(dst_splines);
CurveComponent &dst_component = r_realized_geometry.get_component_for_write<CurveComponent>();
dst_component.replace(dst_curve);
dst_component.replace(curve_eval_to_curves(*dst_curve));
}
/** \} */

View File

@ -143,9 +143,9 @@ static void node_geo_exec(GeoNodeExecParams params)
}
const CurveComponent &curve_component = *geometry_set.get_component_for_read<CurveComponent>();
const CurveEval &curve = *curve_component.get_for_read();
const Span<SplinePtr> splines = curve.splines();
curve.assert_valid_point_attributes();
const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*curve_component.get_for_read());
const Span<SplinePtr> splines = curve->splines();
curve->assert_valid_point_attributes();
evaluate_splines(splines);
@ -167,9 +167,9 @@ static void node_geo_exec(GeoNodeExecParams params)
end_result.get_component_for_write<PointCloudComponent>();
CurveToPointsResults start_attributes = curve_to_points_create_result_attributes(
start_point_component, curve);
start_point_component, *curve);
CurveToPointsResults end_attributes = curve_to_points_create_result_attributes(
end_point_component, curve);
end_point_component, *curve);
copy_endpoint_attributes(splines, offsets.as_span(), start_attributes, end_attributes);
copy_spline_domain_attributes(curve_component, offsets.as_span(), start_point_component);

View File

@ -26,8 +26,8 @@ static void node_geo_exec(GeoNodeExecParams params)
/* Retrieve data for write access so we can avoid new allocations for the reversed data. */
CurveComponent &curve_component = geometry_set.get_component_for_write<CurveComponent>();
CurveEval &curve = *curve_component.get_for_write();
MutableSpan<SplinePtr> splines = curve.splines();
std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*curve_component.get_for_read());
MutableSpan<SplinePtr> splines = curve->splines();
const std::string selection_name = params.extract_input<std::string>("Selection");
VArray<bool> selection = curve_component.attribute_get_for_read(
@ -41,6 +41,8 @@ static void node_geo_exec(GeoNodeExecParams params)
}
});
geometry_set.replace_curve(curve_eval_to_curves(*curve));
params.set_output("Curve", geometry_set);
}

View File

@ -89,9 +89,8 @@ static void node_geo_exec(GeoNodeExecParams params)
geometry_set = geometry::realize_instances_legacy(geometry_set);
CurveComponent &curve_component = geometry_set.get_component_for_write<CurveComponent>();
const CurveEval *curve = curve_component.get_for_read();
if (curve != nullptr) {
if (curve_component.has_curves()) {
const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*curve_component.get_for_read());
const std::string selection_name = params.extract_input<std::string>("Selection");
OutputAttribute_Typed<bool> selection =
curve_component.attribute_try_get_for_output_only<bool>(selection_name, ATTR_DOMAIN_POINT);

View File

@ -63,8 +63,8 @@ static void node_geo_exec(GeoNodeExecParams params)
/* Retrieve data for write access so we can avoid new allocations for the handles data. */
CurveComponent &curve_component = geometry_set.get_component_for_write<CurveComponent>();
CurveEval &curve = *curve_component.get_for_write();
MutableSpan<SplinePtr> splines = curve.splines();
std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*curve_component.get_for_read());
MutableSpan<SplinePtr> splines = curve->splines();
const std::string selection_name = params.extract_input<std::string>("Selection");
VArray<bool> selection = curve_component.attribute_get_for_read(
@ -101,6 +101,8 @@ static void node_geo_exec(GeoNodeExecParams params)
bezier_spline.mark_cache_invalid();
}
geometry_set.replace_curve(curve_eval_to_curves(*curve));
if (!has_bezier_spline) {
params.error_message_add(NodeWarningType::Info, TIP_("No Bezier splines in input curve"));
}

View File

@ -242,34 +242,34 @@ static void node_geo_exec(GeoNodeExecParams params)
}
const CurveComponent *curve_component = geometry_set.get_component_for_read<CurveComponent>();
const CurveEval &curve = *curve_component->get_for_read();
const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*curve_component->get_for_read());
const std::string selection_name = params.extract_input<std::string>("Selection");
VArray<bool> selection = curve_component->attribute_get_for_read(
selection_name, ATTR_DOMAIN_CURVE, true);
std::unique_ptr<CurveEval> new_curve = std::make_unique<CurveEval>();
for (const int i : curve.splines().index_range()) {
for (const int i : curve->splines().index_range()) {
if (selection[i]) {
switch (output_type) {
case GEO_NODE_SPLINE_TYPE_POLY:
new_curve->add_spline(convert_to_poly_spline(*curve.splines()[i]));
new_curve->add_spline(convert_to_poly_spline(*curve->splines()[i]));
break;
case GEO_NODE_SPLINE_TYPE_BEZIER:
new_curve->add_spline(convert_to_bezier(*curve.splines()[i], params));
new_curve->add_spline(convert_to_bezier(*curve->splines()[i], params));
break;
case GEO_NODE_SPLINE_TYPE_NURBS:
new_curve->add_spline(convert_to_nurbs(*curve.splines()[i]));
new_curve->add_spline(convert_to_nurbs(*curve->splines()[i]));
break;
}
}
else {
new_curve->add_spline(curve.splines()[i]->copy());
new_curve->add_spline(curve->splines()[i]->copy());
}
}
new_curve->attributes = curve.attributes;
params.set_output("Curve", GeometrySet::create_with_curve(new_curve.release()));
new_curve->attributes = curve->attributes;
params.set_output("Curve", GeometrySet::create_with_curve(curve_eval_to_curves(*new_curve)));
}
} // namespace blender::nodes::node_geo_legacy_curve_spline_type_cc

View File

@ -354,9 +354,11 @@ static void node_geo_exec(GeoNodeExecParams params)
return;
}
std::unique_ptr<CurveEval> output_curve = subdivide_curve(*component.get_for_read(), cuts);
std::unique_ptr<CurveEval> output_curve = subdivide_curve(
*curves_to_curve_eval(*component.get_for_read()), cuts);
params.set_output("Geometry", GeometrySet::create_with_curve(output_curve.release()));
params.set_output("Geometry",
GeometrySet::create_with_curve(curve_eval_to_curves(*output_curve)));
}
} // namespace blender::nodes::node_geo_legacy_curve_subdivide_cc

View File

@ -289,13 +289,13 @@ static void node_geo_exec(GeoNodeExecParams params)
}
const CurveComponent &curve_component = *geometry_set.get_component_for_read<CurveComponent>();
const CurveEval &curve = *curve_component.get_for_read();
const Span<SplinePtr> splines = curve.splines();
curve.assert_valid_point_attributes();
const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*curve_component.get_for_read());
const Span<SplinePtr> splines = curve->splines();
curve->assert_valid_point_attributes();
evaluate_splines(splines);
const Array<int> offsets = calculate_spline_point_offsets(params, mode, curve, splines);
const Array<int> offsets = calculate_spline_point_offsets(params, mode, *curve, splines);
const int total_size = offsets.last();
if (total_size == 0) {
params.set_output("Geometry", GeometrySet());
@ -306,7 +306,7 @@ static void node_geo_exec(GeoNodeExecParams params)
PointCloudComponent &point_component = result.get_component_for_write<PointCloudComponent>();
CurveToPointsResults new_attributes = curve_to_points_create_result_attributes(point_component,
curve);
*curve);
switch (mode) {
case GEO_NODE_CURVE_RESAMPLE_COUNT:
case GEO_NODE_CURVE_RESAMPLE_LENGTH:

View File

@ -235,9 +235,9 @@ static void delete_curve_selection(const CurveComponent &in_component,
const bool invert)
{
std::unique_ptr<CurveEval> r_curve = curve_delete(
*in_component.get_for_read(), selection_name, invert);
*curves_to_curve_eval(*in_component.get_for_read()), selection_name, invert);
if (r_curve) {
r_component.replace(r_curve.release());
r_component.replace(curve_eval_to_curves(*r_curve));
}
else {
r_component.clear();

View File

@ -48,7 +48,7 @@ static void node_geo_exec(GeoNodeExecParams params)
std::unique_ptr<CurveEval> curve = geometry::mesh_to_curve_convert(
component, IndexMask(selected_edge_indices));
params.set_output("Curve", GeometrySet::create_with_curve(curve.release()));
params.set_output("Curve", GeometrySet::create_with_curve(curve_eval_to_curves(*curve)));
}
} // namespace blender::nodes::node_geo_legacy_mesh_to_curve_cc

View File

@ -162,8 +162,9 @@ static Mesh *compute_hull(const GeometrySet &geometry_set)
}
if (geometry_set.has_curve()) {
const CurveEval &curve = *geometry_set.get_curve_for_read();
for (const SplinePtr &spline : curve.splines()) {
const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(
*geometry_set.get_curve_for_read());
for (const SplinePtr &spline : curve->splines()) {
positions_span = spline->evaluated_positions();
total_size += positions_span.size();
count++;
@ -202,8 +203,9 @@ static Mesh *compute_hull(const GeometrySet &geometry_set)
}
if (geometry_set.has_curve()) {
const CurveEval &curve = *geometry_set.get_curve_for_read();
for (const SplinePtr &spline : curve.splines()) {
const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(
*geometry_set.get_curve_for_read());
for (const SplinePtr &spline : curve->splines()) {
Span<float3> array = spline->evaluated_positions();
positions.as_mutable_span().slice(offset, array.size()).copy_from(array);
offset += array.size();
@ -273,7 +275,7 @@ static Mesh *convex_hull_from_instances(const GeometrySet &geometry_set)
read_positions(*set.get_component_for_read<MeshComponent>(), transforms, &coords);
}
if (set.has_curve()) {
read_curve_positions(*set.get_curve_for_read(), transforms, &coords);
read_curve_positions(*curves_to_curve_eval(*set.get_curve_for_read()), transforms, &coords);
}
}
return hull_from_bullet(nullptr, coords);

View File

@ -59,10 +59,13 @@ class EndpointFieldInput final : public GeometryFieldInput {
}
const CurveComponent &curve_component = static_cast<const CurveComponent &>(component);
const CurveEval *curve = curve_component.get_for_read();
if (!curve_component.has_curves()) {
return nullptr;
}
const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*curve_component.get_for_read());
Array<int> control_point_offsets = curve->control_point_offsets();
if (curve == nullptr || control_point_offsets.last() == 0) {
return nullptr;
}

View File

@ -117,8 +117,9 @@ static void curve_fill_calculate(GeometrySet &geometry_set, const GeometryNodeCu
return;
}
const CurveEval &curve = *geometry_set.get_curve_for_read();
if (curve.splines().is_empty()) {
const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(
*geometry_set.get_curve_for_read());
if (curve->splines().is_empty()) {
geometry_set.replace_curve(nullptr);
return;
}
@ -127,7 +128,7 @@ static void curve_fill_calculate(GeometrySet &geometry_set, const GeometryNodeCu
CDT_CONSTRAINTS_VALID_BMESH_WITH_HOLES :
CDT_INSIDE_WITH_HOLES;
const blender::meshintersect::CDT_result<double> results = do_cdt(curve, output_type);
const blender::meshintersect::CDT_result<double> results = do_cdt(*curve, output_type);
Mesh *mesh = cdt_to_mesh(results);
geometry_set.replace_mesh(mesh);

View File

@ -603,10 +603,10 @@ static void calculate_curve_fillet(GeometrySet &geometry_set,
fillet_param.limit_radius = limit_radius;
const CurveEval &input_curve = *geometry_set.get_curve_for_read();
std::unique_ptr<CurveEval> output_curve = fillet_curve(input_curve, fillet_param);
const std::unique_ptr<CurveEval> input_curve = curves_to_curve_eval(*component.get_for_read());
std::unique_ptr<CurveEval> output_curve = fillet_curve(*input_curve, fillet_param);
geometry_set.replace_curve(output_curve.release());
geometry_set.replace_curve(curve_eval_to_curves(*output_curve));
}
static void node_geo_exec(GeoNodeExecParams params)

View File

@ -92,14 +92,14 @@ class HandleTypeFieldInput final : public GeometryFieldInput {
}
const CurveComponent &curve_component = static_cast<const CurveComponent &>(component);
const CurveEval *curve = curve_component.get_for_read();
const Curves *curve = curve_component.get_for_read();
if (curve == nullptr) {
return {};
}
if (domain == ATTR_DOMAIN_POINT) {
Array<bool> selection(mask.min_array_size());
select_by_handle_type(*curve, type_, mode_, selection);
select_by_handle_type(*curves_to_curve_eval(*curve), type_, mode_, selection);
return VArray<bool>::ForContainer(std::move(selection));
}
return {};

View File

@ -18,9 +18,9 @@ static void node_geo_exec(GeoNodeExecParams params)
params.set_default_remaining_outputs();
return;
}
const CurveEval &curve = *curve_set.get_curve_for_read();
const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*curve_set.get_curve_for_read());
float length = 0.0f;
for (const SplinePtr &spline : curve.splines()) {
for (const SplinePtr &spline : curve->splines()) {
length += spline->length();
}
params.set_output("Length", length);

View File

@ -335,7 +335,7 @@ static void node_geo_exec(GeoNodeExecParams params)
r_center,
r_normal,
r_radius);
params.set_output("Curve", GeometrySet::create_with_curve(curve.release()));
params.set_output("Curve", GeometrySet::create_with_curve(curve_eval_to_curves(*curve)));
params.set_output("Center", r_center);
params.set_output("Normal", r_normal);
params.set_output("Radius", r_radius);
@ -350,7 +350,7 @@ static void node_geo_exec(GeoNodeExecParams params)
params.extract_input<bool>("Connect Center"),
params.extract_input<bool>("Invert Arc"));
params.set_output("Curve", GeometrySet::create_with_curve(curve.release()));
params.set_output("Curve", GeometrySet::create_with_curve(curve_eval_to_curves(*curve)));
break;
}
}

View File

@ -113,7 +113,7 @@ static void node_geo_exec(GeoNodeExecParams params)
params.extract_input<float3>("End Handle"),
std::max(params.extract_input<int>("Resolution"), 1),
mode);
params.set_output("Curve", GeometrySet::create_with_curve(curve.release()));
params.set_output("Curve", GeometrySet::create_with_curve(curve_eval_to_curves(*curve)));
}
} // namespace blender::nodes::node_geo_curve_primitive_bezier_segment_cc

View File

@ -203,7 +203,7 @@ static void node_geo_exec(GeoNodeExecParams params)
}
if (curve) {
params.set_output("Curve", GeometrySet::create_with_curve(curve.release()));
params.set_output("Curve", GeometrySet::create_with_curve(curve_eval_to_curves(*curve)));
}
else {
params.set_default_remaining_outputs();

View File

@ -111,7 +111,7 @@ static void node_geo_exec(GeoNodeExecParams params)
params.extract_input<float>("Length"));
}
params.set_output("Curve", GeometrySet::create_with_curve(curve.release()));
params.set_output("Curve", GeometrySet::create_with_curve(curve_eval_to_curves(*curve)));
}
} // namespace blender::nodes::node_geo_curve_primitive_line_cc

View File

@ -61,7 +61,7 @@ static void node_geo_exec(GeoNodeExecParams params)
params.extract_input<float3>("Middle"),
params.extract_input<float3>("End"),
std::max(params.extract_input<int>("Resolution"), 3));
params.set_output("Curve", GeometrySet::create_with_curve(curve.release()));
params.set_output("Curve", GeometrySet::create_with_curve(curve_eval_to_curves(*curve)));
}
} // namespace blender::nodes::node_geo_curve_primitive_quadratic_bezier_cc

View File

@ -264,7 +264,7 @@ static void node_geo_exec(GeoNodeExecParams params)
curve->add_spline(std::move(spline));
curve->attributes.reallocate(curve->splines().size());
params.set_output("Curve", GeometrySet::create_with_curve(curve.release()));
params.set_output("Curve", GeometrySet::create_with_curve(curve_eval_to_curves(*curve)));
}
} // namespace blender::nodes::node_geo_curve_primitive_quadrilateral_cc

View File

@ -86,7 +86,7 @@ static void node_geo_exec(GeoNodeExecParams params)
params.extract_input<float>("End Radius"),
params.extract_input<float>("Height"),
params.extract_input<bool>("Reverse"));
params.set_output("Curve", GeometrySet::create_with_curve(curve.release()));
params.set_output("Curve", GeometrySet::create_with_curve(curve_eval_to_curves(*curve)));
}
} // namespace blender::nodes::node_geo_curve_primitive_spiral_cc

View File

@ -83,7 +83,7 @@ static void node_geo_exec(GeoNodeExecParams params)
std::max(params.extract_input<float>("Outer Radius"), 0.0f),
params.extract_input<float>("Twist"),
std::max(params.extract_input<int>("Points"), 3));
GeometrySet output = GeometrySet::create_with_curve(curve.release());
GeometrySet output = GeometrySet::create_with_curve(curve_eval_to_curves(*curve));
if (params.output_is_required("Outer Points")) {
StrongAnonymousAttributeID attribute_output("Outer Points");

View File

@ -158,7 +158,7 @@ static SplinePtr resample_spline_evaluated(const Spline &src)
static std::unique_ptr<CurveEval> resample_curve(const CurveComponent *component,
const SampleModeParam &mode_param)
{
const CurveEval *input_curve = component->get_for_read();
const std::unique_ptr<CurveEval> input_curve = curves_to_curve_eval(*component->get_for_read());
GeometryComponentFieldContext field_context{*component, ATTR_DOMAIN_CURVE};
const int domain_size = component->attribute_domain_size(ATTR_DOMAIN_CURVE);
@ -242,7 +242,7 @@ static void geometry_set_curve_resample(GeometrySet &geometry_set,
std::unique_ptr<CurveEval> output_curve = resample_curve(
geometry_set.get_component_for_read<CurveComponent>(), mode_param);
geometry_set.replace_curve(output_curve.release());
geometry_set.replace_curve(curve_eval_to_curves(*output_curve));
}
static void node_geo_exec(GeoNodeExecParams params)

View File

@ -34,13 +34,15 @@ static void node_geo_exec(GeoNodeExecParams params)
selection_evaluator.evaluate();
const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0);
CurveEval &curve = *component.get_for_write();
MutableSpan<SplinePtr> splines = curve.splines();
std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*component.get_for_write());
MutableSpan<SplinePtr> splines = curve->splines();
threading::parallel_for(selection.index_range(), 128, [&](IndexRange range) {
for (const int i : range) {
splines[selection[i]]->reverse();
}
});
component.replace(curve_eval_to_curves(*curve));
});
params.set_output("Curve", std::move(geometry_set));

View File

@ -127,7 +127,8 @@ class SampleCurveFunction : public fn::MultiFunction {
}
const CurveComponent *curve_component = geometry_set_.get_component_for_read<CurveComponent>();
const CurveEval *curve = curve_component->get_for_read();
const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(
*curve_component->get_for_read());
Span<SplinePtr> splines = curve->splines();
if (splines.is_empty()) {
return return_default();
@ -234,12 +235,13 @@ static void node_geo_exec(GeoNodeExecParams params)
return;
}
const CurveEval *curve = component->get_for_read();
if (curve == nullptr) {
if (!component->has_curves()) {
params.set_default_remaining_outputs();
return;
}
const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*component->get_for_read());
if (curve->splines().is_empty()) {
params.set_default_remaining_outputs();
return;

View File

@ -66,8 +66,8 @@ static void node_geo_exec(GeoNodeExecParams params)
/* Retrieve data for write access so we can avoid new allocations for the handles data. */
CurveComponent &curve_component = geometry_set.get_component_for_write<CurveComponent>();
CurveEval &curve = *curve_component.get_for_write();
MutableSpan<SplinePtr> splines = curve.splines();
std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*curve_component.get_for_read());
MutableSpan<SplinePtr> splines = curve->splines();
GeometryComponentFieldContext field_context{curve_component, ATTR_DOMAIN_POINT};
const int domain_size = curve_component.attribute_domain_size(ATTR_DOMAIN_POINT);
@ -108,6 +108,8 @@ static void node_geo_exec(GeoNodeExecParams params)
}
bezier_spline.mark_cache_invalid();
}
curve_component.replace(curve_eval_to_curves(*curve));
});
if (!has_bezier_spline) {
params.error_message_add(NodeWarningType::Info, TIP_("No Bezier splines in input curve"));

View File

@ -205,8 +205,9 @@ class CurveParameterFieldInput final : public GeometryFieldInput {
{
if (component.type() == GEO_COMPONENT_TYPE_CURVE) {
const CurveComponent &curve_component = static_cast<const CurveComponent &>(component);
const CurveEval *curve = curve_component.get_for_read();
if (curve) {
if (curve_component.has_curves()) {
const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(
*curve_component.get_for_read());
return construct_curve_parameter_varray(*curve, mask, domain);
}
}
@ -238,8 +239,8 @@ class CurveLengthFieldInput final : public GeometryFieldInput {
{
if (component.type() == GEO_COMPONENT_TYPE_CURVE) {
const CurveComponent &curve_component = static_cast<const CurveComponent &>(component);
const CurveEval *curve = curve_component.get_for_read();
if (curve) {
if (curve_component.has_curves()) {
std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*curve_component.get_for_read());
return construct_curve_length_varray(*curve, mask, domain);
}
}
@ -271,8 +272,9 @@ class IndexOnSplineFieldInput final : public GeometryFieldInput {
{
if (component.type() == GEO_COMPONENT_TYPE_CURVE) {
const CurveComponent &curve_component = static_cast<const CurveComponent &>(component);
const CurveEval *curve = curve_component.get_for_read();
if (curve) {
if (curve_component.has_curves()) {
const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(
*curve_component.get_for_read());
return construct_index_on_spline_varray(*curve, mask, domain);
}
}

View File

@ -367,40 +367,43 @@ static void node_geo_exec(GeoNodeExecParams params)
}
const CurveComponent *curve_component = geometry_set.get_component_for_read<CurveComponent>();
const CurveEval &curve = *curve_component->get_for_read();
const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(
*curve_component->get_for_read());
GeometryComponentFieldContext field_context{*curve_component, ATTR_DOMAIN_CURVE};
const int domain_size = curve_component->attribute_domain_size(ATTR_DOMAIN_CURVE);
Span<SplinePtr> src_splines = curve->splines();
fn::FieldEvaluator selection_evaluator{field_context, domain_size};
selection_evaluator.add(selection_field);
selection_evaluator.evaluate();
const VArray<bool> &selection = selection_evaluator.get_evaluated<bool>(0);
std::unique_ptr<CurveEval> new_curve = std::make_unique<CurveEval>();
new_curve->resize(curve.splines().size());
new_curve->resize(src_splines.size());
threading::parallel_for(curve.splines().index_range(), 512, [&](IndexRange range) {
threading::parallel_for(src_splines.index_range(), 512, [&](IndexRange range) {
for (const int i : range) {
if (selection[i]) {
switch (output_type) {
case GEO_NODE_SPLINE_TYPE_POLY:
new_curve->splines()[i] = convert_to_poly_spline(*curve.splines()[i]);
new_curve->splines()[i] = convert_to_poly_spline(*src_splines[i]);
break;
case GEO_NODE_SPLINE_TYPE_BEZIER:
new_curve->splines()[i] = convert_to_bezier(*curve.splines()[i], params);
new_curve->splines()[i] = convert_to_bezier(*src_splines[i], params);
break;
case GEO_NODE_SPLINE_TYPE_NURBS:
new_curve->splines()[i] = convert_to_nurbs(*curve.splines()[i]);
new_curve->splines()[i] = convert_to_nurbs(*src_splines[i]);
break;
}
}
else {
new_curve->splines()[i] = curve.splines()[i]->copy();
new_curve->splines()[i] = src_splines[i]->copy();
}
}
});
new_curve->attributes = curve.attributes;
geometry_set.replace_curve(new_curve.release());
new_curve->attributes = curve->attributes;
geometry_set.replace_curve(curve_eval_to_curves(*new_curve));
});
params.set_output("Curve", std::move(geometry_set));

View File

@ -340,9 +340,9 @@ static void node_geo_exec(GeoNodeExecParams params)
if (cuts.is_single() && cuts.get_internal_single() < 1) {
return;
}
std::unique_ptr<CurveEval> output_curve = subdivide_curve(*component.get_for_read(), cuts);
geometry_set.replace_curve(output_curve.release());
std::unique_ptr<CurveEval> output_curve = subdivide_curve(
*curves_to_curve_eval(*component.get_for_read()), cuts);
geometry_set.replace_curve(curve_eval_to_curves(*output_curve));
});
params.set_output("Curve", geometry_set);
}

View File

@ -27,14 +27,16 @@ static void geometry_set_curve_to_mesh(GeometrySet &geometry_set,
const GeometrySet &profile_set,
const bool fill_caps)
{
const CurveEval *curve = geometry_set.get_curve_for_read();
const CurveEval *profile_curve = profile_set.get_curve_for_read();
const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(
*geometry_set.get_curve_for_read());
const Curves *profile_curves = profile_set.get_curve_for_read();
if (profile_curve == nullptr) {
if (profile_curves == nullptr) {
Mesh *mesh = bke::curve_to_wire_mesh(*curve);
geometry_set.replace_mesh(mesh);
}
else {
const std::unique_ptr<CurveEval> profile_curve = curves_to_curve_eval(*profile_curves);
Mesh *mesh = bke::curve_to_mesh_sweep(*curve, *profile_curve, fill_caps);
geometry_set.replace_mesh(mesh);
}

View File

@ -325,11 +325,12 @@ static void node_geo_exec(GeoNodeExecParams params)
geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES});
return;
}
const CurveEval &curve = *geometry_set.get_curve_for_read();
const Span<SplinePtr> splines = curve.splines();
curve.assert_valid_point_attributes();
const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(
*geometry_set.get_curve_for_read());
const Span<SplinePtr> splines = curve->splines();
curve->assert_valid_point_attributes();
const Array<int> offsets = calculate_spline_point_offsets(params, mode, curve, splines);
const Array<int> offsets = calculate_spline_point_offsets(params, mode, *curve, splines);
const int total_size = offsets.last();
if (total_size == 0) {
geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES});
@ -339,7 +340,7 @@ static void node_geo_exec(GeoNodeExecParams params)
geometry_set.replace_pointcloud(BKE_pointcloud_new_nomain(total_size));
PointCloudComponent &points = geometry_set.get_component_for_write<PointCloudComponent>();
ResultAttributes point_attributes = create_attributes_for_transfer(
points, curve, attribute_outputs);
points, *curve, attribute_outputs);
switch (mode) {
case GEO_NODE_CURVE_RESAMPLE_COUNT:
@ -351,7 +352,7 @@ static void node_geo_exec(GeoNodeExecParams params)
break;
}
copy_spline_domain_attributes(curve, offsets, points);
copy_spline_domain_attributes(*curve, offsets, points);
if (!point_attributes.rotations.is_empty()) {
curve_create_default_rotation_attribute(

View File

@ -515,8 +515,8 @@ static void geometry_set_curve_trim(GeometrySet &geometry_set,
const blender::VArray<float> &starts = evaluator.get_evaluated<float>(0);
const blender::VArray<float> &ends = evaluator.get_evaluated<float>(1);
CurveEval &curve = *geometry_set.get_curve_for_write();
MutableSpan<SplinePtr> splines = curve.splines();
std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*geometry_set.get_curve_for_read());
MutableSpan<SplinePtr> splines = curve->splines();
threading::parallel_for(splines.index_range(), 128, [&](IndexRange range) {
for (const int i : range) {
@ -565,6 +565,8 @@ static void geometry_set_curve_trim(GeometrySet &geometry_set,
}
}
});
geometry_set.replace_curve(curve_eval_to_curves(*curve));
}
static void node_geo_exec(GeoNodeExecParams params)

View File

@ -475,9 +475,9 @@ static void separate_curve_selection(GeometrySet &geometry_set,
selection_evaluator.evaluate();
const VArray_Span<bool> &selection = selection_evaluator.get_evaluated<bool>(0);
std::unique_ptr<CurveEval> r_curve = curve_separate(
*src_component.get_for_read(), selection, selection_domain, invert);
*curves_to_curve_eval(*src_component.get_for_read()), selection, selection_domain, invert);
if (r_curve) {
geometry_set.replace_curve(r_curve.release());
geometry_set.replace_curve(curve_eval_to_curves(*r_curve));
}
else {
geometry_set.replace_curve(nullptr);

View File

@ -529,7 +529,8 @@ static void duplicate_splines(GeometrySet &geometry_set,
const GeometryComponent &src_component = *geometry_set.get_component_for_read(
GEO_COMPONENT_TYPE_CURVE);
const CurveEval &curve = *geometry_set.get_curve_for_read();
const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(
*geometry_set.get_curve_for_read());
const int domain_size = src_component.attribute_domain_size(ATTR_DOMAIN_CURVE);
GeometryComponentFieldContext field_context{src_component, ATTR_DOMAIN_CURVE};
FieldEvaluator evaluator{field_context, domain_size};
@ -547,11 +548,11 @@ static void duplicate_splines(GeometrySet &geometry_set,
int count = std::max(counts[selection[i_spline]], 0);
curve_offsets[i_spline] = dst_splines_size;
dst_splines_size += count;
dst_points_size += count * curve.splines()[selection[i_spline]]->size();
dst_points_size += count * curve->splines()[selection[i_spline]]->size();
}
curve_offsets.last() = dst_splines_size;
Array<int> control_point_offsets = curve.control_point_offsets();
Array<int> control_point_offsets = curve->control_point_offsets();
Array<int> point_mapping(dst_points_size);
std::unique_ptr<CurveEval> new_curve = std::make_unique<CurveEval>();
@ -559,8 +560,8 @@ static void duplicate_splines(GeometrySet &geometry_set,
for (const int i_spline : selection.index_range()) {
const IndexRange spline_range = range_for_offsets_index(curve_offsets, i_spline);
for ([[maybe_unused]] const int i_duplicate : IndexRange(spline_range.size())) {
SplinePtr spline = curve.splines()[selection[i_spline]]->copy();
for (const int i_point : IndexRange(curve.splines()[selection[i_spline]]->size())) {
SplinePtr spline = curve->splines()[selection[i_spline]]->copy();
for (const int i_point : IndexRange(curve->splines()[selection[i_spline]]->size())) {
point_mapping[point_index++] = control_point_offsets[selection[i_spline]] + i_point;
}
new_curve->add_spline(std::move(spline));
@ -569,7 +570,7 @@ static void duplicate_splines(GeometrySet &geometry_set,
new_curve->attributes.reallocate(new_curve->splines().size());
CurveComponent dst_component;
dst_component.replace(new_curve.release(), GeometryOwnershipType::Editable);
dst_component.replace(curve_eval_to_curves(*new_curve), GeometryOwnershipType::Editable);
Vector<std::string> skip(
{"position", "radius", "resolution", "cyclic", "tilt", "handle_left", "handle_right"});
@ -577,7 +578,7 @@ static void duplicate_splines(GeometrySet &geometry_set,
copy_spline_attributes_without_id(
geometry_set, point_mapping, curve_offsets, skip, src_component, dst_component);
copy_stable_id_splines(curve, selection, curve_offsets, src_component, dst_component);
copy_stable_id_splines(*curve, selection, curve_offsets, src_component, dst_component);
if (attributes.duplicate_index) {
create_duplicate_index_attribute(
@ -786,8 +787,9 @@ static void duplicate_points_curve(const GeometryComponentType component_type,
Array<int> offsets = accumulate_counts_to_offsets(selection, counts);
CurveComponent &curve_component = geometry_set.get_component_for_write<CurveComponent>();
const CurveEval &curve = *geometry_set.get_curve_for_read();
Array<int> control_point_offsets = curve.control_point_offsets();
const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(
*geometry_set.get_curve_for_read());
Array<int> control_point_offsets = curve->control_point_offsets();
std::unique_ptr<CurveEval> new_curve = std::make_unique<CurveEval>();
Array<int> parent(domain_size);
@ -802,7 +804,7 @@ static void duplicate_points_curve(const GeometryComponentType component_type,
for (const int i_point : selection) {
const IndexRange point_range = range_for_offsets_index(offsets, i_point);
for ([[maybe_unused]] const int i_duplicate : IndexRange(point_range.size())) {
const SplinePtr &parent_spline = curve.splines()[parent[i_point]];
const SplinePtr &parent_spline = curve->splines()[parent[i_point]];
switch (parent_spline->type()) {
case CurveType::CURVE_TYPE_BEZIER: {
std::unique_ptr<BezierSpline> spline = std::make_unique<BezierSpline>();
@ -833,7 +835,7 @@ static void duplicate_points_curve(const GeometryComponentType component_type,
}
new_curve->attributes.reallocate(new_curve->splines().size());
CurveComponent dst_component;
dst_component.replace(new_curve.release(), GeometryOwnershipType::Editable);
dst_component.replace(curve_eval_to_curves(*new_curve), GeometryOwnershipType::Editable);
copy_point_attributes_without_id(
geometry_set, GEO_COMPONENT_TYPE_CURVE, false, offsets, src_component, dst_component);

View File

@ -19,10 +19,10 @@ static void node_declare(NodeDeclarationBuilder &b)
static VArray<float> construct_spline_length_gvarray(const CurveComponent &component,
const AttributeDomain domain)
{
const CurveEval *curve = component.get_for_read();
if (curve == nullptr) {
if (!component.has_curves()) {
return {};
}
const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*component.get_for_read());
Span<SplinePtr> splines = curve->splines();
auto length_fn = [splines](int i) { return splines[i]->length(); };
@ -76,10 +76,10 @@ class SplineLengthFieldInput final : public GeometryFieldInput {
static VArray<int> construct_spline_count_gvarray(const CurveComponent &component,
const AttributeDomain domain)
{
const CurveEval *curve = component.get_for_read();
if (curve == nullptr) {
if (!component.has_curves()) {
return {};
}
const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*component.get_for_read());
Span<SplinePtr> splines = curve->splines();
auto count_fn = [splines](int i) { return splines[i]->size(); };

View File

@ -77,21 +77,12 @@ static Array<float3> curve_tangent_point_domain(const CurveEval &curve)
static VArray<float3> construct_curve_tangent_gvarray(const CurveComponent &component,
const AttributeDomain domain)
{
const CurveEval *curve = component.get_for_read();
if (curve == nullptr) {
return nullptr;
if (!component.has_curves()) {
return {};
}
const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*component.get_for_read());
if (domain == ATTR_DOMAIN_POINT) {
const Span<SplinePtr> splines = curve->splines();
/* Use a reference to evaluated tangents if possible to avoid an allocation and a copy.
* This is only possible when there is only one poly spline. */
if (splines.size() == 1 && splines.first()->type() == CURVE_TYPE_POLY) {
const PolySpline &spline = static_cast<PolySpline &>(*splines.first());
return VArray<float3>::ForSpan(spline.evaluated_tangents());
}
Array<float3> tangents = curve_tangent_point_domain(*curve);
return VArray<float3>::ForContainer(std::move(tangents));
}

View File

@ -35,7 +35,7 @@ static void node_geo_exec(GeoNodeExecParams params)
}
std::unique_ptr<CurveEval> curve = geometry::mesh_to_curve_convert(component, selection);
geometry_set.replace_curve(curve.release());
geometry_set.replace_curve(curve_eval_to_curves(*curve));
geometry_set.keep_only({GEO_COMPONENT_TYPE_CURVE, GEO_COMPONENT_TYPE_INSTANCES});
});

View File

@ -35,7 +35,7 @@ static void node_init(bNodeTree *UNUSED(tree), bNode *node)
}
static void set_position_in_component(const GeometryNodeCurveHandleMode mode,
GeometryComponent &component,
CurveComponent &component,
const Field<bool> &selection_field,
const Field<float3> &position_field,
const Field<float3> &offset_field)
@ -53,8 +53,7 @@ static void set_position_in_component(const GeometryNodeCurveHandleMode mode,
evaluator.evaluate();
const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
CurveComponent &curve_component = *static_cast<CurveComponent *>(&component);
CurveEval *curve = curve_component.get_for_write();
std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*component.get_for_read());
int current_point = 0;
int current_mask = 0;
@ -126,6 +125,8 @@ static void set_position_in_component(const GeometryNodeCurveHandleMode mode,
}
}
}
component.replace(curve_eval_to_curves(*curve), GeometryOwnershipType::Owned);
}
static void node_geo_exec(GeoNodeExecParams params)
@ -140,9 +141,11 @@ static void node_geo_exec(GeoNodeExecParams params)
bool has_bezier = false;
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
if (geometry_set.has_curve() &&
geometry_set.get_curve_for_read()->has_spline_with_type(CURVE_TYPE_BEZIER)) {
has_bezier = true;
if (geometry_set.has_curve()) {
const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(
*geometry_set.get_curve_for_read());
has_bezier = curve->has_spline_with_type(CURVE_TYPE_BEZIER);
set_position_in_component(mode,
geometry_set.get_component_for_write<CurveComponent>(),
selection_field,

View File

@ -45,7 +45,9 @@ static void node_geo_exec(GeoNodeExecParams params)
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
if (geometry_set.has_curve()) {
if (only_poly) {
for (const SplinePtr &spline : geometry_set.get_curve_for_read()->splines()) {
const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(
*geometry_set.get_curve_for_read());
for (const SplinePtr &spline : curve->splines()) {
if (ELEM(spline->type(), CURVE_TYPE_BEZIER, CURVE_TYPE_NURBS)) {
only_poly = false;
break;

View File

@ -298,7 +298,8 @@ static Map<int, int> create_curve_instances(GeoNodeExecParams &params,
layout.pivot_points.add_new(layout.char_codes[i], pivot_point);
}
GeometrySet geometry_set_curve = GeometrySet::create_with_curve(curve_eval.release());
GeometrySet geometry_set_curve = GeometrySet::create_with_curve(
curve_eval_to_curves(*curve_eval));
handles.add_new(layout.char_codes[i],
instance_component.add_reference(std::move(geometry_set_curve)));
}

View File

@ -10,6 +10,7 @@
#include "DNA_pointcloud_types.h"
#include "DNA_volume_types.h"
#include "BKE_curves.hh"
#include "BKE_mesh.h"
#include "BKE_pointcloud.h"
#include "BKE_spline.hh"
@ -125,8 +126,10 @@ static void translate_geometry_set(GeometrySet &geometry,
const float3 translation,
const Depsgraph &depsgraph)
{
if (CurveEval *curve = geometry.get_curve_for_write()) {
if (Curves *curves = geometry.get_curve_for_write()) {
std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*curves);
curve->translate(translation);
geometry.replace_curve(curve_eval_to_curves(*curve));
}
if (Mesh *mesh = geometry.get_mesh_for_write()) {
translate_mesh(*mesh, translation);
@ -146,8 +149,10 @@ void transform_geometry_set(GeometrySet &geometry,
const float4x4 &transform,
const Depsgraph &depsgraph)
{
if (CurveEval *curve = geometry.get_curve_for_write()) {
if (Curves *curves = geometry.get_curve_for_write()) {
std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*curves);
curve->transform(transform);
geometry.replace_curve(curve_eval_to_curves(*curve));
}
if (Mesh *mesh = geometry.get_mesh_for_write()) {
transform_mesh(*mesh, transform);