diff --git a/source/blender/blenkernel/BKE_curves.hh b/source/blender/blenkernel/BKE_curves.hh index 1f0d5c3b20f..b9a7da58a35 100644 --- a/source/blender/blenkernel/BKE_curves.hh +++ b/source/blender/blenkernel/BKE_curves.hh @@ -865,13 +865,16 @@ int calculate_evaluated_num( int knots_num(int points_num, int8_t order, bool cyclic); /** - * Copies custom knots into given `MutableSpan`. + * Depending on KnotsMode calculates knots or copies custom knots into given `MutableSpan`. * Adds `order - 1` length tail for cyclic curves. */ -void copy_custom_knots(const int8_t order, - const bool cyclic, - Span custom_knots, - MutableSpan knots); +void load_curve_knots(KnotsMode mode, + int points_num, + int8_t order, + bool cyclic, + IndexRange curve_knots, + Span custom_knots, + MutableSpan knots); /** * Calculate the knots for a curve given its properties, based on built-in standards defined by diff --git a/source/blender/blenkernel/BKE_curves_utils.hh b/source/blender/blenkernel/BKE_curves_utils.hh index 47be4e2f656..f7f66702238 100644 --- a/source/blender/blenkernel/BKE_curves_utils.hh +++ b/source/blender/blenkernel/BKE_curves_utils.hh @@ -560,6 +560,43 @@ void write_all_positions(bke::CurvesGeometry &curves, } // namespace bezier +namespace nurbs { + +/** + * Gathers NURBS custom knots of selected curves from one `CurvesGeometry` instance to another. + * Should be used to implement operator's custom knot copying logic. + * `dst_curve_offset` can be used to append knots to already existing ones in the `CurvesGeometry`. + */ +void gather_custom_knots(const bke::CurvesGeometry &src, + const IndexMask &src_curves, + int dst_curve_offset, + bke::CurvesGeometry &dst); + +/** + * Overwrites `NURBS_KNOT_MODE_CUSTOM` to given ones for regular and cyclic curves. + * The purpose is to to update knot modes for curves when knot copying or calculation is not + * possible or too complex. Curve operators not supporting NURBS custom knots should call this + * function with `IndexMask` `CurvesGeometry.curves_range()`, if resulting curves are created by + * copying all attributes. This way `NURBS_KNOT_MODE_CUSTOM` values might be copied though custom + * knots not. + */ +void update_custom_knot_modes(const IndexMask &mask, + const KnotsMode mode_for_regular, + const KnotsMode mode_for_cyclic, + bke::CurvesGeometry &curves); + +/** + * Copies NURBS custom knots from one `CurvesGeometry` instance to another excluding + * `exclude_curves`. + * For excluded curves with `NURBS_KNOT_MODE_CUSTOM` knot mode is overwritten to + * `NURBS_KNOT_MODE_NORMAL`. + */ +void copy_custom_knots(const bke::CurvesGeometry &src_curves, + const IndexMask &exclude_curves, + bke::CurvesGeometry &dst_curves); + +} // namespace nurbs + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/blenkernel/intern/curve_nurbs.cc b/source/blender/blenkernel/intern/curve_nurbs.cc index 796f479793a..de6703e2929 100644 --- a/source/blender/blenkernel/intern/curve_nurbs.cc +++ b/source/blender/blenkernel/intern/curve_nurbs.cc @@ -52,10 +52,10 @@ int knots_num(const int points_num, const int8_t order, const bool cyclic) return points_num + order; } -void copy_custom_knots(const int8_t order, - const bool cyclic, - const Span custom_knots, - MutableSpan knots) +static void copy_custom_knots(const int8_t order, + const bool cyclic, + const Span custom_knots, + MutableSpan knots) { knots.slice(0, custom_knots.size()).copy_from(custom_knots); if (cyclic) { @@ -112,6 +112,24 @@ void calculate_knots(const int points_num, } } +void load_curve_knots(const KnotsMode mode, + const int points_num, + const int8_t order, + const bool cyclic, + const IndexRange curve_knots, + const Span custom_knots, + MutableSpan knots) +{ + if (mode == NURBS_KNOT_MODE_CUSTOM) { + BLI_assert(!custom_knots.is_empty()); + BLI_assert(!curve_knots.is_empty()); + copy_custom_knots(order, cyclic, custom_knots.slice(curve_knots), knots); + } + else { + calculate_knots(points_num, mode, order, cyclic, knots); + } +} + Vector calculate_multiplicity_sequence(const Span knots) { Vector multiplicity; diff --git a/source/blender/blenkernel/intern/curves_geometry.cc b/source/blender/blenkernel/intern/curves_geometry.cc index ad8a6f36b5f..c0b2042c265 100644 --- a/source/blender/blenkernel/intern/curves_geometry.cc +++ b/source/blender/blenkernel/intern/curves_geometry.cc @@ -774,15 +774,14 @@ void CurvesGeometry::ensure_nurbs_basis_cache() const } const int knots_num = curves::nurbs::knots_num(points.size(), order, is_cyclic); knots.reinitialize(knots_num); - /* Some curves edit tools might not support custom knots, for example GP extrude. - * These tools create empty `custom_knots` with mode NURBS_KNOT_MODE_CUSTOM. */ - if (mode == NURBS_KNOT_MODE_CUSTOM && !custom_knots.is_empty()) { - bke::curves::nurbs::copy_custom_knots( - order, is_cyclic, custom_knots.slice(custom_knots_by_curve[curve_index]), knots); - } - else { - curves::nurbs::calculate_knots(points.size(), mode, order, is_cyclic, knots); - } + curves::nurbs::load_curve_knots(mode, + points.size(), + order, + is_cyclic, + custom_knots_by_curve[curve_index], + custom_knots, + knots); + curves::nurbs::calculate_basis_cache( points.size(), evaluated_points.size(), order, is_cyclic, knots, r_data[curve_index]); } @@ -1589,6 +1588,15 @@ void CurvesGeometry::remove_curves(const IndexMask &curves_to_delete, *this = curves_copy_curve_selection(*this, curves_to_copy, attribute_filter); } +static void reverse_custom_knots(MutableSpan custom_knots) +{ + const float last = custom_knots.last(); + custom_knots.reverse(); + for (float &knot_value : custom_knots) { + knot_value = last - knot_value; + } +} + template static void reverse_curve_point_data(const CurvesGeometry &curves, const IndexMask &curve_selection, @@ -1651,6 +1659,17 @@ void CurvesGeometry::reverse_curves(const IndexMask &curves_to_reverse) return; }); + if (this->nurbs_has_custom_knots()) { + const OffsetIndices custom_knots_by_curve = this->nurbs_custom_knots_by_curve(); + MutableSpan custom_knots = this->nurbs_custom_knots_for_write(); + curves_to_reverse.foreach_index(GrainSize(256), [&](const int64_t curve) { + const IndexRange curve_knots = custom_knots_by_curve[curve]; + if (!custom_knots.is_empty()) { + reverse_custom_knots(custom_knots.slice(curve_knots)); + } + }); + } + /* In order to maintain the shape of Bezier curves, handle attributes must reverse, but also the * values for the left and right must swap. Use a utility to swap and reverse at the same time, * to avoid loading the attribute twice. Generally we can expect the right layer to exist when diff --git a/source/blender/blenkernel/intern/curves_utils.cc b/source/blender/blenkernel/intern/curves_utils.cc index d6ef3ebc621..dd03af66cb7 100644 --- a/source/blender/blenkernel/intern/curves_utils.cc +++ b/source/blender/blenkernel/intern/curves_utils.cc @@ -9,6 +9,8 @@ #include "BKE_curves_utils.hh" #include "BKE_customdata.hh" +#include "BLI_array_utils.hh" + namespace blender::bke::curves { IndexMask curve_to_point_selection(OffsetIndices points_by_curve, @@ -214,4 +216,63 @@ void write_all_positions(bke::CurvesGeometry &curves, } // namespace bezier +namespace nurbs { + +void gather_custom_knots(const bke::CurvesGeometry &src, + const IndexMask &src_curves, + const int dst_curve_offset, + bke::CurvesGeometry &dst) +{ + const OffsetIndices src_knots_by_curve = src.nurbs_custom_knots_by_curve(); + const int start_offset = dst.nurbs_custom_knots_by_curve()[dst_curve_offset].start(); + Array dst_offsets(src_curves.size() + 1); + + offset_indices::gather_selected_offsets( + src_knots_by_curve, src_curves, start_offset, dst_offsets); + + array_utils::gather_group_to_group(src_knots_by_curve, + dst_offsets.as_span(), + src_curves, + src.nurbs_custom_knots(), + dst.nurbs_custom_knots_for_write()); +} + +void update_custom_knot_modes(const IndexMask &mask, + const KnotsMode mode_for_regular, + const KnotsMode mode_for_cyclic, + bke::CurvesGeometry &curves) +{ + const VArray cyclic = curves.cyclic(); + MutableSpan knot_modes = curves.nurbs_knots_modes_for_write(); + mask.foreach_index(GrainSize(512), [&](const int64_t curve) { + int8_t &knot_mode = knot_modes[curve]; + if (knot_mode == NURBS_KNOT_MODE_CUSTOM) { + knot_mode = cyclic[curve] ? mode_for_cyclic : mode_for_regular; + } + }); + curves.nurbs_custom_knots_update_size(); +} + +void copy_custom_knots(const bke::CurvesGeometry &src_curves, + const IndexMask &exclude_curves, + bke::CurvesGeometry &dst_curves) +{ + BLI_assert(src_curves.curves_num() == dst_curves.curves_num()); + + if (src_curves.nurbs_has_custom_knots()) { + /* Ensure excluded curves don't have NURBS_KNOT_MODE_CUSTOM set. */ + bke::curves::nurbs::update_custom_knot_modes( + exclude_curves, NURBS_KNOT_MODE_NORMAL, NURBS_KNOT_MODE_NORMAL, dst_curves); + IndexMaskMemory memory; + bke::curves::nurbs::gather_custom_knots( + src_curves, + IndexMask::from_difference( + src_curves.nurbs_custom_knot_curves(memory), exclude_curves, memory), + 0, + dst_curves); + } +} + +} // namespace nurbs + } // namespace blender::bke::curves diff --git a/source/blender/blenloader/intern/versioning_450.cc b/source/blender/blenloader/intern/versioning_450.cc index 7ba0ed7d857..e5b87403e2a 100644 --- a/source/blender/blenloader/intern/versioning_450.cc +++ b/source/blender/blenloader/intern/versioning_450.cc @@ -29,7 +29,10 @@ #include "BKE_anim_data.hh" #include "BKE_animsys.h" #include "BKE_armature.hh" +#include "BKE_curves.hh" +#include "BKE_customdata.hh" #include "BKE_fcurve.hh" +#include "BKE_grease_pencil.hh" #include "BKE_main.hh" #include "BKE_mesh_legacy_convert.hh" #include "BKE_node.hh" @@ -68,6 +71,49 @@ static void version_fix_fcurve_noise_offset(FCurve &fcurve) } } +/** + * Fixes situation when `CurvesGeometry` instance has curves with `NURBS_KNOT_MODE_CUSTOM`, but has + * no custom knots. + */ +static void fix_curve_nurbs_knot_mode_custom(Main *bmain) +{ + auto fix_curves = [](blender::bke::CurvesGeometry &curves) { + if (curves.custom_knots != nullptr) { + return; + } + + int8_t *knot_modes = static_cast(CustomData_get_layer_named_for_write( + &curves.curve_data, CD_PROP_INT8, "knots_mode", curves.curve_num)); + if (knot_modes == nullptr) { + return; + } + + for (const int curve : curves.curves_range()) { + int8_t &knot_mode = knot_modes[curve]; + if (knot_mode == NURBS_KNOT_MODE_CUSTOM) { + knot_mode = NURBS_KNOT_MODE_NORMAL; + } + } + curves.nurbs_custom_knots_update_size(); + }; + + LISTBASE_FOREACH (Curves *, curves_id, &bmain->hair_curves) { + blender::bke::CurvesGeometry &curves = curves_id->geometry.wrap(); + fix_curves(curves); + } + + LISTBASE_FOREACH (GreasePencil *, grease_pencil, &bmain->grease_pencils) { + for (GreasePencilDrawingBase *base : grease_pencil->drawings()) { + if (base->type != GP_DRAWING) { + continue; + } + blender::bke::greasepencil::Drawing &drawing = + reinterpret_cast(base)->wrap(); + fix_curves(drawing.strokes_for_write()); + } + } +} + static void nlastrips_apply_fcurve_versioning(ListBase &strips) { LISTBASE_FOREACH (NlaStrip *, strip, &strips) { @@ -6184,6 +6230,10 @@ void blo_do_versions_450(FileData * /*fd*/, Library * /*lib*/, Main *bmain) FOREACH_NODETREE_END; } + if (!MAIN_VERSION_FILE_ATLEAST(bmain, 405, 86)) { + fix_curve_nurbs_knot_mode_custom(bmain); + } + /* Always run this versioning (keep at the bottom of the function). Meshes are written with the * legacy format which always needs to be converted to the new format on file load. To be moved * to a subversion check in 5.0. */ diff --git a/source/blender/editors/curves/intern/curves_edit.cc b/source/blender/editors/curves/intern/curves_edit.cc index 02117e24130..9dfe1974dca 100644 --- a/source/blender/editors/curves/intern/curves_edit.cc +++ b/source/blender/editors/curves/intern/curves_edit.cc @@ -106,6 +106,7 @@ static void append_point_knots(const Span src_ranges, for (const int appended_curve : dst_to_src_curve.index_range()) { const int dst_curve = appended_curve + old_curves_num; if (knot_modes[dst_curve] != NURBS_KNOT_MODE_CUSTOM) { + range++; continue; } const int src_curve = dst_to_src_curve[appended_curve]; @@ -244,12 +245,7 @@ static void append_curve_knots(const IndexMask &mask, bke::CurvesGeometry &curve { curves.nurbs_custom_knots_update_size(); const int old_curves_num = curves.curves_num() - mask.size(); - const OffsetIndices knots_by_curve = curves.nurbs_custom_knots_by_curve(); - MutableSpan knots = curves.nurbs_custom_knots_for_write(); - mask.foreach_index(GrainSize(512), [&](const int src_curve, const int appended_curve) { - const int dst_curve = old_curves_num + appended_curve; - knots.slice(knots_by_curve[dst_curve]).copy_from(knots.slice(knots_by_curve[src_curve])); - }); + bke::curves::nurbs::gather_custom_knots(curves, mask, old_curves_num, curves); } void duplicate_curves(bke::CurvesGeometry &curves, const IndexMask &mask) @@ -703,6 +699,10 @@ void resize_curves(bke::CurvesGeometry &curves, }); dst_curves.update_curve_types(); + if (dst_curves.nurbs_has_custom_knots()) { + bke::curves::nurbs::update_custom_knot_modes( + dst_curves.curves_range(), NURBS_KNOT_MODE_NORMAL, NURBS_KNOT_MODE_NORMAL, dst_curves); + } /* Move the result into `curves`. */ curves = std::move(dst_curves); diff --git a/source/blender/editors/grease_pencil/intern/grease_pencil_edit.cc b/source/blender/editors/grease_pencil/intern/grease_pencil_edit.cc index 1bf2e056cc2..0cfdeb86cb2 100644 --- a/source/blender/editors/grease_pencil/intern/grease_pencil_edit.cc +++ b/source/blender/editors/grease_pencil/intern/grease_pencil_edit.cc @@ -1217,6 +1217,11 @@ static bke::CurvesGeometry set_start_point(const bke::CurvesGeometry &curves, dst_attributes); dst_curves.update_curve_types(); + /* TODO: change to copying knots by point. */ + if (curves.nurbs_has_custom_knots()) { + bke::curves::nurbs::update_custom_knot_modes( + dst_curves.curves_range(), NURBS_KNOT_MODE_NORMAL, NURBS_KNOT_MODE_NORMAL, dst_curves); + } return dst_curves; } @@ -3066,6 +3071,24 @@ static bke::CurvesGeometry extrude_grease_pencil_curves(const bke::CurvesGeometr dst_attributes); dst.update_curve_types(); + if (src.nurbs_has_custom_knots()) { + IndexMaskMemory memory; + const VArray curve_types = src.curve_types(); + const VArray knot_modes = dst.nurbs_knots_modes(); + const OffsetIndices dst_points_by_curve = dst.points_by_curve(); + const IndexMask include_curves = IndexMask::from_predicate( + src.curves_range(), GrainSize(512), memory, [&](const int64_t curve_index) { + return curve_types[curve_index] == CURVE_TYPE_NURBS && + knot_modes[curve_index] == NURBS_KNOT_MODE_CUSTOM && + points_by_curve[curve_index].size() == dst_points_by_curve[curve_index].size(); + }); + bke::curves::nurbs::update_custom_knot_modes( + include_curves.complement(dst.curves_range(), memory), + NURBS_KNOT_MODE_ENDPOINT, + NURBS_KNOT_MODE_NORMAL, + dst); + bke::curves::nurbs::gather_custom_knots(src, include_curves, 0, dst); + } return dst; } diff --git a/source/blender/editors/grease_pencil/intern/grease_pencil_geom.cc b/source/blender/editors/grease_pencil/intern/grease_pencil_geom.cc index 3277a8e7340..aab3a46e5d6 100644 --- a/source/blender/editors/grease_pencil/intern/grease_pencil_geom.cc +++ b/source/blender/editors/grease_pencil/intern/grease_pencil_geom.cc @@ -341,6 +341,10 @@ blender::bke::CurvesGeometry curves_merge_by_distance(const bke::CurvesGeometry }); }); + if (dst_curves.nurbs_has_custom_knots()) { + bke::curves::nurbs::update_custom_knot_modes( + dst_curves.curves_range(), NURBS_KNOT_MODE_NORMAL, NURBS_KNOT_MODE_NORMAL, dst_curves); + } return dst_curves; } diff --git a/source/blender/geometry/intern/curves_remove_and_split.cc b/source/blender/geometry/intern/curves_remove_and_split.cc index fb51095aa71..75b5413c4a0 100644 --- a/source/blender/geometry/intern/curves_remove_and_split.cc +++ b/source/blender/geometry/intern/curves_remove_and_split.cc @@ -6,6 +6,7 @@ #include "BKE_attribute.hh" #include "BKE_curves.hh" +#include "BKE_curves_utils.hh" #include "BKE_deform.hh" #include "GEO_curves_remove_and_split.hh" @@ -109,6 +110,10 @@ bke::CurvesGeometry remove_points_and_split(const bke::CurvesGeometry &curves, dst_curves.update_curve_types(); dst_curves.remove_attributes_based_on_types(); + if (curves.nurbs_has_custom_knots()) { + bke::curves::nurbs::update_custom_knot_modes( + dst_curves.curves_range(), NURBS_KNOT_MODE_NORMAL, NURBS_KNOT_MODE_NORMAL, dst_curves); + } return dst_curves; } diff --git a/source/blender/geometry/intern/extend_curves.cc b/source/blender/geometry/intern/extend_curves.cc index 4a2efc8d443..c3bc7fbc0a2 100644 --- a/source/blender/geometry/intern/extend_curves.cc +++ b/source/blender/geometry/intern/extend_curves.cc @@ -360,7 +360,10 @@ bke::CurvesGeometry extend_curves(bke::CurvesGeometry &src_curves, } } }); - + if (src_curves.nurbs_has_custom_knots()) { + bke::curves::nurbs::update_custom_knot_modes( + dst_curves.curves_range(), NURBS_KNOT_MODE_NORMAL, NURBS_KNOT_MODE_NORMAL, dst_curves); + } return dst_curves; } diff --git a/source/blender/geometry/intern/fillet_curves.cc b/source/blender/geometry/intern/fillet_curves.cc index 95c058a3974..e532b90e2ca 100644 --- a/source/blender/geometry/intern/fillet_curves.cc +++ b/source/blender/geometry/intern/fillet_curves.cc @@ -516,7 +516,10 @@ static bke::CurvesGeometry fillet_curves(const bke::CurvesGeometry &src_curves, dst_points_by_curve, unselected, dst_attributes); - + if (src_curves.nurbs_has_custom_knots()) { + bke::curves::nurbs::update_custom_knot_modes( + dst_curves.curves_range(), NURBS_KNOT_MODE_NORMAL, NURBS_KNOT_MODE_NORMAL, dst_curves); + } return dst_curves; } diff --git a/source/blender/geometry/intern/merge_curves.cc b/source/blender/geometry/intern/merge_curves.cc index a1d5936f9fb..1e0bec6a5de 100644 --- a/source/blender/geometry/intern/merge_curves.cc +++ b/source/blender/geometry/intern/merge_curves.cc @@ -5,6 +5,7 @@ #include "BLI_array_utils.hh" #include "BLI_stack.hh" +#include "BKE_curves_utils.hh" #include "BKE_deform.hh" #include "GEO_merge_curves.hh" @@ -305,6 +306,16 @@ bke::CurvesGeometry curves_merge_endpoints(const bke::CurvesGeometry &src_curves bke::CurvesGeometry merged_curves = join_curves_ranges(ordered_curves, joined_curves_by_new); merged_curves.cyclic_for_write().copy_from(cyclic); + /** + * `curves_merge_endpoints` seems to be working only with CURVE_TYPE_POLY, still adding this here + * in advance. + */ + if (src_curves.nurbs_has_custom_knots()) { + bke::curves::nurbs::update_custom_knot_modes(merged_curves.curves_range(), + NURBS_KNOT_MODE_NORMAL, + NURBS_KNOT_MODE_NORMAL, + merged_curves); + } return merged_curves; } diff --git a/source/blender/geometry/intern/reorder.cc b/source/blender/geometry/intern/reorder.cc index 559bf75d106..88c4900060a 100644 --- a/source/blender/geometry/intern/reorder.cc +++ b/source/blender/geometry/intern/reorder.cc @@ -6,6 +6,7 @@ #include "BKE_attribute_filters.hh" #include "BKE_attribute_math.hh" #include "BKE_curves.hh" +#include "BKE_curves_utils.hh" #include "BKE_deform.hh" #include "BKE_geometry_set.hh" #include "BKE_instances.hh" @@ -291,6 +292,12 @@ static void copy_and_reorder_curves(const bke::CurvesGeometry &src_curves, attribute_filter, dst_curves.attributes_for_write()); dst_curves.tag_topology_changed(); + if (src_curves.nurbs_has_custom_knots()) { + dst_curves.nurbs_custom_knots_update_size(); + IndexMaskMemory memory; + bke::curves::nurbs::gather_custom_knots( + src_curves, IndexMask::from_indices(old_by_new_map, memory), 0, dst_curves); + } } static void copy_and_reorder_instaces(const bke::Instances &src_instances, diff --git a/source/blender/geometry/intern/resample_curves.cc b/source/blender/geometry/intern/resample_curves.cc index ad87163debf..a2fd28deade 100644 --- a/source/blender/geometry/intern/resample_curves.cc +++ b/source/blender/geometry/intern/resample_curves.cc @@ -441,6 +441,7 @@ static CurvesGeometry resample_to_uniform(const CurvesGeometry &src_curves, resample_to_uniform(src_curves, selection, output_ids, dst_curves); + bke::curves::nurbs::copy_custom_knots(src_curves, selection, dst_curves); return dst_curves; } @@ -475,6 +476,7 @@ CurvesGeometry resample_to_count(const CurvesGeometry &src_curves, resample_to_uniform(src_curves, selection, output_ids, dst_curves); + bke::curves::nurbs::copy_custom_knots(src_curves, selection, dst_curves); return dst_curves; } @@ -526,6 +528,7 @@ CurvesGeometry resample_to_length(const CurvesGeometry &src_curves, resample_to_uniform(src_curves, selection, output_ids, dst_curves); + bke::curves::nurbs::copy_custom_knots(src_curves, selection, dst_curves); return dst_curves; } @@ -622,6 +625,7 @@ CurvesGeometry resample_to_evaluated(const CurvesGeometry &src_curves, attribute.finish(); } + bke::curves::nurbs::copy_custom_knots(src_curves, selection, dst_curves); return dst_curves; } diff --git a/source/blender/geometry/intern/set_curve_type.cc b/source/blender/geometry/intern/set_curve_type.cc index a401e50afd8..43698082de9 100644 --- a/source/blender/geometry/intern/set_curve_type.cc +++ b/source/blender/geometry/intern/set_curve_type.cc @@ -450,7 +450,7 @@ static bke::CurvesGeometry convert_curves_to_bezier(const bke::CurvesGeometry &s attribute.dst.finish(); } - + bke::curves::nurbs::copy_custom_knots(src_curves, selection, dst_curves); return dst_curves; } @@ -623,7 +623,7 @@ static bke::CurvesGeometry convert_curves_to_nurbs(const bke::CurvesGeometry &sr attribute.dst.finish(); } - + bke::curves::nurbs::copy_custom_knots(src_curves, IndexMask(), dst_curves); return dst_curves; } @@ -754,7 +754,7 @@ static bke::CurvesGeometry convert_curves_to_catmull_rom_or_poly( attribute.dst.finish(); } - + bke::curves::nurbs::copy_custom_knots(src_curves, selection, dst_curves); return dst_curves; } diff --git a/source/blender/geometry/intern/subdivide_curves.cc b/source/blender/geometry/intern/subdivide_curves.cc index d14a3c34f15..3442dc58739 100644 --- a/source/blender/geometry/intern/subdivide_curves.cc +++ b/source/blender/geometry/intern/subdivide_curves.cc @@ -419,6 +419,7 @@ bke::CurvesGeometry subdivide_curves(const bke::CurvesGeometry &src_curves, attribute.dst.finish(); } + bke::curves::nurbs::copy_custom_knots(src_curves, selection, dst_curves); return dst_curves; } diff --git a/source/blender/geometry/intern/trim_curves.cc b/source/blender/geometry/intern/trim_curves.cc index c7dfdce85ae..381d2610450 100644 --- a/source/blender/geometry/intern/trim_curves.cc +++ b/source/blender/geometry/intern/trim_curves.cc @@ -1070,6 +1070,10 @@ bke::CurvesGeometry trim_curves(const bke::CurvesGeometry &src_curves, dst_curves.remove_attributes_based_on_types(); dst_curves.tag_topology_changed(); + if (src_curves.nurbs_has_custom_knots()) { + bke::curves::nurbs::update_custom_knot_modes( + dst_curves.curves_range(), NURBS_KNOT_MODE_NORMAL, NURBS_KNOT_MODE_NORMAL, dst_curves); + } return dst_curves; } diff --git a/source/blender/io/usd/intern/usd_writer_curves.cc b/source/blender/io/usd/intern/usd_writer_curves.cc index bbb34a4e6c4..340dc170939 100644 --- a/source/blender/io/usd/intern/usd_writer_curves.cc +++ b/source/blender/io/usd/intern/usd_writer_curves.cc @@ -304,14 +304,13 @@ static void populate_curve_props_for_nurbs(const bke::CurvesGeometry &curves, const int knots_num = bke::curves::nurbs::knots_num(tot_points, order, is_cyclic); Array temp_knots(knots_num); - - if (mode == NURBS_KNOT_MODE_CUSTOM) { - bke::curves::nurbs::copy_custom_knots( - order, is_cyclic, custom_knots.slice(custom_knots_by_curve[i_curve]), temp_knots); - } - else { - bke::curves::nurbs::calculate_knots(tot_points, mode, order, is_cyclic, temp_knots); - } + bke::curves::nurbs::load_curve_knots(mode, + tot_points, + order, + is_cyclic, + custom_knots_by_curve[i_curve], + custom_knots, + temp_knots); /* Knots should be the concatenation of all batched curves. * https://graphics.pixar.com/usd/dev/api/class_usd_geom_nurbs_curves.html#details */