GPv3: Draw Tool: Make "Simplify" option a screen space threshold

Previously, the "Simplify" option was a world space distance threshold.
This meant that zooming in and out of the view changed the way
this option behaved. There were complaints from artists about this.

This change improves two things:
* The simplify algorithm uses the screen space coordinates rather than the
  3D positions.
* The UI setting is in pixels making it much easier to tweak (no need
  for values in the 1e-4 range).

Pull Request: https://projects.blender.org/blender/blender/pulls/122719
This commit is contained in:
Falk David
2024-06-05 15:06:49 +02:00
committed by Falk David
parent dd63668790
commit a40a4ccb2f
7 changed files with 90 additions and 32 deletions

View File

@@ -2735,7 +2735,7 @@ class VIEW3D_PT_tools_grease_pencil_v3_brush_post_processing(View3DPanel, Panel)
col1.prop(gp_settings, "pen_subdivision_steps", text="Subdivisions")
col1 = col.column(align=True)
col1.prop(gp_settings, "simplify_factor")
col1.prop(gp_settings, "simplify_pixel_threshold", slider=True)
col1 = col.column(align=True)
col1.prop(gp_settings, "use_trim")

View File

@@ -799,6 +799,9 @@ void BKE_gpencil_brush_preset_set(Main *bmain, Brush *brush, const short type)
brush->gpencil_settings->draw_smoothlvl = 1;
brush->gpencil_settings->draw_subdivide = 0;
brush->gpencil_settings->simplify_f = 0.002f;
if (U.experimental.use_grease_pencil_version3) {
brush->gpencil_settings->simplify_px = 0.4f;
}
brush->gpencil_settings->draw_random_press = 0.0f;
brush->gpencil_settings->draw_jitter = 0.0f;
@@ -844,6 +847,9 @@ void BKE_gpencil_brush_preset_set(Main *bmain, Brush *brush, const short type)
brush->gpencil_settings->draw_smoothlvl = 2;
brush->gpencil_settings->draw_subdivide = 0;
brush->gpencil_settings->simplify_f = 0.000f;
if (U.experimental.use_grease_pencil_version3) {
brush->gpencil_settings->simplify_px = 0.0f;
}
brush->gpencil_settings->flag |= GP_BRUSH_GROUP_RANDOM;
brush->gpencil_settings->draw_random_press = 0.6f;
@@ -891,6 +897,9 @@ void BKE_gpencil_brush_preset_set(Main *bmain, Brush *brush, const short type)
brush->gpencil_settings->draw_smoothlvl = 1;
brush->gpencil_settings->draw_subdivide = 0;
brush->gpencil_settings->simplify_f = 0.002f;
if (U.experimental.use_grease_pencil_version3) {
brush->gpencil_settings->simplify_px = 0.4f;
}
brush->gpencil_settings->flag &= ~GP_BRUSH_GROUP_RANDOM;
brush->gpencil_settings->draw_random_press = 0.0f;
@@ -938,6 +947,9 @@ void BKE_gpencil_brush_preset_set(Main *bmain, Brush *brush, const short type)
brush->gpencil_settings->draw_smoothlvl = 1;
brush->gpencil_settings->draw_subdivide = 0;
brush->gpencil_settings->simplify_f = 0.002f;
if (U.experimental.use_grease_pencil_version3) {
brush->gpencil_settings->simplify_px = 0.4f;
}
brush->gpencil_settings->flag &= ~GP_BRUSH_GROUP_RANDOM;
brush->gpencil_settings->draw_random_press = 0.0f;
@@ -990,6 +1002,9 @@ void BKE_gpencil_brush_preset_set(Main *bmain, Brush *brush, const short type)
brush->gpencil_settings->draw_smoothlvl = 1;
brush->gpencil_settings->draw_subdivide = 1;
brush->gpencil_settings->simplify_f = 0.002f;
if (U.experimental.use_grease_pencil_version3) {
brush->gpencil_settings->simplify_px = 0.4f;
}
brush->gpencil_settings->draw_random_press = 0.0f;
brush->gpencil_settings->draw_random_strength = 0.0f;
@@ -1031,6 +1046,9 @@ void BKE_gpencil_brush_preset_set(Main *bmain, Brush *brush, const short type)
brush->gpencil_settings->draw_smoothlvl = 1;
brush->gpencil_settings->draw_subdivide = 0;
brush->gpencil_settings->simplify_f = 0.000f;
if (U.experimental.use_grease_pencil_version3) {
brush->gpencil_settings->simplify_px = 0.0f;
}
brush->gpencil_settings->draw_random_press = 0.0f;
brush->gpencil_settings->draw_random_strength = 0.0f;
@@ -1075,6 +1093,9 @@ void BKE_gpencil_brush_preset_set(Main *bmain, Brush *brush, const short type)
brush->gpencil_settings->draw_smoothlvl = 1;
brush->gpencil_settings->draw_subdivide = 0;
brush->gpencil_settings->simplify_f = 0.002f;
if (U.experimental.use_grease_pencil_version3) {
brush->gpencil_settings->simplify_px = 0.4f;
}
brush->gpencil_settings->draw_random_press = 0.0f;
brush->gpencil_settings->draw_jitter = 0.0f;

