From e700e1f71c1728359e8d54430d229021ec17dfa4 Mon Sep 17 00:00:00 2001 From: Philipp Oeser Date: Wed, 15 Jan 2025 15:54:43 +0100 Subject: [PATCH] Fix #115843: Expose curves sculpt collision distance The hardcoded value doesn't work well with real scale human heads for example (was already adjusted once in a76b5d3a077e). The result for too high values is a complete "freeze" of the whole curve (since the solution from e7606139ba has the problem that it keeps running into max iterations of the collision solver). As long as no better solver is implemented, it is better to have an adjustable value (to work on differently sizes objects) to not run into the above issue (same as the old particle hair system had) and show it in sculptmode next to the button which enables collision. This is done per `Curves` (same as the flag `CV_SCULPT_COLLISION_ENABLED`), similar to symmetry settings [alternatively, it could be part of `BrushCurvesSculptSettings` but I think it makes more sense in Curves] and then passed on to the `CurvesConstraintSolver`. Includes versioning code (to set the default for old files). Pull Request: https://projects.blender.org/blender/blender/pulls/132997 --- scripts/startup/bl_ui/space_view3d.py | 6 +++++- source/blender/blenkernel/BKE_blender_version.h | 2 +- source/blender/blenkernel/intern/curves.cc | 1 + source/blender/blenloader/intern/versioning_400.cc | 8 ++++++++ .../editors/sculpt_paint/curves_sculpt_brush.cc | 7 +++++-- .../blender/editors/sculpt_paint/curves_sculpt_comb.cc | 6 ++++-- .../editors/sculpt_paint/curves_sculpt_intern.hh | 4 +++- .../editors/sculpt_paint/curves_sculpt_pinch.cc | 6 ++++-- .../blender/editors/sculpt_paint/curves_sculpt_puff.cc | 6 ++++-- source/blender/geometry/GEO_curve_constraints.hh | 3 ++- source/blender/geometry/intern/curve_constraints.cc | 10 +++++----- source/blender/makesdna/DNA_curves_defaults.h | 1 + source/blender/makesdna/DNA_curves_types.h | 4 ++++ source/blender/makesrna/intern/rna_curves.cc | 8 ++++++++ 14 files changed, 55 insertions(+), 17 deletions(-) diff --git a/scripts/startup/bl_ui/space_view3d.py b/scripts/startup/bl_ui/space_view3d.py index c18dd8c9cc5..e258891fdd5 100644 --- a/scripts/startup/bl_ui/space_view3d.py +++ b/scripts/startup/bl_ui/space_view3d.py @@ -170,7 +170,11 @@ class VIEW3D_HT_tool_header(Header): sub.prop(ob.data, "use_mirror_y", text="Y", toggle=True) sub.prop(ob.data, "use_mirror_z", text="Z", toggle=True) - layout.prop(ob.data, "use_sculpt_collision", icon='MOD_PHYSICS', icon_only=True, toggle=True) + row = layout.row(align=True) + row.prop(ob.data, "use_sculpt_collision", icon='MOD_PHYSICS', icon_only=True, toggle=True) + sub = row.row(align=True) + sub.active = ob.data.use_sculpt_collision + sub.prop(ob.data, "surface_collision_distance") # Expand panels from the side-bar as popovers. popover_kw = {"space_type": 'VIEW_3D', "region_type": 'UI', "category": "Tool"} diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index 77f876a98c2..1cc3f9aabfa 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -31,7 +31,7 @@ extern "C" { /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION -#define BLENDER_FILE_SUBVERSION 22 +#define BLENDER_FILE_SUBVERSION 23 /* Minimum Blender version that supports reading file written with the current * version. Older Blender versions will test this and cancel loading the file, showing a warning to diff --git a/source/blender/blenkernel/intern/curves.cc b/source/blender/blenkernel/intern/curves.cc index 02e07380474..d9181ba23b7 100644 --- a/source/blender/blenkernel/intern/curves.cc +++ b/source/blender/blenkernel/intern/curves.cc @@ -315,6 +315,7 @@ void curves_copy_parameters(const Curves &src, Curves &dst) if (src.surface_uv_map != nullptr) { dst.surface_uv_map = BLI_strdup(src.surface_uv_map); } + dst.surface_collision_distance = src.surface_collision_distance; } CurvesSurfaceTransforms::CurvesSurfaceTransforms(const Object &curves_ob, const Object *surface_ob) diff --git a/source/blender/blenloader/intern/versioning_400.cc b/source/blender/blenloader/intern/versioning_400.cc index 68bfc3b83fa..7877af59bb8 100644 --- a/source/blender/blenloader/intern/versioning_400.cc +++ b/source/blender/blenloader/intern/versioning_400.cc @@ -5638,6 +5638,14 @@ void blo_do_versions_400(FileData *fd, Library * /*lib*/, Main *bmain) } } + if (!MAIN_VERSION_FILE_ATLEAST(bmain, 404, 23)) { + if (!DNA_struct_member_exists(fd->filesdna, "Curves", "float", "surface_collision_distance")) { + LISTBASE_FOREACH (Curves *, curves, &bmain->hair_curves) { + curves->surface_collision_distance = 0.005f; + } + } + } + /* Always run this versioning; meshes are written with the legacy format which always needs to * be converted to the new format on file load. Can be moved to a subversion check in a larger * breaking release. */ diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_brush.cc b/source/blender/editors/sculpt_paint/curves_sculpt_brush.cc index c5eb270fd73..e8b2d0fd52c 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_brush.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_brush.cc @@ -439,9 +439,11 @@ void report_invalid_uv_map(ReportList *reports) void CurvesConstraintSolver::initialize(const bke::CurvesGeometry &curves, const IndexMask &curve_selection, - const bool use_surface_collision) + const bool use_surface_collision, + const float surface_collision_distance) { use_surface_collision_ = use_surface_collision; + surface_collision_distance_ = surface_collision_distance; segment_lengths_.reinitialize(curves.points_num()); geometry::curve_constraints::compute_segment_lengths( curves.points_by_curve(), curves.positions(), curve_selection, segment_lengths_); @@ -463,7 +465,8 @@ void CurvesConstraintSolver::solve_step(bke::CurvesGeometry &curves, start_positions_, *surface, transforms, - curves.positions_for_write()); + curves.positions_for_write(), + surface_collision_distance_); start_positions_ = curves.positions(); } else { diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_comb.cc b/source/blender/editors/sculpt_paint/curves_sculpt_comb.cc index 2b7956bf7c3..100533fc10a 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_comb.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_comb.cc @@ -140,8 +140,10 @@ struct CombOperationExecutor { if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE || (U.uiflag & USER_ORBIT_SELECTION)) { this->initialize_spherical_brush_reference_point(); } - self_->constraint_solver_.initialize( - *curves_orig_, curve_selection_, curves_id_orig_->flag & CV_SCULPT_COLLISION_ENABLED); + self_->constraint_solver_.initialize(*curves_orig_, + curve_selection_, + curves_id_orig_->flag & CV_SCULPT_COLLISION_ENABLED, + curves_id_orig_->surface_collision_distance); self_->curve_lengths_.reinitialize(curves_orig_->curves_num()); const Span segment_lengths = self_->constraint_solver_.segment_lengths(); diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_intern.hh b/source/blender/editors/sculpt_paint/curves_sculpt_intern.hh index ca97d655a78..7077949d4cd 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_intern.hh +++ b/source/blender/editors/sculpt_paint/curves_sculpt_intern.hh @@ -162,13 +162,15 @@ void report_invalid_uv_map(ReportList *reports); struct CurvesConstraintSolver { private: bool use_surface_collision_; + float surface_collision_distance_; Array start_positions_; Array segment_lengths_; public: void initialize(const bke::CurvesGeometry &curves, const IndexMask &curve_selection, - const bool use_surface_collision); + const bool use_surface_collision, + const float surface_collision_distance); void solve_step(bke::CurvesGeometry &curves, const IndexMask &curve_selection, diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_pinch.cc b/source/blender/editors/sculpt_paint/curves_sculpt_pinch.cc index 29cafacdf58..b7339253566 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_pinch.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_pinch.cc @@ -125,8 +125,10 @@ struct PinchOperationExecutor { math::transform_point(transforms_.curves_to_world, self_->brush_3d_.position_cu)); } - self_->constraint_solver_.initialize( - *curves_, curve_selection_, curves_id_->flag & CV_SCULPT_COLLISION_ENABLED); + self_->constraint_solver_.initialize(*curves_, + curve_selection_, + curves_id_->flag & CV_SCULPT_COLLISION_ENABLED, + curves_id_->surface_collision_distance); } Array changed_curves(curves_->curves_num(), false); diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_puff.cc b/source/blender/editors/sculpt_paint/curves_sculpt_puff.cc index 0141811ea35..64d85c51a35 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_puff.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_puff.cc @@ -134,8 +134,10 @@ struct PuffOperationExecutor { math::transform_point(transforms_.curves_to_world, self_->brush_3d_.position_cu)); } - self_->constraint_solver_.initialize( - *curves_, curve_selection_, curves_id_->flag & CV_SCULPT_COLLISION_ENABLED); + self_->constraint_solver_.initialize(*curves_, + curve_selection_, + curves_id_->flag & CV_SCULPT_COLLISION_ENABLED, + curves_id_->surface_collision_distance); } Array curve_weights(curves_->curves_num(), 0.0f); diff --git a/source/blender/geometry/GEO_curve_constraints.hh b/source/blender/geometry/GEO_curve_constraints.hh index 0b70fc1a772..183611f6dd2 100644 --- a/source/blender/geometry/GEO_curve_constraints.hh +++ b/source/blender/geometry/GEO_curve_constraints.hh @@ -24,6 +24,7 @@ void solve_length_and_collision_constraints(OffsetIndices points_by_curve, Span start_positions, const Mesh &surface, const bke::CurvesSurfaceTransforms &transforms, - MutableSpan positions); + MutableSpan positions, + const float surface_collision_distance); } // namespace blender::geometry::curve_constraints diff --git a/source/blender/geometry/intern/curve_constraints.cc b/source/blender/geometry/intern/curve_constraints.cc index 5fbed9fe27d..a6d2c9c8183 100644 --- a/source/blender/geometry/intern/curve_constraints.cc +++ b/source/blender/geometry/intern/curve_constraints.cc @@ -67,13 +67,13 @@ void solve_length_and_collision_constraints(const OffsetIndices points_by_c const Span start_positions_cu, const Mesh &surface, const bke::CurvesSurfaceTransforms &transforms, - MutableSpan positions_cu) + MutableSpan positions_cu, + const float surface_collision_distance) { solve_length_constraints(points_by_curve, curve_selection, segment_lengths_cu, positions_cu); blender::bke::BVHTreeFromMesh surface_bvh = surface.bvh_corner_tris(); - const float radius = 0.005f; const int max_collisions = 5; curve_selection.foreach_segment(GrainSize(64), [&](const IndexMaskSegment segment) { @@ -108,11 +108,11 @@ void solve_length_and_collision_constraints(const OffsetIndices points_by_c max_ray_length_su); BVHTreeRayHit hit; hit.index = -1; - hit.dist = max_ray_length_su + radius; + hit.dist = max_ray_length_su + surface_collision_distance; BLI_bvhtree_ray_cast(surface_bvh.tree, start_pos_su, ray_direction_su, - radius, + surface_collision_distance, &hit, surface_bvh.raycast_callback, &surface_bvh); @@ -135,7 +135,7 @@ void solve_length_and_collision_constraints(const OffsetIndices points_by_c math::transform_direction(transforms.surface_to_curves_normal, hit_normal_su)); /* Slide on a plane that is slightly above the surface. */ - const float3 plane_pos_cu = hit_pos_cu + hit_normal_cu * radius; + const float3 plane_pos_cu = hit_pos_cu + hit_normal_cu * surface_collision_distance; const float3 plane_normal_cu = hit_normal_cu; /* Decompose the current segment into the part normal and tangent to the collision diff --git a/source/blender/makesdna/DNA_curves_defaults.h b/source/blender/makesdna/DNA_curves_defaults.h index b90a4855e4c..ad4a061d3d3 100644 --- a/source/blender/makesdna/DNA_curves_defaults.h +++ b/source/blender/makesdna/DNA_curves_defaults.h @@ -17,6 +17,7 @@ #define _DNA_DEFAULT_Curves \ { \ .flag = 0, \ + .surface_collision_distance = 0.005f, \ } /** \} */ diff --git a/source/blender/makesdna/DNA_curves_types.h b/source/blender/makesdna/DNA_curves_types.h index 953c3249037..12ed9cc4188 100644 --- a/source/blender/makesdna/DNA_curves_types.h +++ b/source/blender/makesdna/DNA_curves_types.h @@ -204,6 +204,10 @@ typedef struct Curves { */ char *surface_uv_map; + /* Distance to keep the curves away from the surface. */ + float surface_collision_distance; + char _pad2[4]; + /* Draw cache to store data used for viewport drawing. */ void *batch_cache; } Curves; diff --git a/source/blender/makesrna/intern/rna_curves.cc b/source/blender/makesrna/intern/rna_curves.cc index 9aee68abaf3..d52f3f51c07 100644 --- a/source/blender/makesrna/intern/rna_curves.cc +++ b/source/blender/makesrna/intern/rna_curves.cc @@ -552,6 +552,14 @@ static void rna_def_curves(BlenderRNA *brna) RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_update(prop, 0, "rna_Curves_update_draw"); + prop = RNA_def_property(srna, "surface_collision_distance", PROP_FLOAT, PROP_DISTANCE); + RNA_def_property_float_sdna(prop, nullptr, "surface_collision_distance"); + RNA_def_property_range(prop, FLT_EPSILON, FLT_MAX); + RNA_def_property_ui_range(prop, 0.0, 10.0f, 0.001, 3); + RNA_def_property_ui_text( + prop, "Collision distance", "Distance to keep the curves away from the surface"); + RNA_def_property_update(prop, 0, "rna_Curves_update_draw"); + /* attributes */ rna_def_attributes_common(srna, AttributeOwnerType::Curves);