Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source/blender/blenkernel/BKE_blender_version.h2
-rw-r--r--source/blender/blenkernel/BKE_curves.hh12
-rw-r--r--source/blender/blenkernel/BKE_curves_utils.hh58
-rw-r--r--source/blender/blenkernel/intern/curves_geometry.cc34
-rw-r--r--source/blender/blenkernel/intern/curves_utils.cc84
-rw-r--r--source/blender/blenlib/BLI_index_mask.hh2
-rw-r--r--source/blender/blenloader/intern/versioning_300.c50
-rw-r--r--source/blender/geometry/intern/resample_curves.cc62
-rw-r--r--source/blender/makesdna/DNA_node_types.h6
-rw-r--r--source/blender/makesrna/RNA_enum_items.h2
-rw-r--r--source/blender/makesrna/intern/rna_curves.c8
-rw-r--r--source/blender/makesrna/intern/rna_nodetree.c10
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc811
13 files changed, 815 insertions, 326 deletions
diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h
index ab13a2e85d0..2cd753da9d3 100644
--- a/source/blender/blenkernel/BKE_blender_version.h
+++ b/source/blender/blenkernel/BKE_blender_version.h
@@ -25,7 +25,7 @@ extern "C" {
/* Blender file format version. */
#define BLENDER_FILE_VERSION BLENDER_VERSION
-#define BLENDER_FILE_SUBVERSION 0
+#define BLENDER_FILE_SUBVERSION 1
/* Minimum Blender version that supports reading file written with the current
* version. Older Blender versions will test this and show a warning if the file
diff --git a/source/blender/blenkernel/BKE_curves.hh b/source/blender/blenkernel/BKE_curves.hh
index dc67f1e7403..28d03f96db1 100644
--- a/source/blender/blenkernel/BKE_curves.hh
+++ b/source/blender/blenkernel/BKE_curves.hh
@@ -182,6 +182,7 @@ class CurvesGeometry : public ::CurvesGeometry {
void update_curve_types();
bool has_curve_with_type(CurveType type) const;
+ bool has_curve_with_type(Span<CurveType> types) const;
/** Return true if all of the curves have the provided type. */
bool is_single_type(CurveType type) const;
/** Return the number of curves with each type. */
@@ -394,6 +395,11 @@ class CurvesGeometry : public ::CurvesGeometry {
*/
void reverse_curves(IndexMask curves_to_reverse);
+ /**
+ * Remove any attributes that are unused based on the types in the curves.
+ */
+ void remove_attributes_based_on_types();
+
/* --------------------------------------------------------------------
* Attributes.
*/
@@ -710,6 +716,12 @@ inline bool CurvesGeometry::has_curve_with_type(const CurveType type) const
return this->curve_type_counts()[type] > 0;
}
+inline bool CurvesGeometry::has_curve_with_type(const Span<CurveType> types) const
+{
+ return std::any_of(
+ types.begin(), types.end(), [&](CurveType type) { return this->has_curve_with_type(type); });
+}
+
inline const std::array<int, CURVE_TYPES_NUM> &CurvesGeometry::curve_type_counts() const
{
BLI_assert(this->runtime->type_counts == calculate_type_counts(this->curve_types()));
diff --git a/source/blender/blenkernel/BKE_curves_utils.hh b/source/blender/blenkernel/BKE_curves_utils.hh
index 62b060093e9..f223e173ea9 100644
--- a/source/blender/blenkernel/BKE_curves_utils.hh
+++ b/source/blender/blenkernel/BKE_curves_utils.hh
@@ -9,9 +9,53 @@
* \brief Low-level operations for curves.
*/
+#include "BLI_function_ref.hh"
+#include "BLI_generic_pointer.hh"
+
namespace blender::bke::curves {
/**
+ * Copy the provided point attribute values between all curves in the #curve_ranges index
+ * ranges, assuming that all curves have the same number of control points in #src_curves
+ * and #dst_curves.
+ */
+void copy_point_data(const CurvesGeometry &src_curves,
+ const CurvesGeometry &dst_curves,
+ Span<IndexRange> curve_ranges,
+ GSpan src,
+ GMutableSpan dst);
+
+void copy_point_data(const CurvesGeometry &src_curves,
+ const CurvesGeometry &dst_curves,
+ IndexMask src_curve_selection,
+ GSpan src,
+ GMutableSpan dst);
+
+template<typename T>
+void copy_point_data(const CurvesGeometry &src_curves,
+ const CurvesGeometry &dst_curves,
+ const IndexMask src_curve_selection,
+ const Span<T> src,
+ MutableSpan<T> dst)
+{
+ copy_point_data(src_curves, dst_curves, src_curve_selection, GSpan(src), GMutableSpan(dst));
+}
+
+void fill_points(const CurvesGeometry &curves,
+ IndexMask curve_selection,
+ GPointer value,
+ GMutableSpan dst);
+
+template<typename T>
+void fill_points(const CurvesGeometry &curves,
+ const IndexMask curve_selection,
+ const T &value,
+ MutableSpan<T> dst)
+{
+ fill_points(curves, curve_selection, &value, dst);
+}
+
+/**
* Copy the size of every curve in #curve_ranges to the corresponding index in #counts.
*/
void fill_curve_counts(const bke::CurvesGeometry &curves,
@@ -23,4 +67,18 @@ void fill_curve_counts(const bke::CurvesGeometry &curves,
*/
void accumulate_counts_to_offsets(MutableSpan<int> counts_to_offsets, int start_offset = 0);
+IndexMask indices_for_type(const VArray<int8_t> &types,
+ const std::array<int, CURVE_TYPES_NUM> &type_counts,
+ const CurveType type,
+ const IndexMask selection,
+ Vector<int64_t> &r_indices);
+
+void foreach_curve_by_type(const VArray<int8_t> &types,
+ const std::array<int, CURVE_TYPES_NUM> &type_counts,
+ IndexMask selection,
+ FunctionRef<void(IndexMask)> catmull_rom_fn,
+ FunctionRef<void(IndexMask)> poly_fn,
+ FunctionRef<void(IndexMask)> bezier_fn,
+ FunctionRef<void(IndexMask)> nurbs_fn);
+
} // namespace blender::bke::curves
diff --git a/source/blender/blenkernel/intern/curves_geometry.cc b/source/blender/blenkernel/intern/curves_geometry.cc
index eb9b201c5b5..3c060bd1b8f 100644
--- a/source/blender/blenkernel/intern/curves_geometry.cc
+++ b/source/blender/blenkernel/intern/curves_geometry.cc
@@ -18,6 +18,7 @@
#include "BKE_attribute_math.hh"
#include "BKE_curves.hh"
+#include "BKE_curves_utils.hh"
namespace blender::bke {
@@ -550,16 +551,8 @@ IndexMask CurvesGeometry::indices_for_curve_type(const CurveType type,
const IndexMask selection,
Vector<int64_t> &r_indices) const
{
- if (this->curve_type_counts()[type] == this->curves_num()) {
- return selection;
- }
- const VArray<int8_t> types = this->curve_types();
- if (types.is_single()) {
- return types.get_internal_single() == type ? IndexMask(this->curves_num()) : IndexMask(0);
- }
- Span<int8_t> types_span = types.get_internal_span();
- return index_mask_ops::find_indices_based_on_predicate(
- selection, 1024, r_indices, [&](const int index) { return types_span[index] == type; });
+ return curves::indices_for_type(
+ this->curve_types(), this->curve_type_counts(), type, selection, r_indices);
}
void CurvesGeometry::ensure_nurbs_basis_cache() const
@@ -1322,6 +1315,27 @@ void CurvesGeometry::reverse_curves(const IndexMask curves_to_reverse)
this->tag_topology_changed();
}
+void CurvesGeometry::remove_attributes_based_on_types()
+{
+ const int points_num = this->points_num();
+ const int curves_num = this->curves_num();
+ if (!this->has_curve_with_type(CURVE_TYPE_BEZIER)) {
+ CustomData_free_layer_named(&this->point_data, ATTR_HANDLE_TYPE_LEFT.c_str(), points_num);
+ CustomData_free_layer_named(&this->point_data, ATTR_HANDLE_TYPE_RIGHT.c_str(), points_num);
+ CustomData_free_layer_named(&this->point_data, ATTR_HANDLE_POSITION_LEFT.c_str(), points_num);
+ CustomData_free_layer_named(&this->point_data, ATTR_HANDLE_POSITION_RIGHT.c_str(), points_num);
+ }
+ if (!this->has_curve_with_type(CURVE_TYPE_NURBS)) {
+ CustomData_free_layer_named(&this->point_data, ATTR_NURBS_WEIGHT.c_str(), points_num);
+ CustomData_free_layer_named(&this->curve_data, ATTR_NURBS_ORDER.c_str(), curves_num);
+ CustomData_free_layer_named(&this->curve_data, ATTR_NURBS_KNOTS_MODE.c_str(), curves_num);
+ }
+ if (!this->has_curve_with_type({CURVE_TYPE_BEZIER, CURVE_TYPE_CATMULL_ROM, CURVE_TYPE_NURBS})) {
+ CustomData_free_layer_named(&this->curve_data, ATTR_RESOLUTION.c_str(), curves_num);
+ }
+ this->update_customdata_pointers();
+}
+
/** \} */
/* -------------------------------------------------------------------- */
diff --git a/source/blender/blenkernel/intern/curves_utils.cc b/source/blender/blenkernel/intern/curves_utils.cc
index 78c2382b62f..802469399ab 100644
--- a/source/blender/blenkernel/intern/curves_utils.cc
+++ b/source/blender/blenkernel/intern/curves_utils.cc
@@ -4,6 +4,8 @@
* \ingroup bke
*/
+#include "BLI_index_mask_ops.hh"
+
#include "BKE_curves_utils.hh"
namespace blender::bke::curves {
@@ -35,4 +37,86 @@ void accumulate_counts_to_offsets(MutableSpan<int> counts_to_offsets, const int
counts_to_offsets.last() = offset;
}
+void copy_point_data(const CurvesGeometry &src_curves,
+ const CurvesGeometry &dst_curves,
+ const Span<IndexRange> curve_ranges,
+ const GSpan src,
+ GMutableSpan dst)
+{
+ threading::parallel_for(curve_ranges.index_range(), 512, [&](IndexRange range) {
+ for (const IndexRange range : curve_ranges.slice(range)) {
+ const IndexRange src_points = src_curves.points_for_curves(range);
+ const IndexRange dst_points = dst_curves.points_for_curves(range);
+ /* The arrays might be large, so a threaded copy might make sense here too. */
+ dst.slice(dst_points).copy_from(src.slice(src_points));
+ }
+ });
+}
+
+void copy_point_data(const CurvesGeometry &src_curves,
+ const CurvesGeometry &dst_curves,
+ const IndexMask src_curve_selection,
+ const GSpan src,
+ GMutableSpan dst)
+{
+ threading::parallel_for(src_curve_selection.index_range(), 512, [&](IndexRange range) {
+ for (const int i : src_curve_selection.slice(range)) {
+ const IndexRange src_points = src_curves.points_for_curve(i);
+ const IndexRange dst_points = dst_curves.points_for_curve(i);
+ /* The arrays might be large, so a threaded copy might make sense here too. */
+ dst.slice(dst_points).copy_from(src.slice(src_points));
+ }
+ });
+}
+
+void fill_points(const CurvesGeometry &curves,
+ const IndexMask curve_selection,
+ const GPointer value,
+ GMutableSpan dst)
+{
+ BLI_assert(*value.type() == dst.type());
+ const CPPType &type = dst.type();
+ threading::parallel_for(curve_selection.index_range(), 512, [&](IndexRange range) {
+ for (const int i : curve_selection.slice(range)) {
+ const IndexRange points = curves.points_for_curve(i);
+ type.fill_assign_n(value.get(), dst.slice(curves.points_for_curve(i)).data(), points.size());
+ }
+ });
+}
+
+IndexMask indices_for_type(const VArray<int8_t> &types,
+ const std::array<int, CURVE_TYPES_NUM> &type_counts,
+ const CurveType type,
+ const IndexMask selection,
+ Vector<int64_t> &r_indices)
+{
+ if (type_counts[type] == types.size()) {
+ return selection;
+ }
+ if (types.is_single()) {
+ return types.get_internal_single() == type ? IndexMask(types.size()) : IndexMask(0);
+ }
+ Span<int8_t> types_span = types.get_internal_span();
+ return index_mask_ops::find_indices_based_on_predicate(
+ selection, 4096, r_indices, [&](const int index) { return types_span[index] == type; });
+}
+
+void foreach_curve_by_type(const VArray<int8_t> &types,
+ const std::array<int, CURVE_TYPES_NUM> &counts,
+ const IndexMask selection,
+ FunctionRef<void(IndexMask)> catmull_rom_fn,
+ FunctionRef<void(IndexMask)> poly_fn,
+ FunctionRef<void(IndexMask)> bezier_fn,
+ FunctionRef<void(IndexMask)> nurbs_fn)
+{
+ Vector<int64_t> catmull_rom;
+ Vector<int64_t> poly;
+ Vector<int64_t> bezier;
+ Vector<int64_t> nurbs;
+ catmull_rom_fn(indices_for_type(types, counts, CURVE_TYPE_CATMULL_ROM, selection, catmull_rom));
+ poly_fn(indices_for_type(types, counts, CURVE_TYPE_POLY, selection, poly));
+ bezier_fn(indices_for_type(types, counts, CURVE_TYPE_BEZIER, selection, bezier));
+ nurbs_fn(indices_for_type(types, counts, CURVE_TYPE_NURBS, selection, nurbs));
+}
+
} // namespace blender::bke::curves
diff --git a/source/blender/blenlib/BLI_index_mask.hh b/source/blender/blenlib/BLI_index_mask.hh
index b87ab0afc98..22bdf090181 100644
--- a/source/blender/blenlib/BLI_index_mask.hh
+++ b/source/blender/blenlib/BLI_index_mask.hh
@@ -278,7 +278,7 @@ class IndexMask {
* before each range in the return value starts.
*/
Vector<IndexRange> extract_ranges_invert(const IndexRange full_range,
- Vector<int64_t> *r_skip_amounts) const;
+ Vector<int64_t> *r_skip_amounts = nullptr) const;
};
} // namespace blender
diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c
index 76d12cc1109..57240b93ab1 100644
--- a/source/blender/blenloader/intern/versioning_300.c
+++ b/source/blender/blenloader/intern/versioning_300.c
@@ -25,6 +25,7 @@
#include "DNA_collection_types.h"
#include "DNA_constraint_types.h"
#include "DNA_curve_types.h"
+#include "DNA_curves_types.h"
#include "DNA_genfile.h"
#include "DNA_gpencil_modifier_types.h"
#include "DNA_lineart_types.h"
@@ -3021,18 +3022,7 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
}
- /**
- * Versioning code until next subversion bump goes here.
- *
- * \note Be sure to check when bumping the version:
- * - "versioning_userdef.c", #blo_do_versions_userdef
- * - "versioning_userdef.c", #do_versions_theme
- *
- * \note Keep this message at the bottom of the function.
- */
- {
- /* Keep this block, even when empty. */
-
+ if (!MAIN_VERSION_ATLEAST(bmain, 303, 1)) {
FOREACH_NODETREE_BEGIN (bmain, ntree, id) {
versioning_replace_legacy_combined_and_separate_color_nodes(ntree);
}
@@ -3066,5 +3056,41 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
SEQ_for_each_callback(&ed->seqbase, version_merge_still_offsets, NULL);
}
}
+
+ /* Use the curves type enum for the set spline type node, instead of a special one. */
+ FOREACH_NODETREE_BEGIN (bmain, ntree, id) {
+ if (ntree->type == NTREE_GEOMETRY) {
+ LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
+ if (node->type == GEO_NODE_CURVE_SPLINE_TYPE) {
+ NodeGeometryCurveSplineType *storage = (NodeGeometryCurveSplineType *)node->storage;
+ switch (storage->spline_type) {
+ case 0: /* GEO_NODE_SPLINE_TYPE_BEZIER */
+ storage->spline_type = CURVE_TYPE_BEZIER;
+ break;
+ case 1: /* GEO_NODE_SPLINE_TYPE_NURBS */
+ storage->spline_type = CURVE_TYPE_NURBS;
+ break;
+ case 2: /* GEO_NODE_SPLINE_TYPE_POLY */
+ storage->spline_type = CURVE_TYPE_POLY;
+ break;
+ }
+ }
+ }
+ }
+ }
+ FOREACH_NODETREE_END;
+ }
+
+ /**
+ * Versioning code until next subversion bump goes here.
+ *
+ * \note Be sure to check when bumping the version:
+ * - "versioning_userdef.c", #blo_do_versions_userdef
+ * - "versioning_userdef.c", #do_versions_theme
+ *
+ * \note Keep this message at the bottom of the function.
+ */
+ {
+ /* Keep this block, even when empty. */
}
}
diff --git a/source/blender/geometry/intern/resample_curves.cc b/source/blender/geometry/intern/resample_curves.cc
index 9eaab327e01..36525e1bdf0 100644
--- a/source/blender/geometry/intern/resample_curves.cc
+++ b/source/blender/geometry/intern/resample_curves.cc
@@ -165,38 +165,6 @@ static void gather_point_attributes_to_interpolate(const CurveComponent &src_com
dst_curves.update_customdata_pointers();
}
-/**
- * Copy the provided point attribute values between all curves in the #curve_ranges index
- * ranges, assuming that all curves are the same size in #src_curves and #dst_curves.
- */
-template<typename T>
-static void copy_between_curves(const bke::CurvesGeometry &src_curves,
- const bke::CurvesGeometry &dst_curves,
- const Span<IndexRange> curve_ranges,
- const Span<T> src,
- const MutableSpan<T> dst)
-{
- threading::parallel_for(curve_ranges.index_range(), 512, [&](IndexRange range) {
- for (const IndexRange range : curve_ranges.slice(range)) {
- const IndexRange src_points = src_curves.points_for_curves(range);
- const IndexRange dst_points = dst_curves.points_for_curves(range);
- /* The arrays might be large, so a threaded copy might make sense here too. */
- dst.slice(dst_points).copy_from(src.slice(src_points));
- }
- });
-}
-static void copy_between_curves(const bke::CurvesGeometry &src_curves,
- const bke::CurvesGeometry &dst_curves,
- const Span<IndexRange> unselected_ranges,
- const GSpan src,
- const GMutableSpan dst)
-{
- attribute_math::convert_to_static_type(src.type(), [&](auto dummy) {
- using T = decltype(dummy);
- copy_between_curves(src_curves, dst_curves, unselected_ranges, src.typed<T>(), dst.typed<T>());
- });
-}
-
static Curves *resample_to_uniform(const CurveComponent &src_component,
const fn::Field<bool> &selection_field,
const fn::Field<int> &count_field)
@@ -328,20 +296,21 @@ static Curves *resample_to_uniform(const CurveComponent &src_component,
/* Any attribute data from unselected curve points can be directly copied. */
for (const int i : attributes.src.index_range()) {
- copy_between_curves(
+ bke::curves::copy_point_data(
src_curves, dst_curves, unselected_ranges, attributes.src[i], attributes.dst[i]);
}
for (const int i : attributes.src_no_interpolation.index_range()) {
- copy_between_curves(src_curves,
- dst_curves,
- unselected_ranges,
- attributes.src_no_interpolation[i],
- attributes.dst_no_interpolation[i]);
+ bke::curves::copy_point_data(src_curves,
+ dst_curves,
+ unselected_ranges,
+ attributes.src_no_interpolation[i],
+ attributes.dst_no_interpolation[i]);
}
/* Copy positions for unselected curves. */
Span<float3> src_positions = src_curves.positions();
- copy_between_curves(src_curves, dst_curves, unselected_ranges, src_positions, dst_positions);
+ bke::curves::copy_point_data(
+ src_curves, dst_curves, unselected_ranges, src_positions, dst_positions);
for (bke::OutputAttribute &attribute : attributes.dst_attributes) {
attribute.save();
@@ -449,20 +418,21 @@ Curves *resample_to_evaluated(const CurveComponent &src_component,
/* Any attribute data from unselected curve points can be directly copied. */
for (const int i : attributes.src.index_range()) {
- copy_between_curves(
+ bke::curves::copy_point_data(
src_curves, dst_curves, unselected_ranges, attributes.src[i], attributes.dst[i]);
}
for (const int i : attributes.src_no_interpolation.index_range()) {
- copy_between_curves(src_curves,
- dst_curves,
- unselected_ranges,
- attributes.src_no_interpolation[i],
- attributes.dst_no_interpolation[i]);
+ bke::curves::copy_point_data(src_curves,
+ dst_curves,
+ unselected_ranges,
+ attributes.src_no_interpolation[i],
+ attributes.dst_no_interpolation[i]);
}
/* Copy positions for unselected curves. */
Span<float3> src_positions = src_curves.positions();
- copy_between_curves(src_curves, dst_curves, unselected_ranges, src_positions, dst_positions);
+ bke::curves::copy_point_data(
+ src_curves, dst_curves, unselected_ranges, src_positions, dst_positions);
for (bke::OutputAttribute &attribute : attributes.dst_attributes) {
attribute.save();
diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h
index 9dc2971b322..25c8a1f1514 100644
--- a/source/blender/makesdna/DNA_node_types.h
+++ b/source/blender/makesdna/DNA_node_types.h
@@ -1915,12 +1915,6 @@ typedef enum GeometryNodeBooleanOperation {
GEO_NODE_BOOLEAN_DIFFERENCE = 2,
} GeometryNodeBooleanOperation;
-typedef enum GeometryNodeSplineType {
- GEO_NODE_SPLINE_TYPE_BEZIER = 0,
- GEO_NODE_SPLINE_TYPE_NURBS = 1,
- GEO_NODE_SPLINE_TYPE_POLY = 2,
-} GeometryNodeSplineType;
-
typedef enum GeometryNodeCurvePrimitiveCircleMode {
GEO_NODE_CURVE_PRIMITIVE_CIRCLE_TYPE_POINTS = 0,
GEO_NODE_CURVE_PRIMITIVE_CIRCLE_TYPE_RADIUS = 1
diff --git a/source/blender/makesrna/RNA_enum_items.h b/source/blender/makesrna/RNA_enum_items.h
index 7c6a434a97a..61c5c1a6c72 100644
--- a/source/blender/makesrna/RNA_enum_items.h
+++ b/source/blender/makesrna/RNA_enum_items.h
@@ -228,6 +228,8 @@ DEF_ENUM(rna_enum_transform_orientation_items)
DEF_ENUM(rna_enum_velocity_unit_items)
+DEF_ENUM(rna_enum_curves_types)
+
/* Not available to RNA pre-processing (`makesrna`).
* Defined in editors for example. */
#ifndef RNA_MAKESRNA
diff --git a/source/blender/makesrna/intern/rna_curves.c b/source/blender/makesrna/intern/rna_curves.c
index b50082056bf..bc3e5203ed0 100644
--- a/source/blender/makesrna/intern/rna_curves.c
+++ b/source/blender/makesrna/intern/rna_curves.c
@@ -18,6 +18,14 @@
#include "WM_types.h"
+const EnumPropertyItem rna_enum_curves_types[] = {
+ {CURVE_TYPE_CATMULL_ROM, "CATMULL_ROM", 0, "Catmull Rom", ""},
+ {CURVE_TYPE_POLY, "POLY", 0, "Poly", ""},
+ {CURVE_TYPE_BEZIER, "BEZIER", 0, "Bezier", ""},
+ {CURVE_TYPE_NURBS, "NURBS", 0, "NURBS", ""},
+ {0, NULL, 0, NULL, NULL},
+};
+
#ifdef RNA_RUNTIME
# include "BLI_math_vector.h"
diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c
index 841c250df4c..53f207328c9 100644
--- a/source/blender/makesrna/intern/rna_nodetree.c
+++ b/source/blender/makesrna/intern/rna_nodetree.c
@@ -9575,18 +9575,14 @@ static void def_geo_distribute_points_on_faces(StructRNA *srna)
static void def_geo_curve_spline_type(StructRNA *srna)
{
- static const EnumPropertyItem type_items[] = {
- {GEO_NODE_SPLINE_TYPE_BEZIER, "BEZIER", ICON_NONE, "Bezier", "Set the splines to Bezier"},
- {GEO_NODE_SPLINE_TYPE_NURBS, "NURBS", ICON_NONE, "NURBS", "Set the splines to NURBS"},
- {GEO_NODE_SPLINE_TYPE_POLY, "POLY", ICON_NONE, "Poly", "Set the splines to Poly"},
- {0, NULL, 0, NULL, NULL}};
-
PropertyRNA *prop;
RNA_def_struct_sdna_from(srna, "NodeGeometryCurveSplineType", "storage");
prop = RNA_def_property(srna, "spline_type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "spline_type");
- RNA_def_property_enum_items(prop, type_items);
+ RNA_def_property_enum_items(prop, rna_enum_curves_types);
+ RNA_def_property_ui_text(prop, "Type", "The curve type to change the selected curves to");
+
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc
index 500804e41f0..6c95ab3bdc9 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc
@@ -1,6 +1,10 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
-#include "BKE_spline.hh"
+#include <numeric>
+
+#include "BKE_attribute_math.hh"
+#include "BKE_curves.hh"
+#include "BKE_curves_utils.hh"
#include "BLI_task.hh"
@@ -29,10 +33,39 @@ static void node_init(bNodeTree *UNUSED(tree), bNode *node)
{
NodeGeometryCurveSplineType *data = MEM_cnew<NodeGeometryCurveSplineType>(__func__);
- data->spline_type = GEO_NODE_SPLINE_TYPE_POLY;
+ data->spline_type = CURVE_TYPE_POLY;
node->storage = data;
}
+/**
+ * This function answers the question about possible conversion method for NURBS-to-Bezier. In
+ * general for 3rd degree NURBS curves there is one-to-one relation with 3rd degree Bezier curves
+ * that can be exploit for conversion - Bezier handles sit on NURBS hull segments and in the middle
+ * between those handles are Bezier anchor points.
+ */
+static bool is_nurbs_to_bezier_one_to_one(const KnotsMode knots_mode)
+{
+ if (ELEM(knots_mode, NURBS_KNOT_MODE_NORMAL, NURBS_KNOT_MODE_ENDPOINT)) {
+ return true;
+ }
+ return false;
+}
+
+/**
+ * As an optimization, just change the types on a mutable curves data-block when the conversion is
+ * simple. This could be expanded to more cases where the number of points doesn't change in the
+ * future, though that might require properly initializing some attributes, or removing others.
+ */
+static bool conversion_can_change_point_num(const CurveType dst_type)
+{
+ if (ELEM(dst_type, CURVE_TYPE_CATMULL_ROM, CURVE_TYPE_POLY)) {
+ /* The conversion to Catmull Rom or Poly should never change the number of points, no matter
+ * the source type (Bezier to Catmull Rom conversion cannot maintain the same shape anyway). */
+ return false;
+ }
+ return true;
+}
+
template<typename T>
static void scale_input_assign(const Span<T> src,
const int scale,
@@ -44,32 +77,97 @@ static void scale_input_assign(const Span<T> src,
}
}
-template<typename T>
-static void scale_output_assign(const Span<T> src,
- const int scale,
- const int offset,
- MutableSpan<T> dst)
+/**
+ * The Bezier control point and its handles become three control points on the NURBS curve,
+ * so each attribute value is duplicated three times.
+ */
+template<typename T> static void bezier_generic_to_nurbs(const Span<T> src, MutableSpan<T> dst)
{
for (const int i : src.index_range()) {
- dst[i * scale + offset] = src[i];
+ dst[i * 3] = src[i];
+ dst[i * 3 + 1] = src[i];
+ dst[i * 3 + 2] = src[i];
+ }
+}
+
+static void bezier_generic_to_nurbs(const GSpan src, GMutableSpan dst)
+{
+ attribute_math::convert_to_static_type(src.type(), [&](auto dummy) {
+ using T = decltype(dummy);
+ bezier_generic_to_nurbs(src.typed<T>(), dst.typed<T>());
+ });
+}
+
+static void bezier_positions_to_nurbs(const Span<float3> src_positions,
+ const Span<float3> src_handles_l,
+ const Span<float3> src_handles_r,
+ MutableSpan<float3> dst_positions)
+{
+ for (const int i : src_positions.index_range()) {
+ dst_positions[i * 3] = src_handles_l[i];
+ dst_positions[i * 3 + 1] = src_positions[i];
+ dst_positions[i * 3 + 2] = src_handles_r[i];
+ }
+}
+
+static void catmull_rom_to_bezier_handles(const Span<float3> src_positions,
+ const bool cyclic,
+ MutableSpan<float3> dst_handles_l,
+ MutableSpan<float3> dst_handles_r)
+{
+ /* Catmull Rom curves are the same as Bezier curves with automatically defined handle positions.
+ * This constant defines the portion of the distance between the next/previous points to use for
+ * the length of the handles. */
+ constexpr float handle_scale = 1.0f / 6.0f;
+
+ if (src_positions.size() == 1) {
+ dst_handles_l.first() = src_positions.first();
+ dst_handles_r.first() = src_positions.first();
+ return;
+ }
+
+ const float3 first_offset = cyclic ? src_positions[1] - src_positions.last() :
+ src_positions[1] - src_positions[0];
+ dst_handles_r.first() = src_positions.first() + first_offset * handle_scale;
+ dst_handles_l.first() = src_positions.first() - first_offset * handle_scale;
+
+ const float3 last_offset = cyclic ? src_positions.first() - src_positions.last(1) :
+ src_positions.last() - src_positions.last(1);
+ dst_handles_l.last() = src_positions.last() - last_offset * handle_scale;
+ dst_handles_r.last() = src_positions.last() + last_offset * handle_scale;
+
+ for (const int i : src_positions.index_range().drop_front(1).drop_back(1)) {
+ const float3 left_offset = src_positions[i - 1] - src_positions[i + 1];
+ dst_handles_l[i] = src_positions[i] + left_offset * handle_scale;
+
+ const float3 right_offset = src_positions[i + 1] - src_positions[i - 1];
+ dst_handles_r[i] = src_positions[i] + right_offset * handle_scale;
}
}
+static void catmull_rom_to_nurbs_positions(const Span<float3> src_positions,
+ const bool cyclic,
+ MutableSpan<float3> dst_positions)
+{
+ /* Convert the Catmull Rom position data to Bezier handles in order to reuse the Bezier to
+ * NURBS positions assignment. If this becomes a bottleneck, this step could be avoided. */
+ Array<float3, 32> bezier_handles_l(src_positions.size());
+ Array<float3, 32> bezier_handles_r(src_positions.size());
+ catmull_rom_to_bezier_handles(src_positions, cyclic, bezier_handles_l, bezier_handles_r);
+ bezier_positions_to_nurbs(src_positions, bezier_handles_l, bezier_handles_r, dst_positions);
+}
+
template<typename T>
static void nurbs_to_bezier_assign(const Span<T> src,
const MutableSpan<T> dst,
const KnotsMode knots_mode)
{
switch (knots_mode) {
- case NURBS_KNOT_MODE_BEZIER:
- scale_input_assign<T>(src, 3, 1, dst);
- break;
case NURBS_KNOT_MODE_NORMAL:
for (const int i : dst.index_range()) {
dst[i] = src[(i + 1) % src.size()];
}
break;
- case NURBS_KNOT_MODE_ENDPOINT_BEZIER:
case NURBS_KNOT_MODE_ENDPOINT:
for (const int i : dst.index_range().drop_back(1).drop_front(1)) {
dst[i] = src[i + 1];
@@ -77,31 +175,18 @@ static void nurbs_to_bezier_assign(const Span<T> src,
dst.first() = src.first();
dst.last() = src.last();
break;
+ default:
+ /* Every 3rd NURBS position (starting from index 1) should have its attributes transfered. */
+ scale_input_assign<T>(src, 3, 1, dst);
}
}
-template<typename CopyFn>
-static void copy_attributes(const Spline &input_spline, Spline &output_spline, CopyFn copy_fn)
+static void nurbs_to_bezier_assign(const GSpan src, const KnotsMode knots_mode, GMutableSpan dst)
{
- input_spline.attributes.foreach_attribute(
- [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) {
- std::optional<GSpan> src = input_spline.attributes.get_for_read(attribute_id);
- BLI_assert(src);
- if (!output_spline.attributes.create(attribute_id, meta_data.data_type)) {
- BLI_assert_unreachable();
- return false;
- }
- std::optional<GMutableSpan> dst = output_spline.attributes.get_for_write(attribute_id);
- if (!dst) {
- BLI_assert_unreachable();
- return false;
- }
-
- copy_fn(*src, *dst);
-
- return true;
- },
- ATTR_DOMAIN_POINT);
+ attribute_math::convert_to_static_type(src.type(), [&](auto dummy) {
+ using T = decltype(dummy);
+ nurbs_to_bezier_assign(src.typed<T>(), dst.typed<T>(), knots_mode);
+ });
}
static Vector<float3> create_nurbs_to_bezier_handles(const Span<float3> nurbs_positions,
@@ -109,22 +194,8 @@ static Vector<float3> create_nurbs_to_bezier_handles(const Span<float3> nurbs_po
{
const int nurbs_positions_num = nurbs_positions.size();
Vector<float3> handle_positions;
- if (knots_mode == NURBS_KNOT_MODE_BEZIER) {
- for (const int i : IndexRange(nurbs_positions_num)) {
- if (i % 3 == 1) {
- continue;
- }
- handle_positions.append(nurbs_positions[i]);
- }
- if (nurbs_positions_num % 3 == 1) {
- handle_positions.pop_last();
- }
- else if (nurbs_positions_num % 3 == 2) {
- const int last_index = nurbs_positions_num - 1;
- handle_positions.append(2 * nurbs_positions[last_index] - nurbs_positions[last_index - 1]);
- }
- }
- else {
+
+ if (is_nurbs_to_bezier_one_to_one(knots_mode)) {
const bool is_periodic = knots_mode == NURBS_KNOT_MODE_NORMAL;
if (is_periodic) {
handle_positions.append(nurbs_positions[1] +
@@ -134,11 +205,15 @@ static Vector<float3> create_nurbs_to_bezier_handles(const Span<float3> nurbs_po
handle_positions.append(2 * nurbs_positions[0] - nurbs_positions[1]);
handle_positions.append(nurbs_positions[1]);
}
+
+ /* Place Bezier handles on interior NURBS hull segments. Those handles can be either placed on
+ * endpoints, midpoints or 1/3 of the distance of a hull segment. */
const int segments_num = nurbs_positions_num - 1;
const bool ignore_interior_segment = segments_num == 3 && is_periodic == false;
if (ignore_interior_segment == false) {
const float mid_offset = (float)(segments_num - 1) / 2.0f;
for (const int i : IndexRange(1, segments_num - 2)) {
+ /* Divisor can have values: 1, 2 or 3. */
const int divisor = is_periodic ?
3 :
std::min(3, (int)(-std::abs(i - mid_offset) + mid_offset + 1.0f));
@@ -151,6 +226,7 @@ static Vector<float3> create_nurbs_to_bezier_handles(const Span<float3> nurbs_po
}
}
}
+
const int last_index = nurbs_positions_num - 1;
if (is_periodic) {
handle_positions.append(
@@ -162,199 +238,435 @@ static Vector<float3> create_nurbs_to_bezier_handles(const Span<float3> nurbs_po
handle_positions.append(2 * nurbs_positions[last_index] - nurbs_positions[last_index - 1]);
}
}
+ else {
+ for (const int i : IndexRange(nurbs_positions_num)) {
+ if (i % 3 == 1) {
+ continue;
+ }
+ handle_positions.append(nurbs_positions[i]);
+ }
+ if (nurbs_positions_num % 3 == 1) {
+ handle_positions.pop_last();
+ }
+ else if (nurbs_positions_num % 3 == 2) {
+ const int last_index = nurbs_positions_num - 1;
+ handle_positions.append(2 * nurbs_positions[last_index] - nurbs_positions[last_index - 1]);
+ }
+ }
+
return handle_positions;
}
-static Array<float3> create_nurbs_to_bezier_positions(const Span<float3> nurbs_positions,
- const Span<float3> handle_positions,
- const KnotsMode knots_mode)
+static void create_nurbs_to_bezier_positions(const Span<float3> nurbs_positions,
+ const Span<float3> handle_positions,
+ const KnotsMode knots_mode,
+ MutableSpan<float3> bezier_positions)
{
- if (knots_mode == NURBS_KNOT_MODE_BEZIER) {
- /* Every third NURBS position (starting from index 1) should be converted to Bezier position */
- const int scale = 3;
- const int offset = 1;
- Array<float3> bezier_positions((nurbs_positions.size() + offset) / scale);
- scale_input_assign(nurbs_positions, scale, offset, bezier_positions.as_mutable_span());
- return bezier_positions;
+ if (is_nurbs_to_bezier_one_to_one(knots_mode)) {
+ for (const int i : bezier_positions.index_range()) {
+ bezier_positions[i] = math::interpolate(
+ handle_positions[i * 2], handle_positions[i * 2 + 1], 0.5f);
+ }
}
-
- Array<float3> bezier_positions(handle_positions.size() / 2);
- for (const int i : IndexRange(bezier_positions.size())) {
- bezier_positions[i] = math::interpolate(
- handle_positions[i * 2], handle_positions[i * 2 + 1], 0.5f);
+ else {
+ /* Every 3rd NURBS position (starting from index 1) should be converted to Bezier position. */
+ scale_input_assign(nurbs_positions, 3, 1, bezier_positions);
}
- return bezier_positions;
}
-static SplinePtr convert_to_poly_spline(const Spline &input)
+static int to_bezier_size(const CurveType src_type,
+ const bool cyclic,
+ const KnotsMode knots_mode,
+ const int src_size)
{
- std::unique_ptr<PolySpline> output = std::make_unique<PolySpline>();
- output->resize(input.positions().size());
- output->positions().copy_from(input.positions());
- output->radii().copy_from(input.radii());
- output->tilts().copy_from(input.tilts());
- Spline::copy_base_settings(input, *output);
- output->attributes = input.attributes;
- return output;
+ switch (src_type) {
+ case CURVE_TYPE_NURBS: {
+ if (is_nurbs_to_bezier_one_to_one(knots_mode)) {
+ return cyclic ? src_size : src_size - 2;
+ }
+ return (src_size + 1) / 3;
+ }
+ default:
+ return src_size;
+ }
}
-static SplinePtr poly_to_nurbs(const Spline &input)
+static int to_nurbs_size(const CurveType src_type, const int src_size)
{
- std::unique_ptr<NURBSpline> output = std::make_unique<NURBSpline>();
- output->resize(input.positions().size());
- output->positions().copy_from(input.positions());
- output->radii().copy_from(input.radii());
- output->tilts().copy_from(input.tilts());
- output->weights().fill(1.0f);
- output->set_resolution(12);
- output->set_order(4);
- Spline::copy_base_settings(input, *output);
- output->knots_mode = NURBS_KNOT_MODE_BEZIER;
- output->attributes = input.attributes;
- return output;
+ switch (src_type) {
+ case CURVE_TYPE_BEZIER:
+ case CURVE_TYPE_CATMULL_ROM:
+ return src_size * 3;
+ default:
+ return src_size;
+ }
}
-static SplinePtr bezier_to_nurbs(const Spline &input)
+static void retrieve_curve_sizes(const bke::CurvesGeometry &curves, MutableSpan<int> sizes)
{
- const BezierSpline &bezier_spline = static_cast<const BezierSpline &>(input);
- std::unique_ptr<NURBSpline> output = std::make_unique<NURBSpline>();
- output->resize(input.size() * 3);
-
- scale_output_assign(bezier_spline.handle_positions_left(), 3, 0, output->positions());
- scale_output_assign(input.radii(), 3, 0, output->radii());
- scale_output_assign(input.tilts(), 3, 0, output->tilts());
-
- scale_output_assign(bezier_spline.positions(), 3, 1, output->positions());
- scale_output_assign(input.radii(), 3, 1, output->radii());
- scale_output_assign(input.tilts(), 3, 1, output->tilts());
-
- scale_output_assign(bezier_spline.handle_positions_right(), 3, 2, output->positions());
- scale_output_assign(input.radii(), 3, 2, output->radii());
- scale_output_assign(input.tilts(), 3, 2, output->tilts());
-
- Spline::copy_base_settings(input, *output);
- output->weights().fill(1.0f);
- output->set_resolution(12);
- output->set_order(4);
- output->set_cyclic(input.is_cyclic());
- output->knots_mode = NURBS_KNOT_MODE_BEZIER;
- output->attributes.reallocate(output->size());
- copy_attributes(input, *output, [](GSpan src, GMutableSpan dst) {
- attribute_math::convert_to_static_type(src.type(), [&](auto dummy) {
- using T = decltype(dummy);
- scale_output_assign<T>(src.typed<T>(), 3, 0, dst.typed<T>());
- scale_output_assign<T>(src.typed<T>(), 3, 1, dst.typed<T>());
- scale_output_assign<T>(src.typed<T>(), 3, 2, dst.typed<T>());
- });
+ threading::parallel_for(curves.curves_range(), 4096, [&](IndexRange range) {
+ for (const int i : range) {
+ sizes[i] = curves.points_for_curve(i).size();
+ }
});
- return output;
}
-static SplinePtr poly_to_bezier(const Spline &input)
+struct GenericAttributes : NonCopyable, NonMovable {
+ Vector<GSpan> src;
+ Vector<GMutableSpan> dst;
+
+ Vector<OutputAttribute> attributes;
+};
+
+static void retrieve_generic_point_attributes(const CurveComponent &src_component,
+ CurveComponent &dst_component,
+ GenericAttributes &attributes)
{
- std::unique_ptr<BezierSpline> output = std::make_unique<BezierSpline>();
- output->resize(input.size());
- output->positions().copy_from(input.positions());
- output->radii().copy_from(input.radii());
- output->tilts().copy_from(input.tilts());
- output->handle_types_left().fill(BEZIER_HANDLE_VECTOR);
- output->handle_types_right().fill(BEZIER_HANDLE_VECTOR);
- output->set_resolution(12);
- Spline::copy_base_settings(input, *output);
- output->attributes = input.attributes;
- return output;
+ src_component.attribute_foreach(
+ [&](const AttributeIDRef &id, const AttributeMetaData meta_data) {
+ if (meta_data.domain != ATTR_DOMAIN_POINT) {
+ /* Curve domain attributes are all copied directly to the result in one step. */
+ return true;
+ }
+ if (src_component.attribute_is_builtin(id)) {
+ if (!(id.is_named() && ELEM(id, "tilt", "radius"))) {
+ return true;
+ }
+ }
+
+ GVArray src_attribute = src_component.attribute_try_get_for_read(id, ATTR_DOMAIN_POINT);
+ BLI_assert(src_attribute);
+ attributes.src.append(src_attribute.get_internal_span());
+
+ OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only(
+ id, ATTR_DOMAIN_POINT, meta_data.data_type);
+ attributes.dst.append(dst_attribute.as_span());
+ attributes.attributes.append(std::move(dst_attribute));
+
+ return true;
+ });
}
-static SplinePtr nurbs_to_bezier(const Spline &input)
+static void convert_to_bezier(const CurveComponent &src_component,
+ const bke::CurvesGeometry &src_curves,
+ const IndexMask selection,
+ CurveComponent &dst_component,
+ bke::CurvesGeometry &dst_curves)
{
- const NURBSpline &nurbs_spline = static_cast<const NURBSpline &>(input);
- Span<float3> nurbs_positions;
- Vector<float3> nurbs_positions_vector;
- KnotsMode knots_mode;
- if (nurbs_spline.is_cyclic()) {
- nurbs_positions_vector = nurbs_spline.positions();
- nurbs_positions_vector.append(nurbs_spline.positions()[0]);
- nurbs_positions_vector.append(nurbs_spline.positions()[1]);
- nurbs_positions = nurbs_positions_vector;
- knots_mode = NURBS_KNOT_MODE_NORMAL;
+ const VArray<int8_t> src_knot_modes = src_curves.nurbs_knots_modes();
+ const VArray<int8_t> src_types = src_curves.curve_types();
+ const VArray<bool> src_cyclic = src_curves.cyclic();
+ const Span<float3> src_positions = src_curves.positions();
+
+ MutableSpan<int> dst_offsets = dst_curves.offsets_for_write();
+ retrieve_curve_sizes(src_curves, dst_curves.offsets_for_write());
+ threading::parallel_for(selection.index_range(), 1024, [&](IndexRange range) {
+ for (const int i : selection.slice(range)) {
+ dst_offsets[i] = to_bezier_size(
+ CurveType(src_types[i]), src_cyclic[i], KnotsMode(src_knot_modes[i]), dst_offsets[i]);
+ }
+ });
+ bke::curves::accumulate_counts_to_offsets(dst_offsets);
+ dst_curves.resize(dst_offsets.last(), dst_curves.curves_num());
+
+ GenericAttributes attributes;
+ retrieve_generic_point_attributes(src_component, dst_component, attributes);
+
+ MutableSpan<float3> dst_positions = dst_curves.positions_for_write();
+ MutableSpan<float3> dst_handles_l = dst_curves.handle_positions_left_for_write();
+ MutableSpan<float3> dst_handles_r = dst_curves.handle_positions_right_for_write();
+ MutableSpan<int8_t> dst_types_l = dst_curves.handle_types_left_for_write();
+ MutableSpan<int8_t> dst_types_r = dst_curves.handle_types_right_for_write();
+ MutableSpan<float> dst_weights = dst_curves.nurbs_weights_for_write();
+
+ auto catmull_rom_to_bezier = [&](IndexMask selection) {
+ bke::curves::fill_points<int8_t>(dst_curves, selection, BEZIER_HANDLE_ALIGN, dst_types_l);
+ bke::curves::fill_points<int8_t>(dst_curves, selection, BEZIER_HANDLE_ALIGN, dst_types_r);
+ bke::curves::copy_point_data(src_curves, dst_curves, selection, src_positions, dst_positions);
+
+ threading::parallel_for(selection.index_range(), 512, [&](IndexRange range) {
+ for (const int i : selection.slice(range)) {
+ const IndexRange src_points = src_curves.points_for_curve(i);
+ const IndexRange dst_points = dst_curves.points_for_curve(i);
+ catmull_rom_to_bezier_handles(src_positions.slice(src_points),
+ src_cyclic[i],
+ dst_handles_l.slice(dst_points),
+ dst_handles_r.slice(dst_points));
+ }
+ });
+
+ for (const int i : attributes.src.index_range()) {
+ bke::curves::copy_point_data(
+ src_curves, dst_curves, selection, attributes.src[i], attributes.dst[i]);
+ }
+ };
+
+ auto poly_to_bezier = [&](IndexMask selection) {
+ bke::curves::copy_point_data(src_curves, dst_curves, selection, src_positions, dst_positions);
+ bke::curves::fill_points<int8_t>(dst_curves, selection, BEZIER_HANDLE_VECTOR, dst_types_l);
+ bke::curves::fill_points<int8_t>(dst_curves, selection, BEZIER_HANDLE_VECTOR, dst_types_r);
+ dst_curves.calculate_bezier_auto_handles();
+ for (const int i : attributes.src.index_range()) {
+ bke::curves::copy_point_data(
+ src_curves, dst_curves, selection, attributes.src[i], attributes.dst[i]);
+ }
+ };
+
+ auto bezier_to_bezier = [&](IndexMask selection) {
+ const VArray_Span<int8_t> src_types_l = src_curves.handle_types_left();
+ const VArray_Span<int8_t> src_types_r = src_curves.handle_types_right();
+ const Span<float3> src_handles_l = src_curves.handle_positions_left();
+ const Span<float3> src_handles_r = src_curves.handle_positions_right();
+
+ bke::curves::copy_point_data(src_curves, dst_curves, selection, src_positions, dst_positions);
+ bke::curves::copy_point_data(src_curves, dst_curves, selection, src_handles_l, dst_handles_l);
+ bke::curves::copy_point_data(src_curves, dst_curves, selection, src_handles_r, dst_handles_r);
+ bke::curves::copy_point_data(src_curves, dst_curves, selection, src_types_l, dst_types_l);
+ bke::curves::copy_point_data(src_curves, dst_curves, selection, src_types_r, dst_types_r);
+
+ dst_curves.calculate_bezier_auto_handles();
+
+ for (const int i : attributes.src.index_range()) {
+ bke::curves::copy_point_data(
+ src_curves, dst_curves, selection, attributes.src[i], attributes.dst[i]);
+ }
+ };
+
+ auto nurbs_to_bezier = [&](IndexMask selection) {
+ bke::curves::fill_points<int8_t>(dst_curves, selection, BEZIER_HANDLE_ALIGN, dst_types_l);
+ bke::curves::fill_points<int8_t>(dst_curves, selection, BEZIER_HANDLE_ALIGN, dst_types_r);
+ bke::curves::fill_points<float>(dst_curves, selection, 0.0f, dst_weights);
+
+ threading::parallel_for(selection.index_range(), 64, [&](IndexRange range) {
+ for (const int i : selection.slice(range)) {
+ const IndexRange src_points = src_curves.points_for_curve(i);
+ const IndexRange dst_points = dst_curves.points_for_curve(i);
+ const Span<float3> src_curve_positions = src_positions.slice(src_points);
+
+ KnotsMode knots_mode = KnotsMode(src_knot_modes[i]);
+ Span<float3> nurbs_positions = src_curve_positions;
+ Vector<float3> nurbs_positions_vector;
+ if (src_cyclic[i] && is_nurbs_to_bezier_one_to_one(knots_mode)) {
+ /* For conversion treat this as periodic closed curve. Extend NURBS hull to first and
+ * second point which will act as a sceleton for placing Bezier handles. */
+ nurbs_positions_vector.extend(src_curve_positions);
+ nurbs_positions_vector.append(src_curve_positions[0]);
+ nurbs_positions_vector.append(src_curve_positions[1]);
+ nurbs_positions = nurbs_positions_vector;
+ knots_mode = NURBS_KNOT_MODE_NORMAL;
+ }
+
+ const Vector<float3> handle_positions = create_nurbs_to_bezier_handles(nurbs_positions,
+ knots_mode);
+
+ scale_input_assign(handle_positions.as_span(), 2, 0, dst_handles_l.slice(dst_points));
+ scale_input_assign(handle_positions.as_span(), 2, 1, dst_handles_r.slice(dst_points));
+
+ create_nurbs_to_bezier_positions(
+ nurbs_positions, handle_positions, knots_mode, dst_positions.slice(dst_points));
+ }
+ });
+
+ for (const int i_attribute : attributes.src.index_range()) {
+ threading::parallel_for(selection.index_range(), 512, [&](IndexRange range) {
+ for (const int i : selection.slice(range)) {
+ const IndexRange src_points = src_curves.points_for_curve(i);
+ const IndexRange dst_points = dst_curves.points_for_curve(i);
+ nurbs_to_bezier_assign(attributes.src[i_attribute].slice(src_points),
+ KnotsMode(src_knot_modes[i]),
+ attributes.dst[i_attribute].slice(dst_points));
+ }
+ });
+ }
+ };
+
+ bke::curves::foreach_curve_by_type(src_curves.curve_types(),
+ src_curves.curve_type_counts(),
+ selection,
+ catmull_rom_to_bezier,
+ poly_to_bezier,
+ bezier_to_bezier,
+ nurbs_to_bezier);
+
+ const Vector<IndexRange> unselected_ranges = selection.extract_ranges_invert(
+ src_curves.curves_range());
+
+ for (const int i : attributes.src.index_range()) {
+ bke::curves::copy_point_data(
+ src_curves, dst_curves, unselected_ranges, attributes.src[i], attributes.dst[i]);
}
- else {
- nurbs_positions = nurbs_spline.positions();
- knots_mode = nurbs_spline.knots_mode;
+
+ for (OutputAttribute &attribute : attributes.attributes) {
+ attribute.save();
}
- const Vector<float3> handle_positions = create_nurbs_to_bezier_handles(nurbs_positions,
- knots_mode);
- BLI_assert(handle_positions.size() % 2 == 0);
- const Array<float3> bezier_positions = create_nurbs_to_bezier_positions(
- nurbs_positions, handle_positions.as_span(), knots_mode);
- BLI_assert(handle_positions.size() == bezier_positions.size() * 2);
-
- std::unique_ptr<BezierSpline> output = std::make_unique<BezierSpline>();
- output->resize(bezier_positions.size());
- output->positions().copy_from(bezier_positions);
- nurbs_to_bezier_assign(nurbs_spline.radii(), output->radii(), knots_mode);
- nurbs_to_bezier_assign(nurbs_spline.tilts(), output->tilts(), knots_mode);
- scale_input_assign(handle_positions.as_span(), 2, 0, output->handle_positions_left());
- scale_input_assign(handle_positions.as_span(), 2, 1, output->handle_positions_right());
- output->handle_types_left().fill(BEZIER_HANDLE_ALIGN);
- output->handle_types_right().fill(BEZIER_HANDLE_ALIGN);
- output->set_resolution(nurbs_spline.resolution());
- Spline::copy_base_settings(nurbs_spline, *output);
- output->attributes.reallocate(output->size());
- copy_attributes(nurbs_spline, *output, [knots_mode](GSpan src, GMutableSpan dst) {
- attribute_math::convert_to_static_type(src.type(), [&](auto dummy) {
- using T = decltype(dummy);
- nurbs_to_bezier_assign(src.typed<T>(), dst.typed<T>(), knots_mode);
- });
- });
- return output;
}
-static SplinePtr convert_to_bezier(const Spline &input, GeoNodeExecParams params)
+static void convert_to_nurbs(const CurveComponent &src_component,
+ const bke::CurvesGeometry &src_curves,
+ const IndexMask selection,
+ CurveComponent &dst_component,
+ bke::CurvesGeometry &dst_curves)
{
- switch (input.type()) {
- case CURVE_TYPE_BEZIER:
- return input.copy();
- case CURVE_TYPE_POLY:
- return poly_to_bezier(input);
- case CURVE_TYPE_NURBS:
- if (input.size() < 4) {
- params.error_message_add(
- NodeWarningType::Info,
- TIP_("NURBS must have minimum of 4 points for Bezier Conversion"));
- return input.copy();
+ const VArray<int8_t> src_types = src_curves.curve_types();
+ const VArray<bool> src_cyclic = src_curves.cyclic();
+ const Span<float3> src_positions = src_curves.positions();
+
+ MutableSpan<int> dst_offsets = dst_curves.offsets_for_write();
+ retrieve_curve_sizes(src_curves, dst_curves.offsets_for_write());
+ threading::parallel_for(selection.index_range(), 1024, [&](IndexRange range) {
+ for (const int i : selection.slice(range)) {
+ dst_offsets[i] = to_nurbs_size(CurveType(src_types[i]), dst_offsets[i]);
+ }
+ });
+ bke::curves::accumulate_counts_to_offsets(dst_offsets);
+ dst_curves.resize(dst_offsets.last(), dst_curves.curves_num());
+
+ GenericAttributes attributes;
+ retrieve_generic_point_attributes(src_component, dst_component, attributes);
+
+ MutableSpan<float3> dst_positions = dst_curves.positions_for_write();
+
+ auto fill_weights_if_necessary = [&](const IndexMask selection) {
+ if (!src_curves.nurbs_weights().is_empty()) {
+ bke::curves::fill_points(dst_curves, selection, 1.0f, dst_curves.nurbs_weights_for_write());
+ }
+ };
+
+ auto catmull_rom_to_nurbs = [&](IndexMask selection) {
+ dst_curves.nurbs_orders_for_write().fill_indices(selection, 4);
+ dst_curves.nurbs_knots_modes_for_write().fill_indices(selection, NURBS_KNOT_MODE_BEZIER);
+ fill_weights_if_necessary(selection);
+
+ threading::parallel_for(selection.index_range(), 512, [&](IndexRange range) {
+ for (const int i : selection.slice(range)) {
+ const IndexRange src_points = src_curves.points_for_curve(i);
+ const IndexRange dst_points = dst_curves.points_for_curve(i);
+ catmull_rom_to_nurbs_positions(
+ src_positions.slice(src_points), src_cyclic[i], dst_positions.slice(dst_points));
+ }
+ });
+
+ for (const int i_attribute : attributes.src.index_range()) {
+ threading::parallel_for(selection.index_range(), 512, [&](IndexRange range) {
+ for (const int i : selection.slice(range)) {
+ const IndexRange src_points = src_curves.points_for_curve(i);
+ const IndexRange dst_points = dst_curves.points_for_curve(i);
+ bezier_generic_to_nurbs(attributes.src[i_attribute].slice(src_points),
+ attributes.dst[i_attribute].slice(dst_points));
+ }
+ });
+ }
+ };
+
+ auto poly_to_nurbs = [&](IndexMask selection) {
+ dst_curves.nurbs_orders_for_write().fill_indices(selection, 4);
+ bke::curves::copy_point_data(src_curves, dst_curves, selection, src_positions, dst_positions);
+ fill_weights_if_necessary(selection);
+
+ /* Avoid using "Endpoint" knots modes for cyclic curves, since it adds a sharp point at the
+ * start/end. */
+ if (src_cyclic.is_single()) {
+ bke::curves::fill_points<int8_t>(dst_curves,
+ selection,
+ src_cyclic.get_internal_single() ? NURBS_KNOT_MODE_NORMAL :
+ NURBS_KNOT_MODE_ENDPOINT,
+ dst_curves.nurbs_knots_modes_for_write());
+ }
+ else {
+ VArray_Span<bool> cyclic{src_cyclic};
+ MutableSpan<int8_t> knots_modes = dst_curves.nurbs_knots_modes_for_write();
+ threading::parallel_for(selection.index_range(), 1024, [&](IndexRange range) {
+ for (const int i : selection.slice(range)) {
+ knots_modes[i] = cyclic[i] ? NURBS_KNOT_MODE_NORMAL : NURBS_KNOT_MODE_ENDPOINT;
+ }
+ });
+ }
+
+ for (const int i_attribute : attributes.src.index_range()) {
+ bke::curves::copy_point_data(src_curves,
+ dst_curves,
+ selection,
+ attributes.src[i_attribute],
+ attributes.dst[i_attribute]);
+ }
+ };
+
+ auto bezier_to_nurbs = [&](IndexMask selection) {
+ const Span<float3> src_handles_l = src_curves.handle_positions_left();
+ const Span<float3> src_handles_r = src_curves.handle_positions_right();
+
+ dst_curves.nurbs_orders_for_write().fill_indices(selection, 4);
+ dst_curves.nurbs_knots_modes_for_write().fill_indices(selection, NURBS_KNOT_MODE_BEZIER);
+ fill_weights_if_necessary(selection);
+
+ threading::parallel_for(selection.index_range(), 512, [&](IndexRange range) {
+ for (const int i : selection.slice(range)) {
+ const IndexRange src_points = src_curves.points_for_curve(i);
+ const IndexRange dst_points = dst_curves.points_for_curve(i);
+ bezier_positions_to_nurbs(src_positions.slice(src_points),
+ src_handles_l.slice(src_points),
+ src_handles_r.slice(src_points),
+ dst_positions.slice(dst_points));
}
- return nurbs_to_bezier(input);
- case CURVE_TYPE_CATMULL_ROM: {
- BLI_assert_unreachable();
- return {};
+ });
+
+ for (const int i_attribute : attributes.src.index_range()) {
+ threading::parallel_for(selection.index_range(), 512, [&](IndexRange range) {
+ for (const int i : selection.slice(range)) {
+ const IndexRange src_points = src_curves.points_for_curve(i);
+ const IndexRange dst_points = dst_curves.points_for_curve(i);
+ bezier_generic_to_nurbs(attributes.src[i_attribute].slice(src_points),
+ attributes.dst[i_attribute].slice(dst_points));
+ }
+ });
+ }
+ };
+
+ auto nurbs_to_nurbs = [&](IndexMask selection) {
+ bke::curves::copy_point_data(src_curves, dst_curves, selection, src_positions, dst_positions);
+
+ if (!src_curves.nurbs_weights().is_empty()) {
+ bke::curves::copy_point_data(src_curves,
+ dst_curves,
+ selection,
+ src_curves.nurbs_weights(),
+ dst_curves.nurbs_weights_for_write());
}
+
+ for (const int i_attribute : attributes.src.index_range()) {
+ bke::curves::copy_point_data(src_curves,
+ dst_curves,
+ selection,
+ attributes.src[i_attribute],
+ attributes.dst[i_attribute]);
+ }
+ };
+
+ bke::curves::foreach_curve_by_type(src_curves.curve_types(),
+ src_curves.curve_type_counts(),
+ selection,
+ catmull_rom_to_nurbs,
+ poly_to_nurbs,
+ bezier_to_nurbs,
+ nurbs_to_nurbs);
+
+ const Vector<IndexRange> unselected_ranges = selection.extract_ranges_invert(
+ src_curves.curves_range());
+
+ for (const int i : attributes.src.index_range()) {
+ bke::curves::copy_point_data(
+ src_curves, dst_curves, unselected_ranges, attributes.src[i], attributes.dst[i]);
}
- BLI_assert_unreachable();
- return {};
-}
-static SplinePtr convert_to_nurbs(const Spline &input)
-{
- switch (input.type()) {
- case CURVE_TYPE_NURBS:
- return input.copy();
- case CURVE_TYPE_BEZIER:
- return bezier_to_nurbs(input);
- case CURVE_TYPE_POLY:
- return poly_to_nurbs(input);
- case CURVE_TYPE_CATMULL_ROM:
- BLI_assert_unreachable();
- return {};
+ for (OutputAttribute &attribute : attributes.attributes) {
+ attribute.save();
}
- BLI_assert_unreachable();
- return {};
}
static void node_geo_exec(GeoNodeExecParams params)
{
const NodeGeometryCurveSplineType &storage = node_storage(params.node());
- const GeometryNodeSplineType dst_type = (const GeometryNodeSplineType)storage.spline_type;
+ const CurveType dst_type = CurveType(storage.spline_type);
GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve");
Field<bool> selection_field = params.extract_input<Field<bool>>("Selection");
@@ -363,45 +675,58 @@ static void node_geo_exec(GeoNodeExecParams params)
if (!geometry_set.has_curves()) {
return;
}
+ const CurveComponent &src_component = *geometry_set.get_component_for_read<CurveComponent>();
+ const Curves &src_curves_id = *src_component.get_for_read();
+ const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap(src_curves_id.geometry);
+ if (src_curves.is_single_type(dst_type)) {
+ return;
+ }
- const CurveComponent *curve_component = geometry_set.get_component_for_read<CurveComponent>();
- 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_num = curve_component->attribute_domain_num(ATTR_DOMAIN_CURVE);
-
- Span<SplinePtr> src_splines = curve->splines();
-
- fn::FieldEvaluator selection_evaluator{field_context, domain_num};
- 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(src_splines.size());
-
- threading::parallel_for(src_splines.index_range(), 512, [&](IndexRange range) {
- for (const int i : range) {
- if (selection[i]) {
- switch (dst_type) {
- case GEO_NODE_SPLINE_TYPE_POLY:
- 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(*src_splines[i], params);
- break;
- case GEO_NODE_SPLINE_TYPE_NURBS:
- new_curve->splines()[i] = convert_to_nurbs(*src_splines[i]);
- break;
- }
- }
- else {
- new_curve->splines()[i] = src_splines[i]->copy();
- }
- }
- });
- new_curve->attributes = curve->attributes;
- geometry_set.replace_curves(curve_eval_to_curves(*new_curve));
+ GeometryComponentFieldContext field_context{src_component, ATTR_DOMAIN_CURVE};
+ const int domain_size = src_component.attribute_domain_num(ATTR_DOMAIN_CURVE);
+
+ fn::FieldEvaluator evaluator{field_context, domain_size};
+ evaluator.set_selection(selection_field);
+ evaluator.evaluate();
+ const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
+ if (!conversion_can_change_point_num(dst_type)) {
+ CurveComponent &dst_component = geometry_set.get_component_for_write<CurveComponent>();
+ Curves &curves_id = *dst_component.get_for_write();
+ bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry);
+ curves.fill_curve_types(selection, dst_type);
+ curves.remove_attributes_based_on_types();
+ return;
+ }
+
+ Curves *dst_curves_id = bke::curves_new_nomain(0, src_curves.curves_num());
+ bke::CurvesGeometry &dst_curves = bke::CurvesGeometry::wrap(dst_curves_id->geometry);
+ CurveComponent dst_component;
+ dst_component.replace(dst_curves_id, GeometryOwnershipType::Editable);
+ /* 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());
+
+ dst_curves.fill_curve_types(selection, dst_type);
+
+ switch (dst_type) {
+ case CURVE_TYPE_CATMULL_ROM:
+ case CURVE_TYPE_POLY:
+ /* Converting to Catmull Rom curves or poly curves should be handled
+ * above by the optimization to avoid changing the point count. */
+ BLI_assert_unreachable();
+ break;
+ case CURVE_TYPE_BEZIER:
+ convert_to_bezier(src_component, src_curves, selection, dst_component, dst_curves);
+ break;
+ case CURVE_TYPE_NURBS:
+ convert_to_nurbs(src_component, src_curves, selection, dst_component, dst_curves);
+ break;
+ }
+
+ geometry_set.replace_curves(dst_curves_id);
});
params.set_output("Curve", std::move(geometry_set));