[Bf-blender-cvs] [fced604acfc] master: Curves: improve Snake Hook brush
Jacques Lucke
noreply at git.blender.org
Tue Mar 29 14:46:25 CEST 2022
Commit: fced604acfc90ae2bdca068ce1898cdc39838b5f
Author: Jacques Lucke
Date: Tue Mar 29 14:46:14 2022 +0200
Branches: master
https://developer.blender.org/rBfced604acfc90ae2bdca068ce1898cdc39838b5f
Curves: improve Snake Hook brush
This implements the spherical brush and different falloff
modes for the Snake Hook brush.
Differential Revision: https://developer.blender.org/D14408
===================================================================
M release/scripts/startup/bl_ui/space_view3d.py
M source/blender/editors/sculpt_paint/curves_sculpt_snake_hook.cc
===================================================================
diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py
index f40d96e30a0..1c42bc85fae 100644
--- a/release/scripts/startup/bl_ui/space_view3d.py
+++ b/release/scripts/startup/bl_ui/space_view3d.py
@@ -514,6 +514,10 @@ class _draw_tool_settings_context_mode:
layout.prop(tool_settings.curves_sculpt, "interpolate_length")
layout.prop(tool_settings.curves_sculpt, "interpolate_shape")
+ if brush.curves_sculpt_tool == 'SNAKE_HOOK':
+ layout.prop(brush, "falloff_shape", expand=True)
+ layout.prop(brush, "curve_preset")
+
if brush.curves_sculpt_tool == 'TEST1':
layout.prop(tool_settings.curves_sculpt, "distance")
diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_snake_hook.cc b/source/blender/editors/sculpt_paint/curves_sculpt_snake_hook.cc
index 682cd3b47ca..117b2265015 100644
--- a/source/blender/editors/sculpt_paint/curves_sculpt_snake_hook.cc
+++ b/source/blender/editors/sculpt_paint/curves_sculpt_snake_hook.cc
@@ -36,6 +36,14 @@
#include "ED_screen.h"
#include "ED_view3d.h"
+/**
+ * The code below uses a prefix naming convention to indicate the coordinate space:
+ * cu: Local space of the curves object that is being edited.
+ * su: Local space of the surface object.
+ * wo: World space.
+ * re: 2D coordinates within the region.
+ */
+
namespace blender::ed::sculpt_paint {
using blender::bke::CurvesGeometry;
@@ -45,116 +53,231 @@ using blender::bke::CurvesGeometry;
*/
class SnakeHookOperation : public CurvesSculptStrokeOperation {
private:
- float2 last_mouse_position_;
+ float2 last_mouse_position_re_;
+
+ CurvesBrush3D brush_3d_;
+
+ friend struct SnakeHookOperatorExecutor;
public:
- void on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension) override
+ void on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension) override;
+};
+
+/**
+ * Utility class that actually executes the update when the stroke is updated. That's useful
+ * because it avoids passing a very large number of parameters between functions.
+ */
+struct SnakeHookOperatorExecutor {
+ SnakeHookOperation *self_ = nullptr;
+ bContext *C_ = nullptr;
+ Scene *scene_ = nullptr;
+ Object *object_ = nullptr;
+ ARegion *region_ = nullptr;
+ View3D *v3d_ = nullptr;
+ RegionView3D *rv3d_ = nullptr;
+
+ CurvesSculpt *curves_sculpt_ = nullptr;
+ Brush *brush_ = nullptr;
+ float brush_radius_re_;
+ float brush_strength_;
+ eBrushFalloffShape falloff_shape_;
+
+ Curves *curves_id_ = nullptr;
+ CurvesGeometry *curves_ = nullptr;
+
+ float4x4 curves_to_world_mat_;
+ float4x4 world_to_curves_mat_;
+
+ float2 brush_pos_prev_re_;
+ float2 brush_pos_re_;
+ float2 brush_pos_diff_re_;
+
+ void execute(SnakeHookOperation &self, bContext *C, const StrokeExtension &stroke_extension)
{
- BLI_SCOPED_DEFER([&]() { last_mouse_position_ = stroke_extension.mouse_position; });
+ BLI_SCOPED_DEFER([&]() { self.last_mouse_position_re_ = stroke_extension.mouse_position; });
+
+ self_ = &self;
+ C_ = C;
+ scene_ = CTX_data_scene(C);
+ object_ = CTX_data_active_object(C);
+ region_ = CTX_wm_region(C);
+ v3d_ = CTX_wm_view3d(C);
+ rv3d_ = CTX_wm_region_view3d(C);
+
+ curves_sculpt_ = scene_->toolsettings->curves_sculpt;
+ brush_ = BKE_paint_brush(&curves_sculpt_->paint);
+ brush_radius_re_ = BKE_brush_size_get(scene_, brush_);
+ brush_strength_ = BKE_brush_alpha_get(scene_, brush_);
+ falloff_shape_ = static_cast<eBrushFalloffShape>(brush_->falloff_shape);
+
+ curves_to_world_mat_ = object_->obmat;
+ world_to_curves_mat_ = curves_to_world_mat_.inverted();
+
+ curves_id_ = static_cast<Curves *>(object_->data);
+ curves_ = &CurvesGeometry::wrap(curves_id_->geometry);
+
+ brush_pos_prev_re_ = self.last_mouse_position_re_;
+ brush_pos_re_ = stroke_extension.mouse_position;
+ brush_pos_diff_re_ = brush_pos_re_ - brush_pos_prev_re_;
if (stroke_extension.is_first) {
+ if (falloff_shape_ == PAINT_FALLOFF_SHAPE_SPHERE) {
+ std::optional<CurvesBrush3D> brush_3d = sample_curves_3d_brush(
+ *C_, *object_, brush_pos_re_, brush_radius_re_);
+ if (brush_3d.has_value()) {
+ self_->brush_3d_ = *brush_3d;
+ }
+ }
return;
}
- Scene &scene = *CTX_data_scene(C);
- Object &object = *CTX_data_active_object(C);
- ARegion *region = CTX_wm_region(C);
- View3D *v3d = CTX_wm_view3d(C);
- RegionView3D *rv3d = CTX_wm_region_view3d(C);
+ if (falloff_shape_ == PAINT_FALLOFF_SHAPE_SPHERE) {
+ this->spherical_snake_hook();
+ }
+ else if (falloff_shape_ == PAINT_FALLOFF_SHAPE_TUBE) {
+ this->projected_snake_hook();
+ }
+ else {
+ BLI_assert_unreachable();
+ }
- CurvesSculpt &curves_sculpt = *scene.toolsettings->curves_sculpt;
- Brush &brush = *BKE_paint_brush(&curves_sculpt.paint);
- const float brush_radius = BKE_brush_size_get(&scene, &brush);
- const float brush_strength = BKE_brush_alpha_get(&scene, &brush);
+ curves_->tag_positions_changed();
+ DEG_id_tag_update(&curves_id_->id, ID_RECALC_GEOMETRY);
+ ED_region_tag_redraw(region_);
+ }
- const float4x4 ob_mat = object.obmat;
- const float4x4 ob_imat = ob_mat.inverted();
+ void projected_snake_hook()
+ {
+ MutableSpan<float3> positions_cu = curves_->positions();
float4x4 projection;
- ED_view3d_ob_project_mat_get(rv3d, &object, projection.values);
+ ED_view3d_ob_project_mat_get(rv3d_, object_, projection.values);
- Curves &curves_id = *static_cast<Curves *>(object.data);
- CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry);
- MutableSpan<float3> positions = curves.positions();
+ threading::parallel_for(curves_->curves_range(), 256, [&](const IndexRange curves_range) {
+ for (const int curve_i : curves_range) {
+ const IndexRange points = curves_->points_for_curve(curve_i);
+ const int last_point_i = points.last();
+ const float3 old_pos_cu = positions_cu[last_point_i];
- const float2 mouse_prev = last_mouse_position_;
- const float2 mouse_cur = stroke_extension.mouse_position;
- const float2 mouse_diff = mouse_cur - mouse_prev;
+ float2 old_pos_re;
+ ED_view3d_project_float_v2_m4(region_, old_pos_cu, old_pos_re, projection.values);
- threading::parallel_for(curves.curves_range(), 256, [&](const IndexRange curves_range) {
- for (const int curve_i : curves_range) {
- const IndexRange curve_points = curves.points_for_curve(curve_i);
- const int last_point_i = curve_points.last();
+ const float distance_to_brush_re = math::distance(old_pos_re, brush_pos_prev_re_);
+ if (distance_to_brush_re > brush_radius_re_) {
+ continue;
+ }
- const float3 old_position = positions[last_point_i];
+ const float radius_falloff = BKE_brush_curve_strength(
+ brush_, distance_to_brush_re, brush_radius_re_);
+ const float weight = brush_strength_ * radius_falloff;
- float2 old_position_screen;
- ED_view3d_project_float_v2_m4(
- region, old_position, old_position_screen, projection.values);
+ const float2 new_position_re = old_pos_re + brush_pos_diff_re_ * weight;
+ float3 new_position_wo;
+ ED_view3d_win_to_3d(
+ v3d_, region_, curves_to_world_mat_ * old_pos_cu, new_position_re, new_position_wo);
+ const float3 new_position_cu = world_to_curves_mat_ * new_position_wo;
- const float distance_screen = math::distance(old_position_screen, mouse_prev);
- if (distance_screen > brush_radius) {
+ this->move_last_point_and_resample(positions_cu.slice(points), new_position_cu);
+ }
+ });
+ }
+
+ void spherical_snake_hook()
+ {
+ MutableSpan<float3> positions_cu = curves_->positions();
+
+ float4x4 projection;
+ ED_view3d_ob_project_mat_get(rv3d_, object_, projection.values);
+
+ float3 brush_start_wo, brush_end_wo;
+ ED_view3d_win_to_3d(v3d_,
+ region_,
+ curves_to_world_mat_ * self_->brush_3d_.position_cu,
+ brush_pos_prev_re_,
+ brush_start_wo);
+ ED_view3d_win_to_3d(v3d_,
+ region_,
+ curves_to_world_mat_ * self_->brush_3d_.position_cu,
+ brush_pos_re_,
+ brush_end_wo);
+ const float3 brush_start_cu = world_to_curves_mat_ * brush_start_wo;
+ const float3 brush_end_cu = world_to_curves_mat_ * brush_end_wo;
+ const float3 brush_diff_cu = brush_end_cu - brush_start_cu;
+
+ const float brush_radius_cu = self_->brush_3d_.radius_cu;
+ const float brush_radius_sq_cu = pow2f(brush_radius_cu);
+
+ threading::parallel_for(curves_->curves_range(), 256, [&](const IndexRange curves_range) {
+ for (const int curve_i : curves_range) {
+ const IndexRange points = curves_->points_for_curve(curve_i);
+ const int last_point_i = points.last();
+ const float3 old_pos_cu = positions_cu[last_point_i];
+
+ const float distance_to_brush_sq_cu = dist_squared_to_line_segment_v3(
+ old_pos_cu, brush_start_cu, brush_end_cu);
+ if (distance_to_brush_sq_cu > brush_radius_sq_cu) {
continue;
}
- const float radius_falloff = pow2f(1.0f - distance_screen / brush_radius);
- const float weight = brush_strength * radius_falloff;
+ const float distance_to_brush_cu = std::sqrt(distance_to_brush_sq_cu);
+
+ const float radius_falloff = BKE_brush_curve_strength(
+ brush_, distance_to_brush_cu, brush_radius_cu);
+ const float weight = brush_strength_ * radius_falloff;
- const float2 new_position_screen = old_position_screen + mouse_diff * weight;
- float3 new_position;
- ED_view3d_win_to_3d(v3d, region, ob_mat * old_position, new_position_screen, new_position);
- new_position = ob_imat * new_position;
+ const float3 new_pos_cu = old_pos_cu + weight * brush_diff_cu;
- this->move_last
@@ Diff output truncated at 10240 characters. @@
More information about the Bf-blender-cvs
mailing list