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:
@@ -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")
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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. */
|
||||
|
||||
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user