View File

@@ -795,43 +795,37 @@ static void smooth_stroke(bke::greasepencil::Drawing &drawing,
}
static void simplify_stroke(bke::greasepencil::Drawing &drawing,
Span<float2> screen_space_positions,
const float epsilon,
const int active_curve)
{
const bke::CurvesGeometry &curves = drawing.strokes();
const IndexRange points = curves.points_by_curve()[active_curve];
BLI_assert(screen_space_positions.size() == points.size());
IndexMaskMemory memory;
IndexMask points_to_delete = geometry::simplify_curve_attribute(
curves.positions(),
IndexRange::from_single(active_curve),
curves.points_by_curve(),
curves.cyclic(),
epsilon,
curves.positions(),
memory);
const VArray<float> radii = drawing.radii();
if (!radii.is_empty() && radii.is_span()) {
const IndexMask radii_mask = geometry::simplify_curve_attribute(
curves.positions(),
IndexRange::from_single(active_curve),
curves.points_by_curve(),
curves.cyclic(),
epsilon,
radii.get_internal_span(),
memory);
points_to_delete = IndexMask::from_intersection(points_to_delete, radii_mask, memory);
if (epsilon <= 0.0f) {
return;
}
Array<bool> points_to_delete_arr(drawing.strokes().points_num(), false);
points_to_delete_arr.as_mutable_span().slice(points).fill(true);
geometry::curve_simplify(curves.positions().slice(points),
curves.cyclic()[active_curve],
epsilon,
screen_space_positions,
points_to_delete_arr.as_mutable_span().slice(points));
IndexMaskMemory memory;
const IndexMask points_to_delete = IndexMask::from_bools(points_to_delete_arr, memory);
if (!points_to_delete.is_empty()) {
drawing.strokes_for_write().remove_points(points_to_delete, {});
}
}
static void trim_end_points(bke::greasepencil::Drawing &drawing,
const float epsilon,
const bool on_back,
const int active_curve)
static int trim_end_points(bke::greasepencil::Drawing &drawing,
const float epsilon,
const bool on_back,
const int active_curve)
{
const IndexRange points = drawing.strokes().points_by_curve()[active_curve];
bke::CurvesGeometry &curves = drawing.strokes_for_write();
@@ -849,13 +843,13 @@ static void trim_end_points(bke::greasepencil::Drawing &drawing,
}
if (num_points_to_remove <= 0) {
return;
return 0;
}
if (!on_back) {
curves.resize(curves.points_num() - num_points_to_remove, curves.curves_num());
curves.offsets_for_write().last() = curves.points_num();
return;
return num_points_to_remove;
}
bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
@@ -891,6 +885,8 @@ static void trim_end_points(bke::greasepencil::Drawing &drawing,
offsets[src_curve] = offsets[src_curve] - num_points_to_remove;
}
offsets.last() = curves.points_num();
return num_points_to_remove;
}
static void deselect_stroke(const bContext &C,
@@ -939,15 +935,18 @@ void PaintOperation::on_stroke_done(const bContext &C)
const int active_curve = on_back ? drawing.strokes().curves_range().first() :
drawing.strokes().curves_range().last();
/* Remove trailing points with radii close to zero. */
trim_end_points(drawing, 1e-5f, on_back, active_curve);
const int num_points_removed = trim_end_points(drawing, 1e-5f, on_back, active_curve);
/* Set the selection of the newly drawn stroke to false. */
deselect_stroke(C, drawing, active_curve);
if (do_post_processing) {
if (settings->draw_smoothfac > 0.0f) {
smooth_stroke(drawing, settings->draw_smoothfac, settings->draw_smoothlvl, active_curve);
}
if (settings->simplify_f > 0.0f) {
simplify_stroke(drawing, settings->simplify_f, active_curve);
if (settings->simplify_px > 0.0f) {
simplify_stroke(drawing,
this->screen_space_smoothed_coords_.as_span().drop_back(num_points_removed),
settings->simplify_px,
active_curve);
}
}
drawing.set_texture_matrices({texture_space_}, IndexRange::from_single(active_curve));

View File

@@ -20,4 +20,13 @@ IndexMask simplify_curve_attribute(const Span<float3> positions,
GSpan attribute_data,
IndexMaskMemory &memory);
/**
* Same as above, but only for a single curve. All spans are expected to be the size of the curve.
*/
void curve_simplify(const Span<float3> positions,
const bool cyclic,
const float epsilon,
const GSpan attribute_data,
MutableSpan<bool> points_to_delete);
} // namespace blender::geometry

View File

@@ -117,6 +117,23 @@ static void curve_simplify(const Span<float3> positions,
}
}
void curve_simplify(const Span<float3> positions,
const bool cyclic,
const float epsilon,
const GSpan attribute_data,
MutableSpan<bool> points_to_delete)
{
bke::attribute_math::convert_to_static_type(attribute_data.type(), [&](auto dummy) {
using T = decltype(dummy);
if constexpr (std::is_same_v<T, float> || std::is_same_v<T, float2> ||
std::is_same_v<T, float3>)
{
curve_simplify(positions, cyclic, epsilon, attribute_data.typed<T>(), points_to_delete);
}
});
}
IndexMask simplify_curve_attribute(const Span<float3> positions,
const IndexMask &curves_selection,
const OffsetIndices<int> points_by_curve,

View File

@@ -133,7 +133,8 @@ typedef struct BrushGpencilSettings {
/** Factor for external line thickness conversion to outline. */
float outline_fac;
char _pad1[4];
/** Screen space simplify threshold. Points within this margin are treated as a straight line. */
float simplify_px;
/* optional link of material to replace default in context */
/** Material. */

View File

@@ -1576,6 +1576,17 @@ static void rna_def_gpencil_options(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Simplify", "Factor of Simplify using adaptive algorithm");
RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, ParameterFlag(0));
prop = RNA_def_property(srna, "simplify_pixel_threshold", PROP_FLOAT, PROP_PIXEL);
RNA_def_property_float_sdna(prop, nullptr, "simplify_px");
RNA_def_property_range(prop, 0, 10.0);
RNA_def_property_ui_range(prop, 0, 10.0, 1.0f, 1);
RNA_def_property_ui_text(
prop,
"Simplify",
"Threashold in screen space used for the simplify algorithm. Points within this threashold "
"are treated as if they were in a straight line");
RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, ParameterFlag(0));
/* Curves for pressure */
prop = RNA_def_property(srna, "curve_sensitivity", PROP_POINTER, PROP_NONE);
RNA_def_property_pointer_sdna(prop, nullptr, "curve_sensitivity");