/* * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "BLI_array.hh" #include "BLI_index_range.hh" #include "BLI_listbase.h" #include "BLI_map.hh" #include "BLI_span.hh" #include "BLI_string_ref.hh" #include "BLI_task.hh" #include "BLI_vector.hh" #include "DNA_curve_types.h" #include "BKE_curve.h" #include "BKE_spline.hh" using blender::Array; using blender::float3; using blender::float4x4; using blender::IndexRange; using blender::Map; using blender::MutableSpan; using blender::Span; using blender::StringRefNull; using blender::Vector; blender::Span CurveEval::splines() const { return splines_; } blender::MutableSpan CurveEval::splines() { return splines_; } /** * \return True if the curve contains a spline with the given type. * * \note If you are looping over all of the splines in the same scope anyway, * it's better to avoid calling this function, in case there are many splines. */ bool CurveEval::has_spline_with_type(const Spline::Type type) const { for (const SplinePtr &spline : this->splines()) { if (spline->type() == type) { return true; } } return false; } void CurveEval::resize(const int size) { splines_.resize(size); attributes.reallocate(size); } /** * \warning Call #reallocate on the spline's attributes after adding all splines. */ void CurveEval::add_spline(SplinePtr spline) { splines_.append(std::move(spline)); } void CurveEval::remove_splines(blender::IndexMask mask) { for (int i = mask.size() - 1; i >= 0; i--) { splines_.remove_and_reorder(mask.indices()[i]); } } void CurveEval::translate(const float3 &translation) { for (SplinePtr &spline : this->splines()) { spline->translate(translation); spline->mark_cache_invalid(); } } void CurveEval::transform(const float4x4 &matrix) { for (SplinePtr &spline : this->splines()) { spline->transform(matrix); } } void CurveEval::bounds_min_max(float3 &min, float3 &max, const bool use_evaluated) const { for (const SplinePtr &spline : this->splines()) { spline->bounds_min_max(min, max, use_evaluated); } } /** * Return the start indices for each of the curve spline's evaluated points, as if they were part * of a flattened array. This can be used to facilitate parallelism by avoiding the need to * accumulate an offset while doing more complex calculations. * * \note The result array is one longer than the spline count; the last element is the total size. */ blender::Array CurveEval::control_point_offsets() const { Array offsets(splines_.size() + 1); int offset = 0; for (const int i : splines_.index_range()) { offsets[i] = offset; offset += splines_[i]->size(); } offsets.last() = offset; return offsets; } /** * Exactly like #control_point_offsets, but uses the number of evaluated points instead. */ blender::Array CurveEval::evaluated_point_offsets() const { Array offsets(splines_.size() + 1); int offset = 0; for (const int i : splines_.index_range()) { offsets[i] = offset; offset += splines_[i]->evaluated_points_size(); } offsets.last() = offset; return offsets; } static BezierSpline::HandleType handle_type_from_dna_bezt(const eBezTriple_Handle dna_handle_type) { switch (dna_handle_type) { case HD_FREE: return BezierSpline::HandleType::Free; case HD_AUTO: return BezierSpline::HandleType::Auto; case HD_VECT: return BezierSpline::HandleType::Vector; case HD_ALIGN: return BezierSpline::HandleType::Align; case HD_AUTO_ANIM: return BezierSpline::HandleType::Auto; case HD_ALIGN_DOUBLESIDE: return BezierSpline::HandleType::Align; } BLI_assert_unreachable(); return BezierSpline::HandleType::Auto; } static Spline::NormalCalculationMode normal_mode_from_dna_curve(const int twist_mode) { switch (twist_mode) { case CU_TWIST_Z_UP: return Spline::NormalCalculationMode::ZUp; case CU_TWIST_MINIMUM: return Spline::NormalCalculationMode::Minimum; case CU_TWIST_TANGENT: return Spline::NormalCalculationMode::Tangent; } BLI_assert_unreachable(); return Spline::NormalCalculationMode::Minimum; } static NURBSpline::KnotsMode knots_mode_from_dna_nurb(const short flag) { switch (flag & (CU_NURB_ENDPOINT | CU_NURB_BEZIER)) { case CU_NURB_ENDPOINT: return NURBSpline::KnotsMode::EndPoint; case CU_NURB_BEZIER: return NURBSpline::KnotsMode::Bezier; default: return NURBSpline::KnotsMode::Normal; } BLI_assert_unreachable(); return NURBSpline::KnotsMode::Normal; } static SplinePtr spline_from_dna_bezier(const Nurb &nurb) { std::unique_ptr spline = std::make_unique(); spline->set_resolution(nurb.resolu); spline->set_cyclic(nurb.flagu & CU_NURB_CYCLIC); Span src_points{nurb.bezt, nurb.pntsu}; spline->resize(src_points.size()); MutableSpan positions = spline->positions(); MutableSpan handle_positions_left = spline->handle_positions_left(); MutableSpan handle_positions_right = spline->handle_positions_right(); MutableSpan handle_types_left = spline->handle_types_left(); MutableSpan handle_types_right = spline->handle_types_right(); MutableSpan radii = spline->radii(); MutableSpan tilts = spline->tilts(); blender::threading::parallel_for(src_points.index_range(), 2048, [&](IndexRange range) { for (const int i : range) { const BezTriple &bezt = src_points[i]; positions[i] = bezt.vec[1]; handle_positions_left[i] = bezt.vec[0]; handle_types_left[i] = handle_type_from_dna_bezt((eBezTriple_Handle)bezt.h1); handle_positions_right[i] = bezt.vec[2]; handle_types_right[i] = handle_type_from_dna_bezt((eBezTriple_Handle)bezt.h2); radii[i] = bezt.radius; tilts[i] = bezt.tilt; } }); return spline; } static SplinePtr spline_from_dna_nurbs(const Nurb &nurb) { std::unique_ptr spline = std::make_unique(); spline->set_resolution(nurb.resolu); spline->set_cyclic(nurb.flagu & CU_NURB_CYCLIC); spline->set_order(nurb.orderu); spline->knots_mode = knots_mode_from_dna_nurb(nurb.flagu); Span src_points{nurb.bp, nurb.pntsu}; spline->resize(src_points.size()); MutableSpan positions = spline->positions(); MutableSpan weights = spline->weights(); MutableSpan radii = spline->radii(); MutableSpan tilts = spline->tilts(); blender::threading::parallel_for(src_points.index_range(), 2048, [&](IndexRange range) { for (const int i : range) { const BPoint &bp = src_points[i]; positions[i] = bp.vec; weights[i] = bp.vec[3]; radii[i] = bp.radius; tilts[i] = bp.tilt; } }); return spline; } static SplinePtr spline_from_dna_poly(const Nurb &nurb) { std::unique_ptr spline = std::make_unique(); spline->set_cyclic(nurb.flagu & CU_NURB_CYCLIC); Span src_points{nurb.bp, nurb.pntsu}; spline->resize(src_points.size()); MutableSpan positions = spline->positions(); MutableSpan radii = spline->radii(); MutableSpan tilts = spline->tilts(); blender::threading::parallel_for(src_points.index_range(), 2048, [&](IndexRange range) { for (const int i : range) { const BPoint &bp = src_points[i]; positions[i] = bp.vec; radii[i] = bp.radius; tilts[i] = bp.tilt; } }); return spline; } std::unique_ptr curve_eval_from_dna_curve(const Curve &dna_curve, const ListBase &nurbs_list) { Vector nurbs(nurbs_list); std::unique_ptr curve = std::make_unique(); curve->resize(nurbs.size()); MutableSpan splines = curve->splines(); blender::threading::parallel_for(nurbs.index_range(), 256, [&](IndexRange range) { for (const int i : range) { switch (nurbs[i]->type) { case CU_BEZIER: splines[i] = spline_from_dna_bezier(*nurbs[i]); break; case CU_NURBS: splines[i] = spline_from_dna_nurbs(*nurbs[i]); break; case CU_POLY: splines[i] = spline_from_dna_poly(*nurbs[i]); break; default: BLI_assert_unreachable(); break; } } }); /* Normal mode is stored separately in each spline to facilitate combining * splines from multiple curve objects, where the value may be different. */ const Spline::NormalCalculationMode normal_mode = normal_mode_from_dna_curve( dna_curve.twist_mode); for (SplinePtr &spline : curve->splines()) { spline->normal_mode = normal_mode; } return curve; } std::unique_ptr 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)); } /** * Check the invariants that curve control point attributes should always uphold, necessary * because attributes are stored on splines rather than in a flat array on the curve: * - The same set of attributes exists on every spline. * - Attributes with the same name have the same type on every spline. */ void CurveEval::assert_valid_point_attributes() const { #ifdef DEBUG if (splines_.size() == 0) { return; } const int layer_len = splines_.first()->attributes.data.totlayer; Map map; for (const SplinePtr &spline : splines_) { BLI_assert(spline->attributes.data.totlayer == layer_len); spline->attributes.foreach_attribute( [&](StringRefNull name, const AttributeMetaData &meta_data) { map.add_or_modify( name, [&](AttributeMetaData *map_data) { /* All unique attribute names should be added on the first spline. */ BLI_assert(spline == splines_.first()); *map_data = meta_data; }, [&](AttributeMetaData *map_data) { /* Attributes on different splines should all have the same type. */ BLI_assert(meta_data == *map_data); }); return true; }, ATTR_DOMAIN_POINT); } #endif }