From 718d0b92532b412c58542af5d3706ce3856f336c Mon Sep 17 00:00:00 2001 From: Falk David Date: Tue, 4 Jul 2023 18:21:14 +0200 Subject: [PATCH 01/75] Add switch cases for new paint mode --- source/blender/blenkernel/intern/paint.cc | 2 ++ source/blender/editors/sculpt_paint/paint_curve.cc | 3 +++ 2 files changed, 5 insertions(+) diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index 6f3235abe14..29312449404 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -1085,6 +1085,8 @@ eObjectMode BKE_paint_object_mode_from_paintmode(ePaintMode mode) return OB_MODE_EDIT; case PAINT_MODE_SCULPT_CURVES: return OB_MODE_SCULPT_CURVES; + case PAINT_MODE_GPENCIL: + return OB_MODE_PAINT_GREASE_PENCIL; case PAINT_MODE_INVALID: default: return OB_MODE_OBJECT; diff --git a/source/blender/editors/sculpt_paint/paint_curve.cc b/source/blender/editors/sculpt_paint/paint_curve.cc index f3509836b5b..17fcfdd9872 100644 --- a/source/blender/editors/sculpt_paint/paint_curve.cc +++ b/source/blender/editors/sculpt_paint/paint_curve.cc @@ -686,6 +686,9 @@ static int paintcurve_draw_exec(bContext *C, wmOperator * /*op*/) case PAINT_MODE_SCULPT_CURVES: name = "SCULPT_CURVES_OT_brush_stroke"; break; + case PAINT_MODE_GPENCIL: + name = "GREASE_PENCIL_OT_brush_stroke"; + break; default: return OPERATOR_PASS_THROUGH; } -- 2.30.2 From 387788db0252eed9495b4d929a082936e65d2670 Mon Sep 17 00:00:00 2001 From: Falk David Date: Tue, 4 Jul 2023 18:22:32 +0200 Subject: [PATCH 02/75] Handle error cases in `grease_pencil_stroke_invoke` --- .../sculpt_paint/grease_pencil_draw_ops.cc | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/source/blender/editors/sculpt_paint/grease_pencil_draw_ops.cc b/source/blender/editors/sculpt_paint/grease_pencil_draw_ops.cc index 6a1e1d8810c..4a2ad2a8c64 100644 --- a/source/blender/editors/sculpt_paint/grease_pencil_draw_ops.cc +++ b/source/blender/editors/sculpt_paint/grease_pencil_draw_ops.cc @@ -4,6 +4,7 @@ #include "BKE_context.h" #include "BKE_grease_pencil.hh" +#include "BKE_report.h" #include "DEG_depsgraph_query.h" @@ -119,12 +120,38 @@ static void stroke_done(const bContext *C, PaintStroke *stroke) static int grease_pencil_stroke_invoke(bContext *C, wmOperator *op, const wmEvent *event) { + const Scene *scene = CTX_data_scene(C); + const Object *object = CTX_data_active_object(C); + if (!object || object->type != OB_GREASE_PENCIL) { + return OPERATOR_CANCELLED; + } + + GreasePencil &grease_pencil = *static_cast(object->data); + if (!grease_pencil.has_active_layer()) { + BKE_report(op->reports, RPT_ERROR, "No active Grease Pencil layer"); + return OPERATOR_CANCELLED; + } + const Paint *paint = BKE_paint_get_active_from_context(C); const Brush *brush = BKE_paint_brush_for_read(paint); if (brush == nullptr) { return OPERATOR_CANCELLED; } + const int current_frame = scene->r.cfra; + const bke::greasepencil::Layer &active_layer = *grease_pencil.get_active_layer(); + if (active_layer.drawing_index_at(current_frame) == -1) { + if (!IS_AUTOKEY_ON(scene)) { + BKE_report(op->reports, RPT_ERROR, "No Grease Pencil frame to draw on"); + return OPERATOR_CANCELLED; + } + else { + /* Create a new blank frame here. */ + grease_pencil.insert_blank_frame( + *grease_pencil.get_active_layer_for_write(), current_frame, 1, BEZT_KEYTYPE_KEYFRAME); + } + } + op->customdata = paint_stroke_new(C, op, stroke_get_location, -- 2.30.2 From 0dc042923378cc3d5af9509ef82533ab89d9aac3 Mon Sep 17 00:00:00 2001 From: Falk David Date: Tue, 4 Jul 2023 18:25:00 +0200 Subject: [PATCH 03/75] Refactor `on_stroke_done` --- .../sculpt_paint/grease_pencil_paint.cc | 27 ++++++++++++------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc index 5b0a1705feb..2ca38c70cd8 100644 --- a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc +++ b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc @@ -85,19 +85,28 @@ void PaintOperation::on_stroke_done(const bContext &C) GreasePencil &grease_pencil_orig = *static_cast(obact->data); GreasePencil &grease_pencil_eval = *static_cast(ob_eval->data); - BLI_assert(grease_pencil_orig.has_active_layer()); - const bke::greasepencil::Layer &active_layer_orig = *grease_pencil_orig.get_active_layer(); - int index_orig = active_layer_orig.drawing_index_at(scene->r.cfra); - - bke::greasepencil::Drawing &drawing_orig = - reinterpret_cast(grease_pencil_orig.drawings(index_orig))->wrap(); const Span stroke_points = grease_pencil_eval.runtime->stroke_buffer(); + + /* No stroke to create, return. */ + if (stroke_points.size() == 0) { + return; + } + + /* The object should have an active layer. */ + BLI_assert(grease_pencil_orig.has_active_layer()); + + /* Create the new stroke from the stroke buffer. */ + const bke::greasepencil::Layer &active_layer_orig = *grease_pencil_orig.get_active_layer(); + const int index_orig = active_layer_orig.drawing_index_at(scene->r.cfra); + + bke::greasepencil::Drawing &drawing_orig = + reinterpret_cast(grease_pencil_orig.drawings()[index_orig])->wrap(); CurvesGeometry &curves = drawing_orig.strokes_for_write(); - int num_old_curves = curves.curves_num(); - int num_old_points = curves.points_num(); + const int num_old_curves = curves.curves_num(); + const int num_old_points = curves.points_num(); curves.resize(num_old_points + stroke_points.size(), num_old_curves + 1); curves.offsets_for_write()[num_old_curves] = num_old_points; @@ -120,7 +129,7 @@ void PaintOperation::on_stroke_done(const bContext &C) opacities[point_i] = point.opacity; } - /* Set material index attribute. */ + /* TODO: Set material index attribute. */ int material_index = 0; SpanAttributeWriter materials = attributes.lookup_or_add_for_write_span( "material_index", ATTR_DOMAIN_CURVE); -- 2.30.2 From f779393c06b5a418dc97ac6d6bf90ff601defb5c Mon Sep 17 00:00:00 2001 From: Falk David Date: Wed, 5 Jul 2023 18:26:36 +0200 Subject: [PATCH 04/75] Add some tool settings to header --- .../startup/bl_ui/properties_paint_common.py | 7 +++ scripts/startup/bl_ui/space_view3d.py | 47 ++++++++++++++++++- 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/scripts/startup/bl_ui/properties_paint_common.py b/scripts/startup/bl_ui/properties_paint_common.py index 616824d2416..693b33b4f28 100644 --- a/scripts/startup/bl_ui/properties_paint_common.py +++ b/scripts/startup/bl_ui/properties_paint_common.py @@ -82,6 +82,8 @@ class UnifiedPaintPanel: return tool_settings.gpencil_vertex_paint elif mode == 'SCULPT_CURVES': return tool_settings.curves_sculpt + elif mode == 'PAINT_GREASE_PENCIL': + return tool_settings.gpencil_paint return None @staticmethod @@ -866,6 +868,11 @@ def brush_shared_settings(layout, context, brush, popover=False): strength = True direction = brush.curves_sculpt_tool in {'GROW_SHRINK', 'SELECTION_PAINT'} + # Sculpt Curves # + if mode == 'PAINT_GREASE_PENCIL': + size = True + strength = True + ### Draw settings. ### ups = context.scene.tool_settings.unified_paint_settings diff --git a/scripts/startup/bl_ui/space_view3d.py b/scripts/startup/bl_ui/space_view3d.py index ccbe271d340..c0a0d4805c9 100644 --- a/scripts/startup/bl_ui/space_view3d.py +++ b/scripts/startup/bl_ui/space_view3d.py @@ -577,6 +577,51 @@ class _draw_tool_settings_context_mode: return True + @staticmethod + def PAINT_GREASE_PENCIL(context, layout, tool): + if (tool is None) or (not tool.has_datablock): + return False + + tool_settings = context.tool_settings + paint = tool_settings.gpencil_paint + + brush = paint.brush + if brush is None: + return False + + row = layout.row(align=True) + row.template_ID_preview(paint, "brush", rows=3, cols=8, hide_buttons=True) + + from bl_ui.properties_paint_common import ( + brush_basic__draw_color_selector, + ) + brush_basic__draw_color_selector(context, layout, brush, brush.gpencil_settings, None) + + UnifiedPaintPanel.prop_unified( + layout, + context, + brush, + "size", + unified_name="use_unified_size", + pressure_name="use_pressure_size", + text="Radius", + slider=True, + header=True, + ) + + UnifiedPaintPanel.prop_unified( + layout, + context, + brush, + "strength", + pressure_name="use_pressure_strength", + unified_name="use_unified_strength", + slider=True, + header=True, + ) + + return True + class VIEW3D_HT_header(Header): bl_space_type = 'VIEW_3D' @@ -620,7 +665,7 @@ class VIEW3D_HT_header(Header): else: if (object_mode not in { 'SCULPT', 'SCULPT_CURVES', 'VERTEX_PAINT', 'WEIGHT_PAINT', 'TEXTURE_PAINT', - 'PAINT_GPENCIL', 'SCULPT_GPENCIL', 'WEIGHT_GPENCIL', 'VERTEX_GPENCIL', + 'PAINT_GPENCIL', 'SCULPT_GPENCIL', 'WEIGHT_GPENCIL', 'VERTEX_GPENCIL', 'PAINT_GREASE_PENCIL', }) or has_pose_mode: show_snap = True else: -- 2.30.2 From 8f21513286d5fd953e51093989fd0976c55deb09 Mon Sep 17 00:00:00 2001 From: Falk David Date: Mon, 10 Jul 2023 12:45:24 +0200 Subject: [PATCH 05/75] Add core material functions --- .../blender/blenkernel/BKE_grease_pencil.hh | 10 +++ .../blenkernel/intern/grease_pencil.cc | 79 +++++++++++++++++++ 2 files changed, 89 insertions(+) diff --git a/source/blender/blenkernel/BKE_grease_pencil.hh b/source/blender/blenkernel/BKE_grease_pencil.hh index 5bd517dd881..03dd1e01703 100644 --- a/source/blender/blenkernel/BKE_grease_pencil.hh +++ b/source/blender/blenkernel/BKE_grease_pencil.hh @@ -770,6 +770,7 @@ GreasePencil *BKE_grease_pencil_copy_for_eval(const GreasePencil *grease_pencil_ BoundBox *BKE_grease_pencil_boundbox_get(Object *ob); void BKE_grease_pencil_data_update(Depsgraph *depsgraph, Scene *scene, Object *object); +int BKE_grease_pencil_object_material_index_get(Object *ob, Material *ma); int BKE_grease_pencil_object_material_index_get_by_name(Object *ob, const char *name); Material *BKE_grease_pencil_object_material_new(Main *bmain, Object *ob, @@ -779,6 +780,15 @@ Material *BKE_grease_pencil_object_material_ensure_by_name(Main *bmain, Object *ob, const char *name, int *r_index); +Material *BKE_grease_pencil_brush_material_get(Brush *brush); +Material *BKE_grease_pencil_object_material_ensure_from_brush(Main *bmain, + Object *ob, + Brush *brush); +Material *BKE_grease_pencil_object_material_ensure_from_active_input_brush(Main *bmain, + Object *ob, + Brush *brush); +Material *BKE_grease_pencil_object_material_ensure_from_active_input_material(Object *ob); +Material *BKE_grease_pencil_object_material_ensure_active(Object *ob); bool BKE_grease_pencil_references_cyclic_check(const GreasePencil *id_reference, const GreasePencil *grease_pencil); diff --git a/source/blender/blenkernel/intern/grease_pencil.cc b/source/blender/blenkernel/intern/grease_pencil.cc index 6a1c7c250da..1734148b523 100644 --- a/source/blender/blenkernel/intern/grease_pencil.cc +++ b/source/blender/blenkernel/intern/grease_pencil.cc @@ -41,6 +41,7 @@ #include "DNA_ID.h" #include "DNA_ID_enums.h" +#include "DNA_brush_types.h" #include "DNA_grease_pencil_types.h" #include "DNA_material_types.h" #include "DNA_modifier_types.h" @@ -1182,6 +1183,19 @@ void BKE_grease_pencil_data_update(Depsgraph *depsgraph, Scene *scene, Object *o /** \name Grease Pencil material functions * \{ */ +int BKE_grease_pencil_object_material_index_get(Object *ob, Material *ma) +{ + short *totcol = BKE_object_material_len_p(ob); + Material *read_ma = NULL; + for (short i = 0; i < *totcol; i++) { + read_ma = BKE_object_material_get(ob, i + 1); + if (ma == read_ma) { + return i; + } + } + return -1; +} + int BKE_grease_pencil_object_material_index_get_by_name(Object *ob, const char *name) { short *totcol = BKE_object_material_len_p(ob); @@ -1226,6 +1240,71 @@ Material *BKE_grease_pencil_object_material_ensure_by_name(Main *bmain, return BKE_grease_pencil_object_material_new(bmain, ob, name, r_index); } +Material *BKE_grease_pencil_brush_material_get(Brush *brush) +{ + if (brush == nullptr) { + return nullptr; + } + if (brush->gpencil_settings == nullptr) { + return nullptr; + } + return brush->gpencil_settings->material; +} + +Material *BKE_grease_pencil_object_material_ensure_from_brush(Main *bmain, + Object *ob, + Brush *brush) +{ + if (brush->gpencil_settings->flag & GP_BRUSH_MATERIAL_PINNED) { + Material *ma = BKE_grease_pencil_brush_material_get(brush); + + /* check if the material is already on object material slots and add it if missing */ + if (ma && BKE_grease_pencil_object_material_index_get(ob, ma) < 0) { + BKE_object_material_slot_add(bmain, ob); + BKE_object_material_assign(bmain, ob, ma, ob->totcol, BKE_MAT_ASSIGN_USERPREF); + } + + return ma; + } + + /* Use the active material instead. */ + return BKE_object_material_get(ob, ob->actcol); +} + +Material *BKE_grease_pencil_object_material_ensure_from_active_input_brush(Main *bmain, + Object *ob, + Brush *brush) +{ + if (brush == nullptr) { + return BKE_grease_pencil_object_material_ensure_from_active_input_material(ob); + } + if (Material *ma = BKE_grease_pencil_object_material_ensure_from_brush(bmain, ob, brush)) { + return ma; + } + if (brush->gpencil_settings->flag & GP_BRUSH_MATERIAL_PINNED) { + /* It is easier to just unpin a null material, instead of setting a new one. */ + brush->gpencil_settings->flag &= ~GP_BRUSH_MATERIAL_PINNED; + } + return BKE_grease_pencil_object_material_ensure_from_active_input_material(ob); +} + +Material *BKE_grease_pencil_object_material_ensure_from_active_input_material(Object *ob) +{ + if (Material *ma = BKE_object_material_get(ob, ob->actcol)) { + return ma; + } + return BKE_material_default_gpencil(); +} + +Material *BKE_grease_pencil_object_material_ensure_active(Object *ob) +{ + Material *ma = BKE_grease_pencil_object_material_ensure_from_active_input_material(ob); + if (ma->gp_style == nullptr) { + BKE_gpencil_material_attr_init(ma); + } + return ma; +} + /** \} */ /* ------------------------------------------------------------------- */ -- 2.30.2 From acaf30e151d37496b393c5480a49213d2296475a Mon Sep 17 00:00:00 2001 From: Falk David Date: Wed, 12 Jul 2023 11:13:36 +0200 Subject: [PATCH 06/75] Add OB_GREASE_PENCIL to material slot poll --- source/blender/makesrna/intern/rna_object.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/makesrna/intern/rna_object.cc b/source/blender/makesrna/intern/rna_object.cc index d81dd03a8fe..e2f9344e905 100644 --- a/source/blender/makesrna/intern/rna_object.cc +++ b/source/blender/makesrna/intern/rna_object.cc @@ -1381,7 +1381,7 @@ static bool rna_MaterialSlot_material_poll(PointerRNA *ptr, PointerRNA value) Object *ob = reinterpret_cast(ptr->owner_id); Material *ma = static_cast(value.data); - if (ob->type == OB_GPENCIL_LEGACY) { + if (ELEM(ob->type, OB_GPENCIL_LEGACY, OB_GREASE_PENCIL)) { /* GP Materials only */ return (ma->gp_style != nullptr); } -- 2.30.2 From 46116026bd8a127527db592689d3199f33db3c63 Mon Sep 17 00:00:00 2001 From: Falk David Date: Mon, 17 Jul 2023 14:10:59 +0200 Subject: [PATCH 07/75] Refactor --- .../sculpt_paint/grease_pencil_paint.cc | 35 +++++++++++-------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc index 2ca38c70cd8..88fbd86a435 100644 --- a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc +++ b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc @@ -34,34 +34,39 @@ class PaintOperation : public GreasePencilStrokeOperation { * because it avoids passing a very large number of parameters between functions. */ struct PaintOperationExecutor { + Object *object_ = nullptr; + ARegion *region_ = nullptr; + GreasePencil *grease_pencil_ = nullptr; + bke::greasepencil::StrokeCache *stroke_cache_ = nullptr; - PaintOperationExecutor(const bContext & /*C*/) {} - - void execute(PaintOperation & /*self*/, const bContext &C, const InputSample &extension_sample) + PaintOperationExecutor(const bContext &C) { - using namespace blender::bke; Depsgraph *depsgraph = CTX_data_depsgraph_pointer(&C); - ARegion *region = CTX_wm_region(&C); - Object *obact = CTX_data_active_object(&C); - Object *ob_eval = DEG_get_evaluated_object(depsgraph, obact); + object_ = CTX_data_active_object(&C); + Object *object_eval = DEG_get_evaluated_object(depsgraph, object_); + region_ = CTX_wm_region(&C); /** * Note: We write to the evaluated object here, so that the additional copy from orig -> eval * is not needed for every update. After the stroke is done, the result is written to the * original object. */ - GreasePencil &grease_pencil = *static_cast(ob_eval->data); + grease_pencil_ = static_cast(object_eval->data); + stroke_cache_ = &grease_pencil_->runtime->stroke_cache; + } + void execute(PaintOperation & /*self*/, const bContext &C, const InputSample &extension_sample) + { float4 plane{0.0f, -1.0f, 0.0f, 0.0f}; float3 proj_pos; - ED_view3d_win_to_3d_on_plane(region, plane, extension_sample.mouse_position, false, proj_pos); + ED_view3d_win_to_3d_on_plane(region_, plane, extension_sample.mouse_position, false, proj_pos); bke::greasepencil::StrokePoint new_point{ proj_pos, extension_sample.pressure * 100.0f, 1.0f, float4(1.0f)}; - grease_pencil.runtime->stroke_cache.points.append(std::move(new_point)); + stroke_cache_->points.append(std::move(new_point)); - BKE_grease_pencil_batch_cache_dirty_tag(&grease_pencil, BKE_GREASEPENCIL_BATCH_DIRTY_ALL); + BKE_grease_pencil_batch_cache_dirty_tag(grease_pencil_, BKE_GREASEPENCIL_BATCH_DIRTY_ALL); } }; @@ -80,11 +85,11 @@ void PaintOperation::on_stroke_done(const bContext &C) using namespace blender::bke; Depsgraph *depsgraph = CTX_data_depsgraph_pointer(&C); Scene *scene = CTX_data_scene(&C); - Object *obact = CTX_data_active_object(&C); - Object *ob_eval = DEG_get_evaluated_object(depsgraph, obact); + Object *object = CTX_data_active_object(&C); + Object *object_eval = DEG_get_evaluated_object(depsgraph, object); - GreasePencil &grease_pencil_orig = *static_cast(obact->data); - GreasePencil &grease_pencil_eval = *static_cast(ob_eval->data); + GreasePencil &grease_pencil_orig = *static_cast(object->data); + GreasePencil &grease_pencil_eval = *static_cast(object_eval->data); const Span stroke_points = grease_pencil_eval.runtime->stroke_buffer(); -- 2.30.2 From 11f5f0de249d6f8e6158b4b89a403e08b9ea1d44 Mon Sep 17 00:00:00 2001 From: Falk David Date: Mon, 17 Jul 2023 14:39:16 +0200 Subject: [PATCH 08/75] Use bush size and alpha --- .../sculpt_paint/grease_pencil_paint.cc | 69 ++++++++++++------- 1 file changed, 45 insertions(+), 24 deletions(-) diff --git a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc index 88fbd86a435..328262ec9d9 100644 --- a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc +++ b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc @@ -2,6 +2,7 @@ * * SPDX-License-Identifier: GPL-2.0-or-later */ +#include "BKE_brush.h" #include "BKE_context.h" #include "BKE_curves.hh" #include "BKE_grease_pencil.h" @@ -20,6 +21,10 @@ namespace blender::ed::sculpt_paint::greasepencil { class PaintOperation : public GreasePencilStrokeOperation { + private: + bke::greasepencil::StrokeCache *stroke_cache_; + + friend struct PaintOperationExecutor; public: ~PaintOperation() override {} @@ -34,44 +39,61 @@ class PaintOperation : public GreasePencilStrokeOperation { * because it avoids passing a very large number of parameters between functions. */ struct PaintOperationExecutor { - Object *object_ = nullptr; - ARegion *region_ = nullptr; - GreasePencil *grease_pencil_ = nullptr; - bke::greasepencil::StrokeCache *stroke_cache_ = nullptr; + PaintOperationExecutor(const bContext & /*C*/) {} - PaintOperationExecutor(const bContext &C) + void execute(PaintOperation &self, const bContext &C, const InputSample &extension_sample) { Depsgraph *depsgraph = CTX_data_depsgraph_pointer(&C); - object_ = CTX_data_active_object(&C); - Object *object_eval = DEG_get_evaluated_object(depsgraph, object_); - region_ = CTX_wm_region(&C); + Scene *scene = CTX_data_scene(&C); + Object *object = CTX_data_active_object(&C); + Object *object_eval = DEG_get_evaluated_object(depsgraph, object); + ARegion *region = CTX_wm_region(&C); + Paint *paint = &scene->toolsettings->gp_paint->paint; + Brush *brush = BKE_paint_brush(paint); + int brush_size = BKE_brush_size_get(scene, brush); + float brush_alpha = BKE_brush_alpha_get(scene, brush); /** - * Note: We write to the evaluated object here, so that the additional copy from orig -> eval - * is not needed for every update. After the stroke is done, the result is written to the - * original object. + * Note: We write to the evaluated object here, so that the additional copy from orig -> + * eval is not needed for every update. After the stroke is done, the result is written to + * the original object. */ - grease_pencil_ = static_cast(object_eval->data); - stroke_cache_ = &grease_pencil_->runtime->stroke_cache; - } + GreasePencil *grease_pencil = static_cast(object_eval->data); - void execute(PaintOperation & /*self*/, const bContext &C, const InputSample &extension_sample) - { float4 plane{0.0f, -1.0f, 0.0f, 0.0f}; float3 proj_pos; - ED_view3d_win_to_3d_on_plane(region_, plane, extension_sample.mouse_position, false, proj_pos); + ED_view3d_win_to_3d_on_plane(region, plane, extension_sample.mouse_position, false, proj_pos); - bke::greasepencil::StrokePoint new_point{ - proj_pos, extension_sample.pressure * 100.0f, 1.0f, float4(1.0f)}; + float radius = (brush_size / 2.0f) * + (BKE_brush_use_size_pressure(brush) ? extension_sample.pressure : 1.0f); + float opacity = brush_alpha * + (BKE_brush_use_alpha_pressure(brush) ? extension_sample.pressure : 1.0f); + float4 vertex_color(1.0f); - stroke_cache_->points.append(std::move(new_point)); + bke::greasepencil::StrokePoint new_point; + new_point.position = proj_pos; + new_point.radius = radius; + new_point.opacity = opacity; + new_point.color = vertex_color; - BKE_grease_pencil_batch_cache_dirty_tag(grease_pencil_, BKE_GREASEPENCIL_BATCH_DIRTY_ALL); + self.stroke_cache_->points.append(std::move(new_point)); + + BKE_grease_pencil_batch_cache_dirty_tag(grease_pencil, BKE_GREASEPENCIL_BATCH_DIRTY_ALL); } }; -void PaintOperation::on_stroke_begin(const bContext & /*C*/, const InputSample & /*start_sample*/) +void PaintOperation::on_stroke_begin(const bContext &C, const InputSample & /*start_sample*/) { + Depsgraph *depsgraph = CTX_data_depsgraph_pointer(&C); + Object *object = CTX_data_active_object(&C); + Object *object_eval = DEG_get_evaluated_object(depsgraph, object); + /** + * Note: We write to the evaluated object here, so that the additional copy from orig -> eval + * is not needed for every update. After the stroke is done, the result is written to the + * original object. + */ + GreasePencil *grease_pencil = static_cast(object_eval->data); + stroke_cache_ = &grease_pencil->runtime->stroke_cache; } void PaintOperation::on_stroke_extended(const bContext &C, const InputSample &extension_sample) @@ -91,8 +113,7 @@ void PaintOperation::on_stroke_done(const bContext &C) GreasePencil &grease_pencil_orig = *static_cast(object->data); GreasePencil &grease_pencil_eval = *static_cast(object_eval->data); - const Span stroke_points = - grease_pencil_eval.runtime->stroke_buffer(); + const Span stroke_points = stroke_cache_->points; /* No stroke to create, return. */ if (stroke_points.size() == 0) { -- 2.30.2 From 262703d3ba2a527efee4b7b2a64fc1ba62ae1f01 Mon Sep 17 00:00:00 2001 From: Falk David Date: Mon, 17 Jul 2023 15:25:47 +0200 Subject: [PATCH 09/75] Add vertex color drawing --- .../draw/engines/gpencil/gpencil_layer.hh | 2 +- .../gpencil/shaders/grease_pencil_vert.glsl | 2 +- .../intern/draw_cache_impl_grease_pencil.cc | 14 ++++---- .../sculpt_paint/grease_pencil_paint.cc | 33 +++++++++++++++++-- 4 files changed, 39 insertions(+), 12 deletions(-) diff --git a/source/blender/draw/engines/gpencil/gpencil_layer.hh b/source/blender/draw/engines/gpencil/gpencil_layer.hh index 4af2e191549..cd085774300 100644 --- a/source/blender/draw/engines/gpencil/gpencil_layer.hh +++ b/source/blender/draw/engines/gpencil/gpencil_layer.hh @@ -36,7 +36,7 @@ class LayerModule { { /* TODO(fclem): All of this is placeholder. */ gpLayer gp_layer; - gp_layer.vertex_color_opacity = 0.0f; + // gp_layer.vertex_color_opacity = 0.0f; unused gp_layer.thickness_offset = 0.0f; gp_layer.tint = float4(1.0f, 1.0f, 1.0f, 0.0f); gp_layer.stroke_index_offset = 0.0f; diff --git a/source/blender/draw/engines/gpencil/shaders/grease_pencil_vert.glsl b/source/blender/draw/engines/gpencil/shaders/grease_pencil_vert.glsl index b123931642c..f931c15092b 100644 --- a/source/blender/draw/engines/gpencil/shaders/grease_pencil_vert.glsl +++ b/source/blender/draw/engines/gpencil/shaders/grease_pencil_vert.glsl @@ -8,7 +8,7 @@ void gpencil_color_output(vec4 stroke_col, vec4 vert_col, float vert_strength, f { /* Mix stroke with other colors. */ vec4 mixed_col = stroke_col; - mixed_col.rgb = mix(mixed_col.rgb, vert_col.rgb, vert_col.a * gpVertexColorOpacity); + mixed_col.rgb = mix(mixed_col.rgb, vert_col.rgb, vert_col.a); mixed_col.rgb = mix(mixed_col.rgb, gpLayerTint.rgb, gpLayerTint.a); mixed_col.a *= vert_strength * gpLayerOpacity; /** diff --git a/source/blender/draw/intern/draw_cache_impl_grease_pencil.cc b/source/blender/draw/intern/draw_cache_impl_grease_pencil.cc index 3d7768f6a1c..7ff3978411b 100644 --- a/source/blender/draw/intern/draw_cache_impl_grease_pencil.cc +++ b/source/blender/draw/intern/draw_cache_impl_grease_pencil.cc @@ -317,6 +317,8 @@ static void grease_pencil_geom_batch_ensure(GreasePencil &grease_pencil, int cfr const VArray cyclic = curves.cyclic(); const VArray radii = drawing.radii(); const VArray opacities = drawing.opacities(); + const VArray vertex_colors = *attributes.lookup_or_default( + "vertex_color", ATTR_DOMAIN_POINT, ColorGeometry4f(0.0f, 0.0f, 0.0f, 0.0f)); /* Assumes that if the ".selection" attribute does not exist, all points are selected. */ const VArray selection_float = *attributes.lookup_or_default( ".selection", ATTR_DOMAIN_POINT, true); @@ -359,9 +361,8 @@ static void grease_pencil_geom_batch_ensure(GreasePencil &grease_pencil, int cfr /* TODO: Populate fill UVs. */ s_vert.uv_fill[0] = s_vert.uv_fill[1] = 0; - /* TODO: Populate vertex color and fill color. */ - copy_v4_v4(c_vert.vcol, float4(0.0f, 0.0f, 0.0f, 0.0f)); - copy_v4_v4(c_vert.fcol, float4(0.0f, 0.0f, 0.0f, 0.0f)); + copy_v4_v4(c_vert.vcol, vertex_colors[point_i]); + copy_v4_v4(c_vert.fcol, vertex_colors[point_i]); c_vert.fcol[3] = (int(c_vert.fcol[3] * 10000.0f) * 10.0f) + 1.0f; int v_mat = (verts_range[idx] << GP_VERTEX_ID_SHIFT) | GP_IS_STROKE_VERTEX_BIT; @@ -455,11 +456,8 @@ static void grease_pencil_geom_batch_ensure(GreasePencil &grease_pencil, int cfr /* TODO */ s_vert.uv_fill[0] = s_vert.uv_fill[1] = 0; - /* TODO */ - copy_v4_v4(c_vert.vcol, float4(0.0f, 0.0f, 0.0f, 0.0f)); - copy_v4_v4(c_vert.fcol, float4(0.0f, 0.0f, 0.0f, 0.0f)); - - /* TODO */ + copy_v4_v4(c_vert.vcol, point.color); + copy_v4_v4(c_vert.fcol, point.color); c_vert.fcol[3] = (int(c_vert.fcol[3] * 10000.0f) * 10.0f) + 1.0f; int v_mat = (verts_range[idx] << GP_VERTEX_ID_SHIFT) | GP_IS_STROKE_VERTEX_BIT; diff --git a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc index 328262ec9d9..888d0cb36b0 100644 --- a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc +++ b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc @@ -3,6 +3,7 @@ * SPDX-License-Identifier: GPL-2.0-or-later */ #include "BKE_brush.h" +#include "BKE_colortools.h" #include "BKE_context.h" #include "BKE_curves.hh" #include "BKE_grease_pencil.h" @@ -53,6 +54,16 @@ struct PaintOperationExecutor { Brush *brush = BKE_paint_brush(paint); int brush_size = BKE_brush_size_get(scene, brush); float brush_alpha = BKE_brush_alpha_get(scene, brush); + + const bool use_vertex_color = (scene->toolsettings->gp_paint->mode == + GPPAINT_FLAG_USE_VERTEXCOLOR); + const bool use_vertex_color_stroke = use_vertex_color && + ELEM(brush->gpencil_settings->vertex_mode, + GPPAINT_MODE_STROKE, + GPPAINT_MODE_BOTH); + // const bool use_vertex_color_fill = use_vertex_color && ELEM( + // brush->gpencil_settings->vertex_mode, GPPAINT_MODE_STROKE, GPPAINT_MODE_BOTH); + /** * Note: We write to the evaluated object here, so that the additional copy from orig -> * eval is not needed for every update. After the stroke is done, the result is written to @@ -68,7 +79,12 @@ struct PaintOperationExecutor { (BKE_brush_use_size_pressure(brush) ? extension_sample.pressure : 1.0f); float opacity = brush_alpha * (BKE_brush_use_alpha_pressure(brush) ? extension_sample.pressure : 1.0f); - float4 vertex_color(1.0f); + float4 vertex_color = use_vertex_color_stroke ? + float4(brush->rgb[0], + brush->rgb[1], + brush->rgb[2], + brush->gpencil_settings->vertex_factor) : + float4(0.0f); bke::greasepencil::StrokePoint new_point; new_point.position = proj_pos; @@ -85,6 +101,7 @@ struct PaintOperationExecutor { void PaintOperation::on_stroke_begin(const bContext &C, const InputSample & /*start_sample*/) { Depsgraph *depsgraph = CTX_data_depsgraph_pointer(&C); + Scene *scene = CTX_data_scene(&C); Object *object = CTX_data_active_object(&C); Object *object_eval = DEG_get_evaluated_object(depsgraph, object); /** @@ -94,6 +111,9 @@ void PaintOperation::on_stroke_begin(const bContext &C, const InputSample & /*st */ GreasePencil *grease_pencil = static_cast(object_eval->data); stroke_cache_ = &grease_pencil->runtime->stroke_cache; + + Paint *paint = &scene->toolsettings->gp_paint->paint; + Brush *brush = BKE_paint_brush(paint); } void PaintOperation::on_stroke_extended(const bContext &C, const InputSample &extension_sample) @@ -147,12 +167,20 @@ void PaintOperation::on_stroke_done(const bContext &C) MutableSpan positions = curves.positions_for_write(); MutableSpan radii = drawing_orig.radii_for_write(); MutableSpan opacities = drawing_orig.opacities_for_write(); + SpanAttributeWriter vertex_colors = + attributes.lookup_or_add_for_write_span( + "vertex_color", + ATTR_DOMAIN_POINT, + AttributeInitVArray( + VArray::ForSingle(ColorGeometry4f(0.0f, 0.0f, 0.0f, 0.0f), + attributes.domain_size(ATTR_DOMAIN_POINT)))); for (const int i : IndexRange(stroke_points.size())) { const bke::greasepencil::StrokePoint &point = stroke_points[i]; const int point_i = new_points_range[i]; positions[point_i] = point.position; radii[point_i] = point.radius; opacities[point_i] = point.opacity; + vertex_colors.span[point_i] = ColorGeometry4f(point.color); } /* TODO: Set material index attribute. */ @@ -167,7 +195,7 @@ void PaintOperation::on_stroke_done(const bContext &C) /* Explicitly set all other attributes besides those processed above to default values. */ Set attributes_to_skip{ - {"position", "radius", "opacity", "material_index", "curve_type"}}; + {"position", "radius", "opacity", "vertex_color", "material_index", "curve_type"}}; attributes.for_all( [&](const bke::AttributeIDRef &id, const bke::AttributeMetaData /*meta_data*/) { if (attributes_to_skip.contains(id.name())) { @@ -185,6 +213,7 @@ void PaintOperation::on_stroke_done(const bContext &C) grease_pencil_eval.runtime->stroke_cache.clear(); drawing_orig.tag_positions_changed(); + vertex_colors.finish(); materials.finish(); DEG_id_tag_update(&grease_pencil_orig.id, ID_RECALC_GEOMETRY); -- 2.30.2 From 66cb28393b301b7efcf555e42daea5e12e785e21 Mon Sep 17 00:00:00 2001 From: Falk David Date: Mon, 17 Jul 2023 16:30:41 +0200 Subject: [PATCH 10/75] Use curvemappings --- .../sculpt_paint/grease_pencil_paint.cc | 24 +++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc index 888d0cb36b0..0fc3c7cc723 100644 --- a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc +++ b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc @@ -75,10 +75,16 @@ struct PaintOperationExecutor { float3 proj_pos; ED_view3d_win_to_3d_on_plane(region, plane, extension_sample.mouse_position, false, proj_pos); - float radius = (brush_size / 2.0f) * - (BKE_brush_use_size_pressure(brush) ? extension_sample.pressure : 1.0f); - float opacity = brush_alpha * - (BKE_brush_use_alpha_pressure(brush) ? extension_sample.pressure : 1.0f); + float radius = brush_size / 2.0f; + if (BKE_brush_use_size_pressure(brush)) { + radius *= BKE_curvemapping_evaluateF( + brush->gpencil_settings->curve_sensitivity, 0, extension_sample.pressure); + } + float opacity = brush_alpha; + if (BKE_brush_use_alpha_pressure(brush)) { + opacity *= BKE_curvemapping_evaluateF( + brush->gpencil_settings->curve_strength, 0, extension_sample.pressure); + } float4 vertex_color = use_vertex_color_stroke ? float4(brush->rgb[0], brush->rgb[1], @@ -114,6 +120,16 @@ void PaintOperation::on_stroke_begin(const bContext &C, const InputSample & /*st Paint *paint = &scene->toolsettings->gp_paint->paint; Brush *brush = BKE_paint_brush(paint); + + BKE_curvemapping_init(brush->gpencil_settings->curve_sensitivity); + BKE_curvemapping_init(brush->gpencil_settings->curve_strength); + BKE_curvemapping_init(brush->gpencil_settings->curve_jitter); + BKE_curvemapping_init(brush->gpencil_settings->curve_rand_pressure); + BKE_curvemapping_init(brush->gpencil_settings->curve_rand_strength); + BKE_curvemapping_init(brush->gpencil_settings->curve_rand_uv); + BKE_curvemapping_init(brush->gpencil_settings->curve_rand_hue); + BKE_curvemapping_init(brush->gpencil_settings->curve_rand_saturation); + BKE_curvemapping_init(brush->gpencil_settings->curve_rand_value); } void PaintOperation::on_stroke_extended(const bContext &C, const InputSample &extension_sample) -- 2.30.2 From 11f13c8f291fdafcb27fd3e2f154c77643f5e75e Mon Sep 17 00:00:00 2001 From: Falk David Date: Tue, 18 Jul 2023 14:09:02 +0200 Subject: [PATCH 11/75] Refactor: Replace StrokePoint struct with arrays --- .../blender/blenkernel/BKE_grease_pencil.hh | 45 ++++++++++++------- .../blenkernel/intern/grease_pencil.cc | 10 +---- .../intern/draw_cache_impl_grease_pencil.cc | 25 +++++------ .../sculpt_paint/grease_pencil_paint.cc | 30 ++++--------- .../makesdna/DNA_grease_pencil_types.h | 1 - 5 files changed, 51 insertions(+), 60 deletions(-) diff --git a/source/blender/blenkernel/BKE_grease_pencil.hh b/source/blender/blenkernel/BKE_grease_pencil.hh index 03dd1e01703..e20eb8d1a81 100644 --- a/source/blender/blenkernel/BKE_grease_pencil.hh +++ b/source/blender/blenkernel/BKE_grease_pencil.hh @@ -11,6 +11,8 @@ #include +#include "BLI_array_utils.hh" +#include "BLI_color.hh" #include "BLI_function_ref.hh" #include "BLI_map.hh" #include "BLI_math_vector_types.hh" @@ -32,29 +34,38 @@ namespace blender::bke { namespace greasepencil { -/** - * A single point for a stroke that is currently being drawn. - */ -struct StrokePoint { - float3 position; - float radius; - float opacity; - float4 color; -}; - /** * Stroke cache for a stroke that is currently being drawn. */ struct StrokeCache { - Vector points; + /* Stroke cache attributes. */ + Vector positions; + Vector radii; + Vector opacities; + Vector vertex_colors; + int64_t size = 0; + Vector triangles; int mat = 0; + void append(float3 position, float radius, float opacity, ColorGeometry4f vertex_color) + { + this->positions.append(position); + this->radii.append(radius); + this->opacities.append(opacity); + this->vertex_colors.append(vertex_color); + this->size++; + } + void clear() { - this->points.clear_and_shrink(); + this->positions.clear_and_shrink(); + this->radii.clear_and_shrink(); + this->opacities.clear_and_shrink(); + this->vertex_colors.clear_and_shrink(); this->triangles.clear_and_shrink(); this->mat = 0; + this->size = 0; } }; @@ -668,6 +679,10 @@ class GreasePencilRuntime { * Allocated and freed by the drawing code. See `DRW_grease_pencil_batch_cache_*` functions. */ void *batch_cache = nullptr; + + /** + * A buffer for a single stroke while drawing. + */ bke::greasepencil::StrokeCache stroke_cache; /* The frame on which the object was evaluated (only valid for evaluated object). */ int eval_frame; @@ -676,11 +691,7 @@ class GreasePencilRuntime { GreasePencilRuntime() {} ~GreasePencilRuntime() {} - /** - * A buffer for a single stroke while drawing. - */ - Span stroke_buffer() const; - bool has_stroke_buffer() const; + bool has_stroke_cache() const; }; } // namespace blender::bke diff --git a/source/blender/blenkernel/intern/grease_pencil.cc b/source/blender/blenkernel/intern/grease_pencil.cc index 1734148b523..a206f837a6a 100644 --- a/source/blender/blenkernel/intern/grease_pencil.cc +++ b/source/blender/blenkernel/intern/grease_pencil.cc @@ -1366,15 +1366,9 @@ void BKE_grease_pencil_batch_cache_free(GreasePencil *grease_pencil) /** \name Grease Pencil runtime API * \{ */ -bool blender::bke::GreasePencilRuntime::has_stroke_buffer() const +bool blender::bke::GreasePencilRuntime::has_stroke_cache() const { - return this->stroke_cache.points.size() > 0; -} - -blender::Span blender::bke::GreasePencilRuntime:: - stroke_buffer() const -{ - return this->stroke_cache.points.as_span(); + return this->stroke_cache.size > 0; } /** \} */ diff --git a/source/blender/draw/intern/draw_cache_impl_grease_pencil.cc b/source/blender/draw/intern/draw_cache_impl_grease_pencil.cc index 7ff3978411b..e03fa19326f 100644 --- a/source/blender/draw/intern/draw_cache_impl_grease_pencil.cc +++ b/source/blender/draw/intern/draw_cache_impl_grease_pencil.cc @@ -257,8 +257,8 @@ static void grease_pencil_geom_batch_ensure(GreasePencil &grease_pencil, int cfr tris_start_offsets_per_visible_drawing.append(std::move(tris_start_offsets)); } - if (grease_pencil.runtime->has_stroke_buffer()) { - const int num_buffer_points = grease_pencil.runtime->stroke_buffer().size(); + if (grease_pencil.runtime->has_stroke_cache()) { + const int num_buffer_points = grease_pencil.runtime->stroke_cache.size; total_verts_num += 1 + num_buffer_points + 1; total_triangles_num += num_buffer_points * 2; /* TODO: triangles for stroke buffer. */ @@ -426,25 +426,24 @@ static void grease_pencil_geom_batch_ensure(GreasePencil &grease_pencil, int cfr }); } - if (grease_pencil.runtime->has_stroke_buffer()) { - Span points = grease_pencil.runtime->stroke_buffer(); + if (grease_pencil.runtime->has_stroke_cache()) { + bke::greasepencil::StrokeCache &stroke_cache = grease_pencil.runtime->stroke_cache; const int verts_start_offset = v_offset; - const int num_verts = 1 + points.size() + 1; + const int num_verts = 1 + stroke_cache.size + 1; IndexRange verts_range = IndexRange(verts_start_offset, num_verts); MutableSpan verts_slice = verts.slice(verts_range); MutableSpan cols_slice = cols.slice(verts_range); - const int material_nr = grease_pencil.runtime->stroke_cache.mat; + const int material_nr = stroke_cache.mat; verts_slice.first().mat = -1; - for (const int i : IndexRange(points.size())) { + for (const int i : IndexRange(stroke_cache.size)) { const int idx = i + 1; GreasePencilStrokeVert &s_vert = verts_slice[idx]; GreasePencilColorVert &c_vert = cols_slice[idx]; - const bke::greasepencil::StrokePoint &point = points[i]; - copy_v3_v3(s_vert.pos, point.position); - s_vert.radius = point.radius; - s_vert.opacity = point.opacity; + copy_v3_v3(s_vert.pos, stroke_cache.positions[i]); + s_vert.radius = stroke_cache.radii[i]; + s_vert.opacity = stroke_cache.opacities[i]; s_vert.point_id = verts_range[idx]; s_vert.stroke_id = verts_range.first(); s_vert.mat = material_nr; @@ -456,8 +455,8 @@ static void grease_pencil_geom_batch_ensure(GreasePencil &grease_pencil, int cfr /* TODO */ s_vert.uv_fill[0] = s_vert.uv_fill[1] = 0; - copy_v4_v4(c_vert.vcol, point.color); - copy_v4_v4(c_vert.fcol, point.color); + copy_v4_v4(c_vert.vcol, stroke_cache.vertex_colors[i]); + copy_v4_v4(c_vert.fcol, stroke_cache.vertex_colors[i]); c_vert.fcol[3] = (int(c_vert.fcol[3] * 10000.0f) * 10.0f) + 1.0f; int v_mat = (verts_range[idx] << GP_VERTEX_ID_SHIFT) | GP_IS_STROKE_VERTEX_BIT; diff --git a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc index 0fc3c7cc723..6c1a64b2a9a 100644 --- a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc +++ b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc @@ -12,6 +12,7 @@ #include "DEG_depsgraph_query.h" +#include "ED_grease_pencil.hh" #include "ED_view3d.hh" #include "WM_api.hh" @@ -92,14 +93,7 @@ struct PaintOperationExecutor { brush->gpencil_settings->vertex_factor) : float4(0.0f); - bke::greasepencil::StrokePoint new_point; - new_point.position = proj_pos; - new_point.radius = radius; - new_point.opacity = opacity; - new_point.color = vertex_color; - - self.stroke_cache_->points.append(std::move(new_point)); - + self.stroke_cache_->append(proj_pos, radius, opacity, ColorGeometry4f(vertex_color)); BKE_grease_pencil_batch_cache_dirty_tag(grease_pencil, BKE_GREASEPENCIL_BATCH_DIRTY_ALL); } }; @@ -149,10 +143,8 @@ void PaintOperation::on_stroke_done(const bContext &C) GreasePencil &grease_pencil_orig = *static_cast(object->data); GreasePencil &grease_pencil_eval = *static_cast(object_eval->data); - const Span stroke_points = stroke_cache_->points; - /* No stroke to create, return. */ - if (stroke_points.size() == 0) { + if (stroke_cache_->size == 0) { return; } @@ -169,10 +161,10 @@ void PaintOperation::on_stroke_done(const bContext &C) const int num_old_curves = curves.curves_num(); const int num_old_points = curves.points_num(); - curves.resize(num_old_points + stroke_points.size(), num_old_curves + 1); + curves.resize(num_old_points + stroke_cache_->size, num_old_curves + 1); curves.offsets_for_write()[num_old_curves] = num_old_points; - curves.offsets_for_write()[num_old_curves + 1] = num_old_points + stroke_points.size(); + curves.offsets_for_write()[num_old_curves + 1] = num_old_points + stroke_cache_->size; const OffsetIndices points_by_curve = curves.points_by_curve(); const IndexRange new_points_range = points_by_curve[curves.curves_num() - 1]; @@ -190,14 +182,10 @@ void PaintOperation::on_stroke_done(const bContext &C) AttributeInitVArray( VArray::ForSingle(ColorGeometry4f(0.0f, 0.0f, 0.0f, 0.0f), attributes.domain_size(ATTR_DOMAIN_POINT)))); - for (const int i : IndexRange(stroke_points.size())) { - const bke::greasepencil::StrokePoint &point = stroke_points[i]; - const int point_i = new_points_range[i]; - positions[point_i] = point.position; - radii[point_i] = point.radius; - opacities[point_i] = point.opacity; - vertex_colors.span[point_i] = ColorGeometry4f(point.color); - } + positions.slice(new_points_range).copy_from(stroke_cache_->positions); + radii.slice(new_points_range).copy_from(stroke_cache_->radii); + opacities.slice(new_points_range).copy_from(stroke_cache_->opacities); + vertex_colors.span.slice(new_points_range).copy_from(stroke_cache_->vertex_colors); /* TODO: Set material index attribute. */ int material_index = 0; diff --git a/source/blender/makesdna/DNA_grease_pencil_types.h b/source/blender/makesdna/DNA_grease_pencil_types.h index 004fe8edf7c..d915232a676 100644 --- a/source/blender/makesdna/DNA_grease_pencil_types.h +++ b/source/blender/makesdna/DNA_grease_pencil_types.h @@ -30,7 +30,6 @@ class Layer; class LayerRuntime; class LayerGroup; class LayerGroupRuntime; -struct StrokePoint; } // namespace greasepencil } // namespace blender::bke using GreasePencilRuntimeHandle = blender::bke::GreasePencilRuntime; -- 2.30.2 From e09afe44bb86256710f74a8e82176cf2cc910344 Mon Sep 17 00:00:00 2001 From: Falk David Date: Tue, 18 Jul 2023 15:39:10 +0200 Subject: [PATCH 12/75] Small cleanup --- source/blender/draw/intern/draw_cache_impl_grease_pencil.cc | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/source/blender/draw/intern/draw_cache_impl_grease_pencil.cc b/source/blender/draw/intern/draw_cache_impl_grease_pencil.cc index e03fa19326f..f5d770b4500 100644 --- a/source/blender/draw/intern/draw_cache_impl_grease_pencil.cc +++ b/source/blender/draw/intern/draw_cache_impl_grease_pencil.cc @@ -258,9 +258,8 @@ static void grease_pencil_geom_batch_ensure(GreasePencil &grease_pencil, int cfr } if (grease_pencil.runtime->has_stroke_cache()) { - const int num_buffer_points = grease_pencil.runtime->stroke_cache.size; - total_verts_num += 1 + num_buffer_points + 1; - total_triangles_num += num_buffer_points * 2; + total_verts_num += 1 + grease_pencil.runtime->stroke_cache.size + 1; + total_triangles_num += grease_pencil.runtime->stroke_cache.size * 2; /* TODO: triangles for stroke buffer. */ } -- 2.30.2 From da01d37d3db21f1b45681f6125e3a9499292b5f2 Mon Sep 17 00:00:00 2001 From: Falk David Date: Tue, 18 Jul 2023 15:39:28 +0200 Subject: [PATCH 13/75] Call destructor of paint operation --- source/blender/editors/sculpt_paint/grease_pencil_draw_ops.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/source/blender/editors/sculpt_paint/grease_pencil_draw_ops.cc b/source/blender/editors/sculpt_paint/grease_pencil_draw_ops.cc index 4a2ad2a8c64..3be475880f9 100644 --- a/source/blender/editors/sculpt_paint/grease_pencil_draw_ops.cc +++ b/source/blender/editors/sculpt_paint/grease_pencil_draw_ops.cc @@ -116,6 +116,7 @@ static void stroke_done(const bContext *C, PaintStroke *stroke) GreasePencilStrokeOperation *operation = static_cast( paint_stroke_mode_data(stroke)); operation->on_stroke_done(*C); + operation->~GreasePencilStrokeOperation(); } static int grease_pencil_stroke_invoke(bContext *C, wmOperator *op, const wmEvent *event) -- 2.30.2 From 430217f08f3778125d1b8d70a892106e3b06d67a Mon Sep 17 00:00:00 2001 From: Falk David Date: Tue, 18 Jul 2023 15:58:26 +0200 Subject: [PATCH 14/75] Initial active smoothing --- .../sculpt_paint/grease_pencil_paint.cc | 34 +++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc index 6c1a64b2a9a..720a190c2cd 100644 --- a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc +++ b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc @@ -10,6 +10,8 @@ #include "BKE_grease_pencil.hh" #include "BKE_scene.h" +#include "BLI_math_base.hh" + #include "DEG_depsgraph_query.h" #include "ED_grease_pencil.hh" @@ -24,13 +26,13 @@ namespace blender::ed::sculpt_paint::greasepencil { class PaintOperation : public GreasePencilStrokeOperation { private: + Vector sampled_positions_; + Vector sampled_radii_; bke::greasepencil::StrokeCache *stroke_cache_; friend struct PaintOperationExecutor; public: - ~PaintOperation() override {} - void on_stroke_begin(const bContext &C, const InputSample &start_sample) override; void on_stroke_extended(const bContext &C, const InputSample &extension_sample) override; void on_stroke_done(const bContext &C) override; @@ -94,6 +96,34 @@ struct PaintOperationExecutor { float4(0.0f); self.stroke_cache_->append(proj_pos, radius, opacity, ColorGeometry4f(vertex_color)); + + self.sampled_positions_.append(proj_pos); + self.sampled_radii_.append(radius); + + const int64_t active_window_size = 64; + const int64_t inverted_active_window_size = math::max( + self.stroke_cache_->size - active_window_size, int64_t(0)); + + Span src_positions = self.sampled_positions_.as_span().drop_front( + inverted_active_window_size); + MutableSpan dst_positions = self.stroke_cache_->positions.as_mutable_span().drop_front( + inverted_active_window_size); + + Span src_radii = self.sampled_radii_.as_span().drop_front(inverted_active_window_size); + MutableSpan dst_radii = self.stroke_cache_->radii.as_mutable_span().drop_front( + inverted_active_window_size); + + ed::greasepencil::gaussian_blur_1D(src_positions, 8, 0.75f, false, true, false, dst_positions); + ed::greasepencil::gaussian_blur_1D(src_radii, 10, 1.0f, false, false, false, dst_radii); + +#ifdef DEBUG + /* Visualize active window. */ + self.stroke_cache_->vertex_colors.fill(ColorGeometry4f(float4(0.0f))); + self.stroke_cache_->vertex_colors.as_mutable_span() + .drop_front(inverted_active_window_size) + .fill(ColorGeometry4f(float4(1.0f, 0.1f, 0.1f, 1.0f))); +#endif + BKE_grease_pencil_batch_cache_dirty_tag(grease_pencil, BKE_GREASEPENCIL_BATCH_DIRTY_ALL); } }; -- 2.30.2 From 8d524253f5cc433c9c4a97c48b289fe0fc8202b9 Mon Sep 17 00:00:00 2001 From: Falk David Date: Fri, 21 Jul 2023 16:22:20 +0200 Subject: [PATCH 15/75] Cleanup comment --- scripts/startup/bl_ui/properties_paint_common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/startup/bl_ui/properties_paint_common.py b/scripts/startup/bl_ui/properties_paint_common.py index 693b33b4f28..ff7d2e9247f 100644 --- a/scripts/startup/bl_ui/properties_paint_common.py +++ b/scripts/startup/bl_ui/properties_paint_common.py @@ -868,7 +868,7 @@ def brush_shared_settings(layout, context, brush, popover=False): strength = True direction = brush.curves_sculpt_tool in {'GROW_SHRINK', 'SELECTION_PAINT'} - # Sculpt Curves # + # Grease Pencil # if mode == 'PAINT_GREASE_PENCIL': size = True strength = True -- 2.30.2 From 7a16c9c7639a74c43561be64eb3801fe8c260dec Mon Sep 17 00:00:00 2001 From: Falk David Date: Fri, 21 Jul 2023 16:23:11 +0200 Subject: [PATCH 16/75] Move UI code to `_draw_tool_settings_context_mode` --- scripts/startup/bl_ui/space_view3d.py | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/scripts/startup/bl_ui/space_view3d.py b/scripts/startup/bl_ui/space_view3d.py index c0a0d4805c9..c829f8aff2e 100644 --- a/scripts/startup/bl_ui/space_view3d.py +++ b/scripts/startup/bl_ui/space_view3d.py @@ -592,10 +592,13 @@ class _draw_tool_settings_context_mode: row = layout.row(align=True) row.template_ID_preview(paint, "brush", rows=3, cols=8, hide_buttons=True) - from bl_ui.properties_paint_common import ( - brush_basic__draw_color_selector, - ) - brush_basic__draw_color_selector(context, layout, brush, brush.gpencil_settings, None) + grease_pencil_tool = brush.gpencil_tool + + if grease_pencil_tool == 'DRAW': + from bl_ui.properties_paint_common import ( + brush_basic__draw_color_selector, + ) + brush_basic__draw_color_selector(context, layout, brush, brush.gpencil_settings, None) UnifiedPaintPanel.prop_unified( layout, @@ -620,6 +623,14 @@ class _draw_tool_settings_context_mode: header=True, ) + if grease_pencil_tool == 'DRAW': + layout.prop(brush.gpencil_settings, "active_smooth_factor") + elif grease_pencil_tool == 'ERASE': + layout.prop(brush.gpencil_settings, "eraser_mode", expand=True) + if brush.gpencil_settings.eraser_mode == "HARD": + layout.prop(brush.gpencil_settings, "use_keep_caps_eraser") + layout.prop(brush.gpencil_settings, "use_active_layer_only") + return True -- 2.30.2 From 1f9c0e3dd047c3cc3694e2f4c98bd6373be33bb5 Mon Sep 17 00:00:00 2001 From: Falk David Date: Fri, 21 Jul 2023 16:23:48 +0200 Subject: [PATCH 17/75] Refactor simplify operator and core function --- .../intern/grease_pencil_edit.cc | 152 ++++++++++++------ .../editors/include/ED_grease_pencil.hh | 2 +- 2 files changed, 104 insertions(+), 50 deletions(-) diff --git a/source/blender/editors/grease_pencil/intern/grease_pencil_edit.cc b/source/blender/editors/grease_pencil/intern/grease_pencil_edit.cc index b0b132e9508..3983ee491fd 100644 --- a/source/blender/editors/grease_pencil/intern/grease_pencil_edit.cc +++ b/source/blender/editors/grease_pencil/intern/grease_pencil_edit.cc @@ -392,6 +392,19 @@ static void GREASE_PENCIL_OT_stroke_smooth(wmOperatorType *ot) /** \name Simplify Stroke Operator * \{ */ +static float dist_to_interpolated( + float3 pos, float3 posA, float3 posB, float val, float valA, float valB) +{ + float dist1 = math::distance_squared(posA, pos); + float dist2 = math::distance_squared(posB, pos); + + if (dist1 + dist2 > 0) { + float interpolated_val = interpf(valB, valA, dist1 / (dist1 + dist2)); + return math::distance(interpolated_val, val); + } + return 0.0f; +} + /** * An implementation of the Ramer-Douglas-Peucker algorithm. * @@ -403,10 +416,11 @@ static void GREASE_PENCIL_OT_stroke_smooth(wmOperatorType *ot) * \param points_to_delete: Writes true to the indecies for which the points should be removed. * \returns the total number of points to remove. */ -int64_t ramer_douglas_peucker_simplify(const IndexRange range, - const float epsilon, - const FunctionRef dist_function, - MutableSpan points_to_delete) +int64_t ramer_douglas_peucker_simplify( + const IndexRange range, + const float epsilon, + const FunctionRef dist_function, + MutableSpan points_to_delete) { /* Mark all points to not be removed. */ points_to_delete.slice(range).fill(false); @@ -420,11 +434,11 @@ int64_t ramer_douglas_peucker_simplify(const IndexRange range, /* Compute the maximum distance and the corresponding distance. */ float max_dist = -1.0f; int max_index = -1; - for (const int64_t sub_index : sub_range.index_range().drop_front(1).drop_back(1)) { - const float dist = dist_function(sub_range, sub_index); + for (const int64_t index : sub_range.drop_front(1).drop_back(1)) { + const float dist = dist_function(sub_range.first(), sub_range.last(), index); if (dist > max_dist) { max_dist = dist; - max_index = sub_index; + max_index = index - sub_range.first(); } } @@ -444,6 +458,41 @@ int64_t ramer_douglas_peucker_simplify(const IndexRange range, return total_points_to_remove; } +static int64_t stroke_simplify(const IndexRange points, + const bool cyclic, + const float epsilon, + const FunctionRef dist_function, + MutableSpan points_to_delete) +{ + int64_t total_points_to_delete = 0; + const Span curve_selection = points_to_delete.slice(points); + if (!curve_selection.contains(true)) { + return total_points_to_delete; + } + + const bool is_last_segment_selected = (curve_selection.first() && curve_selection.last()); + + const Vector selection_ranges = array_utils::find_all_ranges(curve_selection, true); + threading::parallel_for( + selection_ranges.index_range(), 1024, [&](const IndexRange range_of_ranges) { + for (const IndexRange range : selection_ranges.as_span().slice(range_of_ranges)) { + total_points_to_delete += ramer_douglas_peucker_simplify( + range.shift(points.start()), epsilon, dist_function, points_to_delete); + } + }); + + /* For cyclic curves, simplify the last segment. */ + if (cyclic && points.size() > 2 && is_last_segment_selected) { + const float dist = dist_function(points.last(1), points.first(), points.last()); + if (dist <= epsilon) { + points_to_delete[points.last()] = true; + total_points_to_delete++; + } + } + + return total_points_to_delete; +} + static int grease_pencil_stroke_simplify_exec(bContext *C, wmOperator *op) { using namespace blender; @@ -466,14 +515,31 @@ static int grease_pencil_stroke_simplify_exec(bContext *C, wmOperator *op) } const Span positions = curves.positions(); + const VArray radii = drawing.radii(); - /* Distance function for `ramer_douglas_peucker_simplify`. */ - auto dist_func = [&](IndexRange range, int64_t index_in_range) { - const Span position_slice = positions.slice(range); - const float dist_position = dist_to_line_v3( - position_slice[index_in_range], position_slice.first(), position_slice.last()); - return dist_position; - }; + /* Distance functions for `ramer_douglas_peucker_simplify`. */ + const auto dist_function_positions = + [positions](int64_t first_index, int64_t last_index, int64_t index) { + const float dist_position = dist_to_line_v3( + positions[index], positions[first_index], positions[last_index]); + return dist_position; + }; + const auto dist_function_positions_and_radii = + [positions, radii](int64_t first_index, int64_t last_index, int64_t index) { + const float dist_position = dist_to_line_v3( + positions[index], positions[first_index], positions[last_index]); + /* We devide the distance by 2000.0f to convert from "pixels" to an actual distance. + * For some reason, grease pencil storkes the thickness of strokes in pixels rather + * than object space distance. */ + const float dist_radii = dist_to_interpolated(positions[index], + positions[first_index], + positions[last_index], + radii[index], + radii[first_index], + radii[last_index]) / + 2000.0f; + return math::max(dist_position, dist_radii); + }; const VArray cyclic = curves.cyclic(); const OffsetIndices points_by_curve = curves.points_by_curve(); @@ -484,42 +550,30 @@ static int grease_pencil_stroke_simplify_exec(bContext *C, wmOperator *op) selection.materialize(points_to_delete); std::atomic total_points_to_delete = 0; - threading::parallel_for(curves.curves_range(), 128, [&](const IndexRange range) { - for (const int curve_i : range) { - const IndexRange points = points_by_curve[curve_i]; - const Span curve_selection = points_to_delete.as_span().slice(points); - if (!curve_selection.contains(true)) { - continue; + if (radii.is_single()) { + threading::parallel_for(curves.curves_range(), 128, [&](const IndexRange range) { + for (const int curve_i : range) { + const IndexRange points = points_by_curve[curve_i]; + total_points_to_delete += stroke_simplify(points, + cyclic[curve_i], + epsilon, + dist_function_positions, + points_to_delete.as_mutable_span()); } - - const bool is_last_segment_selected = (curve_selection.first() && - curve_selection.last()); - - const Vector selection_ranges = array_utils::find_all_ranges( - curve_selection, true); - threading::parallel_for( - selection_ranges.index_range(), 1024, [&](const IndexRange range_of_ranges) { - for (const IndexRange range : selection_ranges.as_span().slice(range_of_ranges)) - { - total_points_to_delete += ramer_douglas_peucker_simplify( - range.shift(points.start()), - epsilon, - dist_func, - points_to_delete.as_mutable_span()); - } - }); - - /* For cyclic curves, simplify the last segment. */ - if (cyclic[curve_i] && curves.points_num() > 2 && is_last_segment_selected) { - const float dist = dist_to_line_v3( - positions[points.last()], positions[points.last(1)], positions[points.first()]); - if (dist <= epsilon) { - points_to_delete[points.last()] = true; - total_points_to_delete++; - } + }); + } + else if (radii.is_span()) { + threading::parallel_for(curves.curves_range(), 128, [&](const IndexRange range) { + for (const int curve_i : range) { + const IndexRange points = points_by_curve[curve_i]; + total_points_to_delete += stroke_simplify(points, + cyclic[curve_i], + epsilon, + dist_function_positions_and_radii, + points_to_delete.as_mutable_span()); } - } - }); + }); + } if (total_points_to_delete > 0) { IndexMaskMemory memory; @@ -552,7 +606,7 @@ static void GREASE_PENCIL_OT_stroke_simplify(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; /* Simplify parameters. */ - prop = RNA_def_float(ot->srna, "factor", 0.001f, 0.0f, 100.0f, "Factor", "", 0.0f, 100.0f); + prop = RNA_def_float(ot->srna, "factor", 0.01f, 0.0f, 100.0f, "Factor", "", 0.0f, 100.0f); RNA_def_property_flag(prop, PROP_SKIP_SAVE); } diff --git a/source/blender/editors/include/ED_grease_pencil.hh b/source/blender/editors/include/ED_grease_pencil.hh index 7162c30eb7b..7023f1f211f 100644 --- a/source/blender/editors/include/ED_grease_pencil.hh +++ b/source/blender/editors/include/ED_grease_pencil.hh @@ -120,7 +120,7 @@ void gaussian_blur_1D(const GSpan src, int64_t ramer_douglas_peucker_simplify(IndexRange range, float epsilon, - FunctionRef dist_function, + FunctionRef dist_function, MutableSpan dst); } // namespace blender::ed::greasepencil -- 2.30.2 From 04eacd96eaa12f6adcad5c98abcad23af4241213 Mon Sep 17 00:00:00 2001 From: Falk David Date: Fri, 21 Jul 2023 16:24:54 +0200 Subject: [PATCH 18/75] Fix active smoothing --- .../sculpt_paint/grease_pencil_paint.cc | 43 ++++++++++++++----- 1 file changed, 32 insertions(+), 11 deletions(-) diff --git a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc index 720a190c2cd..b32d0861f2d 100644 --- a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc +++ b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc @@ -28,6 +28,9 @@ class PaintOperation : public GreasePencilStrokeOperation { private: Vector sampled_positions_; Vector sampled_radii_; + + Vector smoothed_positions_; + Vector smoothed_radii_; bke::greasepencil::StrokeCache *stroke_cache_; friend struct PaintOperationExecutor; @@ -94,27 +97,45 @@ struct PaintOperationExecutor { brush->rgb[2], brush->gpencil_settings->vertex_factor) : float4(0.0f); + const float active_smooth_factor = brush->gpencil_settings->active_smooth; self.stroke_cache_->append(proj_pos, radius, opacity, ColorGeometry4f(vertex_color)); self.sampled_positions_.append(proj_pos); self.sampled_radii_.append(radius); - const int64_t active_window_size = 64; - const int64_t inverted_active_window_size = math::max( - self.stroke_cache_->size - active_window_size, int64_t(0)); + self.smoothed_positions_.append(proj_pos); + self.smoothed_radii_.append(radius); + + const int64_t smooth_window_size = 16; + const int64_t inverted_copy_window_size = math::max( + self.stroke_cache_->size - smooth_window_size, int64_t(0)); + + const int64_t smooth_radius = 16; + + const int64_t inverted_smooth_window_size = math::max( + self.stroke_cache_->size - smooth_window_size - smooth_radius, int64_t(0)); Span src_positions = self.sampled_positions_.as_span().drop_front( - inverted_active_window_size); - MutableSpan dst_positions = self.stroke_cache_->positions.as_mutable_span().drop_front( - inverted_active_window_size); + inverted_smooth_window_size); + MutableSpan dst_positions = self.smoothed_positions_.as_mutable_span().drop_front( + inverted_smooth_window_size); - Span src_radii = self.sampled_radii_.as_span().drop_front(inverted_active_window_size); - MutableSpan dst_radii = self.stroke_cache_->radii.as_mutable_span().drop_front( - inverted_active_window_size); + Span src_radii = self.sampled_radii_.as_span().drop_front(inverted_smooth_window_size); + MutableSpan dst_radii = self.smoothed_radii_.as_mutable_span().drop_front( + inverted_smooth_window_size); - ed::greasepencil::gaussian_blur_1D(src_positions, 8, 0.75f, false, true, false, dst_positions); - ed::greasepencil::gaussian_blur_1D(src_radii, 10, 1.0f, false, false, false, dst_radii); + ed::greasepencil::gaussian_blur_1D( + src_positions, smooth_radius, active_smooth_factor, false, true, false, dst_positions); + ed::greasepencil::gaussian_blur_1D( + src_radii, smooth_radius, active_smooth_factor, false, false, false, dst_radii); + + self.stroke_cache_->positions.as_mutable_span() + .drop_front(inverted_copy_window_size) + .copy_from(self.smoothed_positions_.as_span().drop_front(inverted_copy_window_size)); + self.stroke_cache_->radii.as_mutable_span() + .drop_front(inverted_copy_window_size) + .copy_from(self.smoothed_radii_.as_span().drop_front(inverted_copy_window_size)); #ifdef DEBUG /* Visualize active window. */ -- 2.30.2 From b970c05261271d091f90ccc068aa12cf7bc016f9 Mon Sep 17 00:00:00 2001 From: Falk David Date: Fri, 21 Jul 2023 16:25:19 +0200 Subject: [PATCH 19/75] Simplify post processing --- .../sculpt_paint/grease_pencil_paint.cc | 126 +++++++++++++++--- 1 file changed, 105 insertions(+), 21 deletions(-) diff --git a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc index b32d0861f2d..908e03c2e81 100644 --- a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc +++ b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc @@ -39,6 +39,10 @@ class PaintOperation : public GreasePencilStrokeOperation { void on_stroke_begin(const bContext &C, const InputSample &start_sample) override; void on_stroke_extended(const bContext &C, const InputSample &extension_sample) override; void on_stroke_done(const bContext &C) override; + + private: + void simplify_stroke_cache(const float epsilon); + void create_stroke_from_stroke_cache(bke::greasepencil::Drawing &drawing_orig); }; /** @@ -183,31 +187,83 @@ void PaintOperation::on_stroke_extended(const bContext &C, const InputSample &ex executor.execute(*this, C, extension_sample); } -void PaintOperation::on_stroke_done(const bContext &C) +static float dist_to_interpolated( + float3 pos, float3 posA, float3 posB, float val, float valA, float valB) { - using namespace blender::bke; - Depsgraph *depsgraph = CTX_data_depsgraph_pointer(&C); - Scene *scene = CTX_data_scene(&C); - Object *object = CTX_data_active_object(&C); - Object *object_eval = DEG_get_evaluated_object(depsgraph, object); + float dist1 = math::distance_squared(posA, pos); + float dist2 = math::distance_squared(posB, pos); - GreasePencil &grease_pencil_orig = *static_cast(object->data); - GreasePencil &grease_pencil_eval = *static_cast(object_eval->data); + if (dist1 + dist2 > 0) { + float interpolated_val = interpf(valB, valA, dist1 / (dist1 + dist2)); + return math::distance(interpolated_val, val); + } + return 0.0f; +} - /* No stroke to create, return. */ - if (stroke_cache_->size == 0) { - return; +void PaintOperation::simplify_stroke_cache(const float epsilon) +{ + const Span positions = stroke_cache_->positions.as_span(); + const Span radii = stroke_cache_->radii.as_span(); + const Span opacities = stroke_cache_->opacities.as_span(); + const Span vertex_colors = stroke_cache_->vertex_colors.as_span(); + + /* Distance function for `ramer_douglas_peucker_simplify`. */ + const auto dist_function = [positions, + radii](int64_t first_index, int64_t last_index, int64_t index) { + const float dist_position = dist_to_line_v3( + positions[index], positions[first_index], positions[last_index]); + /* We devide the distance by 2000.0f to convert from "pixels" to an actual distance. + * For some reason, grease pencil storkes the thickness of strokes in pixels rather + * than object space distance. */ + const float dist_radii = dist_to_interpolated(positions[index], + positions[first_index], + positions[last_index], + radii[index], + radii[first_index], + radii[last_index]) / + 2000.0f; + return math::max(dist_position, dist_radii); + }; + + Array points_to_delete(stroke_cache_->size, false); + int64_t total_points_to_remove = ed::greasepencil::ramer_douglas_peucker_simplify( + IndexRange(stroke_cache_->size), epsilon, dist_function, points_to_delete.as_mutable_span()); + + int64_t new_size = stroke_cache_->size - total_points_to_remove; + + Array old_indices(new_size); + int64_t i = 0; + for (const int64_t old_index : points_to_delete.index_range()) { + if (points_to_delete[old_index] == false) { + old_indices[i] = old_index; + i++; + } } - /* The object should have an active layer. */ - BLI_assert(grease_pencil_orig.has_active_layer()); + Vector new_positions(new_size); + Vector new_radii(new_size); + Vector new_opcaities(new_size); + Vector new_vertex_colors(new_size); + for (const int64_t index : IndexRange(new_size)) { + new_positions[index] = positions[old_indices[index]]; + new_radii[index] = radii[old_indices[index]]; + new_opcaities[index] = opacities[old_indices[index]]; + new_vertex_colors[index] = vertex_colors[old_indices[index]]; + } - /* Create the new stroke from the stroke buffer. */ - const bke::greasepencil::Layer &active_layer_orig = *grease_pencil_orig.get_active_layer(); - const int index_orig = active_layer_orig.drawing_index_at(scene->r.cfra); + stroke_cache_->positions = new_positions; + stroke_cache_->radii = new_radii; + stroke_cache_->opacities = new_opcaities; + stroke_cache_->vertex_colors = new_vertex_colors; + stroke_cache_->size = new_size; - bke::greasepencil::Drawing &drawing_orig = - reinterpret_cast(grease_pencil_orig.drawings()[index_orig])->wrap(); + stroke_cache_->triangles.clear_and_shrink(); +} + +void PaintOperation::create_stroke_from_stroke_cache(bke::greasepencil::Drawing &drawing_orig) +{ + using namespace blender::bke; + /* Create a new stroke from the stroke buffer. */ CurvesGeometry &curves = drawing_orig.strokes_for_write(); const int num_old_curves = curves.curves_num(); @@ -265,12 +321,40 @@ void PaintOperation::on_stroke_done(const bContext &C) return true; }); - grease_pencil_eval.runtime->stroke_cache.clear(); - drawing_orig.tag_positions_changed(); - vertex_colors.finish(); materials.finish(); + drawing_orig.tag_positions_changed(); + stroke_cache_->clear(); +} + +void PaintOperation::on_stroke_done(const bContext &C) +{ + using namespace blender::bke; + Depsgraph *depsgraph = CTX_data_depsgraph_pointer(&C); + Scene *scene = CTX_data_scene(&C); + Object *object = CTX_data_active_object(&C); + Object *object_eval = DEG_get_evaluated_object(depsgraph, object); + + GreasePencil &grease_pencil_orig = *static_cast(object->data); + GreasePencil &grease_pencil_eval = *static_cast(object_eval->data); + + /* No stroke to create, return. */ + if (stroke_cache_->size == 0) { + return; + } + + this->simplify_stroke_cache(0.0005f); + + /* The object should have an active layer. */ + BLI_assert(grease_pencil_orig.has_active_layer()); + const bke::greasepencil::Layer &active_layer_orig = *grease_pencil_orig.get_active_layer(); + const int index_orig = active_layer_orig.drawing_index_at(scene->r.cfra); + + bke::greasepencil::Drawing &drawing_orig = + reinterpret_cast(grease_pencil_orig.drawings()[index_orig])->wrap(); + this->create_stroke_from_stroke_cache(drawing_orig); + DEG_id_tag_update(&grease_pencil_orig.id, ID_RECALC_GEOMETRY); WM_main_add_notifier(NC_GEOM | ND_DATA, &grease_pencil_orig.id); } -- 2.30.2 From 8e98008b6f77907e00284417924ee07c63f2a0a2 Mon Sep 17 00:00:00 2001 From: Falk David Date: Fri, 21 Jul 2023 18:06:34 +0200 Subject: [PATCH 20/75] Use correct material index --- .../blender/draw/intern/draw_cache_impl_grease_pencil.cc | 2 +- source/blender/editors/sculpt_paint/grease_pencil_paint.cc | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/source/blender/draw/intern/draw_cache_impl_grease_pencil.cc b/source/blender/draw/intern/draw_cache_impl_grease_pencil.cc index f5d770b4500..0acdb3f178b 100644 --- a/source/blender/draw/intern/draw_cache_impl_grease_pencil.cc +++ b/source/blender/draw/intern/draw_cache_impl_grease_pencil.cc @@ -445,7 +445,7 @@ static void grease_pencil_geom_batch_ensure(GreasePencil &grease_pencil, int cfr s_vert.opacity = stroke_cache.opacities[i]; s_vert.point_id = verts_range[idx]; s_vert.stroke_id = verts_range.first(); - s_vert.mat = material_nr; + s_vert.mat = material_nr % GPENCIL_MATERIAL_BUFFER_LEN; /* TODO */ s_vert.packed_asp_hard_rot = pack_rotation_aspect_hardness(0.0f, 1.0f, 1.0f); diff --git a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc index 908e03c2e81..aa6ace224af 100644 --- a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc +++ b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc @@ -8,6 +8,7 @@ #include "BKE_curves.hh" #include "BKE_grease_pencil.h" #include "BKE_grease_pencil.hh" +#include "BKE_material.h" #include "BKE_scene.h" #include "BLI_math_base.hh" @@ -179,6 +180,10 @@ void PaintOperation::on_stroke_begin(const bContext &C, const InputSample & /*st BKE_curvemapping_init(brush->gpencil_settings->curve_rand_hue); BKE_curvemapping_init(brush->gpencil_settings->curve_rand_saturation); BKE_curvemapping_init(brush->gpencil_settings->curve_rand_value); + + Material *material = BKE_grease_pencil_object_material_ensure_from_active_input_brush( + CTX_data_main(&C), object, brush); + stroke_cache_->mat = BKE_grease_pencil_object_material_index_get(object, material); } void PaintOperation::on_stroke_extended(const bContext &C, const InputSample &extension_sample) @@ -295,7 +300,7 @@ void PaintOperation::create_stroke_from_stroke_cache(bke::greasepencil::Drawing vertex_colors.span.slice(new_points_range).copy_from(stroke_cache_->vertex_colors); /* TODO: Set material index attribute. */ - int material_index = 0; + int material_index = stroke_cache_->mat; SpanAttributeWriter materials = attributes.lookup_or_add_for_write_span( "material_index", ATTR_DOMAIN_CURVE); -- 2.30.2 From ca97afc7c4bb34c86432db42f6f7bdebe1a60ba2 Mon Sep 17 00:00:00 2001 From: Falk David Date: Fri, 21 Jul 2023 18:07:01 +0200 Subject: [PATCH 21/75] Fix warning --- source/blender/editors/sculpt_paint/grease_pencil_paint.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc index aa6ace224af..889db4d0cdd 100644 --- a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc +++ b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc @@ -342,7 +342,6 @@ void PaintOperation::on_stroke_done(const bContext &C) Object *object_eval = DEG_get_evaluated_object(depsgraph, object); GreasePencil &grease_pencil_orig = *static_cast(object->data); - GreasePencil &grease_pencil_eval = *static_cast(object_eval->data); /* No stroke to create, return. */ if (stroke_cache_->size == 0) { -- 2.30.2 From 86bc64e67b6224eaa67bf713b6518cf6da1e3eaa Mon Sep 17 00:00:00 2001 From: Falk David Date: Fri, 21 Jul 2023 18:12:53 +0200 Subject: [PATCH 22/75] Enable material panel in topbar --- scripts/startup/bl_ui/space_view3d.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/startup/bl_ui/space_view3d.py b/scripts/startup/bl_ui/space_view3d.py index c829f8aff2e..3b39ff5d310 100644 --- a/scripts/startup/bl_ui/space_view3d.py +++ b/scripts/startup/bl_ui/space_view3d.py @@ -8329,7 +8329,7 @@ class TOPBAR_PT_gpencil_materials(GreasePencilMaterialsPanel, Panel): @classmethod def poll(cls, context): ob = context.object - return ob and ob.type == 'GPENCIL' + return ob and (ob.type == 'GPENCIL' or ob.type == 'GREASEPENCIL') class TOPBAR_PT_gpencil_vertexcolor(GreasePencilVertexcolorPanel, Panel): -- 2.30.2 From 000be01aa66058d63b2084762f00698b3bd8ca5b Mon Sep 17 00:00:00 2001 From: Falk David Date: Fri, 21 Jul 2023 18:15:23 +0200 Subject: [PATCH 23/75] Remove unused variables --- .../editors/sculpt_paint/grease_pencil_paint.cc | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc index 889db4d0cdd..82d248fe2f7 100644 --- a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc +++ b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc @@ -336,12 +336,9 @@ void PaintOperation::create_stroke_from_stroke_cache(bke::greasepencil::Drawing void PaintOperation::on_stroke_done(const bContext &C) { using namespace blender::bke; - Depsgraph *depsgraph = CTX_data_depsgraph_pointer(&C); Scene *scene = CTX_data_scene(&C); Object *object = CTX_data_active_object(&C); - Object *object_eval = DEG_get_evaluated_object(depsgraph, object); - - GreasePencil &grease_pencil_orig = *static_cast(object->data); + GreasePencil &grease_pencil = *static_cast(object->data); /* No stroke to create, return. */ if (stroke_cache_->size == 0) { @@ -351,16 +348,16 @@ void PaintOperation::on_stroke_done(const bContext &C) this->simplify_stroke_cache(0.0005f); /* The object should have an active layer. */ - BLI_assert(grease_pencil_orig.has_active_layer()); - const bke::greasepencil::Layer &active_layer_orig = *grease_pencil_orig.get_active_layer(); + BLI_assert(grease_pencil.has_active_layer()); + const bke::greasepencil::Layer &active_layer_orig = *grease_pencil.get_active_layer(); const int index_orig = active_layer_orig.drawing_index_at(scene->r.cfra); bke::greasepencil::Drawing &drawing_orig = - reinterpret_cast(grease_pencil_orig.drawings()[index_orig])->wrap(); + reinterpret_cast(grease_pencil.drawings()[index_orig])->wrap(); this->create_stroke_from_stroke_cache(drawing_orig); - DEG_id_tag_update(&grease_pencil_orig.id, ID_RECALC_GEOMETRY); - WM_main_add_notifier(NC_GEOM | ND_DATA, &grease_pencil_orig.id); + DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY); + WM_main_add_notifier(NC_GEOM | ND_DATA, &grease_pencil.id); } std::unique_ptr new_paint_operation() -- 2.30.2 From e923eb201f6566209e2d26f3f8b6c04cda489dbf Mon Sep 17 00:00:00 2001 From: Falk David Date: Mon, 24 Jul 2023 12:27:56 +0200 Subject: [PATCH 24/75] Add another material API function --- source/blender/blenkernel/BKE_grease_pencil.hh | 1 + source/blender/blenkernel/intern/grease_pencil.cc | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/source/blender/blenkernel/BKE_grease_pencil.hh b/source/blender/blenkernel/BKE_grease_pencil.hh index e20eb8d1a81..90d23efb905 100644 --- a/source/blender/blenkernel/BKE_grease_pencil.hh +++ b/source/blender/blenkernel/BKE_grease_pencil.hh @@ -787,6 +787,7 @@ Material *BKE_grease_pencil_object_material_new(Main *bmain, Object *ob, const char *name, int *r_index); +Material *BKE_grease_pencil_object_material_from_brush_get(Object *ob, Brush *brush); Material *BKE_grease_pencil_object_material_ensure_by_name(Main *bmain, Object *ob, const char *name, diff --git a/source/blender/blenkernel/intern/grease_pencil.cc b/source/blender/blenkernel/intern/grease_pencil.cc index a206f837a6a..5afceb79770 100644 --- a/source/blender/blenkernel/intern/grease_pencil.cc +++ b/source/blender/blenkernel/intern/grease_pencil.cc @@ -1227,6 +1227,18 @@ Material *BKE_grease_pencil_object_material_new(Main *bmain, return ma; } +Material *BKE_grease_pencil_object_material_from_brush_get(Object *ob, Brush *brush) +{ + if ((brush) && (brush->gpencil_settings) && + (brush->gpencil_settings->flag & GP_BRUSH_MATERIAL_PINNED)) + { + Material *ma = BKE_grease_pencil_brush_material_get(brush); + return ma; + } + + return BKE_object_material_get(ob, ob->actcol); +} + Material *BKE_grease_pencil_object_material_ensure_by_name(Main *bmain, Object *ob, const char *name, -- 2.30.2 From 0b299f4c415e845caf895e954f6717ff05deb6d9 Mon Sep 17 00:00:00 2001 From: Falk David Date: Mon, 24 Jul 2023 12:31:30 +0200 Subject: [PATCH 25/75] Draw the cursor --- .../editors/grease_pencil/CMakeLists.txt | 2 + .../intern/grease_pencil_utils.cc | 82 ++++++++ .../editors/include/ED_grease_pencil.hh | 2 + .../editors/sculpt_paint/paint_cursor.cc | 179 +++++++++++++++++- 4 files changed, 260 insertions(+), 5 deletions(-) create mode 100644 source/blender/editors/grease_pencil/intern/grease_pencil_utils.cc diff --git a/source/blender/editors/grease_pencil/CMakeLists.txt b/source/blender/editors/grease_pencil/CMakeLists.txt index 8c7aefd2414..5168b42fc95 100644 --- a/source/blender/editors/grease_pencil/CMakeLists.txt +++ b/source/blender/editors/grease_pencil/CMakeLists.txt @@ -11,6 +11,7 @@ set(INC ../../imbuf ../../makesrna ../../windowmanager + ../../bmesh # RNA_prototypes.h ${CMAKE_BINARY_DIR}/source/blender/makesrna ) @@ -25,6 +26,7 @@ set(SRC intern/grease_pencil_layers.cc intern/grease_pencil_ops.cc intern/grease_pencil_select.cc + intern/grease_pencil_utils.cc ) set(LIB diff --git a/source/blender/editors/grease_pencil/intern/grease_pencil_utils.cc b/source/blender/editors/grease_pencil/intern/grease_pencil_utils.cc new file mode 100644 index 00000000000..e93f49590db --- /dev/null +++ b/source/blender/editors/grease_pencil/intern/grease_pencil_utils.cc @@ -0,0 +1,82 @@ +/* SPDX-FileCopyrightText: 2023 Blender Foundation + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup edgreasepencil + */ + +#include "BKE_brush.h" +#include "BKE_context.h" +#include "BKE_grease_pencil.hh" + +#include "BLI_math_vector.hh" + +#include "DNA_brush_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" + +#include "ED_grease_pencil.h" +#include "ED_view3d.h" + +namespace blender::ed::greasepencil { + +static float3 drawing_origin(const Scene *scene, const Object *object, char align_flag) +{ + BLI_assert(object != nullptr && object->type == OB_GREASE_PENCIL); + if (align_flag & GP_PROJECT_VIEWSPACE) { + if (align_flag & GP_PROJECT_CURSOR) { + return float3(scene->cursor.location); + } + else { + /* Use the object location. */ + return float3(object->object_to_world[3]); + } + } + return float3(scene->cursor.location); +} + +static float3 screen_space_to_3d( + const Scene *scene, const ARegion *region, const View3D *v3d, const Object *object, float2 co) +{ + float3 origin = drawing_origin(scene, object, scene->toolsettings->gpencil_v3d_align); + float3 r_co; + ED_view3d_win_to_3d(v3d, region, origin, co, r_co); + return r_co; +} + +float brush_radius_world_space(bContext &C, int x, int y) +{ + using namespace blender; + ARegion *region = CTX_wm_region(&C); + View3D *v3d = CTX_wm_view3d(&C); + Scene *scene = CTX_data_scene(&C); + Object *object = CTX_data_active_object(&C); + Brush *brush = scene->toolsettings->gp_paint->paint.brush; + + /* Default radius. */ + float radius = 2.0f; + if (brush == nullptr || object->type != OB_GREASE_PENCIL) { + return radius; + } + + /* Use an (arbitrary) screen space offset in the x direction to measure the size. */ + const int x_offest = 64; + const float brush_size = static_cast(BKE_brush_size_get(scene, brush)); + + /* Get two 3d coordinates to measure the distance from. */ + const float2 screen1(x, y); + const float2 screen2(x + x_offest, y); + const float3 pos1 = screen_space_to_3d(scene, region, v3d, object, screen1); + const float3 pos2 = screen_space_to_3d(scene, region, v3d, object, screen2); + + /* Clip extreme zoom level (and avoid division by zero). */ + const float distance = math::max(math::distance(pos1, pos2), 0.001f); + + /* Calculate the radius of the brush in world space. */ + radius = (1.0f / distance) * (brush_size / 64.0f); + + return radius; +} + +} // namespace blender::ed::greasepencil diff --git a/source/blender/editors/include/ED_grease_pencil.hh b/source/blender/editors/include/ED_grease_pencil.hh index 7023f1f211f..d3a06d3e464 100644 --- a/source/blender/editors/include/ED_grease_pencil.hh +++ b/source/blender/editors/include/ED_grease_pencil.hh @@ -101,6 +101,8 @@ bool has_any_frame_selected(const bke::greasepencil::Layer &layer); void create_keyframe_edit_data_selected_frames_list(KeyframeEditData *ked, const bke::greasepencil::Layer &layer); +float brush_radius_world_space(bContext &C, int x, int y); + bool active_grease_pencil_poll(bContext *C); bool editable_grease_pencil_poll(bContext *C); bool editable_grease_pencil_point_selection_poll(bContext *C); diff --git a/source/blender/editors/sculpt_paint/paint_cursor.cc b/source/blender/editors/sculpt_paint/paint_cursor.cc index d783823fd72..811079642a7 100644 --- a/source/blender/editors/sculpt_paint/paint_cursor.cc +++ b/source/blender/editors/sculpt_paint/paint_cursor.cc @@ -16,6 +16,7 @@ #include "DNA_brush_types.h" #include "DNA_color_types.h" #include "DNA_customdata_types.h" +#include "DNA_material_types.h" #include "DNA_mesh_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" @@ -28,6 +29,7 @@ #include "BKE_colortools.h" #include "BKE_context.h" #include "BKE_curve.h" +#include "BKE_grease_pencil.hh" #include "BKE_image.h" #include "BKE_node_runtime.hh" #include "BKE_object.h" @@ -41,6 +43,7 @@ #include "IMB_colormanagement.h" #include "IMB_imbuf_types.h" +#include "ED_grease_pencil.hh" #include "ED_image.hh" #include "ED_view3d.hh" @@ -1203,10 +1206,24 @@ static void SCULPT_layer_brush_height_preview_draw(const uint gpuattr, static bool paint_use_2d_cursor(ePaintMode mode) { - if (mode >= PAINT_MODE_TEXTURE_3D) { - return true; + switch (mode) { + case PAINT_MODE_SCULPT: + case PAINT_MODE_VERTEX: + case PAINT_MODE_WEIGHT: + return false; + case PAINT_MODE_TEXTURE_3D: + case PAINT_MODE_TEXTURE_2D: + case PAINT_MODE_SCULPT_UV: + case PAINT_MODE_VERTEX_GPENCIL: + case PAINT_MODE_SCULPT_GPENCIL: + case PAINT_MODE_WEIGHT_GPENCIL: + case PAINT_MODE_SCULPT_CURVES: + case PAINT_MODE_GPENCIL: + return true; + case PAINT_MODE_INVALID: + BLI_assert_unreachable(); } - return false; + return true; } enum PaintCursorDrawingType { @@ -1422,10 +1439,15 @@ static void paint_update_mouse_cursor(PaintCursorContext *pcontext) * with the UI (dragging a number button for e.g.), see: #102792. */ return; } - WM_cursor_set(pcontext->win, WM_CURSOR_PAINT); + if (pcontext->mode == PAINT_MODE_GPENCIL) { + WM_cursor_set(pcontext->win, WM_CURSOR_DOT); + } + else { + WM_cursor_set(pcontext->win, WM_CURSOR_PAINT); + } } -static void paint_draw_2D_view_brush_cursor(PaintCursorContext *pcontext) +static void paint_draw_2D_view_brush_cursor_default(PaintCursorContext *pcontext) { immUniformColor3fvAlpha(pcontext->outline_col, pcontext->outline_alpha); @@ -1448,6 +1470,153 @@ static void paint_draw_2D_view_brush_cursor(PaintCursorContext *pcontext) 40); } +static void grease_pencil_eraser_draw(PaintCursorContext *pcontext) +{ + float radius = static_cast(BKE_brush_size_get(pcontext->scene, pcontext->brush)); + + /* Redish color with alpha. */ + immUniformColor4ub(255, 100, 100, 20); + imm_draw_circle_fill_2d(pcontext->pos, pcontext->x, pcontext->y, radius, 40); + + immUnbindProgram(); + + immBindBuiltinProgram(GPU_SHADER_3D_LINE_DASHED_UNIFORM_COLOR); + + float viewport_size[4]; + GPU_viewport_size_get_f(viewport_size); + immUniform2f("viewport_size", viewport_size[2], viewport_size[3]); + + immUniformColor4f(1.0f, 0.39f, 0.39f, 0.78f); + immUniform1i("colors_len", 0); /* "simple" mode */ + immUniform1f("dash_width", 12.0f); + immUniform1f("udash_factor", 0.5f); + + /* XXX Dashed shader gives bad results with sets of small segments + * currently, temp hack around the issue. :( */ + const int nsegments = max_ii(8, radius / 2); + imm_draw_circle_wire_2d(pcontext->pos, pcontext->x, pcontext->y, radius, nsegments); +} + +static void grease_pencil_brush_cursor_draw(PaintCursorContext *pcontext) +{ + using namespace blender; + if ((pcontext->region) && (pcontext->region->regiontype != RGN_TYPE_WINDOW)) { + return; + } + if (pcontext->region && !BLI_rcti_isect_pt(&pcontext->region->winrct, pcontext->x, pcontext->y)) + { + return; + } + + Object *object = CTX_data_active_object(pcontext->C); + if (object->type != OB_GREASE_PENCIL) { + return; + } + GreasePencil &grease_pencil = *reinterpret_cast(object->data); + + /* default radius and color */ + float color[3] = {1.0f, 1.0f, 1.0f}; + float darkcolor[3]; + float radius = 2.0f; + + const int x = pcontext->x; + const int y = pcontext->y; + + /* for paint use paint brush size and color */ + if (pcontext->mode == PAINT_MODE_GPENCIL) { + Paint *paint = pcontext->paint; + Brush *brush = pcontext->brush; + if ((brush == nullptr) || (brush->gpencil_settings == nullptr)) { + return; + } + + /* while drawing hide */ + if (grease_pencil.runtime->has_stroke_cache()) { + return; + } + + if ((paint->flags & PAINT_SHOW_BRUSH) == 0) { + return; + } + + /* Eraser has a special shape and use a different shader program. */ + if (brush->gpencil_tool == GPAINT_TOOL_ERASE) { + grease_pencil_eraser_draw(pcontext); + return; + } + + /* Get current drawing material. */ + Material *ma = BKE_grease_pencil_object_material_from_brush_get(object, brush); + if (ma) { + MaterialGPencilStyle *gp_style = ma->gp_style; + + /* Follow user settings for the size of the draw cursor: + * - Fixed size, or + * - Brush size (i.e. stroke thickness) + */ + if ((gp_style) && ((brush->gpencil_settings->flag & GP_BRUSH_STABILIZE_MOUSE) == 0) && + ((brush->gpencil_settings->flag & GP_BRUSH_STABILIZE_MOUSE_TEMP) == 0) && + (brush->gpencil_tool == GPAINT_TOOL_DRAW)) + { + + const bool use_vertex_color = (pcontext->scene->toolsettings->gp_paint->mode == + GPPAINT_FLAG_USE_VERTEXCOLOR); + const bool use_vertex_color_stroke = use_vertex_color && + ELEM(brush->gpencil_settings->vertex_mode, + GPPAINT_MODE_STROKE, + GPPAINT_MODE_BOTH); + + radius = ed::greasepencil::brush_radius_world_space( + *pcontext->C, pcontext->x, pcontext->y); + + copy_v3_v3(color, use_vertex_color_stroke ? brush->rgb : gp_style->stroke_rgba); + } + } + } + + GPU_line_width(1.0f); + /* Inner Ring: Color from UI panel */ + immUniformColor4f(color[0], color[1], color[2], 0.8f); + imm_draw_circle_wire_2d(pcontext->pos, x, y, radius, 32); + + /* Outer Ring: Dark color for contrast on light backgrounds (e.g. gray on white) */ + mul_v3_v3fl(darkcolor, color, 0.40f); + immUniformColor4f(darkcolor[0], darkcolor[1], darkcolor[2], 0.8f); + imm_draw_circle_wire_2d(pcontext->pos, x, y, radius + 1, 32); + + /* Draw line for lazy mouse */ + /* TODO: No stabilize mode yet. */ + // if ((last_mouse_position) && + // (pcontext->xbrush->gpencil_settings->flag & GP_BRUSH_STABILIZE_MOUSE_TEMP)) + // { + // GPU_line_smooth(true); + // GPU_blend(GPU_BLEND_ALPHA); + + // copy_v3_v3(color, pcontext->brush->add_col); + // immUniformColor4f(color[0], color[1], color[2], 0.8f); + + // immBegin(GPU_PRIM_LINES, 2); + // immVertex2f(pos, x, y); + // immVertex2f(pos, + // last_mouse_position[0] + pcontext->region->winrct.xmin, + // last_mouse_position[1] + pcontext->region->winrct.ymin); + // immEnd(); + // } +} + +static void paint_draw_2D_view_brush_cursor(PaintCursorContext *pcontext) +{ + switch (pcontext->mode) { + case PAINT_MODE_GPENCIL: { + grease_pencil_brush_cursor_draw(pcontext); + break; + } + default: { + paint_draw_2D_view_brush_cursor_default(pcontext); + } + } +} + static void paint_draw_legacy_3D_view_brush_cursor(PaintCursorContext *pcontext) { GPU_line_width(1.0f); -- 2.30.2 From 3e869a40121b3074b813f0d3031c608087bb29cc Mon Sep 17 00:00:00 2001 From: Falk David Date: Mon, 24 Jul 2023 12:35:24 +0200 Subject: [PATCH 26/75] Comment out debug visualization --- .../editors/sculpt_paint/grease_pencil_paint.cc | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc index 82d248fe2f7..a6596c7c84f 100644 --- a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc +++ b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc @@ -143,11 +143,11 @@ struct PaintOperationExecutor { .copy_from(self.smoothed_radii_.as_span().drop_front(inverted_copy_window_size)); #ifdef DEBUG - /* Visualize active window. */ - self.stroke_cache_->vertex_colors.fill(ColorGeometry4f(float4(0.0f))); - self.stroke_cache_->vertex_colors.as_mutable_span() - .drop_front(inverted_active_window_size) - .fill(ColorGeometry4f(float4(1.0f, 0.1f, 0.1f, 1.0f))); + // /* Visualize active window. */ + // self.stroke_cache_->vertex_colors.fill(ColorGeometry4f(float4(0.0f))); + // self.stroke_cache_->vertex_colors.as_mutable_span() + // .drop_front(inverted_active_window_size) + // .fill(ColorGeometry4f(float4(1.0f, 0.1f, 0.1f, 1.0f))); #endif BKE_grease_pencil_batch_cache_dirty_tag(grease_pencil, BKE_GREASEPENCIL_BATCH_DIRTY_ALL); -- 2.30.2 From 2cb594efa02cbe09fdaca33a8e5d66e5f855d0cf Mon Sep 17 00:00:00 2001 From: Falk David Date: Mon, 24 Jul 2023 12:57:43 +0200 Subject: [PATCH 27/75] Proper sizing for radial controls --- .../keyconfig/keymap_data/blender_default.py | 5 +++++ .../blender/windowmanager/intern/wm_operators.cc | 16 ++++++++++++---- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/scripts/presets/keyconfig/keymap_data/blender_default.py b/scripts/presets/keyconfig/keymap_data/blender_default.py index 18a6a3a2f97..99e7ccb5fb1 100644 --- a/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -4553,6 +4553,11 @@ def km_grease_pencil_paint(_params): ) items.extend([ + *_template_paint_radial_control("gpencil_paint"), + ("brush.scale_size", {"type": 'LEFT_BRACKET', "value": 'PRESS', "repeat": True}, + {"properties": [("scalar", 0.9)]}), + ("brush.scale_size", {"type": 'RIGHT_BRACKET', "value": 'PRESS', "repeat": True}, + {"properties": [("scalar", 1.0 / 0.9)]}), ("grease_pencil.brush_stroke", {"type": 'LEFTMOUSE', "value": 'PRESS'}, None), ("grease_pencil.brush_stroke", {"type": 'LEFTMOUSE', "value": 'PRESS', "ctrl": True}, {"properties": [("mode", 'INVERT')]}), diff --git a/source/blender/windowmanager/intern/wm_operators.cc b/source/blender/windowmanager/intern/wm_operators.cc index 20f80a3c8fb..09506b1918c 100644 --- a/source/blender/windowmanager/intern/wm_operators.cc +++ b/source/blender/windowmanager/intern/wm_operators.cc @@ -76,6 +76,7 @@ #include "ED_fileselect.hh" #include "ED_gpencil_legacy.hh" +#include "ED_grease_pencil.hh" #include "ED_numinput.hh" #include "ED_screen.hh" #include "ED_undo.hh" @@ -2351,10 +2352,17 @@ static void radial_control_set_initial_mouse(bContext *C, RadialControl *rc, con d[1] *= zoom[1]; } /* Grease pencil draw tool needs to rescale the cursor size. If we don't do that - * the size of the radial is not equals to the actual stroke size. */ - if (rc->ptr.owner_id && GS(rc->ptr.owner_id->name) == ID_BR && rc->prop == &rna_Brush_size) { - rc->scale_fac = ED_gpencil_radial_control_scale( - C, (Brush *)rc->ptr.owner_id, rc->initial_value, event->mval); + * the size of the radial is not equal to the actual stroke size. */ + Object *object = CTX_data_active_object(C); + if (object->type == OB_GREASE_PENCIL && rc->prop == &rna_UnifiedPaintSettings_size) { + float cursor_radius = blender::ed::greasepencil::brush_radius_world_space( + *C, event->mval[0], event->mval[1]); + rc->scale_fac = max_ff(cursor_radius, 1.0f) / max_ff(rc->initial_value, 1.0f); + } + else if (rc->ptr.owner_id && GS(rc->ptr.owner_id->name) == ID_BR && rc->prop == &rna_Brush_size) + { + Brush *brush = reinterpret_cast(rc->ptr.owner_id); + rc->scale_fac = ED_gpencil_radial_control_scale(C, brush, rc->initial_value, event->mval); } else { rc->scale_fac = 1.0f; -- 2.30.2 From 2f90fd96ec2292637bd4a0cccaf2c91e6da1e0ab Mon Sep 17 00:00:00 2001 From: Falk David Date: Mon, 24 Jul 2023 13:45:19 +0200 Subject: [PATCH 28/75] Set cursor for 2d drawing --- source/blender/editors/sculpt_paint/paint_cursor.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source/blender/editors/sculpt_paint/paint_cursor.cc b/source/blender/editors/sculpt_paint/paint_cursor.cc index 811079642a7..e9aeb44bc6f 100644 --- a/source/blender/editors/sculpt_paint/paint_cursor.cc +++ b/source/blender/editors/sculpt_paint/paint_cursor.cc @@ -2111,6 +2111,8 @@ static void paint_draw_cursor(bContext *C, int x, int y, void * /*unused*/) paint_draw_curve_cursor(pcontext.brush, &pcontext.vc); break; case PAINT_CURSOR_2D: + paint_update_mouse_cursor(&pcontext); + paint_cursor_update_rake_rotation(&pcontext); paint_cursor_check_and_draw_alpha_overlays(&pcontext); paint_cursor_update_anchored_location(&pcontext); -- 2.30.2 From 88f6a7f78c30023aec60248886458f7da265d8f5 Mon Sep 17 00:00:00 2001 From: Falk David Date: Mon, 24 Jul 2023 14:07:11 +0200 Subject: [PATCH 29/75] Rescale radial control only for draw tool --- .../blender/windowmanager/intern/wm_operators.cc | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/source/blender/windowmanager/intern/wm_operators.cc b/source/blender/windowmanager/intern/wm_operators.cc index 09506b1918c..6ca5bb95c23 100644 --- a/source/blender/windowmanager/intern/wm_operators.cc +++ b/source/blender/windowmanager/intern/wm_operators.cc @@ -2351,22 +2351,25 @@ static void radial_control_set_initial_mouse(bContext *C, RadialControl *rc, con d[0] *= zoom[0]; d[1] *= zoom[1]; } + rc->scale_fac = 1.0f; /* Grease pencil draw tool needs to rescale the cursor size. If we don't do that * the size of the radial is not equal to the actual stroke size. */ Object *object = CTX_data_active_object(C); if (object->type == OB_GREASE_PENCIL && rc->prop == &rna_UnifiedPaintSettings_size) { - float cursor_radius = blender::ed::greasepencil::brush_radius_world_space( - *C, event->mval[0], event->mval[1]); - rc->scale_fac = max_ff(cursor_radius, 1.0f) / max_ff(rc->initial_value, 1.0f); + ToolSettings *ts = CTX_data_tool_settings(C); + const Brush &brush = *BKE_paint_brush_for_read(&ts->gp_paint->paint); + /* Only the draw brush needs the rescaling. */ + if (brush.gpencil_tool == GPAINT_TOOL_DRAW) { + float cursor_radius = blender::ed::greasepencil::brush_radius_world_space( + *C, event->mval[0], event->mval[1]); + rc->scale_fac = max_ff(cursor_radius, 1.0f) / max_ff(rc->initial_value, 1.0f); + } } else if (rc->ptr.owner_id && GS(rc->ptr.owner_id->name) == ID_BR && rc->prop == &rna_Brush_size) { Brush *brush = reinterpret_cast(rc->ptr.owner_id); rc->scale_fac = ED_gpencil_radial_control_scale(C, brush, rc->initial_value, event->mval); } - else { - rc->scale_fac = 1.0f; - } rc->initial_mouse[0] -= d[0]; rc->initial_mouse[1] -= d[1]; -- 2.30.2 From e25615896b26e2b52164b416a3c6a145d0868341 Mon Sep 17 00:00:00 2001 From: Falk David Date: Wed, 2 Aug 2023 11:14:26 +0200 Subject: [PATCH 30/75] Rewrite `StrokeCache` as class --- .../blender/blenkernel/BKE_grease_pencil.hh | 61 +++++++------- .../blenkernel/intern/grease_pencil.cc | 81 ++++++++++++++++++- .../intern/draw_cache_impl_grease_pencil.cc | 21 ++--- .../sculpt_paint/grease_pencil_paint.cc | 72 +++++++++-------- 4 files changed, 161 insertions(+), 74 deletions(-) diff --git a/source/blender/blenkernel/BKE_grease_pencil.hh b/source/blender/blenkernel/BKE_grease_pencil.hh index 90d23efb905..02d1c9460d3 100644 --- a/source/blender/blenkernel/BKE_grease_pencil.hh +++ b/source/blender/blenkernel/BKE_grease_pencil.hh @@ -37,36 +37,41 @@ namespace greasepencil { /** * Stroke cache for a stroke that is currently being drawn. */ -struct StrokeCache { +class StrokeCache { + public: + void clear(); + void resize(int64_t new_size); + + int64_t size() const; + + Span positions() const; + MutableSpan positions_for_write(); + + Span radii() const; + MutableSpan radii_for_write(); + + Span opacities() const; + MutableSpan opacities_for_write(); + + Span vertex_colors() const; + MutableSpan vertex_colors_for_write(); + + int64_t triangles_num() const; + Span triangles() const; + + int material_index() const; + void set_material_index(int new_material_index); + + private: /* Stroke cache attributes. */ - Vector positions; - Vector radii; - Vector opacities; - Vector vertex_colors; - int64_t size = 0; + Vector positions_; + Vector radii_; + Vector opacities_; + Vector vertex_colors_; + int64_t size_ = 0; - Vector triangles; - int mat = 0; - - void append(float3 position, float radius, float opacity, ColorGeometry4f vertex_color) - { - this->positions.append(position); - this->radii.append(radius); - this->opacities.append(opacity); - this->vertex_colors.append(vertex_color); - this->size++; - } - - void clear() - { - this->positions.clear_and_shrink(); - this->radii.clear_and_shrink(); - this->opacities.clear_and_shrink(); - this->vertex_colors.clear_and_shrink(); - this->triangles.clear_and_shrink(); - this->mat = 0; - this->size = 0; - } + Vector triangles_; + int material_index_ = 0; }; class DrawingRuntime { diff --git a/source/blender/blenkernel/intern/grease_pencil.cc b/source/blender/blenkernel/intern/grease_pencil.cc index 5afceb79770..b9550cbf0c2 100644 --- a/source/blender/blenkernel/intern/grease_pencil.cc +++ b/source/blender/blenkernel/intern/grease_pencil.cc @@ -258,6 +258,85 @@ static MutableSpan get_mutable_attribute(CurvesGeometry &curves, return span; } +void StrokeCache::clear() +{ + positions_.clear_and_shrink(); + radii_.clear_and_shrink(); + opacities_.clear_and_shrink(); + vertex_colors_.clear_and_shrink(); + triangles_.clear_and_shrink(); + material_index_ = 0; + size_ = 0; +} + +void StrokeCache::resize(const int64_t new_size) +{ + positions_.resize(new_size); + radii_.resize(new_size); + opacities_.resize(new_size); + vertex_colors_.resize(new_size); + size_ = new_size; +} + +int64_t StrokeCache::size() const +{ + return size_; +} + +Span StrokeCache::positions() const +{ + return positions_.as_span(); +} +MutableSpan StrokeCache::positions_for_write() +{ + return positions_.as_mutable_span(); +} + +Span StrokeCache::radii() const +{ + return radii_.as_span(); +} +MutableSpan StrokeCache::radii_for_write() +{ + return radii_.as_mutable_span(); +} + +Span StrokeCache::opacities() const +{ + return opacities_.as_span(); +} +MutableSpan StrokeCache::opacities_for_write() +{ + return opacities_.as_mutable_span(); +} + +Span StrokeCache::vertex_colors() const +{ + return vertex_colors_.as_span(); +} +MutableSpan StrokeCache::vertex_colors_for_write() +{ + return vertex_colors_.as_mutable_span(); +} + +int64_t StrokeCache::triangles_num() const +{ + return triangles_.size(); +} +Span StrokeCache::triangles() const +{ + return triangles_.as_span(); +} + +int StrokeCache::material_index() const +{ + return material_index_; +} +void StrokeCache::set_material_index(int new_material_index) +{ + material_index_ = new_material_index; +} + Drawing::Drawing() { this->base.type = GP_DRAWING; @@ -1380,7 +1459,7 @@ void BKE_grease_pencil_batch_cache_free(GreasePencil *grease_pencil) bool blender::bke::GreasePencilRuntime::has_stroke_cache() const { - return this->stroke_cache.size > 0; + return this->stroke_cache.size() > 0; } /** \} */ diff --git a/source/blender/draw/intern/draw_cache_impl_grease_pencil.cc b/source/blender/draw/intern/draw_cache_impl_grease_pencil.cc index 0acdb3f178b..0185818dfeb 100644 --- a/source/blender/draw/intern/draw_cache_impl_grease_pencil.cc +++ b/source/blender/draw/intern/draw_cache_impl_grease_pencil.cc @@ -258,8 +258,9 @@ static void grease_pencil_geom_batch_ensure(GreasePencil &grease_pencil, int cfr } if (grease_pencil.runtime->has_stroke_cache()) { - total_verts_num += 1 + grease_pencil.runtime->stroke_cache.size + 1; - total_triangles_num += grease_pencil.runtime->stroke_cache.size * 2; + total_verts_num += 1 + grease_pencil.runtime->stroke_cache.size() + 1; + total_triangles_num += grease_pencil.runtime->stroke_cache.size() * 2; + total_triangles_num += grease_pencil.runtime->stroke_cache.triangles_num(); /* TODO: triangles for stroke buffer. */ } @@ -428,21 +429,21 @@ static void grease_pencil_geom_batch_ensure(GreasePencil &grease_pencil, int cfr if (grease_pencil.runtime->has_stroke_cache()) { bke::greasepencil::StrokeCache &stroke_cache = grease_pencil.runtime->stroke_cache; const int verts_start_offset = v_offset; - const int num_verts = 1 + stroke_cache.size + 1; + const int num_verts = 1 + stroke_cache.size() + 1; IndexRange verts_range = IndexRange(verts_start_offset, num_verts); MutableSpan verts_slice = verts.slice(verts_range); MutableSpan cols_slice = cols.slice(verts_range); - const int material_nr = stroke_cache.mat; + const int material_nr = stroke_cache.material_index(); verts_slice.first().mat = -1; - for (const int i : IndexRange(stroke_cache.size)) { + for (const int i : IndexRange(stroke_cache.size())) { const int idx = i + 1; GreasePencilStrokeVert &s_vert = verts_slice[idx]; GreasePencilColorVert &c_vert = cols_slice[idx]; - copy_v3_v3(s_vert.pos, stroke_cache.positions[i]); - s_vert.radius = stroke_cache.radii[i]; - s_vert.opacity = stroke_cache.opacities[i]; + copy_v3_v3(s_vert.pos, stroke_cache.positions()[i]); + s_vert.radius = stroke_cache.radii()[i]; + s_vert.opacity = stroke_cache.opacities()[i]; s_vert.point_id = verts_range[idx]; s_vert.stroke_id = verts_range.first(); s_vert.mat = material_nr % GPENCIL_MATERIAL_BUFFER_LEN; @@ -454,8 +455,8 @@ static void grease_pencil_geom_batch_ensure(GreasePencil &grease_pencil, int cfr /* TODO */ s_vert.uv_fill[0] = s_vert.uv_fill[1] = 0; - copy_v4_v4(c_vert.vcol, stroke_cache.vertex_colors[i]); - copy_v4_v4(c_vert.fcol, stroke_cache.vertex_colors[i]); + copy_v4_v4(c_vert.vcol, stroke_cache.vertex_colors()[i]); + copy_v4_v4(c_vert.fcol, stroke_cache.vertex_colors()[i]); c_vert.fcol[3] = (int(c_vert.fcol[3] * 10000.0f) * 10.0f) + 1.0f; int v_mat = (verts_range[idx] << GP_VERTEX_ID_SHIFT) | GP_IS_STROKE_VERTEX_BIT; diff --git a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc index a6596c7c84f..25d1df4612a 100644 --- a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc +++ b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc @@ -104,7 +104,9 @@ struct PaintOperationExecutor { float4(0.0f); const float active_smooth_factor = brush->gpencil_settings->active_smooth; - self.stroke_cache_->append(proj_pos, radius, opacity, ColorGeometry4f(vertex_color)); + self.stroke_cache_->resize(self.stroke_cache_->size() + 1); + self.stroke_cache_->opacities_for_write().last() = opacity; + self.stroke_cache_->vertex_colors_for_write().last() = ColorGeometry4f(vertex_color); self.sampled_positions_.append(proj_pos); self.sampled_radii_.append(radius); @@ -114,12 +116,12 @@ struct PaintOperationExecutor { const int64_t smooth_window_size = 16; const int64_t inverted_copy_window_size = math::max( - self.stroke_cache_->size - smooth_window_size, int64_t(0)); + self.stroke_cache_->size() - smooth_window_size, int64_t(0)); const int64_t smooth_radius = 16; const int64_t inverted_smooth_window_size = math::max( - self.stroke_cache_->size - smooth_window_size - smooth_radius, int64_t(0)); + self.stroke_cache_->size() - smooth_window_size - smooth_radius, int64_t(0)); Span src_positions = self.sampled_positions_.as_span().drop_front( inverted_smooth_window_size); @@ -135,10 +137,10 @@ struct PaintOperationExecutor { ed::greasepencil::gaussian_blur_1D( src_radii, smooth_radius, active_smooth_factor, false, false, false, dst_radii); - self.stroke_cache_->positions.as_mutable_span() + self.stroke_cache_->positions_for_write() .drop_front(inverted_copy_window_size) .copy_from(self.smoothed_positions_.as_span().drop_front(inverted_copy_window_size)); - self.stroke_cache_->radii.as_mutable_span() + self.stroke_cache_->radii_for_write() .drop_front(inverted_copy_window_size) .copy_from(self.smoothed_radii_.as_span().drop_front(inverted_copy_window_size)); @@ -183,7 +185,7 @@ void PaintOperation::on_stroke_begin(const bContext &C, const InputSample & /*st Material *material = BKE_grease_pencil_object_material_ensure_from_active_input_brush( CTX_data_main(&C), object, brush); - stroke_cache_->mat = BKE_grease_pencil_object_material_index_get(object, material); + stroke_cache_->set_material_index(BKE_grease_pencil_object_material_index_get(object, material)); } void PaintOperation::on_stroke_extended(const bContext &C, const InputSample &extension_sample) @@ -207,10 +209,10 @@ static float dist_to_interpolated( void PaintOperation::simplify_stroke_cache(const float epsilon) { - const Span positions = stroke_cache_->positions.as_span(); - const Span radii = stroke_cache_->radii.as_span(); - const Span opacities = stroke_cache_->opacities.as_span(); - const Span vertex_colors = stroke_cache_->vertex_colors.as_span(); + const Span positions = stroke_cache_->positions(); + const Span radii = stroke_cache_->radii(); + const Span opacities = stroke_cache_->opacities(); + const Span vertex_colors = stroke_cache_->vertex_colors(); /* Distance function for `ramer_douglas_peucker_simplify`. */ const auto dist_function = [positions, @@ -230,11 +232,14 @@ void PaintOperation::simplify_stroke_cache(const float epsilon) return math::max(dist_position, dist_radii); }; - Array points_to_delete(stroke_cache_->size, false); + Array points_to_delete(stroke_cache_->size(), false); int64_t total_points_to_remove = ed::greasepencil::ramer_douglas_peucker_simplify( - IndexRange(stroke_cache_->size), epsilon, dist_function, points_to_delete.as_mutable_span()); + IndexRange(stroke_cache_->size()), + epsilon, + dist_function, + points_to_delete.as_mutable_span()); - int64_t new_size = stroke_cache_->size - total_points_to_remove; + int64_t new_size = stroke_cache_->size() - total_points_to_remove; Array old_indices(new_size); int64_t i = 0; @@ -245,24 +250,24 @@ void PaintOperation::simplify_stroke_cache(const float epsilon) } } - Vector new_positions(new_size); - Vector new_radii(new_size); - Vector new_opcaities(new_size); - Vector new_vertex_colors(new_size); + Array new_positions(new_size); + Array new_radii(new_size); + Array new_opacities(new_size); + Array new_vertex_colors(new_size); for (const int64_t index : IndexRange(new_size)) { new_positions[index] = positions[old_indices[index]]; new_radii[index] = radii[old_indices[index]]; - new_opcaities[index] = opacities[old_indices[index]]; + new_opacities[index] = opacities[old_indices[index]]; new_vertex_colors[index] = vertex_colors[old_indices[index]]; } + stroke_cache_->resize(new_size); - stroke_cache_->positions = new_positions; - stroke_cache_->radii = new_radii; - stroke_cache_->opacities = new_opcaities; - stroke_cache_->vertex_colors = new_vertex_colors; - stroke_cache_->size = new_size; + stroke_cache_->positions_for_write().copy_from(new_positions); + stroke_cache_->radii_for_write().copy_from(new_radii); + stroke_cache_->opacities_for_write().copy_from(new_opacities); + stroke_cache_->vertex_colors_for_write().copy_from(new_vertex_colors); - stroke_cache_->triangles.clear_and_shrink(); + // stroke_cache_->triangles.clear_and_shrink(); } void PaintOperation::create_stroke_from_stroke_cache(bke::greasepencil::Drawing &drawing_orig) @@ -273,10 +278,10 @@ void PaintOperation::create_stroke_from_stroke_cache(bke::greasepencil::Drawing const int num_old_curves = curves.curves_num(); const int num_old_points = curves.points_num(); - curves.resize(num_old_points + stroke_cache_->size, num_old_curves + 1); + curves.resize(num_old_points + stroke_cache_->size(), num_old_curves + 1); curves.offsets_for_write()[num_old_curves] = num_old_points; - curves.offsets_for_write()[num_old_curves + 1] = num_old_points + stroke_cache_->size; + curves.offsets_for_write()[num_old_curves + 1] = num_old_points + stroke_cache_->size(); const OffsetIndices points_by_curve = curves.points_by_curve(); const IndexRange new_points_range = points_by_curve[curves.curves_num() - 1]; @@ -294,17 +299,14 @@ void PaintOperation::create_stroke_from_stroke_cache(bke::greasepencil::Drawing AttributeInitVArray( VArray::ForSingle(ColorGeometry4f(0.0f, 0.0f, 0.0f, 0.0f), attributes.domain_size(ATTR_DOMAIN_POINT)))); - positions.slice(new_points_range).copy_from(stroke_cache_->positions); - radii.slice(new_points_range).copy_from(stroke_cache_->radii); - opacities.slice(new_points_range).copy_from(stroke_cache_->opacities); - vertex_colors.span.slice(new_points_range).copy_from(stroke_cache_->vertex_colors); + positions.slice(new_points_range).copy_from(stroke_cache_->positions()); + radii.slice(new_points_range).copy_from(stroke_cache_->radii()); + opacities.slice(new_points_range).copy_from(stroke_cache_->opacities()); + vertex_colors.span.slice(new_points_range).copy_from(stroke_cache_->vertex_colors()); - /* TODO: Set material index attribute. */ - int material_index = stroke_cache_->mat; SpanAttributeWriter materials = attributes.lookup_or_add_for_write_span( "material_index", ATTR_DOMAIN_CURVE); - - materials.span.slice(new_curves_range).fill(material_index); + materials.span.slice(new_curves_range).fill(stroke_cache_->material_index()); /* Set curve_type attribute. */ curves.fill_curve_types(new_curves_range, CURVE_TYPE_POLY); @@ -341,7 +343,7 @@ void PaintOperation::on_stroke_done(const bContext &C) GreasePencil &grease_pencil = *static_cast(object->data); /* No stroke to create, return. */ - if (stroke_cache_->size == 0) { + if (stroke_cache_->size() == 0) { return; } -- 2.30.2 From ca20fce4fea43c742bc42747defdfcacb38bb654 Mon Sep 17 00:00:00 2001 From: Falk David Date: Wed, 2 Aug 2023 11:57:33 +0200 Subject: [PATCH 31/75] Add reserve function --- source/blender/blenkernel/BKE_grease_pencil.hh | 4 ++++ source/blender/blenkernel/intern/grease_pencil.cc | 8 ++++++++ .../blender/editors/sculpt_paint/grease_pencil_paint.cc | 7 ++++++- 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/source/blender/blenkernel/BKE_grease_pencil.hh b/source/blender/blenkernel/BKE_grease_pencil.hh index 02d1c9460d3..85565fe46c3 100644 --- a/source/blender/blenkernel/BKE_grease_pencil.hh +++ b/source/blender/blenkernel/BKE_grease_pencil.hh @@ -40,6 +40,7 @@ namespace greasepencil { class StrokeCache { public: void clear(); + void reserve(int64_t new_capacity); void resize(int64_t new_size); int64_t size() const; @@ -334,6 +335,9 @@ class LayerRuntime { * timeline. */ class Layer : public ::GreasePencilLayer { + public: + using FrameKey = int; + public: Layer(); explicit Layer(StringRefNull name); diff --git a/source/blender/blenkernel/intern/grease_pencil.cc b/source/blender/blenkernel/intern/grease_pencil.cc index b9550cbf0c2..c7650f407de 100644 --- a/source/blender/blenkernel/intern/grease_pencil.cc +++ b/source/blender/blenkernel/intern/grease_pencil.cc @@ -269,6 +269,14 @@ void StrokeCache::clear() size_ = 0; } +void StrokeCache::reserve(const int64_t new_capacity) +{ + positions_.reserve(new_capacity); + radii_.reserve(new_capacity); + opacities_.reserve(new_capacity); + vertex_colors_.reserve(new_capacity); +} + void StrokeCache::resize(const int64_t new_size) { positions_.resize(new_size); diff --git a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc index 25d1df4612a..401e60a4b73 100644 --- a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc +++ b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc @@ -25,14 +25,17 @@ namespace blender::ed::sculpt_paint::greasepencil { +static constexpr int64_t STOKE_CACHE_ALLOCATION_CHUNK_SIZE = 1024; + class PaintOperation : public GreasePencilStrokeOperation { private: + bke::greasepencil::StrokeCache *stroke_cache_; + Vector sampled_positions_; Vector sampled_radii_; Vector smoothed_positions_; Vector smoothed_radii_; - bke::greasepencil::StrokeCache *stroke_cache_; friend struct PaintOperationExecutor; @@ -186,6 +189,8 @@ void PaintOperation::on_stroke_begin(const bContext &C, const InputSample & /*st Material *material = BKE_grease_pencil_object_material_ensure_from_active_input_brush( CTX_data_main(&C), object, brush); stroke_cache_->set_material_index(BKE_grease_pencil_object_material_index_get(object, material)); + + stroke_cache_->reserve(STOKE_CACHE_ALLOCATION_CHUNK_SIZE); } void PaintOperation::on_stroke_extended(const bContext &C, const InputSample &extension_sample) -- 2.30.2 From 5d452d228bef332c0613123bc33cd24c003dc52d Mon Sep 17 00:00:00 2001 From: Falk David Date: Wed, 2 Aug 2023 11:57:59 +0200 Subject: [PATCH 32/75] Refactor logic into function `process_new_sample` --- .../sculpt_paint/grease_pencil_paint.cc | 77 +++++++++++-------- 1 file changed, 43 insertions(+), 34 deletions(-) diff --git a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc index 401e60a4b73..7b7756a9bc8 100644 --- a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc +++ b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc @@ -54,58 +54,57 @@ class PaintOperation : public GreasePencilStrokeOperation { * because it avoids passing a very large number of parameters between functions. */ struct PaintOperationExecutor { - PaintOperationExecutor(const bContext & /*C*/) {} + ARegion *region_; - void execute(PaintOperation &self, const bContext &C, const InputSample &extension_sample) + Brush *brush_; + int brush_size_; + float brush_alpha_; + + BrushGpencilSettings *settings_; + bool use_vertex_color_stroke_; + + PaintOperationExecutor(const bContext &C) { - Depsgraph *depsgraph = CTX_data_depsgraph_pointer(&C); Scene *scene = CTX_data_scene(&C); - Object *object = CTX_data_active_object(&C); - Object *object_eval = DEG_get_evaluated_object(depsgraph, object); - ARegion *region = CTX_wm_region(&C); + region_ = CTX_wm_region(&C); Paint *paint = &scene->toolsettings->gp_paint->paint; - Brush *brush = BKE_paint_brush(paint); - int brush_size = BKE_brush_size_get(scene, brush); - float brush_alpha = BKE_brush_alpha_get(scene, brush); + brush_ = BKE_paint_brush(paint); + settings_ = brush_->gpencil_settings; + brush_size_ = BKE_brush_size_get(scene, brush_); + brush_alpha_ = BKE_brush_alpha_get(scene, brush_); const bool use_vertex_color = (scene->toolsettings->gp_paint->mode == GPPAINT_FLAG_USE_VERTEXCOLOR); - const bool use_vertex_color_stroke = use_vertex_color && - ELEM(brush->gpencil_settings->vertex_mode, - GPPAINT_MODE_STROKE, - GPPAINT_MODE_BOTH); + use_vertex_color_stroke_ = use_vertex_color && ELEM(settings_->vertex_mode, + GPPAINT_MODE_STROKE, + GPPAINT_MODE_BOTH); // const bool use_vertex_color_fill = use_vertex_color && ELEM( // brush->gpencil_settings->vertex_mode, GPPAINT_MODE_STROKE, GPPAINT_MODE_BOTH); + } - /** - * Note: We write to the evaluated object here, so that the additional copy from orig -> - * eval is not needed for every update. After the stroke is done, the result is written to - * the original object. - */ - GreasePencil *grease_pencil = static_cast(object_eval->data); - + void process_new_sample(PaintOperation &self, const InputSample &extension_sample) + { float4 plane{0.0f, -1.0f, 0.0f, 0.0f}; float3 proj_pos; - ED_view3d_win_to_3d_on_plane(region, plane, extension_sample.mouse_position, false, proj_pos); + ED_view3d_win_to_3d_on_plane(region_, plane, extension_sample.mouse_position, false, proj_pos); - float radius = brush_size / 2.0f; - if (BKE_brush_use_size_pressure(brush)) { + float radius = brush_size_ / 2.0f; + if (BKE_brush_use_size_pressure(brush_)) { radius *= BKE_curvemapping_evaluateF( - brush->gpencil_settings->curve_sensitivity, 0, extension_sample.pressure); + settings_->curve_sensitivity, 0, extension_sample.pressure); } - float opacity = brush_alpha; - if (BKE_brush_use_alpha_pressure(brush)) { + float opacity = brush_alpha_; + if (BKE_brush_use_alpha_pressure(brush_)) { opacity *= BKE_curvemapping_evaluateF( - brush->gpencil_settings->curve_strength, 0, extension_sample.pressure); + settings_->curve_strength, 0, extension_sample.pressure); } - float4 vertex_color = use_vertex_color_stroke ? - float4(brush->rgb[0], - brush->rgb[1], - brush->rgb[2], - brush->gpencil_settings->vertex_factor) : - float4(0.0f); - const float active_smooth_factor = brush->gpencil_settings->active_smooth; + float4 vertex_color = use_vertex_color_stroke_ ? float4(brush_->rgb[0], + brush_->rgb[1], + brush_->rgb[2], + settings_->vertex_factor) : + float4(0.0f); + const float active_smooth_factor = settings_->active_smooth; self.stroke_cache_->resize(self.stroke_cache_->size() + 1); self.stroke_cache_->opacities_for_write().last() = opacity; @@ -154,6 +153,16 @@ struct PaintOperationExecutor { // .drop_front(inverted_active_window_size) // .fill(ColorGeometry4f(float4(1.0f, 0.1f, 0.1f, 1.0f))); #endif + } + + void execute(PaintOperation &self, const bContext &C, const InputSample &extension_sample) + { + Depsgraph *depsgraph = CTX_data_depsgraph_pointer(&C); + Object *object = CTX_data_active_object(&C); + Object *object_eval = DEG_get_evaluated_object(depsgraph, object); + GreasePencil *grease_pencil = static_cast(object_eval->data); + + this->process_new_sample(self, extension_sample); BKE_grease_pencil_batch_cache_dirty_tag(grease_pencil, BKE_GREASEPENCIL_BATCH_DIRTY_ALL); } -- 2.30.2 From ba8f926338be5f9370160cc93a8b01461ef24949 Mon Sep 17 00:00:00 2001 From: Falk David Date: Wed, 2 Aug 2023 12:45:12 +0200 Subject: [PATCH 33/75] Rename --- source/blender/editors/sculpt_paint/grease_pencil_paint.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc index 7b7756a9bc8..534776b9b79 100644 --- a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc +++ b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc @@ -83,7 +83,7 @@ struct PaintOperationExecutor { // brush->gpencil_settings->vertex_mode, GPPAINT_MODE_STROKE, GPPAINT_MODE_BOTH); } - void process_new_sample(PaintOperation &self, const InputSample &extension_sample) + void process_extension_sample(PaintOperation &self, const InputSample &extension_sample) { float4 plane{0.0f, -1.0f, 0.0f, 0.0f}; float3 proj_pos; @@ -162,7 +162,7 @@ struct PaintOperationExecutor { Object *object_eval = DEG_get_evaluated_object(depsgraph, object); GreasePencil *grease_pencil = static_cast(object_eval->data); - this->process_new_sample(self, extension_sample); + this->process_extension_sample(self, extension_sample); BKE_grease_pencil_batch_cache_dirty_tag(grease_pencil, BKE_GREASEPENCIL_BATCH_DIRTY_ALL); } -- 2.30.2 From feb27c42ba41e122830702d09c50c9db54552679 Mon Sep 17 00:00:00 2001 From: Falk David Date: Wed, 2 Aug 2023 18:09:41 +0200 Subject: [PATCH 34/75] WIP --- .../blender/blenkernel/BKE_grease_pencil.hh | 3 - .../intern/grease_pencil_utils.cc | 2 +- .../sculpt_paint/grease_pencil_paint.cc | 163 ++++++++++++------ 3 files changed, 115 insertions(+), 53 deletions(-) diff --git a/source/blender/blenkernel/BKE_grease_pencil.hh b/source/blender/blenkernel/BKE_grease_pencil.hh index 85565fe46c3..61958db4783 100644 --- a/source/blender/blenkernel/BKE_grease_pencil.hh +++ b/source/blender/blenkernel/BKE_grease_pencil.hh @@ -335,9 +335,6 @@ class LayerRuntime { * timeline. */ class Layer : public ::GreasePencilLayer { - public: - using FrameKey = int; - public: Layer(); explicit Layer(StringRefNull name); diff --git a/source/blender/editors/grease_pencil/intern/grease_pencil_utils.cc b/source/blender/editors/grease_pencil/intern/grease_pencil_utils.cc index e93f49590db..85166a45e7e 100644 --- a/source/blender/editors/grease_pencil/intern/grease_pencil_utils.cc +++ b/source/blender/editors/grease_pencil/intern/grease_pencil_utils.cc @@ -6,7 +6,7 @@ * \ingroup edgreasepencil */ -#include "BKE_brush.h" +#include "BKE_brush.hh" #include "BKE_context.h" #include "BKE_grease_pencil.hh" diff --git a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc index 534776b9b79..5141399197b 100644 --- a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc +++ b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc @@ -2,7 +2,7 @@ * * SPDX-License-Identifier: GPL-2.0-or-later */ -#include "BKE_brush.h" +#include "BKE_brush.hh" #include "BKE_colortools.h" #include "BKE_context.h" #include "BKE_curves.hh" @@ -27,9 +27,17 @@ namespace blender::ed::sculpt_paint::greasepencil { static constexpr int64_t STOKE_CACHE_ALLOCATION_CHUNK_SIZE = 1024; +struct StrokePoint { + float3 position; + float radius; + float opacity; + float4 vertex_color; +}; + class PaintOperation : public GreasePencilStrokeOperation { private: bke::greasepencil::StrokeCache *stroke_cache_; + int64_t active_index_ = 0; Vector sampled_positions_; Vector sampled_radii_; @@ -83,68 +91,122 @@ struct PaintOperationExecutor { // brush->gpencil_settings->vertex_mode, GPPAINT_MODE_STROKE, GPPAINT_MODE_BOTH); } + StrokePoint stroke_point_from_input_sample(const InputSample &sample) + { + StrokePoint point; + /* TODO: Use correct plane/projection. */ + float4 plane{0.0f, -1.0f, 0.0f, 0.0f}; + ED_view3d_win_to_3d_on_plane(region_, plane, sample.mouse_position, false, point.position); + + point.radius = brush_size_ / 2.0f; + if (BKE_brush_use_size_pressure(brush_)) { + point.radius *= BKE_curvemapping_evaluateF(settings_->curve_sensitivity, 0, sample.pressure); + } + point.opacity = brush_alpha_; + if (BKE_brush_use_alpha_pressure(brush_)) { + point.opacity *= BKE_curvemapping_evaluateF(settings_->curve_strength, 0, sample.pressure); + } + point.vertex_color = use_vertex_color_stroke_ ? float4(brush_->rgb[0], + brush_->rgb[1], + brush_->rgb[2], + settings_->vertex_factor) : + float4(0.0f); + /* TODO: Get fill vertex color. */ + return point; + } + + void process_start_sample(PaintOperation &self, const InputSample &start_sample) + { + StrokePoint start_point = this->stroke_point_from_input_sample(start_sample); + + self.stroke_cache_->resize(1); + self.stroke_cache_->positions_for_write().last() = start_point.position; + self.stroke_cache_->radii_for_write().last() = start_point.radius; + self.stroke_cache_->opacities_for_write().last() = start_point.opacity; + self.stroke_cache_->vertex_colors_for_write().last() = ColorGeometry4f( + start_point.vertex_color); + } + void process_extension_sample(PaintOperation &self, const InputSample &extension_sample) { - float4 plane{0.0f, -1.0f, 0.0f, 0.0f}; - float3 proj_pos; - ED_view3d_win_to_3d_on_plane(region_, plane, extension_sample.mouse_position, false, proj_pos); + StrokePoint point = this->stroke_point_from_input_sample(extension_sample); - float radius = brush_size_ / 2.0f; - if (BKE_brush_use_size_pressure(brush_)) { - radius *= BKE_curvemapping_evaluateF( - settings_->curve_sensitivity, 0, extension_sample.pressure); + float3 prev_position = self.stroke_cache_->positions().last(); + float prev_radius = self.stroke_cache_->radii().last(); + float prev_opacity = self.stroke_cache_->opacities().last(); + ColorGeometry4f prev_vertex_color = self.stroke_cache_->vertex_colors().last(); + + int new_points_num = 1; + /* Calculate if we need to add more than one point based on distance. */ + const float min_distance = 0.05f; + if (self.stroke_cache_->size() > 1) { + const float distance = math::distance(point.position, prev_position); + if (distance > min_distance) { + new_points_num += static_cast(math::floor(distance / min_distance)) - 1; + } } - float opacity = brush_alpha_; - if (BKE_brush_use_alpha_pressure(brush_)) { - opacity *= BKE_curvemapping_evaluateF( - settings_->curve_strength, 0, extension_sample.pressure); + + Array new_positions(new_points_num); + Array new_radii(new_points_num); + Array new_opacities(new_points_num); + Array new_vertex_colors(new_points_num); + const float step = 1.0f / static_cast(new_points_num); + float factor = step; + for (const int i : IndexRange(new_points_num)) { + new_positions[i] = bke::attribute_math::mix2(factor, prev_position, point.position); + new_radii[i] = bke::attribute_math::mix2(factor, prev_radius, point.radius); + new_opacities[i] = bke::attribute_math::mix2(factor, prev_opacity, point.opacity); + new_vertex_colors[i] = bke::attribute_math::mix2( + factor, prev_vertex_color, ColorGeometry4f(point.vertex_color)); + factor += step; } - float4 vertex_color = use_vertex_color_stroke_ ? float4(brush_->rgb[0], - brush_->rgb[1], - brush_->rgb[2], - settings_->vertex_factor) : - float4(0.0f); - const float active_smooth_factor = settings_->active_smooth; + const int64_t old_size = self.stroke_cache_->size(); + self.stroke_cache_->resize(self.stroke_cache_->size() + new_points_num); - self.stroke_cache_->resize(self.stroke_cache_->size() + 1); - self.stroke_cache_->opacities_for_write().last() = opacity; - self.stroke_cache_->vertex_colors_for_write().last() = ColorGeometry4f(vertex_color); + IndexRange new_range(old_size, new_points_num); + self.stroke_cache_->positions_for_write().slice(new_range).copy_from(new_positions); + self.stroke_cache_->radii_for_write().slice(new_range).copy_from(new_radii); + self.stroke_cache_->opacities_for_write().slice(new_range).copy_from(new_opacities); + self.stroke_cache_->vertex_colors_for_write().slice(new_range).copy_from(new_vertex_colors); - self.sampled_positions_.append(proj_pos); - self.sampled_radii_.append(radius); + // const float active_smooth_factor = settings_->active_smooth; - self.smoothed_positions_.append(proj_pos); - self.smoothed_radii_.append(radius); + // self.sampled_positions_.append(proj_pos); + // self.sampled_radii_.append(radius); - const int64_t smooth_window_size = 16; - const int64_t inverted_copy_window_size = math::max( - self.stroke_cache_->size() - smooth_window_size, int64_t(0)); + // self.smoothed_positions_.append(proj_pos); + // self.smoothed_radii_.append(radius); - const int64_t smooth_radius = 16; + // const int64_t smooth_window_size = 16; + // const int64_t inverted_copy_window_size = math::max( + // self.stroke_cache_->size() - smooth_window_size, int64_t(0)); - const int64_t inverted_smooth_window_size = math::max( - self.stroke_cache_->size() - smooth_window_size - smooth_radius, int64_t(0)); + // const int64_t smooth_radius = 16; - Span src_positions = self.sampled_positions_.as_span().drop_front( - inverted_smooth_window_size); - MutableSpan dst_positions = self.smoothed_positions_.as_mutable_span().drop_front( - inverted_smooth_window_size); + // const int64_t inverted_smooth_window_size = math::max( + // self.stroke_cache_->size() - smooth_window_size - smooth_radius, int64_t(0)); - Span src_radii = self.sampled_radii_.as_span().drop_front(inverted_smooth_window_size); - MutableSpan dst_radii = self.smoothed_radii_.as_mutable_span().drop_front( - inverted_smooth_window_size); + // Span src_positions = self.sampled_positions_.as_span().drop_front( + // inverted_smooth_window_size); + // MutableSpan dst_positions = self.smoothed_positions_.as_mutable_span().drop_front( + // inverted_smooth_window_size); - ed::greasepencil::gaussian_blur_1D( - src_positions, smooth_radius, active_smooth_factor, false, true, false, dst_positions); - ed::greasepencil::gaussian_blur_1D( - src_radii, smooth_radius, active_smooth_factor, false, false, false, dst_radii); + // Span src_radii = + // self.sampled_radii_.as_span().drop_front(inverted_smooth_window_size); MutableSpan + // dst_radii = self.smoothed_radii_.as_mutable_span().drop_front( + // inverted_smooth_window_size); - self.stroke_cache_->positions_for_write() - .drop_front(inverted_copy_window_size) - .copy_from(self.smoothed_positions_.as_span().drop_front(inverted_copy_window_size)); - self.stroke_cache_->radii_for_write() - .drop_front(inverted_copy_window_size) - .copy_from(self.smoothed_radii_.as_span().drop_front(inverted_copy_window_size)); + // ed::greasepencil::gaussian_blur_1D( + // src_positions, smooth_radius, active_smooth_factor, false, true, false, dst_positions); + // ed::greasepencil::gaussian_blur_1D( + // src_radii, smooth_radius, active_smooth_factor, false, false, false, dst_radii); + + // self.stroke_cache_->positions_for_write() + // .drop_front(inverted_copy_window_size) + // .copy_from(self.smoothed_positions_.as_span().drop_front(inverted_copy_window_size)); + // self.stroke_cache_->radii_for_write() + // .drop_front(inverted_copy_window_size) + // .copy_from(self.smoothed_radii_.as_span().drop_front(inverted_copy_window_size)); #ifdef DEBUG // /* Visualize active window. */ @@ -168,7 +230,7 @@ struct PaintOperationExecutor { } }; -void PaintOperation::on_stroke_begin(const bContext &C, const InputSample & /*start_sample*/) +void PaintOperation::on_stroke_begin(const bContext &C, const InputSample &start_sample) { Depsgraph *depsgraph = CTX_data_depsgraph_pointer(&C); Scene *scene = CTX_data_scene(&C); @@ -200,6 +262,9 @@ void PaintOperation::on_stroke_begin(const bContext &C, const InputSample & /*st stroke_cache_->set_material_index(BKE_grease_pencil_object_material_index_get(object, material)); stroke_cache_->reserve(STOKE_CACHE_ALLOCATION_CHUNK_SIZE); + + PaintOperationExecutor executor{C}; + executor.process_start_sample(*this, start_sample); } void PaintOperation::on_stroke_extended(const bContext &C, const InputSample &extension_sample) -- 2.30.2 From 01757ab8dd8720f9f6ff8c0765152099ecd6999e Mon Sep 17 00:00:00 2001 From: Falk David Date: Thu, 3 Aug 2023 15:25:21 +0200 Subject: [PATCH 35/75] Use screen space coordinates for processing --- .../sculpt_paint/grease_pencil_paint.cc | 83 +++++++++++++------ 1 file changed, 58 insertions(+), 25 deletions(-) diff --git a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc index 5141399197b..b7a19a5e0da 100644 --- a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc +++ b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc @@ -27,8 +27,8 @@ namespace blender::ed::sculpt_paint::greasepencil { static constexpr int64_t STOKE_CACHE_ALLOCATION_CHUNK_SIZE = 1024; -struct StrokePoint { - float3 position; +struct ScreenSpacePoint { + float2 position; float radius; float opacity; float4 vertex_color; @@ -36,6 +36,8 @@ struct StrokePoint { class PaintOperation : public GreasePencilStrokeOperation { private: + Vector screen_space_coordinates_; + bke::greasepencil::StrokeCache *stroke_cache_; int64_t active_index_ = 0; @@ -91,12 +93,21 @@ struct PaintOperationExecutor { // brush->gpencil_settings->vertex_mode, GPPAINT_MODE_STROKE, GPPAINT_MODE_BOTH); } - StrokePoint stroke_point_from_input_sample(const InputSample &sample) + float3 screen_space_to_object_space(float2 co) { - StrokePoint point; /* TODO: Use correct plane/projection. */ float4 plane{0.0f, -1.0f, 0.0f, 0.0f}; - ED_view3d_win_to_3d_on_plane(region_, plane, sample.mouse_position, false, point.position); + /* TODO: Use object transform. */ + float3 proj_point; + ED_view3d_win_to_3d_on_plane(region_, plane, co, false, proj_point); + return proj_point; + } + + ScreenSpacePoint point_from_input_sample(const InputSample &sample) + { + ScreenSpacePoint point; + + point.position = sample.mouse_position; point.radius = brush_size_ / 2.0f; if (BKE_brush_use_size_pressure(brush_)) { @@ -115,12 +126,36 @@ struct PaintOperationExecutor { return point; } + void update_stroke_cache(PaintOperation &self, + const int64_t new_points_num, + Span new_coordinates, + Span new_radii, + Span new_opacities, + Span new_vertex_colors) + { + const int64_t old_size = self.stroke_cache_->size(); + self.stroke_cache_->resize(self.stroke_cache_->size() + new_points_num); + + IndexRange new_range(old_size, new_points_num); + MutableSpan positions_slice = self.stroke_cache_->positions_for_write().slice( + new_range); + for (const int64_t i : new_coordinates.index_range()) { + positions_slice[i] = screen_space_to_object_space(new_coordinates[i]); + } + self.stroke_cache_->radii_for_write().slice(new_range).copy_from(new_radii); + self.stroke_cache_->opacities_for_write().slice(new_range).copy_from(new_opacities); + self.stroke_cache_->vertex_colors_for_write().slice(new_range).copy_from(new_vertex_colors); + } + void process_start_sample(PaintOperation &self, const InputSample &start_sample) { - StrokePoint start_point = this->stroke_point_from_input_sample(start_sample); + ScreenSpacePoint start_point = this->point_from_input_sample(start_sample); + + self.screen_space_coordinates_.append(start_point.position); self.stroke_cache_->resize(1); - self.stroke_cache_->positions_for_write().last() = start_point.position; + self.stroke_cache_->positions_for_write().last() = screen_space_to_object_space( + start_point.position); self.stroke_cache_->radii_for_write().last() = start_point.radius; self.stroke_cache_->opacities_for_write().last() = start_point.opacity; self.stroke_cache_->vertex_colors_for_write().last() = ColorGeometry4f( @@ -129,45 +164,43 @@ struct PaintOperationExecutor { void process_extension_sample(PaintOperation &self, const InputSample &extension_sample) { - StrokePoint point = this->stroke_point_from_input_sample(extension_sample); + ScreenSpacePoint point = this->point_from_input_sample(extension_sample); - float3 prev_position = self.stroke_cache_->positions().last(); + float2 prev_co = self.screen_space_coordinates_.last(); float prev_radius = self.stroke_cache_->radii().last(); float prev_opacity = self.stroke_cache_->opacities().last(); ColorGeometry4f prev_vertex_color = self.stroke_cache_->vertex_colors().last(); int new_points_num = 1; - /* Calculate if we need to add more than one point based on distance. */ - const float min_distance = 0.05f; - if (self.stroke_cache_->size() > 1) { - const float distance = math::distance(point.position, prev_position); - if (distance > min_distance) { - new_points_num += static_cast(math::floor(distance / min_distance)) - 1; + const float min_distance_px = 10.0f; + if (self.screen_space_coordinates_.size() > 1) { + const float distance_px = math::distance(point.position, prev_co); + if (distance_px > min_distance_px) { + new_points_num += static_cast(math::floor(distance_px / min_distance_px)) - 1; } } - Array new_positions(new_points_num); + Array new_coordinates(new_points_num); Array new_radii(new_points_num); Array new_opacities(new_points_num); Array new_vertex_colors(new_points_num); const float step = 1.0f / static_cast(new_points_num); float factor = step; for (const int i : IndexRange(new_points_num)) { - new_positions[i] = bke::attribute_math::mix2(factor, prev_position, point.position); + new_coordinates[i] = bke::attribute_math::mix2(factor, prev_co, point.position); new_radii[i] = bke::attribute_math::mix2(factor, prev_radius, point.radius); new_opacities[i] = bke::attribute_math::mix2(factor, prev_opacity, point.opacity); new_vertex_colors[i] = bke::attribute_math::mix2( factor, prev_vertex_color, ColorGeometry4f(point.vertex_color)); factor += step; } - const int64_t old_size = self.stroke_cache_->size(); - self.stroke_cache_->resize(self.stroke_cache_->size() + new_points_num); - IndexRange new_range(old_size, new_points_num); - self.stroke_cache_->positions_for_write().slice(new_range).copy_from(new_positions); - self.stroke_cache_->radii_for_write().slice(new_range).copy_from(new_radii); - self.stroke_cache_->opacities_for_write().slice(new_range).copy_from(new_opacities); - self.stroke_cache_->vertex_colors_for_write().slice(new_range).copy_from(new_vertex_colors); + for (float2 co : new_coordinates) { + self.screen_space_coordinates_.append(co); + } + + this->update_stroke_cache( + self, new_points_num, new_coordinates, new_radii, new_opacities, new_vertex_colors); // const float active_smooth_factor = settings_->active_smooth; @@ -426,7 +459,7 @@ void PaintOperation::on_stroke_done(const bContext &C) return; } - this->simplify_stroke_cache(0.0005f); + // this->simplify_stroke_cache(0.0005f); /* The object should have an active layer. */ BLI_assert(grease_pencil.has_active_layer()); -- 2.30.2 From 1a093e84423ee9fc4c1ea863d3a4801f96f991dd Mon Sep 17 00:00:00 2001 From: Falk David Date: Thu, 3 Aug 2023 15:59:25 +0200 Subject: [PATCH 36/75] Overwrite last point if new one is very close --- .../editors/sculpt_paint/grease_pencil_paint.cc | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc index b7a19a5e0da..85845449262 100644 --- a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc +++ b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc @@ -171,13 +171,20 @@ struct PaintOperationExecutor { float prev_opacity = self.stroke_cache_->opacities().last(); ColorGeometry4f prev_vertex_color = self.stroke_cache_->vertex_colors().last(); + /* Overwrite last point if it's very close. */ + if (math::distance(point.position, prev_co) < 3.0f) { + self.stroke_cache_->positions_for_write().last() = screen_space_to_object_space( + point.position); + self.stroke_cache_->radii_for_write().last() = math::max(point.radius, prev_radius); + self.stroke_cache_->opacities_for_write().last() = math::max(point.opacity, prev_opacity); + return; + } + int new_points_num = 1; const float min_distance_px = 10.0f; - if (self.screen_space_coordinates_.size() > 1) { - const float distance_px = math::distance(point.position, prev_co); - if (distance_px > min_distance_px) { - new_points_num += static_cast(math::floor(distance_px / min_distance_px)) - 1; - } + const float distance_px = math::distance(point.position, prev_co); + if (distance_px > min_distance_px) { + new_points_num += static_cast(math::floor(distance_px / min_distance_px)) - 1; } Array new_coordinates(new_points_num); -- 2.30.2 From 4eb178b499721195e3200b190d9b9fd82f210085 Mon Sep 17 00:00:00 2001 From: Falk David Date: Thu, 3 Aug 2023 16:00:23 +0200 Subject: [PATCH 37/75] Rename positon to co --- .../editors/sculpt_paint/grease_pencil_paint.cc | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc index 85845449262..aaf9a1809f6 100644 --- a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc +++ b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc @@ -28,7 +28,7 @@ namespace blender::ed::sculpt_paint::greasepencil { static constexpr int64_t STOKE_CACHE_ALLOCATION_CHUNK_SIZE = 1024; struct ScreenSpacePoint { - float2 position; + float2 co; float radius; float opacity; float4 vertex_color; @@ -107,7 +107,7 @@ struct PaintOperationExecutor { { ScreenSpacePoint point; - point.position = sample.mouse_position; + point.co = sample.mouse_position; point.radius = brush_size_ / 2.0f; if (BKE_brush_use_size_pressure(brush_)) { @@ -151,11 +151,11 @@ struct PaintOperationExecutor { { ScreenSpacePoint start_point = this->point_from_input_sample(start_sample); - self.screen_space_coordinates_.append(start_point.position); + self.screen_space_coordinates_.append(start_point.co); self.stroke_cache_->resize(1); self.stroke_cache_->positions_for_write().last() = screen_space_to_object_space( - start_point.position); + start_point.co); self.stroke_cache_->radii_for_write().last() = start_point.radius; self.stroke_cache_->opacities_for_write().last() = start_point.opacity; self.stroke_cache_->vertex_colors_for_write().last() = ColorGeometry4f( @@ -172,9 +172,8 @@ struct PaintOperationExecutor { ColorGeometry4f prev_vertex_color = self.stroke_cache_->vertex_colors().last(); /* Overwrite last point if it's very close. */ - if (math::distance(point.position, prev_co) < 3.0f) { - self.stroke_cache_->positions_for_write().last() = screen_space_to_object_space( - point.position); + if (math::distance(point.co, prev_co) < 3.0f) { + self.stroke_cache_->positions_for_write().last() = screen_space_to_object_space(point.co); self.stroke_cache_->radii_for_write().last() = math::max(point.radius, prev_radius); self.stroke_cache_->opacities_for_write().last() = math::max(point.opacity, prev_opacity); return; @@ -182,7 +181,7 @@ struct PaintOperationExecutor { int new_points_num = 1; const float min_distance_px = 10.0f; - const float distance_px = math::distance(point.position, prev_co); + const float distance_px = math::distance(point.co, prev_co); if (distance_px > min_distance_px) { new_points_num += static_cast(math::floor(distance_px / min_distance_px)) - 1; } @@ -194,7 +193,7 @@ struct PaintOperationExecutor { const float step = 1.0f / static_cast(new_points_num); float factor = step; for (const int i : IndexRange(new_points_num)) { - new_coordinates[i] = bke::attribute_math::mix2(factor, prev_co, point.position); + new_coordinates[i] = bke::attribute_math::mix2(factor, prev_co, point.co); new_radii[i] = bke::attribute_math::mix2(factor, prev_radius, point.radius); new_opacities[i] = bke::attribute_math::mix2(factor, prev_opacity, point.opacity); new_vertex_colors[i] = bke::attribute_math::mix2( -- 2.30.2 From 2f32da147603281d011961f2ee4f2280dc1f6bc0 Mon Sep 17 00:00:00 2001 From: Falk David Date: Thu, 3 Aug 2023 16:05:40 +0200 Subject: [PATCH 38/75] Cleanup --- .../editors/sculpt_paint/grease_pencil_paint.cc | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc index aaf9a1809f6..b689b9fa0bc 100644 --- a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc +++ b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc @@ -25,6 +25,9 @@ namespace blender::ed::sculpt_paint::greasepencil { +static constexpr float POINT_OVERRIDE_THRESHOLD_PX = 3.0f; +static constexpr float POINT_RESAMPLE_MIN_DISTANCE_PX = 10.0f; + static constexpr int64_t STOKE_CACHE_ALLOCATION_CHUNK_SIZE = 1024; struct ScreenSpacePoint { @@ -172,18 +175,21 @@ struct PaintOperationExecutor { ColorGeometry4f prev_vertex_color = self.stroke_cache_->vertex_colors().last(); /* Overwrite last point if it's very close. */ - if (math::distance(point.co, prev_co) < 3.0f) { + if (math::distance(point.co, prev_co) < POINT_OVERRIDE_THRESHOLD_PX) { self.stroke_cache_->positions_for_write().last() = screen_space_to_object_space(point.co); self.stroke_cache_->radii_for_write().last() = math::max(point.radius, prev_radius); self.stroke_cache_->opacities_for_write().last() = math::max(point.opacity, prev_opacity); return; } + /* If the next sample is far away, we subdivide the segment to add more points. */ int new_points_num = 1; - const float min_distance_px = 10.0f; const float distance_px = math::distance(point.co, prev_co); - if (distance_px > min_distance_px) { - new_points_num += static_cast(math::floor(distance_px / min_distance_px)) - 1; + if (distance_px > POINT_RESAMPLE_MIN_DISTANCE_PX) { + const int subdivisions = static_cast( + math::floor(distance_px / POINT_RESAMPLE_MIN_DISTANCE_PX)) - + 1; + new_points_num += subdivisions; } Array new_coordinates(new_points_num); -- 2.30.2 From d59ad99d4d1357c38c6cc977757afe2447fa760b Mon Sep 17 00:00:00 2001 From: Falk David Date: Mon, 7 Aug 2023 13:06:32 +0200 Subject: [PATCH 39/75] Add curve fitting functions --- .../editors/grease_pencil/CMakeLists.txt | 3 + .../intern/grease_pencil_geom.cc | 104 ++++++++++++++++++ .../editors/include/ED_grease_pencil.hh | 14 +++ 3 files changed, 121 insertions(+) create mode 100644 source/blender/editors/grease_pencil/intern/grease_pencil_geom.cc diff --git a/source/blender/editors/grease_pencil/CMakeLists.txt b/source/blender/editors/grease_pencil/CMakeLists.txt index 5168b42fc95..bf190ac68e8 100644 --- a/source/blender/editors/grease_pencil/CMakeLists.txt +++ b/source/blender/editors/grease_pencil/CMakeLists.txt @@ -12,6 +12,7 @@ set(INC ../../makesrna ../../windowmanager ../../bmesh + ../../../../extern/curve_fit_nd # RNA_prototypes.h ${CMAKE_BINARY_DIR}/source/blender/makesrna ) @@ -23,6 +24,7 @@ set(SRC intern/grease_pencil_add.cc intern/grease_pencil_edit.cc intern/grease_pencil_frames.cc + intern/grease_pencil_geom.cc intern/grease_pencil_layers.cc intern/grease_pencil_ops.cc intern/grease_pencil_select.cc @@ -34,6 +36,7 @@ set(LIB PRIVATE bf::blenlib PRIVATE bf::dna PRIVATE bf::intern::guardedalloc + extern_curve_fit_nd ) blender_add_lib(bf_editor_grease_pencil "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/source/blender/editors/grease_pencil/intern/grease_pencil_geom.cc b/source/blender/editors/grease_pencil/intern/grease_pencil_geom.cc new file mode 100644 index 00000000000..50970ff253d --- /dev/null +++ b/source/blender/editors/grease_pencil/intern/grease_pencil_geom.cc @@ -0,0 +1,104 @@ +/* SPDX-FileCopyrightText: 2023 Blender Foundation + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup edgreasepencil + */ + +#include + +#include "BKE_grease_pencil.hh" + +#include "ED_grease_pencil.h" + +extern "C" { +#include "curve_fit_nd.h" +} + +namespace blender::ed::greasepencil { + +Array fit_curve_polyline_2d(Span points, + const float error_threshold, + const bool use_refit, + const float angle_threshold, + const IndexMask &corner_mask) +{ + Array indices(corner_mask.size()); + corner_mask.to_indices(indices.as_mutable_span()); + uint *indicies_ptr = (corner_mask.size() > 0) ? reinterpret_cast(indices.data()) : + nullptr; + + float *r_cubic_array; + uint *r_corner_index_array; + uint r_cubic_array_len, r_corner_index_array_len; + int error; + if (use_refit) { + error = curve_fit_cubic_to_points_refit_fl(*points.data(), + points.size(), + 2, + error_threshold, + CURVE_FIT_CALC_HIGH_QUALIY, + indicies_ptr, + indices.size(), + angle_threshold, + &r_cubic_array, + &r_cubic_array_len, + nullptr, + &r_corner_index_array, + &r_corner_index_array_len); + } + else { + error = curve_fit_cubic_to_points_fl(*points.data(), + points.size(), + 2, + error_threshold, + CURVE_FIT_CALC_HIGH_QUALIY, + indicies_ptr, + indices.size(), + &r_cubic_array, + &r_cubic_array_len, + nullptr, + nullptr, + nullptr); + } + if (error != 0) { + /* Some error occured. Return. */ + return {}; + } + + Span r_cubic_array_span(reinterpret_cast(r_cubic_array), + r_cubic_array_len * 3); + + Array curve_positions(r_cubic_array_span); + return curve_positions; +} + +IndexMask polyline_detect_corners(Span points, + const float radius_min, + const float radius_max, + const int64_t samples_max, + const float angle_threshold, + IndexMaskMemory &memory) +{ + uint *r_corners; + uint r_corner_len; + const int error = curve_fit_corners_detect_fl(*points.data(), + points.size(), + float2::type_length, + radius_min, + radius_max, + samples_max, + angle_threshold, + &r_corners, + &r_corner_len); + if (error != 0) { + /* Error occured, return. */ + return IndexMask(); + } + BLI_assert(samples_max < std::numeric_limits::max()); + Span indices(reinterpret_cast(r_corners), r_corner_len); + return IndexMask::from_indices(indices, memory); +} + +} // namespace blender::ed::greasepencil diff --git a/source/blender/editors/include/ED_grease_pencil.hh b/source/blender/editors/include/ED_grease_pencil.hh index d3a06d3e464..0ae400c5054 100644 --- a/source/blender/editors/include/ED_grease_pencil.hh +++ b/source/blender/editors/include/ED_grease_pencil.hh @@ -12,6 +12,7 @@ #include "BKE_grease_pencil.hh" #include "BLI_generic_span.hh" +#include "BLI_index_mask.hh" #include "BLI_math_matrix_types.hh" #include "ED_keyframes_edit.hh" @@ -125,4 +126,17 @@ int64_t ramer_douglas_peucker_simplify(IndexRange range, FunctionRef dist_function, MutableSpan dst); +Array fit_curve_polyline_2d(Span points, + float error_threshold, + bool use_refit, + float angle_threshold, + const IndexMask &corner_mask); + +IndexMask polyline_detect_corners(Span points, + float radius_min, + float radius_max, + int64_t samples_max, + float angle_threshold, + IndexMaskMemory &memory); + } // namespace blender::ed::greasepencil -- 2.30.2 From 06dc20dbde53804aea8da624b7b51eee2cba5f7a Mon Sep 17 00:00:00 2001 From: Falk David Date: Mon, 7 Aug 2023 13:07:13 +0200 Subject: [PATCH 40/75] New active smoothing algorithm --- .../intern/grease_pencil_edit.cc | 3 +- .../sculpt_paint/grease_pencil_paint.cc | 298 +++++++++++++----- 2 files changed, 223 insertions(+), 78 deletions(-) diff --git a/source/blender/editors/grease_pencil/intern/grease_pencil_edit.cc b/source/blender/editors/grease_pencil/intern/grease_pencil_edit.cc index 3983ee491fd..72d525e211d 100644 --- a/source/blender/editors/grease_pencil/intern/grease_pencil_edit.cc +++ b/source/blender/editors/grease_pencil/intern/grease_pencil_edit.cc @@ -235,7 +235,8 @@ void gaussian_blur_1D(const GSpan src, bke::attribute_math::convert_to_static_type(src.type(), [&](auto dummy) { using T = decltype(dummy); /* Reduces unnecessary code generation. */ - if constexpr (std::is_same_v || std::is_same_v) { + if constexpr (std::is_same_v || std::is_same_v || + std::is_same_v) { gaussian_blur_1D(src.typed(), iterations, influence, diff --git a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc index b689b9fa0bc..8bab5a82204 100644 --- a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc +++ b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc @@ -30,6 +30,100 @@ static constexpr float POINT_RESAMPLE_MIN_DISTANCE_PX = 10.0f; static constexpr int64_t STOKE_CACHE_ALLOCATION_CHUNK_SIZE = 1024; +static void forward_diff_bezier_2d( + float2 q0, float2 q1, float2 q2, float2 q3, MutableSpan r_p) +{ + float f = static_cast(r_p.size()); + float2 rt0 = q0; + float2 rt1 = 3.0f * (q1 - q0) / f; + f = f * f; + float2 rt2 = 3.0f * (q0 - 2.0f * q1 + q2) / f; + f = f * static_cast(r_p.size()); + float2 rt3 = (q3 - q0 + 3.0f * (q1 - q2)) / f; + + q0 = rt0; + q1 = rt1 + rt2 + rt3; + q2 = 2 * rt2 + 6 * rt3; + q3 = 6 * rt3; + + for (float2 &r : r_p) { + r = q0; + q0 += q1; + q1 += q2; + q2 += q3; + } +} + +/** Sample a bezier curve at a fixed resolution and return the sampled points in an array. */ +static Array sample_curve_2d(Span curve_points, const int64_t resolution) +{ + BLI_assert(curve_points.size() % 3 == 0); + const int64_t num_handles = curve_points.size() / 3; + const int64_t num_segments = num_handles - 1; + const int64_t num_points = num_segments * resolution; + + Array points(num_points); + const Span curve_segments = curve_points.drop_front(1).drop_back(1); + for (const int64_t segment_i : IndexRange(num_segments)) { + IndexRange segment_range(segment_i * resolution, resolution); + forward_diff_bezier_2d(curve_segments[segment_i * 3 + 0], + curve_segments[segment_i * 3 + 1], + curve_segments[segment_i * 3 + 2], + curve_segments[segment_i * 3 + 3], + points.as_mutable_span().slice(segment_range)); + } + return points; +} + +/** Interpolate \a dst such that the points in \a dst lie evenly distributed on \a src. */ +static void interp_polyline_to_polyline(Span src, MutableSpan dst) +{ + using ParamIt = double *; + BLI_assert(src.size() > 1); + Array normalized_parameters_src(src.size(), 0.0f); + double total_dist_src = 0.0f; + for (const int64_t i : src.index_range().drop_front(1)) { + total_dist_src += math::distance(src[i], src[i - 1]); + normalized_parameters_src[i] = total_dist_src; + } + if (total_dist_src < 1e-6f) { + normalized_parameters_src.fill(0.0f); + } + else { + for (const int64_t i : src.index_range()) { + normalized_parameters_src[i] /= total_dist_src; + } + } + + Array accumulated_lengths_dst(dst.size(), 0.0f); + double total_dist_dst = 0.0f; + for (const int64_t i : dst.index_range().drop_front(1)) { + total_dist_dst += math::distance(dst[i], dst[i - 1]); + accumulated_lengths_dst[i] = total_dist_dst; + } + + ParamIt start_it = normalized_parameters_src.begin(); + for (const int64_t i : dst.index_range().drop_front(1)) { + const double target = accumulated_lengths_dst[i] / total_dist_dst; + ParamIt it = std::lower_bound(start_it, normalized_parameters_src.end(), target); + const int64_t index = std::distance(normalized_parameters_src.begin(), it); + start_it = it; + if (index + 1 >= normalized_parameters_src.size()) { + break; + } + if (math::distance_squared(src[index], dst[i]) < 1e-6 || + math::abs(normalized_parameters_src[index + 1] - normalized_parameters_src[index]) < 1e-8) + { + dst[i] = src[index]; + continue; + } + const double t = (target - normalized_parameters_src[index]) / + (normalized_parameters_src[index + 1] - normalized_parameters_src[index]); + + dst[i] = math::interpolate(src[index], src[index + 1], t); + } +} + struct ScreenSpacePoint { float2 co; float radius; @@ -40,15 +134,10 @@ struct ScreenSpacePoint { class PaintOperation : public GreasePencilStrokeOperation { private: Vector screen_space_coordinates_; + Vector> screen_space_smoothed_coordinates_; + int64_t active_smooth_index_ = 0; bke::greasepencil::StrokeCache *stroke_cache_; - int64_t active_index_ = 0; - - Vector sampled_positions_; - Vector sampled_radii_; - - Vector smoothed_positions_; - Vector smoothed_radii_; friend struct PaintOperationExecutor; @@ -129,32 +218,12 @@ struct PaintOperationExecutor { return point; } - void update_stroke_cache(PaintOperation &self, - const int64_t new_points_num, - Span new_coordinates, - Span new_radii, - Span new_opacities, - Span new_vertex_colors) - { - const int64_t old_size = self.stroke_cache_->size(); - self.stroke_cache_->resize(self.stroke_cache_->size() + new_points_num); - - IndexRange new_range(old_size, new_points_num); - MutableSpan positions_slice = self.stroke_cache_->positions_for_write().slice( - new_range); - for (const int64_t i : new_coordinates.index_range()) { - positions_slice[i] = screen_space_to_object_space(new_coordinates[i]); - } - self.stroke_cache_->radii_for_write().slice(new_range).copy_from(new_radii); - self.stroke_cache_->opacities_for_write().slice(new_range).copy_from(new_opacities); - self.stroke_cache_->vertex_colors_for_write().slice(new_range).copy_from(new_vertex_colors); - } - void process_start_sample(PaintOperation &self, const InputSample &start_sample) { ScreenSpacePoint start_point = this->point_from_input_sample(start_sample); self.screen_space_coordinates_.append(start_point.co); + self.screen_space_smoothed_coordinates_.append(Vector({start_point.co})); self.stroke_cache_->resize(1); self.stroke_cache_->positions_for_write().last() = screen_space_to_object_space( @@ -165,6 +234,100 @@ struct PaintOperationExecutor { start_point.vertex_color); } + void active_smoothing(PaintOperation &self) + { + /* Active smoothing is only done on a slice of the points (from the active index to the current + * end of the stroke). */ + const int64_t smooth_window_size = self.stroke_cache_->size() - self.active_smooth_index_; + IndexRange smooth_window(self.active_smooth_index_, smooth_window_size); + + Span screen_space_coords_smooth_slice = self.screen_space_coordinates_.as_span().slice( + smooth_window); + + /* Detect corners in the current slice of coordinates. */ + IndexMaskMemory memory; + const float min_radius_px = 5.0f; + const float max_radius_px = 30.0f; + const int64_t max_samples = 64; + const float angle_threshold = 0.6f; + IndexMask corner_mask = blender::ed::greasepencil::polyline_detect_corners( + screen_space_coords_smooth_slice, + min_radius_px, + max_radius_px, + max_samples, + angle_threshold, + memory); + + /* Pre-blur the coordinates for the curve fitting. This generally leads to a better fit. */ + Array coords_pre_blur(smooth_window.size()); + ed::greasepencil::gaussian_blur_1D(screen_space_coords_smooth_slice, + 3, + 1.0f, + true, + true, + false, + coords_pre_blur.as_mutable_span()); + + /* Curve fitting. The output will be a set of handles (float2 triplets) in a flat array. */ + const float max_error_threshold_px = 5.0f; + const float angle_threshold = 0.6f; + Array curve_points = blender::ed::greasepencil::fit_curve_polyline_2d( + coords_pre_blur, + max_error_threshold_px * settings_->active_smooth, + false, + angle_threshold, + corner_mask); + + /* Sampling the curve at a fixed resolution. */ + const int64_t sample_resolution = 32; + Array sampled_curve_points = sample_curve_2d(curve_points, sample_resolution); + + /* Morphing the coordinates onto the curve. Result is stored in a temporary array. */ + Array coords_smoothed(screen_space_coords_smooth_slice); + interp_polyline_to_polyline(sampled_curve_points, coords_smoothed.as_mutable_span()); + + MutableSpan positions_slice = self.stroke_cache_->positions_for_write().slice( + smooth_window); + bool stop_counting_converged = false; + int num_converged = 0; + for (const int64_t i : smooth_window.index_range()) { + /* Record the curve fitting of this point. */ + self.screen_space_smoothed_coordinates_[i].append(coords_smoothed[i]); + Span smoothed_coords_point = self.screen_space_smoothed_coordinates_[i]; + + /* Get the arithmetic mean of all the curve fittings of this point. */ + float2 mean = smoothed_coords_point[0]; + for (const float2 v : smoothed_coords_point.drop_front(1).drop_back(1)) { + mean += v; + } + + /* We compare the previous arithmetic mean to the current. Going from the back to the front, + * if a point hasn't moved by a minimum threashold, it counts as converged. */ + float2 new_pos = (mean + smoothed_coords_point.last()) / smoothed_coords_point.size(); + if (!stop_counting_converged) { + float2 prev_pos = mean / (smoothed_coords_point.size() - 1); + if (math::distance(new_pos, prev_pos) < 0.05f) { + num_converged++; + } + else { + stop_counting_converged = true; + } + } + + /* Update the positions in the current cache. */ + positions_slice[i] = screen_space_to_object_space(new_pos); + } + + /* Remove all the converged points from the active window and shrink the window accordingly. */ + if (num_converged > 0) { + self.active_smooth_index_ = math::min(self.active_smooth_index_ + num_converged, + self.stroke_cache_->size() - 1); + self.screen_space_smoothed_coordinates_.remove( + 0, + math::min(self.screen_space_smoothed_coordinates_.size() - num_converged, int64_t(0))); + } + } + void process_extension_sample(PaintOperation &self, const InputSample &extension_sample) { ScreenSpacePoint point = this->point_from_input_sample(extension_sample); @@ -192,13 +355,15 @@ struct PaintOperationExecutor { new_points_num += subdivisions; } + /* Subdivide stroke in new_range. */ + IndexRange new_range(self.stroke_cache_->size(), new_points_num); Array new_coordinates(new_points_num); Array new_radii(new_points_num); Array new_opacities(new_points_num); Array new_vertex_colors(new_points_num); const float step = 1.0f / static_cast(new_points_num); float factor = step; - for (const int i : IndexRange(new_points_num)) { + for (const int64_t i : new_range.index_range()) { new_coordinates[i] = bke::attribute_math::mix2(factor, prev_co, point.co); new_radii[i] = bke::attribute_math::mix2(factor, prev_radius, point.radius); new_opacities[i] = bke::attribute_math::mix2(factor, prev_opacity, point.opacity); @@ -207,58 +372,36 @@ struct PaintOperationExecutor { factor += step; } - for (float2 co : new_coordinates) { - self.screen_space_coordinates_.append(co); + self.screen_space_coordinates_.extend(new_coordinates); + for (float2 new_co : new_coordinates) { + self.screen_space_smoothed_coordinates_.append(Vector({new_co})); } - this->update_stroke_cache( - self, new_points_num, new_coordinates, new_radii, new_opacities, new_vertex_colors); + self.stroke_cache_->resize(self.stroke_cache_->size() + new_points_num); + self.stroke_cache_->radii_for_write().slice(new_range).copy_from(new_radii); + self.stroke_cache_->opacities_for_write().slice(new_range).copy_from(new_opacities); + self.stroke_cache_->vertex_colors_for_write().slice(new_range).copy_from(new_vertex_colors); - // const float active_smooth_factor = settings_->active_smooth; - - // self.sampled_positions_.append(proj_pos); - // self.sampled_radii_.append(radius); - - // self.smoothed_positions_.append(proj_pos); - // self.smoothed_radii_.append(radius); - - // const int64_t smooth_window_size = 16; - // const int64_t inverted_copy_window_size = math::max( - // self.stroke_cache_->size() - smooth_window_size, int64_t(0)); - - // const int64_t smooth_radius = 16; - - // const int64_t inverted_smooth_window_size = math::max( - // self.stroke_cache_->size() - smooth_window_size - smooth_radius, int64_t(0)); - - // Span src_positions = self.sampled_positions_.as_span().drop_front( - // inverted_smooth_window_size); - // MutableSpan dst_positions = self.smoothed_positions_.as_mutable_span().drop_front( - // inverted_smooth_window_size); - - // Span src_radii = - // self.sampled_radii_.as_span().drop_front(inverted_smooth_window_size); MutableSpan - // dst_radii = self.smoothed_radii_.as_mutable_span().drop_front( - // inverted_smooth_window_size); - - // ed::greasepencil::gaussian_blur_1D( - // src_positions, smooth_radius, active_smooth_factor, false, true, false, dst_positions); - // ed::greasepencil::gaussian_blur_1D( - // src_radii, smooth_radius, active_smooth_factor, false, false, false, dst_radii); - - // self.stroke_cache_->positions_for_write() - // .drop_front(inverted_copy_window_size) - // .copy_from(self.smoothed_positions_.as_span().drop_front(inverted_copy_window_size)); - // self.stroke_cache_->radii_for_write() - // .drop_front(inverted_copy_window_size) - // .copy_from(self.smoothed_radii_.as_span().drop_front(inverted_copy_window_size)); + const int64_t min_active_smoothing_points_num = 8; + if (self.stroke_cache_->size() >= 8) { + this->active_smoothing(self); + } + else { + MutableSpan positions_slice = self.stroke_cache_->positions_for_write().slice( + new_range); + for (const int64_t i : new_coordinates.index_range()) { + positions_slice[i] = screen_space_to_object_space(new_coordinates[i]); + } + return; + } #ifdef DEBUG - // /* Visualize active window. */ - // self.stroke_cache_->vertex_colors.fill(ColorGeometry4f(float4(0.0f))); - // self.stroke_cache_->vertex_colors.as_mutable_span() - // .drop_front(inverted_active_window_size) - // .fill(ColorGeometry4f(float4(1.0f, 0.1f, 0.1f, 1.0f))); + /* Visualize active window. */ + self.stroke_cache_->vertex_colors_for_write().fill(ColorGeometry4f(float4(0.0f))); + for (const int64_t i : smooth_window.index_range()) { + self.stroke_cache_->vertex_colors_for_write().slice(smooth_window)[i] = ColorGeometry4f( + float4(1.0f, 0.1f, 0.1f, 1.0f)); + } #endif } @@ -471,7 +614,8 @@ void PaintOperation::on_stroke_done(const bContext &C) return; } - // this->simplify_stroke_cache(0.0005f); + const float simplifiy_threashold = 0.001f; + this->simplify_stroke_cache(simplifiy_threashold); /* The object should have an active layer. */ BLI_assert(grease_pencil.has_active_layer()); -- 2.30.2 From 0617b43800313ac3c651244b2b7fcd817c038526 Mon Sep 17 00:00:00 2001 From: Falk David Date: Mon, 7 Aug 2023 13:45:00 +0200 Subject: [PATCH 41/75] Fixes after rebase --- .../intern/grease_pencil_geom.cc | 50 ++++++------------- .../intern/grease_pencil_utils.cc | 4 +- .../editors/include/ED_grease_pencil.hh | 2 - .../sculpt_paint/grease_pencil_paint.cc | 27 ++++------ 4 files changed, 28 insertions(+), 55 deletions(-) diff --git a/source/blender/editors/grease_pencil/intern/grease_pencil_geom.cc b/source/blender/editors/grease_pencil/intern/grease_pencil_geom.cc index 50970ff253d..f945f7e9b72 100644 --- a/source/blender/editors/grease_pencil/intern/grease_pencil_geom.cc +++ b/source/blender/editors/grease_pencil/intern/grease_pencil_geom.cc @@ -10,7 +10,7 @@ #include "BKE_grease_pencil.hh" -#include "ED_grease_pencil.h" +#include "ED_grease_pencil.hh" extern "C" { #include "curve_fit_nd.h" @@ -20,8 +20,6 @@ namespace blender::ed::greasepencil { Array fit_curve_polyline_2d(Span points, const float error_threshold, - const bool use_refit, - const float angle_threshold, const IndexMask &corner_mask) { Array indices(corner_mask.size()); @@ -30,38 +28,20 @@ Array fit_curve_polyline_2d(Span points, nullptr; float *r_cubic_array; - uint *r_corner_index_array; - uint r_cubic_array_len, r_corner_index_array_len; - int error; - if (use_refit) { - error = curve_fit_cubic_to_points_refit_fl(*points.data(), - points.size(), - 2, - error_threshold, - CURVE_FIT_CALC_HIGH_QUALIY, - indicies_ptr, - indices.size(), - angle_threshold, - &r_cubic_array, - &r_cubic_array_len, - nullptr, - &r_corner_index_array, - &r_corner_index_array_len); - } - else { - error = curve_fit_cubic_to_points_fl(*points.data(), - points.size(), - 2, - error_threshold, - CURVE_FIT_CALC_HIGH_QUALIY, - indicies_ptr, - indices.size(), - &r_cubic_array, - &r_cubic_array_len, - nullptr, - nullptr, - nullptr); - } + uint r_cubic_array_len; + int error = curve_fit_cubic_to_points_fl(*points.data(), + points.size(), + 2, + error_threshold, + CURVE_FIT_CALC_HIGH_QUALIY, + indicies_ptr, + indices.size(), + &r_cubic_array, + &r_cubic_array_len, + nullptr, + nullptr, + nullptr); + if (error != 0) { /* Some error occured. Return. */ return {}; diff --git a/source/blender/editors/grease_pencil/intern/grease_pencil_utils.cc b/source/blender/editors/grease_pencil/intern/grease_pencil_utils.cc index 85166a45e7e..63b57c2ebe9 100644 --- a/source/blender/editors/grease_pencil/intern/grease_pencil_utils.cc +++ b/source/blender/editors/grease_pencil/intern/grease_pencil_utils.cc @@ -16,8 +16,8 @@ #include "DNA_object_types.h" #include "DNA_scene_types.h" -#include "ED_grease_pencil.h" -#include "ED_view3d.h" +#include "ED_grease_pencil.hh" +#include "ED_view3d.hh" namespace blender::ed::greasepencil { diff --git a/source/blender/editors/include/ED_grease_pencil.hh b/source/blender/editors/include/ED_grease_pencil.hh index 0ae400c5054..0392dc4db96 100644 --- a/source/blender/editors/include/ED_grease_pencil.hh +++ b/source/blender/editors/include/ED_grease_pencil.hh @@ -128,8 +128,6 @@ int64_t ramer_douglas_peucker_simplify(IndexRange range, Array fit_curve_polyline_2d(Span points, float error_threshold, - bool use_refit, - float angle_threshold, const IndexMask &corner_mask); IndexMask polyline_detect_corners(Span points, diff --git a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc index 8bab5a82204..985a7abe358 100644 --- a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc +++ b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc @@ -270,13 +270,8 @@ struct PaintOperationExecutor { /* Curve fitting. The output will be a set of handles (float2 triplets) in a flat array. */ const float max_error_threshold_px = 5.0f; - const float angle_threshold = 0.6f; Array curve_points = blender::ed::greasepencil::fit_curve_polyline_2d( - coords_pre_blur, - max_error_threshold_px * settings_->active_smooth, - false, - angle_threshold, - corner_mask); + coords_pre_blur, max_error_threshold_px * settings_->active_smooth, corner_mask); /* Sampling the curve at a fixed resolution. */ const int64_t sample_resolution = 32; @@ -326,6 +321,15 @@ struct PaintOperationExecutor { 0, math::min(self.screen_space_smoothed_coordinates_.size() - num_converged, int64_t(0))); } + +#ifdef DEBUG + /* Visualize active window. */ + self.stroke_cache_->vertex_colors_for_write().fill(ColorGeometry4f(float4(0.0f))); + for (const int64_t i : smooth_window.index_range()) { + self.stroke_cache_->vertex_colors_for_write().slice(smooth_window)[i] = ColorGeometry4f( + float4(1.0f, 0.1f, 0.1f, 1.0f)); + } +#endif } void process_extension_sample(PaintOperation &self, const InputSample &extension_sample) @@ -383,7 +387,7 @@ struct PaintOperationExecutor { self.stroke_cache_->vertex_colors_for_write().slice(new_range).copy_from(new_vertex_colors); const int64_t min_active_smoothing_points_num = 8; - if (self.stroke_cache_->size() >= 8) { + if (self.stroke_cache_->size() >= min_active_smoothing_points_num) { this->active_smoothing(self); } else { @@ -394,15 +398,6 @@ struct PaintOperationExecutor { } return; } - -#ifdef DEBUG - /* Visualize active window. */ - self.stroke_cache_->vertex_colors_for_write().fill(ColorGeometry4f(float4(0.0f))); - for (const int64_t i : smooth_window.index_range()) { - self.stroke_cache_->vertex_colors_for_write().slice(smooth_window)[i] = ColorGeometry4f( - float4(1.0f, 0.1f, 0.1f, 1.0f)); - } -#endif } void execute(PaintOperation &self, const bContext &C, const InputSample &extension_sample) -- 2.30.2 From d82b4cea1157356f5e9cddcbd16ec6604e4e9a76 Mon Sep 17 00:00:00 2001 From: Falk David Date: Mon, 7 Aug 2023 14:56:32 +0200 Subject: [PATCH 42/75] Fix crash --- .../blender/editors/sculpt_paint/grease_pencil_paint.cc | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc index 985a7abe358..cbbbdc841fc 100644 --- a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc +++ b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc @@ -317,9 +317,12 @@ struct PaintOperationExecutor { if (num_converged > 0) { self.active_smooth_index_ = math::min(self.active_smooth_index_ + num_converged, self.stroke_cache_->size() - 1); - self.screen_space_smoothed_coordinates_.remove( - 0, - math::min(self.screen_space_smoothed_coordinates_.size() - num_converged, int64_t(0))); + if (self.screen_space_smoothed_coordinates_.size() - num_converged > 0) { + self.screen_space_smoothed_coordinates_.remove(0, num_converged); + } + else { + self.screen_space_smoothed_coordinates_.clear(); + } } #ifdef DEBUG -- 2.30.2 From 1935a4ebb3eff844bf2f4a944c9da0a0d761b8d0 Mon Sep 17 00:00:00 2001 From: Falk David Date: Mon, 7 Aug 2023 19:02:17 +0200 Subject: [PATCH 43/75] Move ramer-douglas-peucker algorithm --- .../intern/grease_pencil_edit.cc | 53 ----------------- .../intern/grease_pencil_geom.cc | 59 +++++++++++++++++++ 2 files changed, 59 insertions(+), 53 deletions(-) diff --git a/source/blender/editors/grease_pencil/intern/grease_pencil_edit.cc b/source/blender/editors/grease_pencil/intern/grease_pencil_edit.cc index 72d525e211d..c1ad3761ad0 100644 --- a/source/blender/editors/grease_pencil/intern/grease_pencil_edit.cc +++ b/source/blender/editors/grease_pencil/intern/grease_pencil_edit.cc @@ -406,59 +406,6 @@ static float dist_to_interpolated( return 0.0f; } -/** - * An implementation of the Ramer-Douglas-Peucker algorithm. - * - * \param range: The range to simplify. - * \param epsilon: The threshold distance from the coord between two points for when a point - * in-between needs to be kept. - * \param dist_function: A function that computes the distance to a point at an index in the range. - * The IndexRange is a subrange of \a range and the index is an index relative to the subrange. - * \param points_to_delete: Writes true to the indecies for which the points should be removed. - * \returns the total number of points to remove. - */ -int64_t ramer_douglas_peucker_simplify( - const IndexRange range, - const float epsilon, - const FunctionRef dist_function, - MutableSpan points_to_delete) -{ - /* Mark all points to not be removed. */ - points_to_delete.slice(range).fill(false); - int64_t total_points_to_remove = 0; - - Stack stack; - stack.push(range); - while (!stack.is_empty()) { - const IndexRange sub_range = stack.pop(); - - /* Compute the maximum distance and the corresponding distance. */ - float max_dist = -1.0f; - int max_index = -1; - for (const int64_t index : sub_range.drop_front(1).drop_back(1)) { - const float dist = dist_function(sub_range.first(), sub_range.last(), index); - if (dist > max_dist) { - max_dist = dist; - max_index = index - sub_range.first(); - } - } - - if (max_dist > epsilon) { - /* Found point outside the epsilon-sized strip. Repeat the search on the left & right side. - */ - stack.push(sub_range.slice(IndexRange(max_index + 1))); - stack.push(sub_range.slice(IndexRange(max_index, sub_range.size() - max_index))); - } - else { - /* Points in `sub_range` are inside the epsilon-sized strip. Mark them to be deleted. */ - const IndexRange inside_range = sub_range.drop_front(1).drop_back(1); - total_points_to_remove += inside_range.size(); - points_to_delete.slice(inside_range).fill(true); - } - } - return total_points_to_remove; -} - static int64_t stroke_simplify(const IndexRange points, const bool cyclic, const float epsilon, diff --git a/source/blender/editors/grease_pencil/intern/grease_pencil_geom.cc b/source/blender/editors/grease_pencil/intern/grease_pencil_geom.cc index f945f7e9b72..e4373efc524 100644 --- a/source/blender/editors/grease_pencil/intern/grease_pencil_geom.cc +++ b/source/blender/editors/grease_pencil/intern/grease_pencil_geom.cc @@ -8,6 +8,8 @@ #include +#include "BLI_stack.hh" + #include "BKE_grease_pencil.hh" #include "ED_grease_pencil.hh" @@ -18,6 +20,63 @@ extern "C" { namespace blender::ed::greasepencil { +/** + * An implementation of the Ramer-Douglas-Peucker algorithm. + * + * \param range: The range to simplify. + * \param epsilon: The threshold distance from the coord between two points for when a point + * in-between needs to be kept. + * \param dist_function: A function that computes the distance to a point at an index in the range. + * The IndexRange is a subrange of \a range and the index is an index relative to the subrange. + * \param points_to_delete: Writes true to the indecies for which the points should be removed. + * \returns the total number of points to remove. + */ +int64_t ramer_douglas_peucker_simplify( + const IndexRange range, + const float epsilon, + const FunctionRef dist_function, + MutableSpan points_to_delete) +{ + /* Mark all points to not be removed. */ + points_to_delete.slice(range).fill(false); + int64_t total_points_to_remove = 0; + + Stack stack; + stack.push(range); + while (!stack.is_empty()) { + const IndexRange sub_range = stack.pop(); + /* Skip ranges with less than 3 points. All points are kept. */ + if (sub_range.size() < 3) { + continue; + } + + /* Compute the maximum distance and the corresponding distance. */ + float max_dist = -1.0f; + int max_index = -1; + for (const int64_t index : sub_range.drop_front(1).drop_back(1)) { + const float dist = dist_function(sub_range.first(), sub_range.last(), index); + if (dist > max_dist) { + max_dist = dist; + max_index = index - sub_range.first(); + } + } + + if (max_dist > epsilon) { + /* Found point outside the epsilon-sized strip. The point at `max_index` will be kept, repeat + * the search on the left & right side. */ + stack.push(sub_range.slice(0, max_index + 1)); + stack.push(sub_range.slice(max_index, sub_range.size() - max_index)); + } + else { + /* Points in `sub_range` are inside the epsilon-sized strip. Mark them to be deleted. */ + const IndexRange inside_range = sub_range.drop_front(1).drop_back(1); + total_points_to_remove += inside_range.size(); + points_to_delete.slice(inside_range).fill(true); + } + } + return total_points_to_remove; +} + Array fit_curve_polyline_2d(Span points, const float error_threshold, const IndexMask &corner_mask) -- 2.30.2 From f04a52cea8e3f3c8d8915da38ee382f82bc5eee5 Mon Sep 17 00:00:00 2001 From: Falk David Date: Mon, 7 Aug 2023 19:03:12 +0200 Subject: [PATCH 44/75] Simplify in pixel space --- .../sculpt_paint/grease_pencil_paint.cc | 70 ++++++++++--------- 1 file changed, 37 insertions(+), 33 deletions(-) diff --git a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc index cbbbdc841fc..b11e768de62 100644 --- a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc +++ b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc @@ -134,7 +134,8 @@ struct ScreenSpacePoint { class PaintOperation : public GreasePencilStrokeOperation { private: Vector screen_space_coordinates_; - Vector> screen_space_smoothed_coordinates_; + Vector> screen_space_curve_fitted_coordinates_; + Vector screen_space_smoothed_coordinates_; int64_t active_smooth_index_ = 0; bke::greasepencil::StrokeCache *stroke_cache_; @@ -147,7 +148,7 @@ class PaintOperation : public GreasePencilStrokeOperation { void on_stroke_done(const bContext &C) override; private: - void simplify_stroke_cache(const float epsilon); + void simplify_stroke_cache(const float epsilon_px); void create_stroke_from_stroke_cache(bke::greasepencil::Drawing &drawing_orig); }; @@ -223,7 +224,8 @@ struct PaintOperationExecutor { ScreenSpacePoint start_point = this->point_from_input_sample(start_sample); self.screen_space_coordinates_.append(start_point.co); - self.screen_space_smoothed_coordinates_.append(Vector({start_point.co})); + self.screen_space_curve_fitted_coordinates_.append(Vector({start_point.co})); + self.screen_space_smoothed_coordinates_.append(start_point.co); self.stroke_cache_->resize(1); self.stroke_cache_->positions_for_write().last() = screen_space_to_object_space( @@ -281,14 +283,16 @@ struct PaintOperationExecutor { Array coords_smoothed(screen_space_coords_smooth_slice); interp_polyline_to_polyline(sampled_curve_points, coords_smoothed.as_mutable_span()); + MutableSpan smoothed_coordinates_slice = + self.screen_space_smoothed_coordinates_.as_mutable_span().slice(smooth_window); MutableSpan positions_slice = self.stroke_cache_->positions_for_write().slice( smooth_window); bool stop_counting_converged = false; int num_converged = 0; for (const int64_t i : smooth_window.index_range()) { /* Record the curve fitting of this point. */ - self.screen_space_smoothed_coordinates_[i].append(coords_smoothed[i]); - Span smoothed_coords_point = self.screen_space_smoothed_coordinates_[i]; + self.screen_space_curve_fitted_coordinates_[i].append(coords_smoothed[i]); + Span smoothed_coords_point = self.screen_space_curve_fitted_coordinates_[i]; /* Get the arithmetic mean of all the curve fittings of this point. */ float2 mean = smoothed_coords_point[0]; @@ -310,6 +314,7 @@ struct PaintOperationExecutor { } /* Update the positions in the current cache. */ + smoothed_coordinates_slice[i] = new_pos; positions_slice[i] = screen_space_to_object_space(new_pos); } @@ -317,11 +322,11 @@ struct PaintOperationExecutor { if (num_converged > 0) { self.active_smooth_index_ = math::min(self.active_smooth_index_ + num_converged, self.stroke_cache_->size() - 1); - if (self.screen_space_smoothed_coordinates_.size() - num_converged > 0) { - self.screen_space_smoothed_coordinates_.remove(0, num_converged); + if (self.screen_space_curve_fitted_coordinates_.size() - num_converged > 0) { + self.screen_space_curve_fitted_coordinates_.remove(0, num_converged); } else { - self.screen_space_smoothed_coordinates_.clear(); + self.screen_space_curve_fitted_coordinates_.clear(); } } @@ -380,8 +385,9 @@ struct PaintOperationExecutor { } self.screen_space_coordinates_.extend(new_coordinates); + self.screen_space_smoothed_coordinates_.extend(new_coordinates); for (float2 new_co : new_coordinates) { - self.screen_space_smoothed_coordinates_.append(Vector({new_co})); + self.screen_space_curve_fitted_coordinates_.append(Vector({new_co})); } self.stroke_cache_->resize(self.stroke_cache_->size() + new_points_num); @@ -459,20 +465,20 @@ void PaintOperation::on_stroke_extended(const bContext &C, const InputSample &ex executor.execute(*this, C, extension_sample); } -static float dist_to_interpolated( - float3 pos, float3 posA, float3 posB, float val, float valA, float valB) +static float dist_to_interpolated_2d( + float2 pos, float2 posA, float2 posB, float val, float valA, float valB) { - float dist1 = math::distance_squared(posA, pos); - float dist2 = math::distance_squared(posB, pos); + const float dist1 = math::distance_squared(posA, pos); + const float dist2 = math::distance_squared(posB, pos); - if (dist1 + dist2 > 0) { - float interpolated_val = interpf(valB, valA, dist1 / (dist1 + dist2)); + if (dist1 + dist2 > 0.0f) { + const float interpolated_val = interpf(valB, valA, dist1 / (dist1 + dist2)); return math::distance(interpolated_val, val); } return 0.0f; } -void PaintOperation::simplify_stroke_cache(const float epsilon) +void PaintOperation::simplify_stroke_cache(const float epsilon_px) { const Span positions = stroke_cache_->positions(); const Span radii = stroke_cache_->radii(); @@ -480,31 +486,29 @@ void PaintOperation::simplify_stroke_cache(const float epsilon) const Span vertex_colors = stroke_cache_->vertex_colors(); /* Distance function for `ramer_douglas_peucker_simplify`. */ - const auto dist_function = [positions, + const Span positions_2d = this->screen_space_smoothed_coordinates_.as_span(); + const auto dist_function = [positions_2d, radii](int64_t first_index, int64_t last_index, int64_t index) { - const float dist_position = dist_to_line_v3( - positions[index], positions[first_index], positions[last_index]); - /* We devide the distance by 2000.0f to convert from "pixels" to an actual distance. - * For some reason, grease pencil storkes the thickness of strokes in pixels rather - * than object space distance. */ - const float dist_radii = dist_to_interpolated(positions[index], - positions[first_index], - positions[last_index], - radii[index], - radii[first_index], - radii[last_index]) / - 2000.0f; - return math::max(dist_position, dist_radii); + const float dist_position_px = dist_to_line_segment_v2( + positions_2d[index], positions_2d[first_index], positions_2d[last_index]); + const float dist_radii_px = dist_to_interpolated_2d(positions_2d[index], + positions_2d[first_index], + positions_2d[last_index], + radii[index], + radii[first_index], + radii[last_index]); + return math::max(dist_position_px, dist_radii_px); }; Array points_to_delete(stroke_cache_->size(), false); int64_t total_points_to_remove = ed::greasepencil::ramer_douglas_peucker_simplify( IndexRange(stroke_cache_->size()), - epsilon, + epsilon_px, dist_function, points_to_delete.as_mutable_span()); int64_t new_size = stroke_cache_->size() - total_points_to_remove; + BLI_assert(new_size > 0); Array old_indices(new_size); int64_t i = 0; @@ -612,8 +616,8 @@ void PaintOperation::on_stroke_done(const bContext &C) return; } - const float simplifiy_threashold = 0.001f; - this->simplify_stroke_cache(simplifiy_threashold); + const float simplifiy_threashold_px = 0.5f; + this->simplify_stroke_cache(simplifiy_threashold_px); /* The object should have an active layer. */ BLI_assert(grease_pencil.has_active_layer()); -- 2.30.2 From 8ac13cede5174572c40f3f4004484ad743919b0c Mon Sep 17 00:00:00 2001 From: Falk David Date: Tue, 8 Aug 2023 12:48:14 +0200 Subject: [PATCH 45/75] Make converging threshold a variable --- source/blender/editors/sculpt_paint/grease_pencil_paint.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc index b11e768de62..40f7974f6e5 100644 --- a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc +++ b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc @@ -287,6 +287,7 @@ struct PaintOperationExecutor { self.screen_space_smoothed_coordinates_.as_mutable_span().slice(smooth_window); MutableSpan positions_slice = self.stroke_cache_->positions_for_write().slice( smooth_window); + const float converging_threshold_px = 0.05f; bool stop_counting_converged = false; int num_converged = 0; for (const int64_t i : smooth_window.index_range()) { @@ -305,7 +306,7 @@ struct PaintOperationExecutor { float2 new_pos = (mean + smoothed_coords_point.last()) / smoothed_coords_point.size(); if (!stop_counting_converged) { float2 prev_pos = mean / (smoothed_coords_point.size() - 1); - if (math::distance(new_pos, prev_pos) < 0.05f) { + if (math::distance(new_pos, prev_pos) < converging_threshold_px) { num_converged++; } else { -- 2.30.2 From 77dda309e2b8bb141a996c94571a6a648a5a9cb5 Mon Sep 17 00:00:00 2001 From: Falk David Date: Tue, 8 Aug 2023 12:49:07 +0200 Subject: [PATCH 46/75] Remove points with pressure of 0 at the end --- .../sculpt_paint/grease_pencil_paint.cc | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc index 40f7974f6e5..d8061f0a151 100644 --- a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc +++ b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc @@ -149,6 +149,7 @@ class PaintOperation : public GreasePencilStrokeOperation { private: void simplify_stroke_cache(const float epsilon_px); + void process_stroke_end(); void create_stroke_from_stroke_cache(bke::greasepencil::Drawing &drawing_orig); }; @@ -540,6 +541,21 @@ void PaintOperation::simplify_stroke_cache(const float epsilon_px) // stroke_cache_->triangles.clear_and_shrink(); } +void PaintOperation::process_stroke_end() +{ + /* Remove points at the end that have a radius close to 0. */ + int64_t points_to_remove = 0; + for (int64_t index = this->stroke_cache_->size() - 1; index >= 0; index--) { + if (this->stroke_cache_->radii()[index] < 1e-5f) { + points_to_remove++; + } + else { + break; + } + } + stroke_cache_->resize(math::max(stroke_cache_->size() - points_to_remove, int64_t(0))); +} + void PaintOperation::create_stroke_from_stroke_cache(bke::greasepencil::Drawing &drawing_orig) { using namespace blender::bke; @@ -620,6 +636,8 @@ void PaintOperation::on_stroke_done(const bContext &C) const float simplifiy_threashold_px = 0.5f; this->simplify_stroke_cache(simplifiy_threashold_px); + this->process_stroke_end(); + /* The object should have an active layer. */ BLI_assert(grease_pencil.has_active_layer()); const bke::greasepencil::Layer &active_layer_orig = *grease_pencil.get_active_layer(); -- 2.30.2 From f593ac71adbe874824d506f899f1b742c379a6e5 Mon Sep 17 00:00:00 2001 From: Falk David Date: Tue, 8 Aug 2023 12:53:13 +0200 Subject: [PATCH 47/75] Get vertex color in executor constructor --- .../sculpt_paint/grease_pencil_paint.cc | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc index d8061f0a151..5fb05752ab1 100644 --- a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc +++ b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc @@ -165,7 +165,7 @@ struct PaintOperationExecutor { float brush_alpha_; BrushGpencilSettings *settings_; - bool use_vertex_color_stroke_; + float4 vertex_color_; PaintOperationExecutor(const bContext &C) { @@ -180,9 +180,15 @@ struct PaintOperationExecutor { const bool use_vertex_color = (scene->toolsettings->gp_paint->mode == GPPAINT_FLAG_USE_VERTEXCOLOR); - use_vertex_color_stroke_ = use_vertex_color && ELEM(settings_->vertex_mode, - GPPAINT_MODE_STROKE, - GPPAINT_MODE_BOTH); + const bool use_vertex_color_stroke = use_vertex_color && ELEM(settings_->vertex_mode, + GPPAINT_MODE_STROKE, + GPPAINT_MODE_BOTH); + vertex_color_ = use_vertex_color_stroke ? float4(brush_->rgb[0], + brush_->rgb[1], + brush_->rgb[2], + settings_->vertex_factor) : + float4(0.0f); + // const bool use_vertex_color_fill = use_vertex_color && ELEM( // brush->gpencil_settings->vertex_mode, GPPAINT_MODE_STROKE, GPPAINT_MODE_BOTH); } @@ -211,11 +217,7 @@ struct PaintOperationExecutor { if (BKE_brush_use_alpha_pressure(brush_)) { point.opacity *= BKE_curvemapping_evaluateF(settings_->curve_strength, 0, sample.pressure); } - point.vertex_color = use_vertex_color_stroke_ ? float4(brush_->rgb[0], - brush_->rgb[1], - brush_->rgb[2], - settings_->vertex_factor) : - float4(0.0f); + point.vertex_color = vertex_color_; /* TODO: Get fill vertex color. */ return point; } -- 2.30.2 From 4e030c071a518a4dcc7f1b19989396130e687d26 Mon Sep 17 00:00:00 2001 From: Falk David Date: Tue, 8 Aug 2023 16:30:07 +0200 Subject: [PATCH 48/75] Add docstring --- source/blender/editors/sculpt_paint/grease_pencil_paint.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc index 5fb05752ab1..9d1dba3f30c 100644 --- a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc +++ b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc @@ -30,6 +30,8 @@ static constexpr float POINT_RESAMPLE_MIN_DISTANCE_PX = 10.0f; static constexpr int64_t STOKE_CACHE_ALLOCATION_CHUNK_SIZE = 1024; +/** Forward differencing of a bezier curve segment (q0,q1,q2,q3). Steps are written to \a r_p. + * Implementation based on #BKE_curve_forward_diff_bezier. */ static void forward_diff_bezier_2d( float2 q0, float2 q1, float2 q2, float2 q3, MutableSpan r_p) { -- 2.30.2 From 24266a8f52748bb09774ab76bf3a8d2b468517d6 Mon Sep 17 00:00:00 2001 From: Falk David Date: Tue, 8 Aug 2023 16:48:32 +0200 Subject: [PATCH 49/75] More cleanup --- .../sculpt_paint/grease_pencil_paint.cc | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc index 9d1dba3f30c..5001c3400b4 100644 --- a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc +++ b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc @@ -208,9 +208,7 @@ struct PaintOperationExecutor { ScreenSpacePoint point_from_input_sample(const InputSample &sample) { ScreenSpacePoint point; - point.co = sample.mouse_position; - point.radius = brush_size_ / 2.0f; if (BKE_brush_use_size_pressure(brush_)) { point.radius *= BKE_curvemapping_evaluateF(settings_->curve_sensitivity, 0, sample.pressure); @@ -300,17 +298,16 @@ struct PaintOperationExecutor { self.screen_space_curve_fitted_coordinates_[i].append(coords_smoothed[i]); Span smoothed_coords_point = self.screen_space_curve_fitted_coordinates_[i]; - /* Get the arithmetic mean of all the curve fittings of this point. */ - float2 mean = smoothed_coords_point[0]; + /* Get the sum of all the curve fittings of this point. */ + float2 sum = smoothed_coords_point[0]; for (const float2 v : smoothed_coords_point.drop_front(1).drop_back(1)) { - mean += v; + sum += v; } - /* We compare the previous arithmetic mean to the current. Going from the back to the front, * if a point hasn't moved by a minimum threashold, it counts as converged. */ - float2 new_pos = (mean + smoothed_coords_point.last()) / smoothed_coords_point.size(); + float2 new_pos = (sum + smoothed_coords_point.last()) / smoothed_coords_point.size(); if (!stop_counting_converged) { - float2 prev_pos = mean / (smoothed_coords_point.size() - 1); + float2 prev_pos = sum / (smoothed_coords_point.size() - 1); if (math::distance(new_pos, prev_pos) < converging_threshold_px) { num_converged++; } @@ -390,22 +387,23 @@ struct PaintOperationExecutor { factor += step; } + /* Update buffers with new points. */ self.screen_space_coordinates_.extend(new_coordinates); self.screen_space_smoothed_coordinates_.extend(new_coordinates); for (float2 new_co : new_coordinates) { self.screen_space_curve_fitted_coordinates_.append(Vector({new_co})); } + /* Resize the stroke cache. */ self.stroke_cache_->resize(self.stroke_cache_->size() + new_points_num); + + /* Write new data to the attributes. */ self.stroke_cache_->radii_for_write().slice(new_range).copy_from(new_radii); self.stroke_cache_->opacities_for_write().slice(new_range).copy_from(new_opacities); self.stroke_cache_->vertex_colors_for_write().slice(new_range).copy_from(new_vertex_colors); const int64_t min_active_smoothing_points_num = 8; - if (self.stroke_cache_->size() >= min_active_smoothing_points_num) { - this->active_smoothing(self); - } - else { + if (self.stroke_cache_->size() < min_active_smoothing_points_num) { MutableSpan positions_slice = self.stroke_cache_->positions_for_write().slice( new_range); for (const int64_t i : new_coordinates.index_range()) { @@ -413,6 +411,8 @@ struct PaintOperationExecutor { } return; } + + this->active_smoothing(self); } void execute(PaintOperation &self, const bContext &C, const InputSample &extension_sample) -- 2.30.2 From 971d5a6ba7990d8bed2363ecc4f951918c89d712 Mon Sep 17 00:00:00 2001 From: Falk David Date: Wed, 9 Aug 2023 15:22:30 +0200 Subject: [PATCH 50/75] Fix else after return --- .../editors/grease_pencil/intern/grease_pencil_utils.cc | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/source/blender/editors/grease_pencil/intern/grease_pencil_utils.cc b/source/blender/editors/grease_pencil/intern/grease_pencil_utils.cc index 63b57c2ebe9..d3ab4ff83be 100644 --- a/source/blender/editors/grease_pencil/intern/grease_pencil_utils.cc +++ b/source/blender/editors/grease_pencil/intern/grease_pencil_utils.cc @@ -28,10 +28,8 @@ static float3 drawing_origin(const Scene *scene, const Object *object, char alig if (align_flag & GP_PROJECT_CURSOR) { return float3(scene->cursor.location); } - else { - /* Use the object location. */ - return float3(object->object_to_world[3]); - } + /* Use the object location. */ + return float3(object->object_to_world[3]); } return float3(scene->cursor.location); } -- 2.30.2 From 4cda04f7667f17fd17ee947018a47dfb4d4e47fd Mon Sep 17 00:00:00 2001 From: Falk David Date: Wed, 9 Aug 2023 15:23:36 +0200 Subject: [PATCH 51/75] Use `bke::curves::bezier::evaluate_segment` --- source/blender/blenkernel/BKE_curves.hh | 8 ++-- .../blender/blenkernel/intern/curve_bezier.cc | 40 +++++++++++++------ .../sculpt_paint/grease_pencil_paint.cc | 36 +++-------------- 3 files changed, 36 insertions(+), 48 deletions(-) diff --git a/source/blender/blenkernel/BKE_curves.hh b/source/blender/blenkernel/BKE_curves.hh index 234a2e98940..deb247177bb 100644 --- a/source/blender/blenkernel/BKE_curves.hh +++ b/source/blender/blenkernel/BKE_curves.hh @@ -643,11 +643,9 @@ void set_handle_position(const float3 &position, * points are referred to as the control points, and the middle points are the corresponding * handles. */ -void evaluate_segment(const float3 &point_0, - const float3 &point_1, - const float3 &point_2, - const float3 &point_3, - MutableSpan result); +template +void evaluate_segment( + const T &point_0, const T &point_1, const T &point_2, const T &point_3, MutableSpan result); /** * Calculate all evaluated points for the Bezier curve. diff --git a/source/blender/blenkernel/intern/curve_bezier.cc b/source/blender/blenkernel/intern/curve_bezier.cc index 5152a6e2b97..ff26350945a 100644 --- a/source/blender/blenkernel/intern/curve_bezier.cc +++ b/source/blender/blenkernel/intern/curve_bezier.cc @@ -208,25 +208,23 @@ void calculate_auto_handles(const bool cyclic, positions_right.last()); } -void evaluate_segment(const float3 &point_0, - const float3 &point_1, - const float3 &point_2, - const float3 &point_3, - MutableSpan result) +template +void evaluate_segment( + const T &point_0, const T &point_1, const T &point_2, const T &point_3, MutableSpan result) { BLI_assert(result.size() > 0); const float inv_len = 1.0f / float(result.size()); const float inv_len_squared = inv_len * inv_len; const float inv_len_cubed = inv_len_squared * inv_len; - const float3 rt1 = 3.0f * (point_1 - point_0) * inv_len; - const float3 rt2 = 3.0f * (point_0 - 2.0f * point_1 + point_2) * inv_len_squared; - const float3 rt3 = (point_3 - point_0 + 3.0f * (point_1 - point_2)) * inv_len_cubed; + const T rt1 = 3.0f * (point_1 - point_0) * inv_len; + const T rt2 = 3.0f * (point_0 - 2.0f * point_1 + point_2) * inv_len_squared; + const T rt3 = (point_3 - point_0 + 3.0f * (point_1 - point_2)) * inv_len_cubed; - float3 q0 = point_0; - float3 q1 = rt1 + rt2 + rt3; - float3 q2 = 2.0f * rt2 + 6.0f * rt3; - float3 q3 = 6.0f * rt3; + T q0 = point_0; + T q1 = rt1 + rt2 + rt3; + T q2 = 2.0f * rt2 + 6.0f * rt3; + T q3 = 6.0f * rt3; for (const int i : result.index_range()) { result[i] = q0; q0 += q1; @@ -234,6 +232,24 @@ void evaluate_segment(const float3 &point_0, q2 += q3; } } +template<> +void evaluate_segment(const float3 &point_0, + const float3 &point_1, + const float3 &point_2, + const float3 &point_3, + MutableSpan result) +{ + evaluate_segment(point_0, point_1, point_2, point_3, result); +} +template<> +void evaluate_segment(const float2 &point_0, + const float2 &point_1, + const float2 &point_2, + const float2 &point_3, + MutableSpan result) +{ + evaluate_segment(point_0, point_1, point_2, point_3, result); +} void calculate_evaluated_positions(const Span positions, const Span handles_left, diff --git a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc index 5001c3400b4..41e71eb867e 100644 --- a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc +++ b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc @@ -30,32 +30,6 @@ static constexpr float POINT_RESAMPLE_MIN_DISTANCE_PX = 10.0f; static constexpr int64_t STOKE_CACHE_ALLOCATION_CHUNK_SIZE = 1024; -/** Forward differencing of a bezier curve segment (q0,q1,q2,q3). Steps are written to \a r_p. - * Implementation based on #BKE_curve_forward_diff_bezier. */ -static void forward_diff_bezier_2d( - float2 q0, float2 q1, float2 q2, float2 q3, MutableSpan r_p) -{ - float f = static_cast(r_p.size()); - float2 rt0 = q0; - float2 rt1 = 3.0f * (q1 - q0) / f; - f = f * f; - float2 rt2 = 3.0f * (q0 - 2.0f * q1 + q2) / f; - f = f * static_cast(r_p.size()); - float2 rt3 = (q3 - q0 + 3.0f * (q1 - q2)) / f; - - q0 = rt0; - q1 = rt1 + rt2 + rt3; - q2 = 2 * rt2 + 6 * rt3; - q3 = 6 * rt3; - - for (float2 &r : r_p) { - r = q0; - q0 += q1; - q1 += q2; - q2 += q3; - } -} - /** Sample a bezier curve at a fixed resolution and return the sampled points in an array. */ static Array sample_curve_2d(Span curve_points, const int64_t resolution) { @@ -68,11 +42,11 @@ static Array sample_curve_2d(Span curve_points, const int64_t re const Span curve_segments = curve_points.drop_front(1).drop_back(1); for (const int64_t segment_i : IndexRange(num_segments)) { IndexRange segment_range(segment_i * resolution, resolution); - forward_diff_bezier_2d(curve_segments[segment_i * 3 + 0], - curve_segments[segment_i * 3 + 1], - curve_segments[segment_i * 3 + 2], - curve_segments[segment_i * 3 + 3], - points.as_mutable_span().slice(segment_range)); + bke::curves::bezier::evaluate_segment(curve_segments[segment_i * 3 + 0], + curve_segments[segment_i * 3 + 1], + curve_segments[segment_i * 3 + 2], + curve_segments[segment_i * 3 + 3], + points.as_mutable_span().slice(segment_range)); } return points; } -- 2.30.2 From f5706fc3f2666a6b78091f8ed45ed64d8bb32063 Mon Sep 17 00:00:00 2001 From: Falk David Date: Wed, 9 Aug 2023 15:23:59 +0200 Subject: [PATCH 52/75] Fix typos --- .../editors/grease_pencil/intern/grease_pencil_edit.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/editors/grease_pencil/intern/grease_pencil_edit.cc b/source/blender/editors/grease_pencil/intern/grease_pencil_edit.cc index c1ad3761ad0..e04175b80f8 100644 --- a/source/blender/editors/grease_pencil/intern/grease_pencil_edit.cc +++ b/source/blender/editors/grease_pencil/intern/grease_pencil_edit.cc @@ -476,8 +476,8 @@ static int grease_pencil_stroke_simplify_exec(bContext *C, wmOperator *op) [positions, radii](int64_t first_index, int64_t last_index, int64_t index) { const float dist_position = dist_to_line_v3( positions[index], positions[first_index], positions[last_index]); - /* We devide the distance by 2000.0f to convert from "pixels" to an actual distance. - * For some reason, grease pencil storkes the thickness of strokes in pixels rather + /* We divide the distance by 2000.0f to convert from "pixels" to an actual distance. + * For some reason, grease pencil strokes the thickness of strokes in pixels rather * than object space distance. */ const float dist_radii = dist_to_interpolated(positions[index], positions[first_index], -- 2.30.2 From 79a64dbe431911cc50dc5d89f5109372cbecf7a0 Mon Sep 17 00:00:00 2001 From: Falk David Date: Wed, 9 Aug 2023 15:25:17 +0200 Subject: [PATCH 53/75] Fix another else after return --- .../editors/sculpt_paint/grease_pencil_draw_ops.cc | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/source/blender/editors/sculpt_paint/grease_pencil_draw_ops.cc b/source/blender/editors/sculpt_paint/grease_pencil_draw_ops.cc index 3be475880f9..6dfe1a0bae8 100644 --- a/source/blender/editors/sculpt_paint/grease_pencil_draw_ops.cc +++ b/source/blender/editors/sculpt_paint/grease_pencil_draw_ops.cc @@ -146,11 +146,9 @@ static int grease_pencil_stroke_invoke(bContext *C, wmOperator *op, const wmEven BKE_report(op->reports, RPT_ERROR, "No Grease Pencil frame to draw on"); return OPERATOR_CANCELLED; } - else { - /* Create a new blank frame here. */ - grease_pencil.insert_blank_frame( - *grease_pencil.get_active_layer_for_write(), current_frame, 1, BEZT_KEYTYPE_KEYFRAME); - } + /* Create a new blank frame here. */ + grease_pencil.insert_blank_frame( + *grease_pencil.get_active_layer_for_write(), current_frame, 1, BEZT_KEYTYPE_KEYFRAME); } op->customdata = paint_stroke_new(C, -- 2.30.2 From 26ce1e2d714b10e23486ac423c1659bfe6b43f57 Mon Sep 17 00:00:00 2001 From: Falk David Date: Wed, 9 Aug 2023 15:26:14 +0200 Subject: [PATCH 54/75] Remove static_cast --- .../blender/editors/grease_pencil/intern/grease_pencil_utils.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/grease_pencil/intern/grease_pencil_utils.cc b/source/blender/editors/grease_pencil/intern/grease_pencil_utils.cc index d3ab4ff83be..0970c314ba2 100644 --- a/source/blender/editors/grease_pencil/intern/grease_pencil_utils.cc +++ b/source/blender/editors/grease_pencil/intern/grease_pencil_utils.cc @@ -60,7 +60,7 @@ float brush_radius_world_space(bContext &C, int x, int y) /* Use an (arbitrary) screen space offset in the x direction to measure the size. */ const int x_offest = 64; - const float brush_size = static_cast(BKE_brush_size_get(scene, brush)); + const float brush_size = float(BKE_brush_size_get(scene, brush)); /* Get two 3d coordinates to measure the distance from. */ const float2 screen1(x, y); -- 2.30.2 From 54a3ae176ecdb9f9a44e4306124d8cde5c9a0ffa Mon Sep 17 00:00:00 2001 From: Falk David Date: Wed, 9 Aug 2023 15:29:18 +0200 Subject: [PATCH 55/75] Remove unnecessary namespace resolution --- source/blender/editors/sculpt_paint/grease_pencil_paint.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc index 41e71eb867e..b5d24d7b47a 100644 --- a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc +++ b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc @@ -229,7 +229,7 @@ struct PaintOperationExecutor { const float max_radius_px = 30.0f; const int64_t max_samples = 64; const float angle_threshold = 0.6f; - IndexMask corner_mask = blender::ed::greasepencil::polyline_detect_corners( + IndexMask corner_mask = ed::greasepencil::polyline_detect_corners( screen_space_coords_smooth_slice, min_radius_px, max_radius_px, @@ -249,7 +249,7 @@ struct PaintOperationExecutor { /* Curve fitting. The output will be a set of handles (float2 triplets) in a flat array. */ const float max_error_threshold_px = 5.0f; - Array curve_points = blender::ed::greasepencil::fit_curve_polyline_2d( + Array curve_points = ed::greasepencil::fit_curve_polyline_2d( coords_pre_blur, max_error_threshold_px * settings_->active_smooth, corner_mask); /* Sampling the curve at a fixed resolution. */ -- 2.30.2 From 35e06072644c62f7d1903efb7ecf83ef3f3b2cf6 Mon Sep 17 00:00:00 2001 From: Falk David Date: Wed, 16 Aug 2023 17:00:10 +0200 Subject: [PATCH 56/75] Fix include after rebase --- source/blender/editors/sculpt_paint/grease_pencil_paint.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc index b5d24d7b47a..d7f0e317492 100644 --- a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc +++ b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc @@ -11,7 +11,7 @@ #include "BKE_material.h" #include "BKE_scene.h" -#include "BLI_math_base.hh" +#include "BLI_math_geom.h" #include "DEG_depsgraph_query.h" -- 2.30.2 From af7cdf2247b1ded7b09e1a49059e12ec0d449dde Mon Sep 17 00:00:00 2001 From: Falk David Date: Wed, 16 Aug 2023 17:00:51 +0200 Subject: [PATCH 57/75] Consider additive drawing when autokey is on --- .../sculpt_paint/grease_pencil_draw_ops.cc | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/source/blender/editors/sculpt_paint/grease_pencil_draw_ops.cc b/source/blender/editors/sculpt_paint/grease_pencil_draw_ops.cc index 6dfe1a0bae8..3348f27f1ec 100644 --- a/source/blender/editors/sculpt_paint/grease_pencil_draw_ops.cc +++ b/source/blender/editors/sculpt_paint/grease_pencil_draw_ops.cc @@ -140,15 +140,23 @@ static int grease_pencil_stroke_invoke(bContext *C, wmOperator *op, const wmEven } const int current_frame = scene->r.cfra; - const bke::greasepencil::Layer &active_layer = *grease_pencil.get_active_layer(); - if (active_layer.drawing_index_at(current_frame) == -1) { + if (grease_pencil.get_active_layer()->drawing_index_at(current_frame) == -1) { if (!IS_AUTOKEY_ON(scene)) { BKE_report(op->reports, RPT_ERROR, "No Grease Pencil frame to draw on"); return OPERATOR_CANCELLED; } - /* Create a new blank frame here. */ - grease_pencil.insert_blank_frame( - *grease_pencil.get_active_layer_for_write(), current_frame, 1, BEZT_KEYTYPE_KEYFRAME); + const ToolSettings *ts = CTX_data_tool_settings(C); + bke::greasepencil::Layer &active_layer = *grease_pencil.get_active_layer_for_write(); + if ((ts->gpencil_flags & GP_TOOL_FLAG_RETAIN_LAST) != 0) { + /* For additive drawing, we duplicate the frame that's currently visible and insert it at the + * current frame. */ + grease_pencil.insert_duplicate_frame( + active_layer, active_layer.frame_key_at(current_frame), current_frame, false); + } + else { + /* Otherwise we just insert a blank keyframe. */ + grease_pencil.insert_blank_frame(active_layer, current_frame, 0, BEZT_KEYTYPE_KEYFRAME); + } } op->customdata = paint_stroke_new(C, -- 2.30.2 From b4891dba8aae0246a8afb40317175e5ba0d7ccd6 Mon Sep 17 00:00:00 2001 From: Falk David Date: Wed, 13 Sep 2023 16:18:05 +0200 Subject: [PATCH 58/75] Fix crash --- source/blender/blenkernel/intern/curve_bezier.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/source/blender/blenkernel/intern/curve_bezier.cc b/source/blender/blenkernel/intern/curve_bezier.cc index ff26350945a..3223944f819 100644 --- a/source/blender/blenkernel/intern/curve_bezier.cc +++ b/source/blender/blenkernel/intern/curve_bezier.cc @@ -209,7 +209,7 @@ void calculate_auto_handles(const bool cyclic, } template -void evaluate_segment( +void evaluate_segment_ex( const T &point_0, const T &point_1, const T &point_2, const T &point_3, MutableSpan result) { BLI_assert(result.size() > 0); @@ -239,7 +239,7 @@ void evaluate_segment(const float3 &point_0, const float3 &point_3, MutableSpan result) { - evaluate_segment(point_0, point_1, point_2, point_3, result); + evaluate_segment_ex(point_0, point_1, point_2, point_3, result); } template<> void evaluate_segment(const float2 &point_0, @@ -248,7 +248,7 @@ void evaluate_segment(const float2 &point_0, const float2 &point_3, MutableSpan result) { - evaluate_segment(point_0, point_1, point_2, point_3, result); + evaluate_segment_ex(point_0, point_1, point_2, point_3, result); } void calculate_evaluated_positions(const Span positions, -- 2.30.2 From 8e3c071bd4724b2436af60f4dd508460d007d7ce Mon Sep 17 00:00:00 2001 From: Falk David Date: Wed, 13 Sep 2023 16:20:23 +0200 Subject: [PATCH 59/75] Fix eraser UI after rebase --- scripts/startup/bl_ui/space_toolsystem_toolbar.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/scripts/startup/bl_ui/space_toolsystem_toolbar.py b/scripts/startup/bl_ui/space_toolsystem_toolbar.py index ad2fb54ebfe..e3bb4640df4 100644 --- a/scripts/startup/bl_ui/space_toolsystem_toolbar.py +++ b/scripts/startup/bl_ui/space_toolsystem_toolbar.py @@ -1744,15 +1744,6 @@ class _defs_paint_grease_pencil: @ToolDef.from_fn def erase(): - def draw_settings(context, layout, _tool): - paint = context.tool_settings.gpencil_paint - brush = paint.brush - if not brush: - return - layout.prop(brush.gpencil_settings, "eraser_mode", expand=True) - if brush.gpencil_settings.eraser_mode == 'HARD': - layout.prop(brush.gpencil_settings, "use_keep_caps_eraser") - layout.prop(brush.gpencil_settings, "use_active_layer_only") return dict( idname="builtin_brush.Erase", label="Erase", -- 2.30.2 From 819c6475dc9cedb7697a0f5b16a4e1dd02acce6e Mon Sep 17 00:00:00 2001 From: Falk David Date: Fri, 29 Sep 2023 12:53:22 +0200 Subject: [PATCH 60/75] Expose vertex colors as attribute on drawings --- source/blender/blenkernel/BKE_grease_pencil.hh | 6 ++++++ .../blender/blenkernel/intern/grease_pencil.cc | 17 ++++++++++++++++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/source/blender/blenkernel/BKE_grease_pencil.hh b/source/blender/blenkernel/BKE_grease_pencil.hh index dd71f2ef880..51d78ddb4f5 100644 --- a/source/blender/blenkernel/BKE_grease_pencil.hh +++ b/source/blender/blenkernel/BKE_grease_pencil.hh @@ -119,6 +119,12 @@ class Drawing : public ::GreasePencilDrawing { VArray opacities() const; MutableSpan opacities_for_write(); + /** + * Vertex colors of the points. Default is black. + */ + VArray vertex_colors() const; + MutableSpan vertex_colors_for_write(); + /** * Add a user for this drawing. When a drawing has multiple users, both users are allowed to * modify this drawings data. diff --git a/source/blender/blenkernel/intern/grease_pencil.cc b/source/blender/blenkernel/intern/grease_pencil.cc index 3c686e50db1..76a64384fc7 100644 --- a/source/blender/blenkernel/intern/grease_pencil.cc +++ b/source/blender/blenkernel/intern/grease_pencil.cc @@ -224,8 +224,9 @@ IDTypeInfo IDType_ID_GP = { namespace blender::bke::greasepencil { -static const std::string ATTR_OPACITY = "opacity"; static const std::string ATTR_RADIUS = "radius"; +static const std::string ATTR_OPACITY = "opacity"; +static const std::string ATTR_VERTEX_COLOR = "vertex_color"; /* Curves attributes getters */ static int domain_num(const CurvesGeometry &curves, const eAttrDomain domain) @@ -463,6 +464,20 @@ MutableSpan Drawing::opacities_for_write() this->strokes_for_write(), ATTR_DOMAIN_POINT, ATTR_OPACITY, 1.0f); } +VArray Drawing::vertex_colors() const +{ + return *this->strokes().attributes().lookup_or_default( + ATTR_VERTEX_COLOR, ATTR_DOMAIN_POINT, ColorGeometry4f(0.0f, 0.0f, 0.0f, 0.0f)); +} + +MutableSpan Drawing::vertex_colors_for_write() +{ + return get_mutable_attribute(this->strokes_for_write(), + ATTR_DOMAIN_POINT, + ATTR_VERTEX_COLOR, + ColorGeometry4f(0.0f, 0.0f, 0.0f, 0.0f)); +} + void Drawing::tag_positions_changed() { this->strokes_for_write().tag_positions_changed(); -- 2.30.2 From cf3935636f7cec4b29ff66791b060f3acc34039d Mon Sep 17 00:00:00 2001 From: Falk David Date: Fri, 29 Sep 2023 12:53:47 +0200 Subject: [PATCH 61/75] Cleanup --- .../editors/grease_pencil/intern/grease_pencil_geom.cc | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/source/blender/editors/grease_pencil/intern/grease_pencil_geom.cc b/source/blender/editors/grease_pencil/intern/grease_pencil_geom.cc index e4373efc524..831d5d17f45 100644 --- a/source/blender/editors/grease_pencil/intern/grease_pencil_geom.cc +++ b/source/blender/editors/grease_pencil/intern/grease_pencil_geom.cc @@ -49,11 +49,11 @@ int64_t ramer_douglas_peucker_simplify( if (sub_range.size() < 3) { continue; } - - /* Compute the maximum distance and the corresponding distance. */ + const IndexRange inside_range = sub_range.drop_front(1).drop_back(1); + /* Compute the maximum distance and the corresponding index. */ float max_dist = -1.0f; int max_index = -1; - for (const int64_t index : sub_range.drop_front(1).drop_back(1)) { + for (const int64_t index : inside_range) { const float dist = dist_function(sub_range.first(), sub_range.last(), index); if (dist > max_dist) { max_dist = dist; @@ -69,7 +69,6 @@ int64_t ramer_douglas_peucker_simplify( } else { /* Points in `sub_range` are inside the epsilon-sized strip. Mark them to be deleted. */ - const IndexRange inside_range = sub_range.drop_front(1).drop_back(1); total_points_to_remove += inside_range.size(); points_to_delete.slice(inside_range).fill(true); } -- 2.30.2 From 3dfe779cd126bdaf09f9a4d8fb28f9e1ab0ab544 Mon Sep 17 00:00:00 2001 From: Falk David Date: Fri, 29 Sep 2023 13:01:25 +0200 Subject: [PATCH 62/75] Remove `StrokeCache` Removes the `StrokeCache` and all it's depending code. The drawing operator now draws directly into the `CurvesGeometry` on the drawing. --- .../blender/blenkernel/BKE_grease_pencil.hh | 48 --- .../blenkernel/intern/grease_pencil.cc | 98 ----- source/blender/blenlib/BLI_virtual_array.hh | 14 + .../intern/draw_cache_impl_grease_pencil.cc | 48 --- .../sculpt_paint/grease_pencil_paint.cc | 340 ++++++++---------- .../editors/sculpt_paint/paint_cursor.cc | 6 - 6 files changed, 154 insertions(+), 400 deletions(-) diff --git a/source/blender/blenkernel/BKE_grease_pencil.hh b/source/blender/blenkernel/BKE_grease_pencil.hh index 51d78ddb4f5..00b6184b6f1 100644 --- a/source/blender/blenkernel/BKE_grease_pencil.hh +++ b/source/blender/blenkernel/BKE_grease_pencil.hh @@ -34,47 +34,6 @@ namespace blender::bke { namespace greasepencil { -/** - * Stroke cache for a stroke that is currently being drawn. - */ -class StrokeCache { - public: - void clear(); - void reserve(int64_t new_capacity); - void resize(int64_t new_size); - - int64_t size() const; - - Span positions() const; - MutableSpan positions_for_write(); - - Span radii() const; - MutableSpan radii_for_write(); - - Span opacities() const; - MutableSpan opacities_for_write(); - - Span vertex_colors() const; - MutableSpan vertex_colors_for_write(); - - int64_t triangles_num() const; - Span triangles() const; - - int material_index() const; - void set_material_index(int new_material_index); - - private: - /* Stroke cache attributes. */ - Vector positions_; - Vector radii_; - Vector opacities_; - Vector vertex_colors_; - int64_t size_ = 0; - - Vector triangles_; - int material_index_ = 0; -}; - class DrawingRuntime { public: /** @@ -686,19 +645,12 @@ class GreasePencilRuntime { * Allocated and freed by the drawing code. See `DRW_grease_pencil_batch_cache_*` functions. */ void *batch_cache = nullptr; - - /** - * A buffer for a single stroke while drawing. - */ - bke::greasepencil::StrokeCache stroke_cache; /* The frame on which the object was evaluated (only valid for evaluated object). */ int eval_frame; public: GreasePencilRuntime() {} ~GreasePencilRuntime() {} - - bool has_stroke_cache() const; }; } // namespace blender::bke diff --git a/source/blender/blenkernel/intern/grease_pencil.cc b/source/blender/blenkernel/intern/grease_pencil.cc index 76a64384fc7..f8dbff40178 100644 --- a/source/blender/blenkernel/intern/grease_pencil.cc +++ b/source/blender/blenkernel/intern/grease_pencil.cc @@ -259,93 +259,6 @@ static MutableSpan get_mutable_attribute(CurvesGeometry &curves, return span; } -void StrokeCache::clear() -{ - positions_.clear_and_shrink(); - radii_.clear_and_shrink(); - opacities_.clear_and_shrink(); - vertex_colors_.clear_and_shrink(); - triangles_.clear_and_shrink(); - material_index_ = 0; - size_ = 0; -} - -void StrokeCache::reserve(const int64_t new_capacity) -{ - positions_.reserve(new_capacity); - radii_.reserve(new_capacity); - opacities_.reserve(new_capacity); - vertex_colors_.reserve(new_capacity); -} - -void StrokeCache::resize(const int64_t new_size) -{ - positions_.resize(new_size); - radii_.resize(new_size); - opacities_.resize(new_size); - vertex_colors_.resize(new_size); - size_ = new_size; -} - -int64_t StrokeCache::size() const -{ - return size_; -} - -Span StrokeCache::positions() const -{ - return positions_.as_span(); -} -MutableSpan StrokeCache::positions_for_write() -{ - return positions_.as_mutable_span(); -} - -Span StrokeCache::radii() const -{ - return radii_.as_span(); -} -MutableSpan StrokeCache::radii_for_write() -{ - return radii_.as_mutable_span(); -} - -Span StrokeCache::opacities() const -{ - return opacities_.as_span(); -} -MutableSpan StrokeCache::opacities_for_write() -{ - return opacities_.as_mutable_span(); -} - -Span StrokeCache::vertex_colors() const -{ - return vertex_colors_.as_span(); -} -MutableSpan StrokeCache::vertex_colors_for_write() -{ - return vertex_colors_.as_mutable_span(); -} - -int64_t StrokeCache::triangles_num() const -{ - return triangles_.size(); -} -Span StrokeCache::triangles() const -{ - return triangles_.as_span(); -} - -int StrokeCache::material_index() const -{ - return material_index_; -} -void StrokeCache::set_material_index(int new_material_index) -{ - material_index_ = new_material_index; -} - Drawing::Drawing() { this->base.type = GP_DRAWING; @@ -1428,17 +1341,6 @@ void BKE_grease_pencil_batch_cache_free(GreasePencil *grease_pencil) /** \} */ -/* ------------------------------------------------------------------- */ -/** \name Grease Pencil runtime API - * \{ */ - -bool blender::bke::GreasePencilRuntime::has_stroke_cache() const -{ - return this->stroke_cache.size() > 0; -} - -/** \} */ - /* ------------------------------------------------------------------- */ /** \name Grease Pencil data-block API * \{ */ diff --git a/source/blender/blenlib/BLI_virtual_array.hh b/source/blender/blenlib/BLI_virtual_array.hh index 59ca7cd4301..3b7548f814d 100644 --- a/source/blender/blenlib/BLI_virtual_array.hh +++ b/source/blender/blenlib/BLI_virtual_array.hh @@ -647,6 +647,20 @@ template class VArrayCommon { } return impl_->size(); } + /** + * Get the first element. + */ + T first() const + { + return (*this)[0]; + } + /** + * Get the nth last element. + */ + T last(const int64_t n = 0) const + { + return (*this)[this->size() - 1 - n]; + } /** True when the size is zero or when there is no virtual array. */ bool is_empty() const diff --git a/source/blender/draw/intern/draw_cache_impl_grease_pencil.cc b/source/blender/draw/intern/draw_cache_impl_grease_pencil.cc index 389f819ee90..7d3776f6f9d 100644 --- a/source/blender/draw/intern/draw_cache_impl_grease_pencil.cc +++ b/source/blender/draw/intern/draw_cache_impl_grease_pencil.cc @@ -256,13 +256,6 @@ static void grease_pencil_geom_batch_ensure(GreasePencil &grease_pencil, int cfr tris_start_offsets_per_visible_drawing.append(std::move(tris_start_offsets)); } - if (grease_pencil.runtime->has_stroke_cache()) { - total_verts_num += 1 + grease_pencil.runtime->stroke_cache.size() + 1; - total_triangles_num += grease_pencil.runtime->stroke_cache.size() * 2; - total_triangles_num += grease_pencil.runtime->stroke_cache.triangles_num(); - /* TODO: triangles for stroke buffer. */ - } - static GPUVertFormat format_edit_points_pos = {0}; if (format_edit_points_pos.attr_len == 0) { GPU_vertformat_attr_add(&format_edit_points_pos, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); @@ -425,47 +418,6 @@ static void grease_pencil_geom_batch_ensure(GreasePencil &grease_pencil, int cfr }); } - if (grease_pencil.runtime->has_stroke_cache()) { - bke::greasepencil::StrokeCache &stroke_cache = grease_pencil.runtime->stroke_cache; - const int verts_start_offset = v_offset; - const int num_verts = 1 + stroke_cache.size() + 1; - IndexRange verts_range = IndexRange(verts_start_offset, num_verts); - MutableSpan verts_slice = verts.slice(verts_range); - MutableSpan cols_slice = cols.slice(verts_range); - const int material_nr = stroke_cache.material_index(); - - verts_slice.first().mat = -1; - for (const int i : IndexRange(stroke_cache.size())) { - const int idx = i + 1; - GreasePencilStrokeVert &s_vert = verts_slice[idx]; - GreasePencilColorVert &c_vert = cols_slice[idx]; - - copy_v3_v3(s_vert.pos, stroke_cache.positions()[i]); - s_vert.radius = stroke_cache.radii()[i]; - s_vert.opacity = stroke_cache.opacities()[i]; - s_vert.point_id = verts_range[idx]; - s_vert.stroke_id = verts_range.first(); - s_vert.mat = material_nr % GPENCIL_MATERIAL_BUFFER_LEN; - - /* TODO */ - s_vert.packed_asp_hard_rot = pack_rotation_aspect_hardness(0.0f, 1.0f, 1.0f); - /* TODO */ - s_vert.u_stroke = 0; - /* TODO */ - s_vert.uv_fill[0] = s_vert.uv_fill[1] = 0; - - copy_v4_v4(c_vert.vcol, stroke_cache.vertex_colors()[i]); - copy_v4_v4(c_vert.fcol, stroke_cache.vertex_colors()[i]); - c_vert.fcol[3] = (int(c_vert.fcol[3] * 10000.0f) * 10.0f) + 1.0f; - - int v_mat = (verts_range[idx] << GP_VERTEX_ID_SHIFT) | GP_IS_STROKE_VERTEX_BIT; - GPU_indexbuf_add_tri_verts(&ibo, v_mat + 0, v_mat + 1, v_mat + 2); - GPU_indexbuf_add_tri_verts(&ibo, v_mat + 2, v_mat + 1, v_mat + 3); - } - - verts_slice.last().mat = -1; - } - /* Mark last 2 verts as invalid. */ verts[total_verts_num + 0].mat = -1; verts[total_verts_num + 1].mat = -1; diff --git a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc index 05d5c05c06c..2c2a4173267 100644 --- a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc +++ b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc @@ -114,8 +114,6 @@ class PaintOperation : public GreasePencilStrokeOperation { Vector screen_space_smoothed_coordinates_; int64_t active_smooth_index_ = 0; - bke::greasepencil::StrokeCache *stroke_cache_; - friend struct PaintOperationExecutor; public: @@ -124,9 +122,8 @@ class PaintOperation : public GreasePencilStrokeOperation { void on_stroke_done(const bContext &C) override; private: - void simplify_stroke_cache(const float epsilon_px); - void process_stroke_end(); - void create_stroke_from_stroke_cache(bke::greasepencil::Drawing &drawing_orig); + void simplify_stroke(bke::greasepencil::Drawing &drawing, float epsilon_px); + void process_stroke_end(bke::greasepencil::Drawing &drawing); }; /** @@ -135,6 +132,7 @@ class PaintOperation : public GreasePencilStrokeOperation { */ struct PaintOperationExecutor { ARegion *region_; + GreasePencil *grease_pencil_; Brush *brush_; int brush_size_; @@ -143,10 +141,14 @@ struct PaintOperationExecutor { BrushGpencilSettings *settings_; float4 vertex_color_; + bke::greasepencil::Drawing *drawing_; + PaintOperationExecutor(const bContext &C) { Scene *scene = CTX_data_scene(&C); region_ = CTX_wm_region(&C); + Object *object = CTX_data_active_object(&C); + grease_pencil_ = static_cast(object->data); Paint *paint = &scene->toolsettings->gp_paint->paint; brush_ = BKE_paint_brush(paint); @@ -167,6 +169,16 @@ struct PaintOperationExecutor { // const bool use_vertex_color_fill = use_vertex_color && ELEM( // brush->gpencil_settings->vertex_mode, GPPAINT_MODE_STROKE, GPPAINT_MODE_BOTH); + + /* The object should have an active layer. */ + BLI_assert(grease_pencil_->has_active_layer()); + bke::greasepencil::Layer &active_layer = *grease_pencil_->get_active_layer_for_write(); + const int drawing_index = active_layer.drawing_index_at(scene->r.cfra); + + /* Drawing should exist. */ + BLI_assert(drawing_index >= 0); + drawing_ = + &reinterpret_cast(grease_pencil_->drawing(drawing_index))->wrap(); } float3 screen_space_to_object_space(float2 co) @@ -196,7 +208,9 @@ struct PaintOperationExecutor { return point; } - void process_start_sample(PaintOperation &self, const InputSample &start_sample) + void process_start_sample(PaintOperation &self, + const InputSample &start_sample, + const int material_index) { ScreenSpacePoint start_point = this->point_from_input_sample(start_sample); @@ -204,22 +218,33 @@ struct PaintOperationExecutor { self.screen_space_curve_fitted_coordinates_.append(Vector({start_point.co})); self.screen_space_smoothed_coordinates_.append(start_point.co); - self.stroke_cache_->resize(1); - self.stroke_cache_->positions_for_write().last() = screen_space_to_object_space( - start_point.co); - self.stroke_cache_->radii_for_write().last() = start_point.radius; - self.stroke_cache_->opacities_for_write().last() = start_point.opacity; - self.stroke_cache_->vertex_colors_for_write().last() = ColorGeometry4f( - start_point.vertex_color); + /* Resize the curves geometry so there is one more curve with a single point. */ + bke::CurvesGeometry &curves = drawing_->strokes_for_write(); + int num_old_points = curves.points_num(); + curves.resize(curves.points_num() + 1, curves.curves_num() + 1); + curves.offsets_for_write().last(1) = num_old_points; + + curves.positions_for_write().last() = screen_space_to_object_space(start_point.co); + drawing_->radii_for_write().last() = start_point.radius; + drawing_->opacities_for_write().last() = start_point.opacity; + drawing_->vertex_colors_for_write().last() = ColorGeometry4f(start_point.vertex_color); + + bke::MutableAttributeAccessor attributes = curves.attributes_for_write(); + bke::SpanAttributeWriter materials = attributes.lookup_or_add_for_write_span( + "material_index", ATTR_DOMAIN_CURVE); + materials.span.last() = material_index; + materials.finish(); + + curves.curve_types_for_write().last() = CURVE_TYPE_POLY; + curves.update_curve_types(); + + drawing_->tag_topology_changed(); } - void active_smoothing(PaintOperation &self) + void active_smoothing(PaintOperation &self, + const IndexRange points_range, + const IndexRange smooth_window) { - /* Active smoothing is only done on a slice of the points (from the active index to the current - * end of the stroke). */ - const int64_t smooth_window_size = self.stroke_cache_->size() - self.active_smooth_index_; - IndexRange smooth_window(self.active_smooth_index_, smooth_window_size); - Span screen_space_coords_smooth_slice = self.screen_space_coordinates_.as_span().slice( smooth_window); @@ -262,9 +287,10 @@ struct PaintOperationExecutor { MutableSpan smoothed_coordinates_slice = self.screen_space_smoothed_coordinates_.as_mutable_span().slice(smooth_window); - MutableSpan positions_slice = self.stroke_cache_->positions_for_write().slice( - smooth_window); - const float converging_threshold_px = 0.05f; + MutableSpan positions_slice = drawing_->strokes_for_write() + .positions_for_write() + .slice(points_range) + .slice(smooth_window); bool stop_counting_converged = false; int num_converged = 0; for (const int64_t i : smooth_window.index_range()) { @@ -297,8 +323,8 @@ struct PaintOperationExecutor { /* Remove all the converged points from the active window and shrink the window accordingly. */ if (num_converged > 0) { - self.active_smooth_index_ = math::min(self.active_smooth_index_ + num_converged, - self.stroke_cache_->size() - 1); + self.active_smooth_index_ = math::min(self.active_smooth_index_ + int64_t(num_converged), + int64_t(drawing_->strokes().points_num() - 1)); if (self.screen_space_curve_fitted_coordinates_.size() - num_converged > 0) { self.screen_space_curve_fitted_coordinates_.remove(0, num_converged); } @@ -317,20 +343,24 @@ struct PaintOperationExecutor { #endif } - void process_extension_sample(PaintOperation &self, const InputSample &extension_sample) + void process_extension_sample(PaintOperation &self, + const InputSample &extension_sample, + const int curve_index) { ScreenSpacePoint point = this->point_from_input_sample(extension_sample); + bke::CurvesGeometry &curves = drawing_->strokes_for_write(); + float2 prev_co = self.screen_space_coordinates_.last(); - float prev_radius = self.stroke_cache_->radii().last(); - float prev_opacity = self.stroke_cache_->opacities().last(); - ColorGeometry4f prev_vertex_color = self.stroke_cache_->vertex_colors().last(); + float prev_radius = drawing_->radii().last(); + float prev_opacity = drawing_->opacities().last(); + ColorGeometry4f prev_vertex_color = drawing_->vertex_colors().last(); /* Overwrite last point if it's very close. */ if (math::distance(point.co, prev_co) < POINT_OVERRIDE_THRESHOLD_PX) { - self.stroke_cache_->positions_for_write().last() = screen_space_to_object_space(point.co); - self.stroke_cache_->radii_for_write().last() = math::max(point.radius, prev_radius); - self.stroke_cache_->opacities_for_write().last() = math::max(point.opacity, prev_opacity); + curves.positions_for_write().last() = screen_space_to_object_space(point.co); + drawing_->radii_for_write().last() = math::max(point.radius, prev_radius); + drawing_->opacities_for_write().last() = math::max(point.opacity, prev_opacity); return; } @@ -345,7 +375,7 @@ struct PaintOperationExecutor { } /* Subdivide stroke in new_range. */ - IndexRange new_range(self.stroke_cache_->size(), new_points_num); + IndexRange new_range(curves.points_num(), new_points_num); Array new_coordinates(new_points_num); Array new_radii(new_points_num); Array new_opacities(new_points_num); @@ -369,52 +399,43 @@ struct PaintOperationExecutor { } /* Resize the stroke cache. */ - self.stroke_cache_->resize(self.stroke_cache_->size() + new_points_num); + curves.resize(curves.points_num() + new_points_num, curves.curves_num()); + curves.offsets_for_write().last() = curves.points_num(); /* Write new data to the attributes. */ - self.stroke_cache_->radii_for_write().slice(new_range).copy_from(new_radii); - self.stroke_cache_->opacities_for_write().slice(new_range).copy_from(new_opacities); - self.stroke_cache_->vertex_colors_for_write().slice(new_range).copy_from(new_vertex_colors); + drawing_->radii_for_write().slice(new_range).copy_from(new_radii); + drawing_->opacities_for_write().slice(new_range).copy_from(new_opacities); + drawing_->vertex_colors_for_write().slice(new_range).copy_from(new_vertex_colors); const int64_t min_active_smoothing_points_num = 8; - if (self.stroke_cache_->size() < min_active_smoothing_points_num) { - MutableSpan positions_slice = self.stroke_cache_->positions_for_write().slice( - new_range); + const IndexRange points_range = curves.points_by_curve()[curve_index]; + if (points_range.size() < min_active_smoothing_points_num) { + MutableSpan positions_slice = curves.positions_for_write().slice(new_range); for (const int64_t i : new_coordinates.index_range()) { positions_slice[i] = screen_space_to_object_space(new_coordinates[i]); } return; } - this->active_smoothing(self); + const IndexRange smooth_window(self.active_smooth_index_, + points_range.size() - self.active_smooth_index_); + this->active_smoothing(self, points_range, smooth_window); } - void execute(PaintOperation &self, const bContext &C, const InputSample &extension_sample) + void execute(PaintOperation &self, const InputSample &extension_sample) { - Depsgraph *depsgraph = CTX_data_depsgraph_pointer(&C); - Object *object = CTX_data_active_object(&C); - Object *object_eval = DEG_get_evaluated_object(depsgraph, object); - GreasePencil *grease_pencil = static_cast(object_eval->data); - - this->process_extension_sample(self, extension_sample); - - BKE_grease_pencil_batch_cache_dirty_tag(grease_pencil, BKE_GREASEPENCIL_BATCH_DIRTY_ALL); + /* New curve was created in process_start_sample.*/ + const int curve_index = drawing_->strokes().curves_range().last(); + this->process_extension_sample(self, extension_sample, curve_index); + drawing_->tag_topology_changed(); } }; void PaintOperation::on_stroke_begin(const bContext &C, const InputSample &start_sample) { - Depsgraph *depsgraph = CTX_data_depsgraph_pointer(&C); Scene *scene = CTX_data_scene(&C); Object *object = CTX_data_active_object(&C); - Object *object_eval = DEG_get_evaluated_object(depsgraph, object); - /** - * Note: We write to the evaluated object here, so that the additional copy from orig -> eval - * is not needed for every update. After the stroke is done, the result is written to the - * original object. - */ - GreasePencil *grease_pencil = static_cast(object_eval->data); - stroke_cache_ = &grease_pencil->runtime->stroke_cache; + GreasePencil *grease_pencil = static_cast(object->data); Paint *paint = &scene->toolsettings->gp_paint->paint; Brush *brush = BKE_paint_brush(paint); @@ -431,18 +452,25 @@ void PaintOperation::on_stroke_begin(const bContext &C, const InputSample &start Material *material = BKE_grease_pencil_object_material_ensure_from_active_input_brush( CTX_data_main(&C), object, brush); - stroke_cache_->set_material_index(BKE_grease_pencil_object_material_index_get(object, material)); - - stroke_cache_->reserve(STOKE_CACHE_ALLOCATION_CHUNK_SIZE); + const int material_index = BKE_grease_pencil_object_material_index_get(object, material); PaintOperationExecutor executor{C}; - executor.process_start_sample(*this, start_sample); + executor.process_start_sample(*this, start_sample, material_index); + + DEG_id_tag_update(&grease_pencil->id, ID_RECALC_GEOMETRY); + WM_event_add_notifier(&C, NC_GEOM | ND_DATA, grease_pencil); } void PaintOperation::on_stroke_extended(const bContext &C, const InputSample &extension_sample) { + Object *object = CTX_data_active_object(&C); + GreasePencil *grease_pencil = static_cast(object->data); + PaintOperationExecutor executor{C}; - executor.execute(*this, C, extension_sample); + executor.execute(*this, extension_sample); + + DEG_id_tag_update(&grease_pencil->id, ID_RECALC_GEOMETRY); + WM_event_add_notifier(&C, NC_GEOM | ND_DATA, grease_pencil); } static float dist_to_interpolated_2d( @@ -458,145 +486,61 @@ static float dist_to_interpolated_2d( return 0.0f; } -void PaintOperation::simplify_stroke_cache(const float epsilon_px) +void PaintOperation::simplify_stroke(bke::greasepencil::Drawing &drawing, const float epsilon_px) { - const Span positions = stroke_cache_->positions(); - const Span radii = stroke_cache_->radii(); - const Span opacities = stroke_cache_->opacities(); - const Span vertex_colors = stroke_cache_->vertex_colors(); + const int stroke_index = drawing.strokes().curves_range().last(); + const IndexRange points_range = drawing.strokes().points_by_curve()[stroke_index]; + bke::CurvesGeometry &curves = drawing.strokes_for_write(); + const VArray radii = drawing.radii(); /* Distance function for `ramer_douglas_peucker_simplify`. */ const Span positions_2d = this->screen_space_smoothed_coordinates_.as_span(); - const auto dist_function = [positions_2d, - radii](int64_t first_index, int64_t last_index, int64_t index) { - const float dist_position_px = dist_to_line_segment_v2( - positions_2d[index], positions_2d[first_index], positions_2d[last_index]); - const float dist_radii_px = dist_to_interpolated_2d(positions_2d[index], - positions_2d[first_index], - positions_2d[last_index], - radii[index], - radii[first_index], - radii[last_index]); - return math::max(dist_position_px, dist_radii_px); - }; + const auto dist_function = + [points_range, positions_2d, radii](int64_t first_index, int64_t last_index, int64_t index) { + /* 2D coordinates are only stored for the current stroke, so offset the indices. */ + const float dist_position_px = dist_to_line_segment_v2( + positions_2d[index - points_range.first()], + positions_2d[first_index - points_range.first()], + positions_2d[last_index - points_range.first()]); + const float dist_radii_px = dist_to_interpolated_2d( + positions_2d[index - points_range.first()], + positions_2d[first_index - points_range.first()], + positions_2d[last_index - points_range.first()], + radii[index], + radii[first_index], + radii[last_index]); + return math::max(dist_position_px, dist_radii_px); + }; - Array points_to_delete(stroke_cache_->size(), false); - int64_t total_points_to_remove = ed::greasepencil::ramer_douglas_peucker_simplify( - IndexRange(stroke_cache_->size()), - epsilon_px, - dist_function, - points_to_delete.as_mutable_span()); + Array points_to_delete(curves.points_num(), false); + int64_t total_points_to_delete = ed::greasepencil::ramer_douglas_peucker_simplify( + points_range, epsilon_px, dist_function, points_to_delete.as_mutable_span()); - int64_t new_size = stroke_cache_->size() - total_points_to_remove; - BLI_assert(new_size > 0); - - Array old_indices(new_size); - int64_t i = 0; - for (const int64_t old_index : points_to_delete.index_range()) { - if (points_to_delete[old_index] == false) { - old_indices[i] = old_index; - i++; - } + if (total_points_to_delete > 0) { + IndexMaskMemory memory; + curves.remove_points(IndexMask::from_bools(points_to_delete, memory)); } - - Array new_positions(new_size); - Array new_radii(new_size); - Array new_opacities(new_size); - Array new_vertex_colors(new_size); - for (const int64_t index : IndexRange(new_size)) { - new_positions[index] = positions[old_indices[index]]; - new_radii[index] = radii[old_indices[index]]; - new_opacities[index] = opacities[old_indices[index]]; - new_vertex_colors[index] = vertex_colors[old_indices[index]]; - } - stroke_cache_->resize(new_size); - - stroke_cache_->positions_for_write().copy_from(new_positions); - stroke_cache_->radii_for_write().copy_from(new_radii); - stroke_cache_->opacities_for_write().copy_from(new_opacities); - stroke_cache_->vertex_colors_for_write().copy_from(new_vertex_colors); - - // stroke_cache_->triangles.clear_and_shrink(); } -void PaintOperation::process_stroke_end() +void PaintOperation::process_stroke_end(bke::greasepencil::Drawing &drawing) { + const int stroke_index = drawing.strokes().curves_range().last(); + const IndexRange points_range = drawing.strokes().points_by_curve()[stroke_index]; + bke::CurvesGeometry &curves = drawing.strokes_for_write(); /* Remove points at the end that have a radius close to 0. */ int64_t points_to_remove = 0; - for (int64_t index = this->stroke_cache_->size() - 1; index >= 0; index--) { - if (this->stroke_cache_->radii()[index] < 1e-5f) { + for (int64_t index = points_range.last(); index >= points_range.first(); index--) { + if (drawing.radii()[index] < 1e-5f) { points_to_remove++; } else { break; } } - stroke_cache_->resize(math::max(stroke_cache_->size() - points_to_remove, int64_t(0))); -} - -void PaintOperation::create_stroke_from_stroke_cache(bke::greasepencil::Drawing &drawing_orig) -{ - using namespace blender::bke; - /* Create a new stroke from the stroke buffer. */ - CurvesGeometry &curves = drawing_orig.strokes_for_write(); - - const int num_old_curves = curves.curves_num(); - const int num_old_points = curves.points_num(); - curves.resize(num_old_points + stroke_cache_->size(), num_old_curves + 1); - - curves.offsets_for_write()[num_old_curves] = num_old_points; - curves.offsets_for_write()[num_old_curves + 1] = num_old_points + stroke_cache_->size(); - - const OffsetIndices points_by_curve = curves.points_by_curve(); - const IndexRange new_points_range = points_by_curve[curves.curves_num() - 1]; - const IndexRange new_curves_range = IndexRange(num_old_curves, 1); - - /* Set position, radius and opacity attribute. */ - bke::MutableAttributeAccessor attributes = curves.attributes_for_write(); - MutableSpan positions = curves.positions_for_write(); - MutableSpan radii = drawing_orig.radii_for_write(); - MutableSpan opacities = drawing_orig.opacities_for_write(); - SpanAttributeWriter vertex_colors = - attributes.lookup_or_add_for_write_span( - "vertex_color", - ATTR_DOMAIN_POINT, - AttributeInitVArray( - VArray::ForSingle(ColorGeometry4f(0.0f, 0.0f, 0.0f, 0.0f), - attributes.domain_size(ATTR_DOMAIN_POINT)))); - positions.slice(new_points_range).copy_from(stroke_cache_->positions()); - radii.slice(new_points_range).copy_from(stroke_cache_->radii()); - opacities.slice(new_points_range).copy_from(stroke_cache_->opacities()); - vertex_colors.span.slice(new_points_range).copy_from(stroke_cache_->vertex_colors()); - - SpanAttributeWriter materials = attributes.lookup_or_add_for_write_span( - "material_index", ATTR_DOMAIN_CURVE); - materials.span.slice(new_curves_range).fill(stroke_cache_->material_index()); - - /* Set curve_type attribute. */ - curves.fill_curve_types(new_curves_range, CURVE_TYPE_POLY); - - /* Explicitly set all other attributes besides those processed above to default values. */ - Set attributes_to_skip{ - {"position", "radius", "opacity", "vertex_color", "material_index", "curve_type"}}; - attributes.for_all( - [&](const bke::AttributeIDRef &id, const bke::AttributeMetaData /*meta_data*/) { - if (attributes_to_skip.contains(id.name())) { - return true; - } - bke::GSpanAttributeWriter attribute = attributes.lookup_for_write_span(id); - const CPPType &type = attribute.span.type(); - GMutableSpan new_data = attribute.span.slice( - attribute.domain == ATTR_DOMAIN_POINT ? new_points_range : new_curves_range); - type.fill_assign_n(type.default_value(), new_data.data(), new_data.size()); - attribute.finish(); - return true; - }); - - vertex_colors.finish(); - materials.finish(); - - drawing_orig.tag_positions_changed(); - stroke_cache_->clear(); + if (points_to_remove > 0) { + curves.resize(curves.points_num() - points_to_remove, curves.curves_num()); + curves.offsets_for_write().last() = curves.points_num(); + } } void PaintOperation::on_stroke_done(const bContext &C) @@ -606,24 +550,20 @@ void PaintOperation::on_stroke_done(const bContext &C) Object *object = CTX_data_active_object(&C); GreasePencil &grease_pencil = *static_cast(object->data); - /* No stroke to create, return. */ - if (stroke_cache_->size() == 0) { - return; - } + /* Grease Pencil should have an active layer. */ + BLI_assert(grease_pencil.has_active_layer()); + bke::greasepencil::Layer &active_layer = *grease_pencil.get_active_layer_for_write(); + const int drawing_index = active_layer.drawing_index_at(scene->r.cfra); + + /* Drawing should exist. */ + BLI_assert(drawing_index >= 0); + bke::greasepencil::Drawing &drawing = + reinterpret_cast(grease_pencil.drawing(drawing_index))->wrap(); const float simplifiy_threashold_px = 0.5f; - this->simplify_stroke_cache(simplifiy_threashold_px); - - this->process_stroke_end(); - - /* The object should have an active layer. */ - BLI_assert(grease_pencil.has_active_layer()); - const bke::greasepencil::Layer &active_layer_orig = *grease_pencil.get_active_layer(); - const int index_orig = active_layer_orig.drawing_index_at(scene->r.cfra); - - bke::greasepencil::Drawing &drawing_orig = - reinterpret_cast(grease_pencil.drawings()[index_orig])->wrap(); - this->create_stroke_from_stroke_cache(drawing_orig); + this->simplify_stroke(drawing, simplifiy_threashold_px); + this->process_stroke_end(drawing); + drawing.tag_topology_changed(); DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY); WM_main_add_notifier(NC_GEOM | ND_DATA, &grease_pencil.id); diff --git a/source/blender/editors/sculpt_paint/paint_cursor.cc b/source/blender/editors/sculpt_paint/paint_cursor.cc index cd75dbcf89c..1d237089f4a 100644 --- a/source/blender/editors/sculpt_paint/paint_cursor.cc +++ b/source/blender/editors/sculpt_paint/paint_cursor.cc @@ -1512,7 +1512,6 @@ static void grease_pencil_brush_cursor_draw(PaintCursorContext *pcontext) if (object->type != OB_GREASE_PENCIL) { return; } - GreasePencil &grease_pencil = *reinterpret_cast(object->data); /* default radius and color */ float color[3] = {1.0f, 1.0f, 1.0f}; @@ -1530,11 +1529,6 @@ static void grease_pencil_brush_cursor_draw(PaintCursorContext *pcontext) return; } - /* while drawing hide */ - if (grease_pencil.runtime->has_stroke_cache()) { - return; - } - if ((paint->flags & PAINT_SHOW_BRUSH) == 0) { return; } -- 2.30.2 From 10f30ff6947b9e625243b27f9e082572061d0068 Mon Sep 17 00:00:00 2001 From: Falk David Date: Fri, 29 Sep 2023 13:01:52 +0200 Subject: [PATCH 63/75] Better converging threshold --- source/blender/editors/sculpt_paint/grease_pencil_paint.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc index 2c2a4173267..91683feacf0 100644 --- a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc +++ b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc @@ -291,6 +291,7 @@ struct PaintOperationExecutor { .positions_for_write() .slice(points_range) .slice(smooth_window); + const float converging_threshold_px = 0.1f; bool stop_counting_converged = false; int num_converged = 0; for (const int64_t i : smooth_window.index_range()) { -- 2.30.2 From cde81710ddfcb41342396a3f84fbe0740e449a72 Mon Sep 17 00:00:00 2001 From: Falk David Date: Fri, 29 Sep 2023 13:02:32 +0200 Subject: [PATCH 64/75] Cleanup --- .../sculpt_paint/grease_pencil_paint.cc | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc index 91683feacf0..29f04c26001 100644 --- a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc +++ b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc @@ -87,7 +87,7 @@ static void interp_polyline_to_polyline(Span src, MutableSpan ds if (index + 1 >= normalized_parameters_src.size()) { break; } - if (math::distance_squared(src[index], dst[i]) < 1e-6 || + if (math::distance_squared(src[index], dst[i]) < 1e-6f || math::abs(normalized_parameters_src[index + 1] - normalized_parameters_src[index]) < 1e-8) { dst[i] = src[index]; @@ -249,23 +249,24 @@ struct PaintOperationExecutor { smooth_window); /* Detect corners in the current slice of coordinates. */ + const float corner_min_radius_px = 5.0f; + const float corner_max_radius_px = 30.0f; + const int64_t corner_max_samples = 64; + const float corner_angle_threshold = 0.6f; IndexMaskMemory memory; - const float min_radius_px = 5.0f; - const float max_radius_px = 30.0f; - const int64_t max_samples = 64; - const float angle_threshold = 0.6f; IndexMask corner_mask = ed::greasepencil::polyline_detect_corners( - screen_space_coords_smooth_slice, - min_radius_px, - max_radius_px, - max_samples, - angle_threshold, + screen_space_coords_smooth_slice.drop_front(1).drop_back(1), + corner_min_radius_px, + corner_max_radius_px, + corner_max_samples, + corner_angle_threshold, memory); /* Pre-blur the coordinates for the curve fitting. This generally leads to a better fit. */ Array coords_pre_blur(smooth_window.size()); + const int pre_blur_iterations = 3; ed::greasepencil::gaussian_blur_1D(screen_space_coords_smooth_slice, - 3, + pre_blur_iterations, 1.0f, true, true, @@ -480,7 +481,7 @@ static float dist_to_interpolated_2d( const float dist1 = math::distance_squared(posA, pos); const float dist2 = math::distance_squared(posB, pos); - if (dist1 + dist2 > 0.0f) { + if (dist1 + dist2 > 1e-5f) { const float interpolated_val = interpf(valB, valA, dist1 / (dist1 + dist2)); return math::distance(interpolated_val, val); } -- 2.30.2 From 2b9183fe6287a57fb50109aae62cee3760f18606 Mon Sep 17 00:00:00 2001 From: Falk David Date: Fri, 29 Sep 2023 16:59:09 +0200 Subject: [PATCH 65/75] Remove debug code --- .../blender/editors/sculpt_paint/grease_pencil_paint.cc | 9 --------- 1 file changed, 9 deletions(-) diff --git a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc index 29f04c26001..5b6862779a6 100644 --- a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc +++ b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc @@ -334,15 +334,6 @@ struct PaintOperationExecutor { self.screen_space_curve_fitted_coordinates_.clear(); } } - -#ifdef DEBUG - /* Visualize active window. */ - self.stroke_cache_->vertex_colors_for_write().fill(ColorGeometry4f(float4(0.0f))); - for (const int64_t i : smooth_window.index_range()) { - self.stroke_cache_->vertex_colors_for_write().slice(smooth_window)[i] = ColorGeometry4f( - float4(1.0f, 0.1f, 0.1f, 1.0f)); - } -#endif } void process_extension_sample(PaintOperation &self, -- 2.30.2 From 6a57c99feed3bd94d521d5e4e818bbb906e9eb2b Mon Sep 17 00:00:00 2001 From: Falk David Date: Thu, 5 Oct 2023 11:35:28 +0200 Subject: [PATCH 66/75] Rewrite `interp_polyline_to_polyline` --- .../sculpt_paint/grease_pencil_paint.cc | 63 ++++++------------- 1 file changed, 18 insertions(+), 45 deletions(-) diff --git a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc index 5b6862779a6..70d0d9d25b5 100644 --- a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc +++ b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc @@ -11,6 +11,7 @@ #include "BKE_material.h" #include "BKE_scene.h" +#include "BLI_length_parameterize.hh" #include "BLI_math_geom.h" #include "DEG_depsgraph_query.hh" @@ -51,53 +52,25 @@ static Array sample_curve_2d(Span curve_points, const int64_t re return points; } -/** Interpolate \a dst such that the points in \a dst lie evenly distributed on \a src. */ -static void interp_polyline_to_polyline(Span src, MutableSpan dst) +/** Morph \a src onto \a target such that the points have the same spacing as in \a src and + * write the result to \a dst. */ +static void morph_points_to_curve(Span src, Span target, MutableSpan dst) { - using ParamIt = double *; - BLI_assert(src.size() > 1); - Array normalized_parameters_src(src.size(), 0.0f); - double total_dist_src = 0.0f; - for (const int64_t i : src.index_range().drop_front(1)) { - total_dist_src += math::distance(src[i], src[i - 1]); - normalized_parameters_src[i] = total_dist_src; - } - if (total_dist_src < 1e-6f) { - normalized_parameters_src.fill(0.0f); - } - else { - for (const int64_t i : src.index_range()) { - normalized_parameters_src[i] /= total_dist_src; - } - } + BLI_assert(src.size() == dst.size()); + Array accumulated_lengths_src(src.size() - 1); + length_parameterize::accumulate_lengths(src, false, accumulated_lengths_src); - Array accumulated_lengths_dst(dst.size(), 0.0f); - double total_dist_dst = 0.0f; - for (const int64_t i : dst.index_range().drop_front(1)) { - total_dist_dst += math::distance(dst[i], dst[i - 1]); - accumulated_lengths_dst[i] = total_dist_dst; - } + Array accumulated_lengths_target(target.size() - 1); + length_parameterize::accumulate_lengths(target, false, accumulated_lengths_target); - ParamIt start_it = normalized_parameters_src.begin(); - for (const int64_t i : dst.index_range().drop_front(1)) { - const double target = accumulated_lengths_dst[i] / total_dist_dst; - ParamIt it = std::lower_bound(start_it, normalized_parameters_src.end(), target); - const int64_t index = std::distance(normalized_parameters_src.begin(), it); - start_it = it; - if (index + 1 >= normalized_parameters_src.size()) { - break; - } - if (math::distance_squared(src[index], dst[i]) < 1e-6f || - math::abs(normalized_parameters_src[index + 1] - normalized_parameters_src[index]) < 1e-8) - { - dst[i] = src[index]; - continue; - } - const double t = (target - normalized_parameters_src[index]) / - (normalized_parameters_src[index + 1] - normalized_parameters_src[index]); + Array segment_indices(accumulated_lengths_src.size()); + Array segment_factors(accumulated_lengths_src.size()); + length_parameterize::sample_at_lengths( + accumulated_lengths_target, accumulated_lengths_src, segment_indices, segment_factors); - dst[i] = math::interpolate(src[index], src[index + 1], t); - } + length_parameterize::interpolate( + target, segment_indices, segment_factors, dst.drop_back(1)); + dst.last() = src.last(); } struct ScreenSpacePoint { @@ -283,8 +256,8 @@ struct PaintOperationExecutor { Array sampled_curve_points = sample_curve_2d(curve_points, sample_resolution); /* Morphing the coordinates onto the curve. Result is stored in a temporary array. */ - Array coords_smoothed(screen_space_coords_smooth_slice); - interp_polyline_to_polyline(sampled_curve_points, coords_smoothed.as_mutable_span()); + Array coords_smoothed(screen_space_coords_smooth_slice.size()); + morph_points_to_curve(screen_space_coords_smooth_slice, sampled_curve_points, coords_smoothed); MutableSpan smoothed_coordinates_slice = self.screen_space_smoothed_coordinates_.as_mutable_span().slice(smooth_window); -- 2.30.2 From 4172c6ce00110b36e699eea6bd80657dfd803347 Mon Sep 17 00:00:00 2001 From: Falk David Date: Thu, 5 Oct 2023 11:36:39 +0200 Subject: [PATCH 67/75] Use `const` --- .../editors/sculpt_paint/grease_pencil_paint.cc | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc index 70d0d9d25b5..e19be1fe7a6 100644 --- a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc +++ b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc @@ -154,10 +154,10 @@ struct PaintOperationExecutor { &reinterpret_cast(grease_pencil_->drawing(drawing_index))->wrap(); } - float3 screen_space_to_object_space(float2 co) + float3 screen_space_to_object_space(const float2 co) { /* TODO: Use correct plane/projection. */ - float4 plane{0.0f, -1.0f, 0.0f, 0.0f}; + const float4 plane{0.0f, -1.0f, 0.0f, 0.0f}; /* TODO: Use object transform. */ float3 proj_point; ED_view3d_win_to_3d_on_plane(region_, plane, co, false, proj_point); @@ -193,7 +193,7 @@ struct PaintOperationExecutor { /* Resize the curves geometry so there is one more curve with a single point. */ bke::CurvesGeometry &curves = drawing_->strokes_for_write(); - int num_old_points = curves.points_num(); + const int num_old_points = curves.points_num(); curves.resize(curves.points_num() + 1, curves.curves_num() + 1); curves.offsets_for_write().last(1) = num_old_points; @@ -227,7 +227,7 @@ struct PaintOperationExecutor { const int64_t corner_max_samples = 64; const float corner_angle_threshold = 0.6f; IndexMaskMemory memory; - IndexMask corner_mask = ed::greasepencil::polyline_detect_corners( + const IndexMask corner_mask = ed::greasepencil::polyline_detect_corners( screen_space_coords_smooth_slice.drop_front(1).drop_back(1), corner_min_radius_px, corner_max_radius_px, @@ -317,10 +317,10 @@ struct PaintOperationExecutor { bke::CurvesGeometry &curves = drawing_->strokes_for_write(); - float2 prev_co = self.screen_space_coordinates_.last(); - float prev_radius = drawing_->radii().last(); - float prev_opacity = drawing_->opacities().last(); - ColorGeometry4f prev_vertex_color = drawing_->vertex_colors().last(); + const float2 prev_co = self.screen_space_coordinates_.last(); + const float prev_radius = drawing_->radii().last(); + const float prev_opacity = drawing_->opacities().last(); + const ColorGeometry4f prev_vertex_color = drawing_->vertex_colors().last(); /* Overwrite last point if it's very close. */ if (math::distance(point.co, prev_co) < POINT_OVERRIDE_THRESHOLD_PX) { -- 2.30.2 From 3b8b8880172418a24ce40d038af1728607690e5e Mon Sep 17 00:00:00 2001 From: Falk David Date: Thu, 5 Oct 2023 11:37:30 +0200 Subject: [PATCH 68/75] Don't repeatedly call `drawing->radii()` --- source/blender/editors/sculpt_paint/grease_pencil_paint.cc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc index e19be1fe7a6..cf5d708934c 100644 --- a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc +++ b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc @@ -493,10 +493,12 @@ void PaintOperation::process_stroke_end(bke::greasepencil::Drawing &drawing) const int stroke_index = drawing.strokes().curves_range().last(); const IndexRange points_range = drawing.strokes().points_by_curve()[stroke_index]; bke::CurvesGeometry &curves = drawing.strokes_for_write(); + const VArray radii = drawing.radii(); + /* Remove points at the end that have a radius close to 0. */ int64_t points_to_remove = 0; - for (int64_t index = points_range.last(); index >= points_range.first(); index--) { - if (drawing.radii()[index] < 1e-5f) { + for (int64_t index = points.last(); index >= points.first(); index--) { + if (radii[index] < 1e-5f) { points_to_remove++; } else { -- 2.30.2 From fc8765c08d39c5c105c267e64b951ceef5be3565 Mon Sep 17 00:00:00 2001 From: Falk David Date: Thu, 5 Oct 2023 11:40:21 +0200 Subject: [PATCH 69/75] Rename `points_range` to `points` --- .../sculpt_paint/grease_pencil_paint.cc | 53 +++++++++---------- 1 file changed, 25 insertions(+), 28 deletions(-) diff --git a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc index cf5d708934c..e1bf60bd1ba 100644 --- a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc +++ b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc @@ -215,7 +215,7 @@ struct PaintOperationExecutor { } void active_smoothing(PaintOperation &self, - const IndexRange points_range, + const IndexRange points, const IndexRange smooth_window) { Span screen_space_coords_smooth_slice = self.screen_space_coordinates_.as_span().slice( @@ -261,10 +261,8 @@ struct PaintOperationExecutor { MutableSpan smoothed_coordinates_slice = self.screen_space_smoothed_coordinates_.as_mutable_span().slice(smooth_window); - MutableSpan positions_slice = drawing_->strokes_for_write() - .positions_for_write() - .slice(points_range) - .slice(smooth_window); + MutableSpan positions_slice = + drawing_->strokes_for_write().positions_for_write().slice(points).slice(smooth_window); const float converging_threshold_px = 0.1f; bool stop_counting_converged = false; int num_converged = 0; @@ -374,8 +372,8 @@ struct PaintOperationExecutor { drawing_->vertex_colors_for_write().slice(new_range).copy_from(new_vertex_colors); const int64_t min_active_smoothing_points_num = 8; - const IndexRange points_range = curves.points_by_curve()[curve_index]; - if (points_range.size() < min_active_smoothing_points_num) { + const IndexRange points = curves.points_by_curve()[curve_index]; + if (points.size() < min_active_smoothing_points_num) { MutableSpan positions_slice = curves.positions_for_write().slice(new_range); for (const int64_t i : new_coordinates.index_range()) { positions_slice[i] = screen_space_to_object_space(new_coordinates[i]); @@ -383,9 +381,10 @@ struct PaintOperationExecutor { return; } + /* Active smoothing is done in a window at the end of the new stroke. */ const IndexRange smooth_window(self.active_smooth_index_, - points_range.size() - self.active_smooth_index_); - this->active_smoothing(self, points_range, smooth_window); + points.size() - self.active_smooth_index_); + this->active_smoothing(self, points, smooth_window); } void execute(PaintOperation &self, const InputSample &extension_sample) @@ -455,32 +454,30 @@ static float dist_to_interpolated_2d( void PaintOperation::simplify_stroke(bke::greasepencil::Drawing &drawing, const float epsilon_px) { const int stroke_index = drawing.strokes().curves_range().last(); - const IndexRange points_range = drawing.strokes().points_by_curve()[stroke_index]; + const IndexRange points = drawing.strokes().points_by_curve()[stroke_index]; bke::CurvesGeometry &curves = drawing.strokes_for_write(); const VArray radii = drawing.radii(); /* Distance function for `ramer_douglas_peucker_simplify`. */ const Span positions_2d = this->screen_space_smoothed_coordinates_.as_span(); - const auto dist_function = - [points_range, positions_2d, radii](int64_t first_index, int64_t last_index, int64_t index) { - /* 2D coordinates are only stored for the current stroke, so offset the indices. */ - const float dist_position_px = dist_to_line_segment_v2( - positions_2d[index - points_range.first()], - positions_2d[first_index - points_range.first()], - positions_2d[last_index - points_range.first()]); - const float dist_radii_px = dist_to_interpolated_2d( - positions_2d[index - points_range.first()], - positions_2d[first_index - points_range.first()], - positions_2d[last_index - points_range.first()], - radii[index], - radii[first_index], - radii[last_index]); - return math::max(dist_position_px, dist_radii_px); - }; + const auto dist_function = [&](int64_t first_index, int64_t last_index, int64_t index) { + /* 2D coordinates are only stored for the current stroke, so offset the indices. */ + const float dist_position_px = dist_to_line_segment_v2( + positions_2d[index - points.first()], + positions_2d[first_index - points.first()], + positions_2d[last_index - points.first()]); + const float dist_radii_px = dist_to_interpolated_2d(positions_2d[index - points.first()], + positions_2d[first_index - points.first()], + positions_2d[last_index - points.first()], + radii[index], + radii[first_index], + radii[last_index]); + return math::max(dist_position_px, dist_radii_px); + }; Array points_to_delete(curves.points_num(), false); int64_t total_points_to_delete = ed::greasepencil::ramer_douglas_peucker_simplify( - points_range, epsilon_px, dist_function, points_to_delete.as_mutable_span()); + points, epsilon_px, dist_function, points_to_delete.as_mutable_span()); if (total_points_to_delete > 0) { IndexMaskMemory memory; @@ -491,7 +488,7 @@ void PaintOperation::simplify_stroke(bke::greasepencil::Drawing &drawing, const void PaintOperation::process_stroke_end(bke::greasepencil::Drawing &drawing) { const int stroke_index = drawing.strokes().curves_range().last(); - const IndexRange points_range = drawing.strokes().points_by_curve()[stroke_index]; + const IndexRange points = drawing.strokes().points_by_curve()[stroke_index]; bke::CurvesGeometry &curves = drawing.strokes_for_write(); const VArray radii = drawing.radii(); -- 2.30.2 From eca2484979fd9c7e240f8fee9a7a0f090120000c Mon Sep 17 00:00:00 2001 From: Falk David Date: Thu, 5 Oct 2023 11:41:01 +0200 Subject: [PATCH 70/75] Directly write interpolated attributes (except positions) --- .../sculpt_paint/grease_pencil_paint.cc | 43 +++++++++---------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc index e1bf60bd1ba..5cdd9db8f3e 100644 --- a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc +++ b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc @@ -338,16 +338,23 @@ struct PaintOperationExecutor { new_points_num += subdivisions; } + /* Resize the curves geometry. */ + const int old_point_num = curves.points_num(); + curves.resize(curves.points_num() + new_points_num, curves.curves_num()); + curves.offsets_for_write().last() = curves.points_num(); + /* Subdivide stroke in new_range. */ - IndexRange new_range(curves.points_num(), new_points_num); - Array new_coordinates(new_points_num); - Array new_radii(new_points_num); - Array new_opacities(new_points_num); - Array new_vertex_colors(new_points_num); + IndexRange new_range(old_point_num, new_points_num); + Array new_screen_space_coordinates(new_points_num); + MutableSpan new_radii = drawing_->radii_for_write().slice(new_range); + MutableSpan new_opacities = drawing_->opacities_for_write().slice(new_range); + MutableSpan new_vertex_colors = drawing_->vertex_colors_for_write().slice( + new_range); const float step = 1.0f / static_cast(new_points_num); float factor = step; for (const int64_t i : new_range.index_range()) { - new_coordinates[i] = bke::attribute_math::mix2(factor, prev_co, point.co); + new_screen_space_coordinates[i] = bke::attribute_math::mix2( + factor, prev_co, point.co); new_radii[i] = bke::attribute_math::mix2(factor, prev_radius, point.radius); new_opacities[i] = bke::attribute_math::mix2(factor, prev_opacity, point.opacity); new_vertex_colors[i] = bke::attribute_math::mix2( @@ -355,28 +362,20 @@ struct PaintOperationExecutor { factor += step; } - /* Update buffers with new points. */ - self.screen_space_coordinates_.extend(new_coordinates); - self.screen_space_smoothed_coordinates_.extend(new_coordinates); - for (float2 new_co : new_coordinates) { + /* Update screen space buffers with new points. */ + self.screen_space_coordinates_.extend(new_screen_space_coordinates); + self.screen_space_smoothed_coordinates_.extend(new_screen_space_coordinates); + for (float2 new_co : new_screen_space_coordinates) { self.screen_space_curve_fitted_coordinates_.append(Vector({new_co})); } - /* Resize the stroke cache. */ - curves.resize(curves.points_num() + new_points_num, curves.curves_num()); - curves.offsets_for_write().last() = curves.points_num(); - - /* Write new data to the attributes. */ - drawing_->radii_for_write().slice(new_range).copy_from(new_radii); - drawing_->opacities_for_write().slice(new_range).copy_from(new_opacities); - drawing_->vertex_colors_for_write().slice(new_range).copy_from(new_vertex_colors); - + /* Only start smoothing if there are enough points. */ const int64_t min_active_smoothing_points_num = 8; const IndexRange points = curves.points_by_curve()[curve_index]; if (points.size() < min_active_smoothing_points_num) { MutableSpan positions_slice = curves.positions_for_write().slice(new_range); - for (const int64_t i : new_coordinates.index_range()) { - positions_slice[i] = screen_space_to_object_space(new_coordinates[i]); + for (const int64_t i : new_screen_space_coordinates.index_range()) { + positions_slice[i] = screen_space_to_object_space(new_screen_space_coordinates[i]); } return; } @@ -389,7 +388,7 @@ struct PaintOperationExecutor { void execute(PaintOperation &self, const InputSample &extension_sample) { - /* New curve was created in process_start_sample.*/ + /* New curve was created in `process_start_sample`.*/ const int curve_index = drawing_->strokes().curves_range().last(); this->process_extension_sample(self, extension_sample, curve_index); drawing_->tag_topology_changed(); -- 2.30.2 From 8bc9fc468c631e72e9c7ebf77cf2e8818d4078c5 Mon Sep 17 00:00:00 2001 From: Falk David Date: Thu, 5 Oct 2023 11:50:55 +0200 Subject: [PATCH 71/75] Remove `ScreenSpacePoint` struct --- .../sculpt_paint/grease_pencil_paint.cc | 104 +++++++++--------- 1 file changed, 52 insertions(+), 52 deletions(-) diff --git a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc index 5cdd9db8f3e..e545e9a25d2 100644 --- a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc +++ b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc @@ -73,18 +73,11 @@ static void morph_points_to_curve(Span src, Span target, Mutable dst.last() = src.last(); } -struct ScreenSpacePoint { - float2 co; - float radius; - float opacity; - float4 vertex_color; -}; - class PaintOperation : public GreasePencilStrokeOperation { private: - Vector screen_space_coordinates_; - Vector> screen_space_curve_fitted_coordinates_; - Vector screen_space_smoothed_coordinates_; + Vector screen_space_positions_; + Vector> screen_space_curve_fitted_positions_; + Vector screen_space_smoothed_positions_; int64_t active_smooth_index_ = 0; friend struct PaintOperationExecutor; @@ -164,32 +157,36 @@ struct PaintOperationExecutor { return proj_point; } - ScreenSpacePoint point_from_input_sample(const InputSample &sample) + float radius_from_input_sample(const InputSample &sample) { - ScreenSpacePoint point; - point.co = sample.mouse_position; - point.radius = brush_size_ / 2.0f; + float radius = brush_size_ / 2.0f; if (BKE_brush_use_size_pressure(brush_)) { - point.radius *= BKE_curvemapping_evaluateF(settings_->curve_sensitivity, 0, sample.pressure); + radius *= BKE_curvemapping_evaluateF(settings_->curve_sensitivity, 0, sample.pressure); } - point.opacity = brush_alpha_; + return radius; + } + + float opacity_from_input_sample(const InputSample &sample) + { + float opacity = brush_alpha_; if (BKE_brush_use_alpha_pressure(brush_)) { - point.opacity *= BKE_curvemapping_evaluateF(settings_->curve_strength, 0, sample.pressure); + opacity *= BKE_curvemapping_evaluateF(settings_->curve_strength, 0, sample.pressure); } - point.vertex_color = vertex_color_; - /* TODO: Get fill vertex color. */ - return point; + return opacity; } void process_start_sample(PaintOperation &self, const InputSample &start_sample, const int material_index) { - ScreenSpacePoint start_point = this->point_from_input_sample(start_sample); + const float2 start_position = start_sample.mouse_position; + const float start_radius = this->radius_from_input_sample(start_sample); + const float start_opacity = this->opacity_from_input_sample(start_sample); + const ColorGeometry4f start_vertex_color = ColorGeometry4f(vertex_color_); - self.screen_space_coordinates_.append(start_point.co); - self.screen_space_curve_fitted_coordinates_.append(Vector({start_point.co})); - self.screen_space_smoothed_coordinates_.append(start_point.co); + self.screen_space_positions_.append(start_position); + self.screen_space_curve_fitted_positions_.append(Vector({start_position})); + self.screen_space_smoothed_positions_.append(start_position); /* Resize the curves geometry so there is one more curve with a single point. */ bke::CurvesGeometry &curves = drawing_->strokes_for_write(); @@ -197,10 +194,10 @@ struct PaintOperationExecutor { curves.resize(curves.points_num() + 1, curves.curves_num() + 1); curves.offsets_for_write().last(1) = num_old_points; - curves.positions_for_write().last() = screen_space_to_object_space(start_point.co); - drawing_->radii_for_write().last() = start_point.radius; - drawing_->opacities_for_write().last() = start_point.opacity; - drawing_->vertex_colors_for_write().last() = ColorGeometry4f(start_point.vertex_color); + curves.positions_for_write().last() = screen_space_to_object_space(start_position); + drawing_->radii_for_write().last() = start_radius; + drawing_->opacities_for_write().last() = start_opacity; + drawing_->vertex_colors_for_write().last() = start_vertex_color; bke::MutableAttributeAccessor attributes = curves.attributes_for_write(); bke::SpanAttributeWriter materials = attributes.lookup_or_add_for_write_span( @@ -218,7 +215,7 @@ struct PaintOperationExecutor { const IndexRange points, const IndexRange smooth_window) { - Span screen_space_coords_smooth_slice = self.screen_space_coordinates_.as_span().slice( + Span screen_space_coords_smooth_slice = self.screen_space_positions_.as_span().slice( smooth_window); /* Detect corners in the current slice of coordinates. */ @@ -247,7 +244,7 @@ struct PaintOperationExecutor { coords_pre_blur.as_mutable_span()); /* Curve fitting. The output will be a set of handles (float2 triplets) in a flat array. */ - const float max_error_threshold_px = 5.0f; + const float max_error_threshold_px = 10.0f; Array curve_points = ed::greasepencil::fit_curve_polyline_2d( coords_pre_blur, max_error_threshold_px * settings_->active_smooth, corner_mask); @@ -260,7 +257,7 @@ struct PaintOperationExecutor { morph_points_to_curve(screen_space_coords_smooth_slice, sampled_curve_points, coords_smoothed); MutableSpan smoothed_coordinates_slice = - self.screen_space_smoothed_coordinates_.as_mutable_span().slice(smooth_window); + self.screen_space_smoothed_positions_.as_mutable_span().slice(smooth_window); MutableSpan positions_slice = drawing_->strokes_for_write().positions_for_write().slice(points).slice(smooth_window); const float converging_threshold_px = 0.1f; @@ -268,8 +265,8 @@ struct PaintOperationExecutor { int num_converged = 0; for (const int64_t i : smooth_window.index_range()) { /* Record the curve fitting of this point. */ - self.screen_space_curve_fitted_coordinates_[i].append(coords_smoothed[i]); - Span smoothed_coords_point = self.screen_space_curve_fitted_coordinates_[i]; + self.screen_space_curve_fitted_positions_[i].append(coords_smoothed[i]); + Span smoothed_coords_point = self.screen_space_curve_fitted_positions_[i]; /* Get the sum of all the curve fittings of this point. */ float2 sum = smoothed_coords_point[0]; @@ -298,11 +295,11 @@ struct PaintOperationExecutor { if (num_converged > 0) { self.active_smooth_index_ = math::min(self.active_smooth_index_ + int64_t(num_converged), int64_t(drawing_->strokes().points_num() - 1)); - if (self.screen_space_curve_fitted_coordinates_.size() - num_converged > 0) { - self.screen_space_curve_fitted_coordinates_.remove(0, num_converged); + if (self.screen_space_curve_fitted_positions_.size() - num_converged > 0) { + self.screen_space_curve_fitted_positions_.remove(0, num_converged); } else { - self.screen_space_curve_fitted_coordinates_.clear(); + self.screen_space_curve_fitted_positions_.clear(); } } } @@ -311,26 +308,29 @@ struct PaintOperationExecutor { const InputSample &extension_sample, const int curve_index) { - ScreenSpacePoint point = this->point_from_input_sample(extension_sample); + const float2 position = extension_sample.mouse_position; + const float radius = this->radius_from_input_sample(extension_sample); + const float opacity = this->opacity_from_input_sample(extension_sample); + const ColorGeometry4f vertex_color = ColorGeometry4f(vertex_color_); bke::CurvesGeometry &curves = drawing_->strokes_for_write(); - const float2 prev_co = self.screen_space_coordinates_.last(); + const float2 prev_position = self.screen_space_positions_.last(); const float prev_radius = drawing_->radii().last(); const float prev_opacity = drawing_->opacities().last(); const ColorGeometry4f prev_vertex_color = drawing_->vertex_colors().last(); /* Overwrite last point if it's very close. */ - if (math::distance(point.co, prev_co) < POINT_OVERRIDE_THRESHOLD_PX) { - curves.positions_for_write().last() = screen_space_to_object_space(point.co); - drawing_->radii_for_write().last() = math::max(point.radius, prev_radius); - drawing_->opacities_for_write().last() = math::max(point.opacity, prev_opacity); + if (math::distance(position, prev_position) < POINT_OVERRIDE_THRESHOLD_PX) { + curves.positions_for_write().last() = screen_space_to_object_space(position); + drawing_->radii_for_write().last() = math::max(radius, prev_radius); + drawing_->opacities_for_write().last() = math::max(opacity, prev_opacity); return; } /* If the next sample is far away, we subdivide the segment to add more points. */ int new_points_num = 1; - const float distance_px = math::distance(point.co, prev_co); + const float distance_px = math::distance(position, prev_position); if (distance_px > POINT_RESAMPLE_MIN_DISTANCE_PX) { const int subdivisions = static_cast( math::floor(distance_px / POINT_RESAMPLE_MIN_DISTANCE_PX)) - @@ -354,19 +354,19 @@ struct PaintOperationExecutor { float factor = step; for (const int64_t i : new_range.index_range()) { new_screen_space_coordinates[i] = bke::attribute_math::mix2( - factor, prev_co, point.co); - new_radii[i] = bke::attribute_math::mix2(factor, prev_radius, point.radius); - new_opacities[i] = bke::attribute_math::mix2(factor, prev_opacity, point.opacity); + factor, prev_position, position); + new_radii[i] = bke::attribute_math::mix2(factor, prev_radius, radius); + new_opacities[i] = bke::attribute_math::mix2(factor, prev_opacity, opacity); new_vertex_colors[i] = bke::attribute_math::mix2( - factor, prev_vertex_color, ColorGeometry4f(point.vertex_color)); + factor, prev_vertex_color, ColorGeometry4f(vertex_color)); factor += step; } /* Update screen space buffers with new points. */ - self.screen_space_coordinates_.extend(new_screen_space_coordinates); - self.screen_space_smoothed_coordinates_.extend(new_screen_space_coordinates); - for (float2 new_co : new_screen_space_coordinates) { - self.screen_space_curve_fitted_coordinates_.append(Vector({new_co})); + self.screen_space_positions_.extend(new_screen_space_coordinates); + self.screen_space_smoothed_positions_.extend(new_screen_space_coordinates); + for (float2 new_position : new_screen_space_coordinates) { + self.screen_space_curve_fitted_positions_.append(Vector({new_position})); } /* Only start smoothing if there are enough points. */ @@ -458,7 +458,7 @@ void PaintOperation::simplify_stroke(bke::greasepencil::Drawing &drawing, const const VArray radii = drawing.radii(); /* Distance function for `ramer_douglas_peucker_simplify`. */ - const Span positions_2d = this->screen_space_smoothed_coordinates_.as_span(); + const Span positions_2d = this->screen_space_smoothed_positions_.as_span(); const auto dist_function = [&](int64_t first_index, int64_t last_index, int64_t index) { /* 2D coordinates are only stored for the current stroke, so offset the indices. */ const float dist_position_px = dist_to_line_segment_v2( -- 2.30.2 From 45f1e9e6feee7775777da711c11ae22e9dfe175f Mon Sep 17 00:00:00 2001 From: Falk David Date: Thu, 5 Oct 2023 11:53:16 +0200 Subject: [PATCH 72/75] Revert threshold change --- source/blender/editors/sculpt_paint/grease_pencil_paint.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc index e545e9a25d2..ae2559f1086 100644 --- a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc +++ b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc @@ -244,7 +244,7 @@ struct PaintOperationExecutor { coords_pre_blur.as_mutable_span()); /* Curve fitting. The output will be a set of handles (float2 triplets) in a flat array. */ - const float max_error_threshold_px = 10.0f; + const float max_error_threshold_px = 5.0f; Array curve_points = ed::greasepencil::fit_curve_polyline_2d( coords_pre_blur, max_error_threshold_px * settings_->active_smooth, corner_mask); -- 2.30.2 From 0c7e6a1ea4203a7fa379248c0bf60f6b923fa132 Mon Sep 17 00:00:00 2001 From: Falk David Date: Thu, 5 Oct 2023 11:53:51 +0200 Subject: [PATCH 73/75] Fix grammar --- source/blender/editors/sculpt_paint/grease_pencil_paint.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc index ae2559f1086..1b4d88a97fa 100644 --- a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc +++ b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc @@ -274,7 +274,7 @@ struct PaintOperationExecutor { sum += v; } /* We compare the previous arithmetic mean to the current. Going from the back to the front, - * if a point hasn't moved by a minimum threashold, it counts as converged. */ + * if a point hasn't moved by a minimum threshold, it counts as converged. */ float2 new_pos = (sum + smoothed_coords_point.last()) / smoothed_coords_point.size(); if (!stop_counting_converged) { float2 prev_pos = sum / (smoothed_coords_point.size() - 1); @@ -524,8 +524,8 @@ void PaintOperation::on_stroke_done(const bContext &C) bke::greasepencil::Drawing &drawing = reinterpret_cast(grease_pencil.drawing(drawing_index))->wrap(); - const float simplifiy_threashold_px = 0.5f; - this->simplify_stroke(drawing, simplifiy_threashold_px); + const float simplifiy_threshold_px = 0.5f; + this->simplify_stroke(drawing, simplifiy_threshold_px); this->process_stroke_end(drawing); drawing.tag_topology_changed(); -- 2.30.2 From f6eccaba2161ede1172f6a47bf49650ef40e32ac Mon Sep 17 00:00:00 2001 From: Falk David Date: Thu, 5 Oct 2023 12:08:02 +0200 Subject: [PATCH 74/75] Multithread curve sampling --- .../sculpt_paint/grease_pencil_paint.cc | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc index 1b4d88a97fa..b331196164e 100644 --- a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc +++ b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc @@ -41,14 +41,16 @@ static Array sample_curve_2d(Span curve_points, const int64_t re Array points(num_points); const Span curve_segments = curve_points.drop_front(1).drop_back(1); - for (const int64_t segment_i : IndexRange(num_segments)) { - IndexRange segment_range(segment_i * resolution, resolution); - bke::curves::bezier::evaluate_segment(curve_segments[segment_i * 3 + 0], - curve_segments[segment_i * 3 + 1], - curve_segments[segment_i * 3 + 2], - curve_segments[segment_i * 3 + 3], - points.as_mutable_span().slice(segment_range)); - } + threading::parallel_for(IndexRange(num_segments), 512 * resolution, [&](const IndexRange range) { + for (const int64_t segment_i : range) { + IndexRange segment_range(segment_i * resolution, resolution); + bke::curves::bezier::evaluate_segment(curve_segments[segment_i * 3 + 0], + curve_segments[segment_i * 3 + 1], + curve_segments[segment_i * 3 + 2], + curve_segments[segment_i * 3 + 3], + points.as_mutable_span().slice(segment_range)); + } + }); return points; } -- 2.30.2 From 81a9e903e81b51a7e6430b1e4c60ff6832615e7f Mon Sep 17 00:00:00 2001 From: Falk David Date: Thu, 5 Oct 2023 15:12:38 +0200 Subject: [PATCH 75/75] Lots of cleanup --- .../intern/grease_pencil_geom.cc | 17 ++- .../intern/grease_pencil_utils.cc | 1 - .../editors/include/ED_grease_pencil.hh | 8 +- .../sculpt_paint/grease_pencil_paint.cc | 108 +++++++++--------- 4 files changed, 65 insertions(+), 69 deletions(-) diff --git a/source/blender/editors/grease_pencil/intern/grease_pencil_geom.cc b/source/blender/editors/grease_pencil/intern/grease_pencil_geom.cc index 831d5d17f45..588f83f0674 100644 --- a/source/blender/editors/grease_pencil/intern/grease_pencil_geom.cc +++ b/source/blender/editors/grease_pencil/intern/grease_pencil_geom.cc @@ -76,14 +76,13 @@ int64_t ramer_douglas_peucker_simplify( return total_points_to_remove; } -Array fit_curve_polyline_2d(Span points, - const float error_threshold, - const IndexMask &corner_mask) +Array polyline_fit_curve(Span points, + const float error_threshold, + const IndexMask &corner_mask) { Array indices(corner_mask.size()); corner_mask.to_indices(indices.as_mutable_span()); - uint *indicies_ptr = (corner_mask.size() > 0) ? reinterpret_cast(indices.data()) : - nullptr; + uint *indicies_ptr = corner_mask.is_empty() ? nullptr : reinterpret_cast(indices.data()); float *r_cubic_array; uint r_cubic_array_len; @@ -115,7 +114,7 @@ Array fit_curve_polyline_2d(Span points, IndexMask polyline_detect_corners(Span points, const float radius_min, const float radius_max, - const int64_t samples_max, + const int samples_max, const float angle_threshold, IndexMaskMemory &memory) { @@ -134,9 +133,9 @@ IndexMask polyline_detect_corners(Span points, /* Error occured, return. */ return IndexMask(); } - BLI_assert(samples_max < std::numeric_limits::max()); - Span indices(reinterpret_cast(r_corners), r_corner_len); - return IndexMask::from_indices(indices, memory); + BLI_assert(samples_max < std::numeric_limits::max()); + Span indices(reinterpret_cast(r_corners), r_corner_len); + return IndexMask::from_indices(indices, memory); } } // namespace blender::ed::greasepencil diff --git a/source/blender/editors/grease_pencil/intern/grease_pencil_utils.cc b/source/blender/editors/grease_pencil/intern/grease_pencil_utils.cc index 0970c314ba2..145b9f78d37 100644 --- a/source/blender/editors/grease_pencil/intern/grease_pencil_utils.cc +++ b/source/blender/editors/grease_pencil/intern/grease_pencil_utils.cc @@ -45,7 +45,6 @@ static float3 screen_space_to_3d( float brush_radius_world_space(bContext &C, int x, int y) { - using namespace blender; ARegion *region = CTX_wm_region(&C); View3D *v3d = CTX_wm_view3d(&C); Scene *scene = CTX_data_scene(&C); diff --git a/source/blender/editors/include/ED_grease_pencil.hh b/source/blender/editors/include/ED_grease_pencil.hh index 7665d4c18b6..3ae9a37473b 100644 --- a/source/blender/editors/include/ED_grease_pencil.hh +++ b/source/blender/editors/include/ED_grease_pencil.hh @@ -126,14 +126,14 @@ int64_t ramer_douglas_peucker_simplify(IndexRange range, FunctionRef dist_function, MutableSpan dst); -Array fit_curve_polyline_2d(Span points, - float error_threshold, - const IndexMask &corner_mask); +Array polyline_fit_curve(Span points, + float error_threshold, + const IndexMask &corner_mask); IndexMask polyline_detect_corners(Span points, float radius_min, float radius_max, - int64_t samples_max, + int samples_max, float angle_threshold, IndexMaskMemory &memory); diff --git a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc index b331196164e..1dc14288b88 100644 --- a/source/blender/editors/sculpt_paint/grease_pencil_paint.cc +++ b/source/blender/editors/sculpt_paint/grease_pencil_paint.cc @@ -29,19 +29,27 @@ namespace blender::ed::sculpt_paint::greasepencil { static constexpr float POINT_OVERRIDE_THRESHOLD_PX = 3.0f; static constexpr float POINT_RESAMPLE_MIN_DISTANCE_PX = 10.0f; -static constexpr int64_t STOKE_CACHE_ALLOCATION_CHUNK_SIZE = 1024; +template +static inline void linear_interpolation(const T &a, const T &b, MutableSpan dst) +{ + dst.first() = a; + const float step = 1.0f / dst.size(); + for (const int i : dst.index_range().drop_front(1)) { + dst[i] = bke::attribute_math::mix2(i * step, a, b); + } +} /** Sample a bezier curve at a fixed resolution and return the sampled points in an array. */ -static Array sample_curve_2d(Span curve_points, const int64_t resolution) +static Array sample_curve_2d(Span positions, const int64_t resolution) { - BLI_assert(curve_points.size() % 3 == 0); - const int64_t num_handles = curve_points.size() / 3; + BLI_assert(positions.size() % 3 == 0); + const int64_t num_handles = positions.size() / 3; const int64_t num_segments = num_handles - 1; const int64_t num_points = num_segments * resolution; Array points(num_points); - const Span curve_segments = curve_points.drop_front(1).drop_back(1); - threading::parallel_for(IndexRange(num_segments), 512 * resolution, [&](const IndexRange range) { + const Span curve_segments = positions.drop_front(1).drop_back(1); + threading::parallel_for(IndexRange(num_segments), 32 * resolution, [&](const IndexRange range) { for (const int64_t segment_i : range) { IndexRange segment_range(segment_i * resolution, resolution); bke::curves::bezier::evaluate_segment(curve_segments[segment_i * 3 + 0], @@ -77,9 +85,9 @@ static void morph_points_to_curve(Span src, Span target, Mutable class PaintOperation : public GreasePencilStrokeOperation { private: - Vector screen_space_positions_; - Vector> screen_space_curve_fitted_positions_; - Vector screen_space_smoothed_positions_; + Vector screen_space_coords_; + Vector> screen_space_curve_fitted_coords_; + Vector screen_space_smoothed_coords_; int64_t active_smooth_index_ = 0; friend struct PaintOperationExecutor; @@ -181,14 +189,14 @@ struct PaintOperationExecutor { const InputSample &start_sample, const int material_index) { - const float2 start_position = start_sample.mouse_position; + const float2 start_coords = start_sample.mouse_position; const float start_radius = this->radius_from_input_sample(start_sample); const float start_opacity = this->opacity_from_input_sample(start_sample); const ColorGeometry4f start_vertex_color = ColorGeometry4f(vertex_color_); - self.screen_space_positions_.append(start_position); - self.screen_space_curve_fitted_positions_.append(Vector({start_position})); - self.screen_space_smoothed_positions_.append(start_position); + self.screen_space_coords_.append(start_coords); + self.screen_space_curve_fitted_coords_.append(Vector({start_coords})); + self.screen_space_smoothed_coords_.append(start_coords); /* Resize the curves geometry so there is one more curve with a single point. */ bke::CurvesGeometry &curves = drawing_->strokes_for_write(); @@ -196,7 +204,7 @@ struct PaintOperationExecutor { curves.resize(curves.points_num() + 1, curves.curves_num() + 1); curves.offsets_for_write().last(1) = num_old_points; - curves.positions_for_write().last() = screen_space_to_object_space(start_position); + curves.positions_for_write().last() = screen_space_to_object_space(start_coords); drawing_->radii_for_write().last() = start_radius; drawing_->opacities_for_write().last() = start_opacity; drawing_->vertex_colors_for_write().last() = start_vertex_color; @@ -217,7 +225,7 @@ struct PaintOperationExecutor { const IndexRange points, const IndexRange smooth_window) { - Span screen_space_coords_smooth_slice = self.screen_space_positions_.as_span().slice( + Span screen_space_coords_smooth_slice = self.screen_space_coords_.as_span().slice( smooth_window); /* Detect corners in the current slice of coordinates. */ @@ -247,7 +255,7 @@ struct PaintOperationExecutor { /* Curve fitting. The output will be a set of handles (float2 triplets) in a flat array. */ const float max_error_threshold_px = 5.0f; - Array curve_points = ed::greasepencil::fit_curve_polyline_2d( + Array curve_points = ed::greasepencil::polyline_fit_curve( coords_pre_blur, max_error_threshold_px * settings_->active_smooth, corner_mask); /* Sampling the curve at a fixed resolution. */ @@ -258,8 +266,8 @@ struct PaintOperationExecutor { Array coords_smoothed(screen_space_coords_smooth_slice.size()); morph_points_to_curve(screen_space_coords_smooth_slice, sampled_curve_points, coords_smoothed); - MutableSpan smoothed_coordinates_slice = - self.screen_space_smoothed_positions_.as_mutable_span().slice(smooth_window); + MutableSpan smoothed_coords_slice = + self.screen_space_smoothed_coords_.as_mutable_span().slice(smooth_window); MutableSpan positions_slice = drawing_->strokes_for_write().positions_for_write().slice(points).slice(smooth_window); const float converging_threshold_px = 0.1f; @@ -267,8 +275,8 @@ struct PaintOperationExecutor { int num_converged = 0; for (const int64_t i : smooth_window.index_range()) { /* Record the curve fitting of this point. */ - self.screen_space_curve_fitted_positions_[i].append(coords_smoothed[i]); - Span smoothed_coords_point = self.screen_space_curve_fitted_positions_[i]; + self.screen_space_curve_fitted_coords_[i].append(coords_smoothed[i]); + Span smoothed_coords_point = self.screen_space_curve_fitted_coords_[i]; /* Get the sum of all the curve fittings of this point. */ float2 sum = smoothed_coords_point[0]; @@ -289,7 +297,7 @@ struct PaintOperationExecutor { } /* Update the positions in the current cache. */ - smoothed_coordinates_slice[i] = new_pos; + smoothed_coords_slice[i] = new_pos; positions_slice[i] = screen_space_to_object_space(new_pos); } @@ -297,11 +305,11 @@ struct PaintOperationExecutor { if (num_converged > 0) { self.active_smooth_index_ = math::min(self.active_smooth_index_ + int64_t(num_converged), int64_t(drawing_->strokes().points_num() - 1)); - if (self.screen_space_curve_fitted_positions_.size() - num_converged > 0) { - self.screen_space_curve_fitted_positions_.remove(0, num_converged); + if (self.screen_space_curve_fitted_coords_.size() - num_converged > 0) { + self.screen_space_curve_fitted_coords_.remove(0, num_converged); } else { - self.screen_space_curve_fitted_positions_.clear(); + self.screen_space_curve_fitted_coords_.clear(); } } } @@ -310,21 +318,21 @@ struct PaintOperationExecutor { const InputSample &extension_sample, const int curve_index) { - const float2 position = extension_sample.mouse_position; + const float2 coords = extension_sample.mouse_position; const float radius = this->radius_from_input_sample(extension_sample); const float opacity = this->opacity_from_input_sample(extension_sample); const ColorGeometry4f vertex_color = ColorGeometry4f(vertex_color_); bke::CurvesGeometry &curves = drawing_->strokes_for_write(); - const float2 prev_position = self.screen_space_positions_.last(); + const float2 prev_coords = self.screen_space_coords_.last(); const float prev_radius = drawing_->radii().last(); const float prev_opacity = drawing_->opacities().last(); const ColorGeometry4f prev_vertex_color = drawing_->vertex_colors().last(); /* Overwrite last point if it's very close. */ - if (math::distance(position, prev_position) < POINT_OVERRIDE_THRESHOLD_PX) { - curves.positions_for_write().last() = screen_space_to_object_space(position); + if (math::distance(coords, prev_coords) < POINT_OVERRIDE_THRESHOLD_PX) { + curves.positions_for_write().last() = screen_space_to_object_space(coords); drawing_->radii_for_write().last() = math::max(radius, prev_radius); drawing_->opacities_for_write().last() = math::max(opacity, prev_opacity); return; @@ -332,11 +340,9 @@ struct PaintOperationExecutor { /* If the next sample is far away, we subdivide the segment to add more points. */ int new_points_num = 1; - const float distance_px = math::distance(position, prev_position); + const float distance_px = math::distance(coords, prev_coords); if (distance_px > POINT_RESAMPLE_MIN_DISTANCE_PX) { - const int subdivisions = static_cast( - math::floor(distance_px / POINT_RESAMPLE_MIN_DISTANCE_PX)) - - 1; + const int subdivisions = int(math::floor(distance_px / POINT_RESAMPLE_MIN_DISTANCE_PX)) - 1; new_points_num += subdivisions; } @@ -347,28 +353,21 @@ struct PaintOperationExecutor { /* Subdivide stroke in new_range. */ IndexRange new_range(old_point_num, new_points_num); - Array new_screen_space_coordinates(new_points_num); + Array new_screen_space_coords(new_points_num); MutableSpan new_radii = drawing_->radii_for_write().slice(new_range); MutableSpan new_opacities = drawing_->opacities_for_write().slice(new_range); MutableSpan new_vertex_colors = drawing_->vertex_colors_for_write().slice( new_range); - const float step = 1.0f / static_cast(new_points_num); - float factor = step; - for (const int64_t i : new_range.index_range()) { - new_screen_space_coordinates[i] = bke::attribute_math::mix2( - factor, prev_position, position); - new_radii[i] = bke::attribute_math::mix2(factor, prev_radius, radius); - new_opacities[i] = bke::attribute_math::mix2(factor, prev_opacity, opacity); - new_vertex_colors[i] = bke::attribute_math::mix2( - factor, prev_vertex_color, ColorGeometry4f(vertex_color)); - factor += step; - } + linear_interpolation(prev_coords, coords, new_screen_space_coords); + linear_interpolation(prev_radius, radius, new_radii); + linear_interpolation(prev_opacity, opacity, new_opacities); + linear_interpolation(prev_vertex_color, vertex_color, new_vertex_colors); /* Update screen space buffers with new points. */ - self.screen_space_positions_.extend(new_screen_space_coordinates); - self.screen_space_smoothed_positions_.extend(new_screen_space_coordinates); - for (float2 new_position : new_screen_space_coordinates) { - self.screen_space_curve_fitted_positions_.append(Vector({new_position})); + self.screen_space_coords_.extend(new_screen_space_coords); + self.screen_space_smoothed_coords_.extend(new_screen_space_coords); + for (float2 new_position : new_screen_space_coords) { + self.screen_space_curve_fitted_coords_.append(Vector({new_position})); } /* Only start smoothing if there are enough points. */ @@ -376,16 +375,15 @@ struct PaintOperationExecutor { const IndexRange points = curves.points_by_curve()[curve_index]; if (points.size() < min_active_smoothing_points_num) { MutableSpan positions_slice = curves.positions_for_write().slice(new_range); - for (const int64_t i : new_screen_space_coordinates.index_range()) { - positions_slice[i] = screen_space_to_object_space(new_screen_space_coordinates[i]); + for (const int64_t i : new_screen_space_coords.index_range()) { + positions_slice[i] = screen_space_to_object_space(new_screen_space_coords[i]); } return; } /* Active smoothing is done in a window at the end of the new stroke. */ - const IndexRange smooth_window(self.active_smooth_index_, - points.size() - self.active_smooth_index_); - this->active_smoothing(self, points, smooth_window); + this->active_smoothing( + self, points, points.index_range().drop_front(self.active_smooth_index_)); } void execute(PaintOperation &self, const InputSample &extension_sample) @@ -446,7 +444,7 @@ static float dist_to_interpolated_2d( const float dist2 = math::distance_squared(posB, pos); if (dist1 + dist2 > 1e-5f) { - const float interpolated_val = interpf(valB, valA, dist1 / (dist1 + dist2)); + const float interpolated_val = math::interpolate(valB, valA, dist1 / (dist1 + dist2)); return math::distance(interpolated_val, val); } return 0.0f; @@ -460,7 +458,7 @@ void PaintOperation::simplify_stroke(bke::greasepencil::Drawing &drawing, const const VArray radii = drawing.radii(); /* Distance function for `ramer_douglas_peucker_simplify`. */ - const Span positions_2d = this->screen_space_smoothed_positions_.as_span(); + const Span positions_2d = this->screen_space_smoothed_coords_.as_span(); const auto dist_function = [&](int64_t first_index, int64_t last_index, int64_t index) { /* 2D coordinates are only stored for the current stroke, so offset the indices. */ const float dist_position_px = dist_to_line_segment_v2( -- 2.30.2