Changeset View
Changeset View
Standalone View
Standalone View
source/blender/blenkernel/intern/curves_geometry.cc
| /* SPDX-License-Identifier: GPL-2.0-or-later */ | /* SPDX-License-Identifier: GPL-2.0-or-later */ | ||||
| /** \file | /** \file | ||||
| * \ingroup bke | * \ingroup bke | ||||
| */ | */ | ||||
| #include <mutex> | #include <mutex> | ||||
| #include <utility> | #include <utility> | ||||
| #include "MEM_guardedalloc.h" | #include "MEM_guardedalloc.h" | ||||
| #include "BLI_bounds.hh" | #include "BLI_bounds.hh" | ||||
| #include "BLI_index_mask_ops.hh" | #include "BLI_index_mask_ops.hh" | ||||
| #include "BLI_length_parameterize.hh" | #include "BLI_length_parameterize.hh" | ||||
| #include "BLI_math_rotation.hh" | |||||
| #include "DNA_curves_types.h" | #include "DNA_curves_types.h" | ||||
| #include "BKE_attribute_math.hh" | #include "BKE_attribute_math.hh" | ||||
| #include "BKE_curves.hh" | #include "BKE_curves.hh" | ||||
| namespace blender::bke { | namespace blender::bke { | ||||
| static const std::string ATTR_POSITION = "position"; | static const std::string ATTR_POSITION = "position"; | ||||
| static const std::string ATTR_RADIUS = "radius"; | static const std::string ATTR_RADIUS = "radius"; | ||||
| static const std::string ATTR_TILT = "tilt"; | |||||
| static const std::string ATTR_CURVE_TYPE = "curve_type"; | static const std::string ATTR_CURVE_TYPE = "curve_type"; | ||||
| static const std::string ATTR_CYCLIC = "cyclic"; | static const std::string ATTR_CYCLIC = "cyclic"; | ||||
| static const std::string ATTR_RESOLUTION = "resolution"; | static const std::string ATTR_RESOLUTION = "resolution"; | ||||
| static const std::string ATTR_NORMAL_MODE = "normal_mode"; | static const std::string ATTR_NORMAL_MODE = "normal_mode"; | ||||
| static const std::string ATTR_HANDLE_TYPE_LEFT = "handle_type_left"; | static const std::string ATTR_HANDLE_TYPE_LEFT = "handle_type_left"; | ||||
| static const std::string ATTR_HANDLE_TYPE_RIGHT = "handle_type_right"; | static const std::string ATTR_HANDLE_TYPE_RIGHT = "handle_type_right"; | ||||
| static const std::string ATTR_HANDLE_POSITION_LEFT = "handle_left"; | static const std::string ATTR_HANDLE_POSITION_LEFT = "handle_left"; | ||||
| static const std::string ATTR_HANDLE_POSITION_RIGHT = "handle_right"; | static const std::string ATTR_HANDLE_POSITION_RIGHT = "handle_right"; | ||||
| ▲ Show 20 Lines • Show All 679 Lines • ▼ Show 20 Lines | if (!bezier_mask.is_empty()) { | ||||
| }); | }); | ||||
| } | } | ||||
| }); | }); | ||||
| this->runtime->tangent_cache_dirty = false; | this->runtime->tangent_cache_dirty = false; | ||||
| return this->runtime->evaluated_tangent_cache; | return this->runtime->evaluated_tangent_cache; | ||||
| } | } | ||||
| static void rotate_directions_around_axes(MutableSpan<float3> directions, | |||||
| const Span<float3> axes, | |||||
| const Span<float> angles) | |||||
| { | |||||
| for (const int i : directions.index_range()) { | |||||
| directions[i] = math::rotate_direction_around_axis(directions[i], axes[i], angles[i]); | |||||
| } | |||||
| } | |||||
| Span<float3> CurvesGeometry::evaluated_normals() const | Span<float3> CurvesGeometry::evaluated_normals() const | ||||
| { | { | ||||
| if (!this->runtime->normal_cache_dirty) { | if (!this->runtime->normal_cache_dirty) { | ||||
| return this->runtime->evaluated_normal_cache; | return this->runtime->evaluated_normal_cache; | ||||
| } | } | ||||
| /* A double checked lock. */ | /* A double checked lock. */ | ||||
| std::scoped_lock lock{this->runtime->normal_cache_mutex}; | std::scoped_lock lock{this->runtime->normal_cache_mutex}; | ||||
| if (!this->runtime->normal_cache_dirty) { | if (!this->runtime->normal_cache_dirty) { | ||||
| return this->runtime->evaluated_normal_cache; | return this->runtime->evaluated_normal_cache; | ||||
| } | } | ||||
| threading::isolate_task([&]() { | threading::isolate_task([&]() { | ||||
| const Span<float3> evaluated_tangents = this->evaluated_tangents(); | const Span<float3> evaluated_tangents = this->evaluated_tangents(); | ||||
| const VArray<bool> cyclic = this->cyclic(); | const VArray<bool> cyclic = this->cyclic(); | ||||
| const VArray<int8_t> normal_mode = this->normal_mode(); | const VArray<int8_t> normal_mode = this->normal_mode(); | ||||
| const VArray<int8_t> types = this->curve_types(); | |||||
| Span<float> tilts; | |||||
| if (const float *tilt_data = (float *)CustomData_get_layer_named( | |||||
JacquesLucke: Why not use an accessor methods for tilt like for other attributes? | |||||
HooglyBooglyAuthorUnsubmitted Done Inline ActionsI guess it was a decision in favor of a longer term solution (a proper attribute API on CurvesGeometry) rather than adding accessor methods for every single named attribute. Tilt is a pretty generic attribute in the scheme of things, which makes me think the accessor methods are not really necessary for it long term. However I don't feel strongly about that at all, I'll add it if you prefer for sure. HooglyBoogly: I guess it was a decision in favor of a longer term solution (a proper attribute API on… | |||||
JacquesLuckeUnsubmitted Not Done Inline ActionsWell, tilt has an impact on evaluated_normals, so imo it is at least as important as those. I think having a separate accessor would be good. JacquesLucke: Well, tilt has an impact on `evaluated_normals`, so imo it is at least as important as those. I… | |||||
| &this->point_data, CD_PROP_FLOAT, ATTR_TILT.c_str())) { | |||||
| tilts = {tilt_data, this->points_num()}; | |||||
| } | |||||
| this->runtime->evaluated_normal_cache.resize(this->evaluated_points_num()); | this->runtime->evaluated_normal_cache.resize(this->evaluated_points_num()); | ||||
| MutableSpan<float3> evaluated_normals = this->runtime->evaluated_normal_cache; | MutableSpan<float3> evaluated_normals = this->runtime->evaluated_normal_cache; | ||||
| threading::parallel_for(this->curves_range(), 128, [&](IndexRange curves_range) { | threading::parallel_for(this->curves_range(), 128, [&](IndexRange curves_range) { | ||||
| /* Reuse a buffer for the evaluated tilts. */ | |||||
| Vector<float> evaluated_tilts; | |||||
| for (const int curve_index : curves_range) { | for (const int curve_index : curves_range) { | ||||
| const IndexRange evaluated_points = this->evaluated_points_for_curve(curve_index); | const IndexRange evaluated_points = this->evaluated_points_for_curve(curve_index); | ||||
| if (UNLIKELY(evaluated_points.is_empty())) { | if (UNLIKELY(evaluated_points.is_empty())) { | ||||
| continue; | continue; | ||||
| } | } | ||||
| switch (normal_mode[curve_index]) { | switch (normal_mode[curve_index]) { | ||||
| case NORMAL_MODE_Z_UP: | case NORMAL_MODE_Z_UP: | ||||
| curves::poly::calculate_normals_z_up(evaluated_tangents.slice(evaluated_points), | curves::poly::calculate_normals_z_up(evaluated_tangents.slice(evaluated_points), | ||||
| evaluated_normals.slice(evaluated_points)); | evaluated_normals.slice(evaluated_points)); | ||||
| break; | break; | ||||
| case NORMAL_MODE_MINIMUM_TWIST: | case NORMAL_MODE_MINIMUM_TWIST: | ||||
| curves::poly::calculate_normals_minimum(evaluated_tangents.slice(evaluated_points), | curves::poly::calculate_normals_minimum(evaluated_tangents.slice(evaluated_points), | ||||
| cyclic[curve_index], | cyclic[curve_index], | ||||
| evaluated_normals.slice(evaluated_points)); | evaluated_normals.slice(evaluated_points)); | ||||
| break; | break; | ||||
| } | } | ||||
| /* If the "tilt" attribute exists, rotate the normals around the tangents by the | |||||
| * evaluated angles. We can avoid copying the tilts to evaluate them for poly curves. */ | |||||
| if (!tilts.is_empty()) { | |||||
| const IndexRange points = this->points_for_curve(curve_index); | |||||
| if (types[curve_index] == CURVE_TYPE_POLY) { | |||||
| rotate_directions_around_axes(evaluated_normals.slice(evaluated_points), | |||||
| evaluated_tangents.slice(evaluated_points), | |||||
| tilts.slice(points)); | |||||
| } | |||||
| else { | |||||
| evaluated_tilts.clear(); | |||||
| evaluated_tilts.resize(evaluated_points.size()); | |||||
| this->interpolate_to_evaluated( | |||||
| curve_index, tilts.slice(points), evaluated_tilts.as_mutable_span()); | |||||
| rotate_directions_around_axes(evaluated_normals.slice(evaluated_points), | |||||
| evaluated_tangents.slice(evaluated_points), | |||||
| evaluated_tilts.as_span()); | |||||
| } | |||||
| } | |||||
| } | } | ||||
| }); | }); | ||||
| }); | }); | ||||
| this->runtime->normal_cache_dirty = false; | this->runtime->normal_cache_dirty = false; | ||||
| return this->runtime->evaluated_normal_cache; | return this->runtime->evaluated_normal_cache; | ||||
| } | } | ||||
| ▲ Show 20 Lines • Show All 569 Lines • Show Last 20 Lines | |||||
Why not use an accessor methods for tilt like for other attributes?