Grease Pencil: Sculpt Mode Auto-Masking option
This implements all the auto masking options in sculpt mode. * Stroke: Only affects strokes that are initially under the cursor. * Layer: Only affect strokes in the same layer as the initial strokes under the cursor. * Material: Only affect strokes with the same material as the initial strokes under the cursor. * Active Layer: Only affect strokes in the active layer. * Active Material: Only affect strokes that use the active material. The `Active Layer` toggle in the toolbar has been moved to this panel. Resolves #130022. Pull Request: https://projects.blender.org/blender/blender/pulls/132986
This commit is contained in:
@@ -4077,6 +4077,10 @@ def km_grease_pencil_sculpt_mode(params):
|
||||
# Active layer
|
||||
op_menu("GREASE_PENCIL_MT_layer_active", {"type": 'Y', "value": 'PRESS'}),
|
||||
|
||||
# Auto-masking menu.
|
||||
op_menu_pie("VIEW3D_MT_grease_pencil_sculpt_automasking_pie", {
|
||||
"type": 'A', "value": 'PRESS', "shift": True, "alt": True}),
|
||||
|
||||
*_template_paint_radial_control("gpencil_sculpt_paint"),
|
||||
*_template_asset_shelf_popup("VIEW3D_AST_brush_gpencil_sculpt", params.spacebar_action),
|
||||
*_template_items_context_panel("VIEW3D_PT_greasepencil_sculpt_context_menu", params.context_menu_event),
|
||||
|
||||
@@ -435,8 +435,6 @@ class _draw_tool_settings_context_mode:
|
||||
# Brush falloff
|
||||
layout.popover("VIEW3D_PT_tools_brush_falloff")
|
||||
|
||||
# Active layer only switch
|
||||
layout.prop(brush.gpencil_settings, "use_active_layer_only")
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
@@ -954,6 +952,12 @@ class VIEW3D_HT_header(Header):
|
||||
panel="VIEW3D_PT_grease_pencil_guide",
|
||||
text="Guides",
|
||||
)
|
||||
if object_mode == 'SCULPT_GREASE_PENCIL':
|
||||
layout.popover(
|
||||
panel="VIEW3D_PT_grease_pencil_sculpt_automasking",
|
||||
text="",
|
||||
icon=VIEW3D_HT_header._grease_pencil_sculpt_automasking_icon(tool_settings.gpencil_sculpt),
|
||||
)
|
||||
|
||||
elif object_mode == 'SCULPT':
|
||||
# If the active tool supports it, show the canvas selector popover.
|
||||
|
||||
@@ -47,7 +47,9 @@ class GreasePencilStrokeOperation : public PaintModeData {
|
||||
namespace greasepencil {
|
||||
|
||||
/* Get list of drawings the tool should be operating on. */
|
||||
Vector<ed::greasepencil::MutableDrawingInfo> get_drawings_for_painting(const bContext &C);
|
||||
Vector<ed::greasepencil::MutableDrawingInfo> get_drawings_for_stroke_operation(const bContext &C);
|
||||
Vector<ed::greasepencil::MutableDrawingInfo> get_drawings_with_masking_for_stroke_operation(
|
||||
const bContext &C);
|
||||
/* Get the brush radius accounting for pen pressure. */
|
||||
float brush_radius(const Scene &scene, const Brush &brush, float pressure);
|
||||
|
||||
@@ -118,15 +120,15 @@ struct GreasePencilStrokeParams {
|
||||
};
|
||||
|
||||
/* Point index mask for a drawing based on selection tool settings. */
|
||||
IndexMask point_selection_mask(const GreasePencilStrokeParams ¶ms,
|
||||
const bool use_masking,
|
||||
IndexMaskMemory &memory);
|
||||
IndexMask stroke_selection_mask(const GreasePencilStrokeParams ¶ms,
|
||||
const bool use_masking,
|
||||
IndexMaskMemory &memory);
|
||||
IndexMask fill_selection_mask(const GreasePencilStrokeParams ¶ms,
|
||||
const bool use_masking,
|
||||
IndexMaskMemory &memory);
|
||||
IndexMask point_mask_for_stroke_operation(const GreasePencilStrokeParams ¶ms,
|
||||
bool use_selection_masking,
|
||||
IndexMaskMemory &memory);
|
||||
IndexMask curve_mask_for_stroke_operation(const GreasePencilStrokeParams ¶ms,
|
||||
bool use_selection_masking,
|
||||
IndexMaskMemory &memory);
|
||||
IndexMask fill_mask_for_stroke_operation(const GreasePencilStrokeParams ¶ms,
|
||||
bool use_selection_masking,
|
||||
IndexMaskMemory &memory);
|
||||
|
||||
bke::crazyspace::GeometryDeformation get_drawing_deformation(
|
||||
const GreasePencilStrokeParams ¶ms);
|
||||
@@ -170,6 +172,13 @@ class GreasePencilStrokeOperationCommon : public GreasePencilStrokeOperation {
|
||||
/** Previous mouse position for computing the direction. */
|
||||
float2 prev_mouse_position;
|
||||
|
||||
/* When auto-masking is used, this contains the index mask of the elements that are affected. */
|
||||
struct AutoMaskingInfo {
|
||||
IndexMask point_mask;
|
||||
IndexMaskMemory memory;
|
||||
};
|
||||
Array<AutoMaskingInfo> auto_masking_info_per_drawing;
|
||||
|
||||
GreasePencilStrokeOperationCommon() {}
|
||||
GreasePencilStrokeOperationCommon(const BrushStrokeMode stroke_mode) : stroke_mode(stroke_mode)
|
||||
{
|
||||
@@ -179,18 +188,25 @@ class GreasePencilStrokeOperationCommon : public GreasePencilStrokeOperation {
|
||||
float2 mouse_delta(const InputSample &input_sample) const;
|
||||
|
||||
void init_stroke(const bContext &C, const InputSample &start_sample);
|
||||
void init_auto_masking(const bContext &C, const InputSample &start_sample);
|
||||
void stroke_extended(const InputSample &extension_sample);
|
||||
|
||||
void foreach_editable_drawing_with_automask(
|
||||
const bContext &C,
|
||||
FunctionRef<bool(const GreasePencilStrokeParams ¶ms, const IndexMask &points)> fn) const;
|
||||
void foreach_editable_drawing_with_automask(
|
||||
const bContext &C,
|
||||
FunctionRef<bool(const GreasePencilStrokeParams ¶ms,
|
||||
const IndexMask &points,
|
||||
const DeltaProjectionFunc &projection_fn)> fn) const;
|
||||
|
||||
/** Used in vertex paint mode. */
|
||||
void foreach_editable_drawing(
|
||||
const bContext &C, FunctionRef<bool(const GreasePencilStrokeParams ¶ms)> fn) const;
|
||||
void foreach_editable_drawing(
|
||||
const bContext &C,
|
||||
GrainSize grain_size,
|
||||
FunctionRef<bool(const GreasePencilStrokeParams ¶ms)> fn) const;
|
||||
void foreach_editable_drawing(
|
||||
const bContext &C,
|
||||
FunctionRef<bool(const GreasePencilStrokeParams ¶ms,
|
||||
const DeltaProjectionFunc &projection_fn)> fn) const;
|
||||
};
|
||||
|
||||
/* Operations */
|
||||
|
||||
@@ -28,19 +28,32 @@
|
||||
|
||||
namespace blender::ed::sculpt_paint::greasepencil {
|
||||
|
||||
Vector<ed::greasepencil::MutableDrawingInfo> get_drawings_for_painting(const bContext &C)
|
||||
Vector<ed::greasepencil::MutableDrawingInfo> get_drawings_for_stroke_operation(const bContext &C)
|
||||
{
|
||||
using namespace blender::bke::greasepencil;
|
||||
|
||||
const Scene &scene = *CTX_data_scene(&C);
|
||||
Object &ob_orig = *CTX_data_active_object(&C);
|
||||
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(ob_orig.data);
|
||||
Paint &paint = *BKE_paint_get_active_from_context(&C);
|
||||
const Brush &brush = *BKE_paint_brush(&paint);
|
||||
const bool active_layer_only = ((brush.gpencil_settings->flag & GP_BRUSH_ACTIVE_LAYER_ONLY) !=
|
||||
0);
|
||||
|
||||
if (active_layer_only) {
|
||||
/* Apply to all editable drawings. */
|
||||
return ed::greasepencil::retrieve_editable_drawings_with_falloff(scene, grease_pencil);
|
||||
}
|
||||
|
||||
Vector<ed::greasepencil::MutableDrawingInfo> get_drawings_with_masking_for_stroke_operation(
|
||||
const bContext &C)
|
||||
{
|
||||
using namespace blender::bke::greasepencil;
|
||||
|
||||
const Scene &scene = *CTX_data_scene(&C);
|
||||
const ToolSettings &ts = *CTX_data_tool_settings(&C);
|
||||
Object &ob_orig = *CTX_data_active_object(&C);
|
||||
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(ob_orig.data);
|
||||
|
||||
const bool active_layer_masking = (ts.gp_sculpt.flag &
|
||||
GP_SCULPT_SETT_FLAG_AUTOMASK_LAYER_ACTIVE) != 0;
|
||||
|
||||
if (active_layer_masking) {
|
||||
/* Apply only to the drawing at the current frame of the active layer. */
|
||||
if (!grease_pencil.has_active_layer()) {
|
||||
return {};
|
||||
@@ -293,35 +306,33 @@ GreasePencilStrokeParams GreasePencilStrokeParams::from_context(
|
||||
drawing};
|
||||
}
|
||||
|
||||
IndexMask point_selection_mask(const GreasePencilStrokeParams ¶ms,
|
||||
const bool use_masking,
|
||||
IndexMaskMemory &memory)
|
||||
IndexMask point_mask_for_stroke_operation(const GreasePencilStrokeParams ¶ms,
|
||||
const bool use_selection_masking,
|
||||
IndexMaskMemory &memory)
|
||||
{
|
||||
|
||||
return use_masking ? ed::greasepencil::retrieve_editable_and_selected_points(
|
||||
params.ob_orig, params.drawing, params.layer_index, memory) :
|
||||
ed::greasepencil::retrieve_editable_points(
|
||||
params.ob_orig, params.drawing, params.layer_index, memory);
|
||||
return use_selection_masking ? ed::greasepencil::retrieve_editable_and_selected_points(
|
||||
params.ob_orig, params.drawing, params.layer_index, memory) :
|
||||
ed::greasepencil::retrieve_editable_points(
|
||||
params.ob_orig, params.drawing, params.layer_index, memory);
|
||||
}
|
||||
|
||||
IndexMask stroke_selection_mask(const GreasePencilStrokeParams ¶ms,
|
||||
const bool use_masking,
|
||||
IndexMaskMemory &memory)
|
||||
IndexMask curve_mask_for_stroke_operation(const GreasePencilStrokeParams ¶ms,
|
||||
const bool use_selection_masking,
|
||||
IndexMaskMemory &memory)
|
||||
{
|
||||
|
||||
return use_masking ? ed::greasepencil::retrieve_editable_and_selected_strokes(
|
||||
params.ob_orig, params.drawing, params.layer_index, memory) :
|
||||
ed::greasepencil::retrieve_editable_strokes(
|
||||
params.ob_orig, params.drawing, params.layer_index, memory);
|
||||
return use_selection_masking ? ed::greasepencil::retrieve_editable_and_selected_strokes(
|
||||
params.ob_orig, params.drawing, params.layer_index, memory) :
|
||||
ed::greasepencil::retrieve_editable_strokes(
|
||||
params.ob_orig, params.drawing, params.layer_index, memory);
|
||||
}
|
||||
|
||||
IndexMask fill_selection_mask(const GreasePencilStrokeParams ¶ms,
|
||||
const bool use_masking,
|
||||
IndexMaskMemory &memory)
|
||||
IndexMask fill_mask_for_stroke_operation(const GreasePencilStrokeParams ¶ms,
|
||||
const bool use_selection_masking,
|
||||
IndexMaskMemory &memory)
|
||||
{
|
||||
return use_masking ? ed::greasepencil::retrieve_editable_and_selected_fill_strokes(
|
||||
params.ob_orig, params.drawing, params.layer_index, memory) :
|
||||
params.drawing.strokes().curves_range();
|
||||
return use_selection_masking ? ed::greasepencil::retrieve_editable_and_selected_fill_strokes(
|
||||
params.ob_orig, params.drawing, params.layer_index, memory) :
|
||||
params.drawing.strokes().curves_range();
|
||||
}
|
||||
|
||||
bke::crazyspace::GeometryDeformation get_drawing_deformation(
|
||||
@@ -395,6 +406,92 @@ float2 GreasePencilStrokeOperationCommon::mouse_delta(const InputSample &input_s
|
||||
return input_sample.mouse_position - this->prev_mouse_position;
|
||||
}
|
||||
|
||||
void GreasePencilStrokeOperationCommon::foreach_editable_drawing_with_automask(
|
||||
const bContext &C,
|
||||
FunctionRef<bool(const GreasePencilStrokeParams ¶ms, const IndexMask &point_mask)> fn)
|
||||
const
|
||||
{
|
||||
using namespace blender::bke::greasepencil;
|
||||
|
||||
const Scene &scene = *CTX_data_scene(&C);
|
||||
Depsgraph &depsgraph = *CTX_data_depsgraph_pointer(&C);
|
||||
ARegion ®ion = *CTX_wm_region(&C);
|
||||
RegionView3D &rv3d = *CTX_wm_region_view3d(&C);
|
||||
Object &object = *CTX_data_active_object(&C);
|
||||
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object.data);
|
||||
|
||||
std::atomic<bool> changed = false;
|
||||
const Vector<MutableDrawingInfo> drawings = get_drawings_with_masking_for_stroke_operation(C);
|
||||
for (const int64_t i : drawings.index_range()) {
|
||||
const MutableDrawingInfo &info = drawings[i];
|
||||
const AutoMaskingInfo &auto_mask_info = this->auto_masking_info_per_drawing[i];
|
||||
GreasePencilStrokeParams params = GreasePencilStrokeParams::from_context(
|
||||
scene,
|
||||
depsgraph,
|
||||
region,
|
||||
rv3d,
|
||||
object,
|
||||
info.layer_index,
|
||||
info.frame_number,
|
||||
info.multi_frame_falloff,
|
||||
info.drawing);
|
||||
|
||||
if (fn(params, auto_mask_info.point_mask)) {
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
|
||||
WM_event_add_notifier(&C, NC_GEOM | ND_DATA, &grease_pencil);
|
||||
}
|
||||
}
|
||||
|
||||
void GreasePencilStrokeOperationCommon::foreach_editable_drawing_with_automask(
|
||||
const bContext &C,
|
||||
FunctionRef<bool(const GreasePencilStrokeParams ¶ms,
|
||||
const IndexMask &point_mask,
|
||||
const DeltaProjectionFunc &projection_fn)> fn) const
|
||||
{
|
||||
using namespace blender::bke::greasepencil;
|
||||
|
||||
const Scene &scene = *CTX_data_scene(&C);
|
||||
Depsgraph &depsgraph = *CTX_data_depsgraph_pointer(&C);
|
||||
ARegion ®ion = *CTX_wm_region(&C);
|
||||
RegionView3D &rv3d = *CTX_wm_region_view3d(&C);
|
||||
Object &object = *CTX_data_active_object(&C);
|
||||
Object &object_eval = *DEG_get_evaluated_object(&depsgraph, &object);
|
||||
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object.data);
|
||||
|
||||
std::atomic<bool> changed = false;
|
||||
const Vector<MutableDrawingInfo> drawings = get_drawings_with_masking_for_stroke_operation(C);
|
||||
threading::parallel_for_each(drawings.index_range(), [&](const int i) {
|
||||
const MutableDrawingInfo &info = drawings[i];
|
||||
const Layer &layer = grease_pencil.layer(info.layer_index);
|
||||
const AutoMaskingInfo &auto_mask_info = this->auto_masking_info_per_drawing[i];
|
||||
const GreasePencilStrokeParams params = GreasePencilStrokeParams::from_context(
|
||||
scene,
|
||||
depsgraph,
|
||||
region,
|
||||
rv3d,
|
||||
object,
|
||||
info.layer_index,
|
||||
info.frame_number,
|
||||
info.multi_frame_falloff,
|
||||
info.drawing);
|
||||
|
||||
const DeltaProjectionFunc projection_fn = get_screen_projection_fn(params, object_eval, layer);
|
||||
if (fn(params, auto_mask_info.point_mask, projection_fn)) {
|
||||
changed = true;
|
||||
}
|
||||
});
|
||||
|
||||
if (changed) {
|
||||
DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
|
||||
WM_event_add_notifier(&C, NC_GEOM | ND_DATA, &grease_pencil);
|
||||
}
|
||||
}
|
||||
|
||||
void GreasePencilStrokeOperationCommon::foreach_editable_drawing(
|
||||
const bContext &C, FunctionRef<bool(const GreasePencilStrokeParams ¶ms)> fn) const
|
||||
{
|
||||
@@ -407,8 +504,8 @@ void GreasePencilStrokeOperationCommon::foreach_editable_drawing(
|
||||
Object &object = *CTX_data_active_object(&C);
|
||||
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object.data);
|
||||
|
||||
std::atomic<bool> changed = false;
|
||||
const Vector<MutableDrawingInfo> drawings = get_drawings_for_painting(C);
|
||||
bool changed = false;
|
||||
const Vector<MutableDrawingInfo> drawings = get_drawings_for_stroke_operation(C);
|
||||
for (const int64_t i : drawings.index_range()) {
|
||||
const MutableDrawingInfo &info = drawings[i];
|
||||
GreasePencilStrokeParams params = GreasePencilStrokeParams::from_context(
|
||||
@@ -447,7 +544,7 @@ void GreasePencilStrokeOperationCommon::foreach_editable_drawing(
|
||||
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object.data);
|
||||
|
||||
std::atomic<bool> changed = false;
|
||||
const Vector<MutableDrawingInfo> drawings = get_drawings_for_painting(C);
|
||||
const Vector<MutableDrawingInfo> drawings = get_drawings_for_stroke_operation(C);
|
||||
threading::parallel_for(drawings.index_range(), grain_size.value, [&](const IndexRange range) {
|
||||
for (const int64_t i : range) {
|
||||
const MutableDrawingInfo &info = drawings[i];
|
||||
@@ -473,48 +570,6 @@ void GreasePencilStrokeOperationCommon::foreach_editable_drawing(
|
||||
}
|
||||
}
|
||||
|
||||
void GreasePencilStrokeOperationCommon::foreach_editable_drawing(
|
||||
const bContext &C,
|
||||
FunctionRef<bool(const GreasePencilStrokeParams ¶ms,
|
||||
const DeltaProjectionFunc &projection_fn)> fn) const
|
||||
{
|
||||
using namespace blender::bke::greasepencil;
|
||||
|
||||
const Scene &scene = *CTX_data_scene(&C);
|
||||
Depsgraph &depsgraph = *CTX_data_depsgraph_pointer(&C);
|
||||
ARegion ®ion = *CTX_wm_region(&C);
|
||||
RegionView3D &rv3d = *CTX_wm_region_view3d(&C);
|
||||
Object &object = *CTX_data_active_object(&C);
|
||||
Object &object_eval = *DEG_get_evaluated_object(&depsgraph, &object);
|
||||
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object.data);
|
||||
|
||||
std::atomic<bool> changed = false;
|
||||
const Vector<MutableDrawingInfo> drawings = get_drawings_for_painting(C);
|
||||
threading::parallel_for_each(drawings, [&](const MutableDrawingInfo &info) {
|
||||
const Layer &layer = grease_pencil.layer(info.layer_index);
|
||||
|
||||
const GreasePencilStrokeParams params = GreasePencilStrokeParams::from_context(
|
||||
scene,
|
||||
depsgraph,
|
||||
region,
|
||||
rv3d,
|
||||
object,
|
||||
info.layer_index,
|
||||
info.frame_number,
|
||||
info.multi_frame_falloff,
|
||||
info.drawing);
|
||||
const DeltaProjectionFunc projection_fn = get_screen_projection_fn(params, object_eval, layer);
|
||||
if (fn(params, projection_fn)) {
|
||||
changed = true;
|
||||
}
|
||||
});
|
||||
|
||||
if (changed) {
|
||||
DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
|
||||
WM_event_add_notifier(&C, NC_GEOM | ND_DATA, &grease_pencil);
|
||||
}
|
||||
}
|
||||
|
||||
void GreasePencilStrokeOperationCommon::init_stroke(const bContext &C,
|
||||
const InputSample &start_sample)
|
||||
{
|
||||
@@ -527,6 +582,150 @@ void GreasePencilStrokeOperationCommon::init_stroke(const bContext &C,
|
||||
this->prev_mouse_position = start_sample.mouse_position;
|
||||
}
|
||||
|
||||
void GreasePencilStrokeOperationCommon::init_auto_masking(const bContext &C,
|
||||
const InputSample &start_sample)
|
||||
{
|
||||
const Scene &scene = *CTX_data_scene(&C);
|
||||
ARegion ®ion = *CTX_wm_region(&C);
|
||||
RegionView3D &rv3d = *CTX_wm_region_view3d(&C);
|
||||
Object &object = *CTX_data_active_object(&C);
|
||||
Depsgraph &depsgraph = *CTX_data_depsgraph_pointer(&C);
|
||||
Paint &paint = *BKE_paint_get_active_from_context(&C);
|
||||
const Brush &brush = *BKE_paint_brush(&paint);
|
||||
|
||||
const eGP_Sculpt_SelectMaskFlag sculpt_selection_flag = eGP_Sculpt_SelectMaskFlag(
|
||||
scene.toolsettings->gpencil_selectmode_sculpt);
|
||||
const bool use_sculpt_selection_masking = (sculpt_selection_flag &
|
||||
(GP_SCULPT_MASK_SELECTMODE_POINT |
|
||||
GP_SCULPT_MASK_SELECTMODE_STROKE |
|
||||
GP_SCULPT_MASK_SELECTMODE_SEGMENT)) != 0;
|
||||
|
||||
const eGP_Sculpt_SettingsFlag sculpt_settings_flag = eGP_Sculpt_SettingsFlag(
|
||||
scene.toolsettings->gp_sculpt.flag);
|
||||
const bool use_auto_mask_stroke = (sculpt_settings_flag & GP_SCULPT_SETT_FLAG_AUTOMASK_STROKE);
|
||||
const bool use_auto_mask_layer = (sculpt_settings_flag &
|
||||
GP_SCULPT_SETT_FLAG_AUTOMASK_LAYER_STROKE);
|
||||
const bool use_auto_mask_material = (sculpt_settings_flag &
|
||||
GP_SCULPT_SETT_FLAG_AUTOMASK_MATERIAL_STROKE);
|
||||
const bool use_auto_mask_active_material = (sculpt_settings_flag &
|
||||
GP_SCULPT_SETT_FLAG_AUTOMASK_MATERIAL_ACTIVE);
|
||||
|
||||
const float radius = brush_radius(scene, brush);
|
||||
const int2 mval_i = int2(math::round(start_sample.mouse_position));
|
||||
const int active_material_index = math::max(object.actcol - 1, 0);
|
||||
|
||||
const Vector<MutableDrawingInfo> drawings = get_drawings_with_masking_for_stroke_operation(C);
|
||||
this->auto_masking_info_per_drawing.reinitialize(drawings.size());
|
||||
|
||||
VectorSet<int> masked_layer_indices;
|
||||
VectorSet<int> masked_material_indices;
|
||||
for (const int drawing_i : drawings.index_range()) {
|
||||
const MutableDrawingInfo &drawing_info = drawings[drawing_i];
|
||||
AutoMaskingInfo &automask_info = this->auto_masking_info_per_drawing[drawing_i];
|
||||
GreasePencilStrokeParams params = GreasePencilStrokeParams::from_context(
|
||||
scene,
|
||||
depsgraph,
|
||||
region,
|
||||
rv3d,
|
||||
object,
|
||||
drawing_info.layer_index,
|
||||
drawing_info.frame_number,
|
||||
drawing_info.multi_frame_falloff,
|
||||
drawing_info.drawing);
|
||||
automask_info.point_mask = point_mask_for_stroke_operation(
|
||||
params, use_sculpt_selection_masking, automask_info.memory);
|
||||
if (automask_info.point_mask.is_empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const bke::CurvesGeometry &curves = drawing_info.drawing.strokes();
|
||||
const OffsetIndices<int> points_by_curve = curves.points_by_curve();
|
||||
const bke::AttributeAccessor attributes = curves.attributes();
|
||||
|
||||
if (use_auto_mask_active_material) {
|
||||
IndexMaskMemory memory;
|
||||
const VArraySpan<int> materials = *attributes.lookup_or_default<int>(
|
||||
"material_index", bke::AttrDomain::Point, 0);
|
||||
const IndexMask active_material_mask = IndexMask::from_predicate(
|
||||
curves.points_range(), GrainSize(4096), memory, [&](const int64_t point_i) {
|
||||
return active_material_index == materials[point_i];
|
||||
});
|
||||
automask_info.point_mask = IndexMask::from_intersection(
|
||||
automask_info.point_mask, active_material_mask, automask_info.memory);
|
||||
if (automask_info.point_mask.is_empty()) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (use_auto_mask_layer || use_auto_mask_stroke || use_auto_mask_material) {
|
||||
Array<float2> view_positions = calculate_view_positions(params, automask_info.point_mask);
|
||||
|
||||
IndexMaskMemory memory;
|
||||
const IndexMask stroke_selection = curve_mask_for_stroke_operation(
|
||||
params, use_sculpt_selection_masking, memory);
|
||||
const IndexMask strokes_under_brush = IndexMask::from_predicate(
|
||||
stroke_selection, GrainSize(512), memory, [&](const int curve_i) {
|
||||
for (const int point_i : points_by_curve[curve_i]) {
|
||||
const float distance = math::distance(mval_i, int2(view_positions[point_i]));
|
||||
if (distance <= radius) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
if (use_auto_mask_layer && !strokes_under_brush.is_empty()) {
|
||||
masked_layer_indices.add(drawing_info.layer_index);
|
||||
}
|
||||
|
||||
if (use_auto_mask_stroke) {
|
||||
automask_info.point_mask = IndexMask::from_intersection(
|
||||
automask_info.point_mask,
|
||||
IndexMask::from_ranges(curves.points_by_curve(), strokes_under_brush, memory),
|
||||
automask_info.memory);
|
||||
}
|
||||
|
||||
if (use_auto_mask_material) {
|
||||
const VArraySpan<int> material_indices = *attributes.lookup<int>("material_index",
|
||||
bke::AttrDomain::Curve);
|
||||
strokes_under_brush.foreach_index(
|
||||
[&](const int curve_i) { masked_material_indices.add(material_indices[curve_i]); });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* When we mask by the initial strokes under the cursor, the other masking options don't affect
|
||||
* the resulting mask. So we can skip the second loop. */
|
||||
if (use_auto_mask_stroke) {
|
||||
return;
|
||||
}
|
||||
|
||||
threading::parallel_for_each(drawings.index_range(), [&](const int drawing_i) {
|
||||
const MutableDrawingInfo &drawing_info = drawings[drawing_i];
|
||||
AutoMaskingInfo &automask_info = this->auto_masking_info_per_drawing[drawing_i];
|
||||
|
||||
if (use_auto_mask_layer && !masked_layer_indices.contains(drawing_info.layer_index)) {
|
||||
automask_info.point_mask = {};
|
||||
return;
|
||||
}
|
||||
|
||||
if (use_auto_mask_material) {
|
||||
const bke::CurvesGeometry &curves = drawing_info.drawing.strokes();
|
||||
const VArraySpan<int> material_indices = *curves.attributes().lookup<int>("material_index");
|
||||
IndexMaskMemory memory;
|
||||
const IndexMask masked_curves = IndexMask::from_predicate(
|
||||
curves.curves_range(), GrainSize(1024), memory, [&](const int curve_i) {
|
||||
return masked_material_indices.contains(material_indices[curve_i]);
|
||||
});
|
||||
|
||||
automask_info.point_mask = IndexMask::from_intersection(
|
||||
automask_info.point_mask,
|
||||
IndexMask::from_ranges(curves.points_by_curve(), masked_curves, memory),
|
||||
automask_info.memory);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void GreasePencilStrokeOperationCommon::stroke_extended(const InputSample &extension_sample)
|
||||
{
|
||||
this->prev_mouse_position = extension_sample.mouse_position;
|
||||
|
||||
@@ -47,8 +47,11 @@ void CloneOperation::on_stroke_begin(const bContext &C, const InputSample &start
|
||||
* - Continuous: Create multiple copies during the stroke (disabled)
|
||||
*
|
||||
* Here we only have the GPv2 behavior that actually works for now. */
|
||||
this->foreach_editable_drawing(
|
||||
C, [&](const GreasePencilStrokeParams ¶ms, const DeltaProjectionFunc &projection_fn) {
|
||||
this->foreach_editable_drawing_with_automask(
|
||||
C,
|
||||
[&](const GreasePencilStrokeParams ¶ms,
|
||||
const IndexMask & /*point_mask*/,
|
||||
const DeltaProjectionFunc &projection_fn) {
|
||||
/* Only insert on the active layer. */
|
||||
if (¶ms.layer != grease_pencil.get_active_layer()) {
|
||||
return false;
|
||||
|
||||
@@ -127,17 +127,16 @@ void GrabOperation::on_stroke_begin(const bContext &C, const InputSample &start_
|
||||
Object &ob_eval = *DEG_get_evaluated_object(&depsgraph, &ob_orig);
|
||||
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(ob_orig.data);
|
||||
|
||||
const bool is_masking = GPENCIL_ANY_SCULPT_MASK(
|
||||
eGP_Sculpt_SelectMaskFlag(scene.toolsettings->gpencil_selectmode_sculpt));
|
||||
|
||||
init_brush(brush);
|
||||
init_auto_masking(C, start_sample);
|
||||
|
||||
this->prev_mouse_position = start_sample.mouse_position;
|
||||
|
||||
const Vector<MutableDrawingInfo> drawings = get_drawings_for_painting(C);
|
||||
const Vector<MutableDrawingInfo> drawings = get_drawings_with_masking_for_stroke_operation(C);
|
||||
this->drawing_data.reinitialize(drawings.size());
|
||||
threading::parallel_for_each(drawings.index_range(), [&](const int i) {
|
||||
const MutableDrawingInfo &info = drawings[i];
|
||||
const AutoMaskingInfo &auto_mask_info = this->auto_masking_info_per_drawing[i];
|
||||
BLI_assert(info.layer_index >= 0);
|
||||
PointWeights &data = this->drawing_data[i];
|
||||
|
||||
@@ -155,10 +154,8 @@ void GrabOperation::on_stroke_begin(const bContext &C, const InputSample &start_
|
||||
info.frame_number,
|
||||
info.multi_frame_falloff,
|
||||
info.drawing};
|
||||
IndexMaskMemory selection_memory;
|
||||
IndexMask selection = point_selection_mask(params, is_masking, selection_memory);
|
||||
|
||||
Array<float2> view_positions = calculate_view_positions(params, selection);
|
||||
Array<float2> view_positions = calculate_view_positions(params, auto_mask_info.point_mask);
|
||||
|
||||
/* Cache points under brush influence. */
|
||||
Vector<float> weights;
|
||||
@@ -167,7 +164,7 @@ void GrabOperation::on_stroke_begin(const bContext &C, const InputSample &start_
|
||||
start_sample.mouse_position,
|
||||
1.0f,
|
||||
info.multi_frame_falloff,
|
||||
selection,
|
||||
auto_mask_info.point_mask,
|
||||
view_positions,
|
||||
weights,
|
||||
data.memory);
|
||||
|
||||
@@ -33,6 +33,7 @@ class PinchOperation : public GreasePencilStrokeOperationCommon {
|
||||
void PinchOperation::on_stroke_begin(const bContext &C, const InputSample &start_sample)
|
||||
{
|
||||
this->init_stroke(C, start_sample);
|
||||
this->init_auto_masking(C, start_sample);
|
||||
}
|
||||
|
||||
void PinchOperation::on_stroke_extended(const bContext &C, const InputSample &extension_sample)
|
||||
@@ -41,25 +42,20 @@ void PinchOperation::on_stroke_extended(const bContext &C, const InputSample &ex
|
||||
Paint &paint = *BKE_paint_get_active_from_context(&C);
|
||||
const Brush &brush = *BKE_paint_brush(&paint);
|
||||
const bool invert = this->is_inverted(brush);
|
||||
const bool is_masking = GPENCIL_ANY_SCULPT_MASK(
|
||||
eGP_Sculpt_SelectMaskFlag(scene.toolsettings->gpencil_selectmode_sculpt));
|
||||
|
||||
this->foreach_editable_drawing(
|
||||
C, [&](const GreasePencilStrokeParams ¶ms, const DeltaProjectionFunc &projection_fn) {
|
||||
IndexMaskMemory selection_memory;
|
||||
const IndexMask selection = point_selection_mask(params, is_masking, selection_memory);
|
||||
if (selection.is_empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this->foreach_editable_drawing_with_automask(
|
||||
C,
|
||||
[&](const GreasePencilStrokeParams ¶ms,
|
||||
const IndexMask &point_mask,
|
||||
const DeltaProjectionFunc &projection_fn) {
|
||||
bke::crazyspace::GeometryDeformation deformation = get_drawing_deformation(params);
|
||||
Array<float2> view_positions = calculate_view_positions(params, selection);
|
||||
Array<float2> view_positions = calculate_view_positions(params, point_mask);
|
||||
bke::CurvesGeometry &curves = params.drawing.strokes_for_write();
|
||||
MutableSpan<float3> positions = curves.positions_for_write();
|
||||
|
||||
const float2 target = extension_sample.mouse_position;
|
||||
|
||||
selection.foreach_index(GrainSize(4096), [&](const int64_t point_i) {
|
||||
point_mask.foreach_index(GrainSize(4096), [&](const int64_t point_i) {
|
||||
const float2 &co = view_positions[point_i];
|
||||
const float influence = brush_point_influence(
|
||||
scene, brush, co, extension_sample, params.multi_frame_falloff);
|
||||
|
||||
@@ -33,6 +33,7 @@ class PushOperation : public GreasePencilStrokeOperationCommon {
|
||||
void PushOperation::on_stroke_begin(const bContext &C, const InputSample &start_sample)
|
||||
{
|
||||
this->init_stroke(C, start_sample);
|
||||
this->init_auto_masking(C, start_sample);
|
||||
}
|
||||
|
||||
void PushOperation::on_stroke_extended(const bContext &C, const InputSample &extension_sample)
|
||||
@@ -41,25 +42,19 @@ void PushOperation::on_stroke_extended(const bContext &C, const InputSample &ext
|
||||
Paint &paint = *BKE_paint_get_active_from_context(&C);
|
||||
const Brush &brush = *BKE_paint_brush(&paint);
|
||||
|
||||
const bool is_masking = GPENCIL_ANY_SCULPT_MASK(
|
||||
eGP_Sculpt_SelectMaskFlag(scene.toolsettings->gpencil_selectmode_sculpt));
|
||||
|
||||
this->foreach_editable_drawing(
|
||||
C, [&](const GreasePencilStrokeParams ¶ms, const DeltaProjectionFunc &projection_fn) {
|
||||
IndexMaskMemory selection_memory;
|
||||
const IndexMask selection = point_selection_mask(params, is_masking, selection_memory);
|
||||
if (selection.is_empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this->foreach_editable_drawing_with_automask(
|
||||
C,
|
||||
[&](const GreasePencilStrokeParams ¶ms,
|
||||
const IndexMask &point_mask,
|
||||
const DeltaProjectionFunc &projection_fn) {
|
||||
bke::crazyspace::GeometryDeformation deformation = get_drawing_deformation(params);
|
||||
Array<float2> view_positions = calculate_view_positions(params, selection);
|
||||
Array<float2> view_positions = calculate_view_positions(params, point_mask);
|
||||
bke::CurvesGeometry &curves = params.drawing.strokes_for_write();
|
||||
MutableSpan<float3> positions = curves.positions_for_write();
|
||||
|
||||
const float2 mouse_delta = this->mouse_delta(extension_sample);
|
||||
|
||||
selection.foreach_index(GrainSize(4096), [&](const int64_t point_i) {
|
||||
point_mask.foreach_index(GrainSize(4096), [&](const int64_t point_i) {
|
||||
const float2 &co = view_positions[point_i];
|
||||
const float influence = brush_point_influence(
|
||||
scene, brush, co, extension_sample, params.multi_frame_falloff);
|
||||
|
||||
@@ -53,6 +53,7 @@ uint32_t RandomizeOperation::unique_seed() const
|
||||
void RandomizeOperation::on_stroke_begin(const bContext &C, const InputSample &start_sample)
|
||||
{
|
||||
this->init_stroke(C, start_sample);
|
||||
this->init_auto_masking(C, start_sample);
|
||||
}
|
||||
|
||||
void RandomizeOperation::on_stroke_extended(const bContext &C, const InputSample &extension_sample)
|
||||
@@ -62,21 +63,15 @@ void RandomizeOperation::on_stroke_extended(const bContext &C, const InputSample
|
||||
const Brush &brush = *BKE_paint_brush(&paint);
|
||||
const int sculpt_mode_flag = brush.gpencil_settings->sculpt_mode_flag;
|
||||
|
||||
const bool is_masking = GPENCIL_ANY_SCULPT_MASK(
|
||||
eGP_Sculpt_SelectMaskFlag(scene.toolsettings->gpencil_selectmode_sculpt));
|
||||
|
||||
this->foreach_editable_drawing(
|
||||
C, [&](const GreasePencilStrokeParams ¶ms, const DeltaProjectionFunc &projection_fn) {
|
||||
this->foreach_editable_drawing_with_automask(
|
||||
C,
|
||||
[&](const GreasePencilStrokeParams ¶ms,
|
||||
const IndexMask &point_mask,
|
||||
const DeltaProjectionFunc &projection_fn) {
|
||||
const uint32_t seed = this->unique_seed();
|
||||
|
||||
IndexMaskMemory selection_memory;
|
||||
const IndexMask selection = point_selection_mask(params, is_masking, selection_memory);
|
||||
if (selection.is_empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bke::crazyspace::GeometryDeformation deformation = get_drawing_deformation(params);
|
||||
Array<float2> view_positions = calculate_view_positions(params, selection);
|
||||
Array<float2> view_positions = calculate_view_positions(params, point_mask);
|
||||
bke::CurvesGeometry &curves = params.drawing.strokes_for_write();
|
||||
bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
|
||||
|
||||
@@ -88,7 +83,7 @@ void RandomizeOperation::on_stroke_extended(const bContext &C, const InputSample
|
||||
const float2 forward = math::normalize(this->mouse_delta(extension_sample));
|
||||
const float2 sideways = float2(-forward.y, forward.x);
|
||||
|
||||
selection.foreach_index(GrainSize(4096), [&](const int64_t point_i) {
|
||||
point_mask.foreach_index(GrainSize(4096), [&](const int64_t point_i) {
|
||||
const float2 &co = view_positions[point_i];
|
||||
const float influence = brush_point_influence(
|
||||
scene, brush, co, extension_sample, params.multi_frame_falloff);
|
||||
@@ -105,7 +100,7 @@ void RandomizeOperation::on_stroke_extended(const bContext &C, const InputSample
|
||||
}
|
||||
if (sculpt_mode_flag & GP_SCULPT_FLAGMODE_APPLY_STRENGTH) {
|
||||
MutableSpan<float> opacities = params.drawing.opacities_for_write();
|
||||
selection.foreach_index(GrainSize(4096), [&](const int64_t point_i) {
|
||||
point_mask.foreach_index(GrainSize(4096), [&](const int64_t point_i) {
|
||||
const float2 &co = view_positions[point_i];
|
||||
const float influence = brush_point_influence(
|
||||
scene, brush, co, extension_sample, params.multi_frame_falloff);
|
||||
@@ -119,7 +114,7 @@ void RandomizeOperation::on_stroke_extended(const bContext &C, const InputSample
|
||||
}
|
||||
if (sculpt_mode_flag & GP_SCULPT_FLAGMODE_APPLY_THICKNESS) {
|
||||
const MutableSpan<float> radii = params.drawing.radii_for_write();
|
||||
selection.foreach_index(GrainSize(4096), [&](const int64_t point_i) {
|
||||
point_mask.foreach_index(GrainSize(4096), [&](const int64_t point_i) {
|
||||
const float2 &co = view_positions[point_i];
|
||||
const float influence = brush_point_influence(
|
||||
scene, brush, co, extension_sample, params.multi_frame_falloff);
|
||||
@@ -135,7 +130,7 @@ void RandomizeOperation::on_stroke_extended(const bContext &C, const InputSample
|
||||
if (sculpt_mode_flag & GP_SCULPT_FLAGMODE_APPLY_UV) {
|
||||
bke::SpanAttributeWriter<float> rotations =
|
||||
attributes.lookup_or_add_for_write_span<float>("rotation", bke::AttrDomain::Point);
|
||||
selection.foreach_index(GrainSize(4096), [&](const int64_t point_i) {
|
||||
point_mask.foreach_index(GrainSize(4096), [&](const int64_t point_i) {
|
||||
const float2 &co = view_positions[point_i];
|
||||
const float influence = brush_point_influence(
|
||||
scene, brush, co, extension_sample, params.multi_frame_falloff);
|
||||
|
||||
@@ -57,6 +57,7 @@ void SmoothOperation::on_stroke_begin(const bContext &C, const InputSample &star
|
||||
else {
|
||||
this->init_stroke(C, start_sample);
|
||||
}
|
||||
this->init_auto_masking(C, start_sample);
|
||||
}
|
||||
|
||||
void SmoothOperation::on_stroke_extended(const bContext &C, const InputSample &extension_sample)
|
||||
@@ -74,91 +75,86 @@ void SmoothOperation::on_stroke_extended(const bContext &C, const InputSample &e
|
||||
}();
|
||||
const int sculpt_mode_flag = brush.gpencil_settings->sculpt_mode_flag;
|
||||
|
||||
const bool is_masking = GPENCIL_ANY_SCULPT_MASK(
|
||||
eGP_Sculpt_SelectMaskFlag(scene.toolsettings->gpencil_selectmode_sculpt));
|
||||
this->foreach_editable_drawing_with_automask(
|
||||
C, [&](const GreasePencilStrokeParams ¶ms, const IndexMask &point_mask) {
|
||||
Array<float2> view_positions = calculate_view_positions(params, point_mask);
|
||||
bke::CurvesGeometry &curves = params.drawing.strokes_for_write();
|
||||
bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
|
||||
const OffsetIndices points_by_curve = curves.points_by_curve();
|
||||
const VArray<bool> cyclic = curves.cyclic();
|
||||
const int iterations = 2;
|
||||
|
||||
this->foreach_editable_drawing(C, [&](const GreasePencilStrokeParams ¶ms) {
|
||||
IndexMaskMemory selection_memory;
|
||||
const IndexMask selection = point_selection_mask(params, is_masking, selection_memory);
|
||||
if (selection.is_empty()) {
|
||||
return false;
|
||||
}
|
||||
const VArray<float> influences = VArray<float>::ForFunc(
|
||||
view_positions.size(), [&](const int64_t point_) {
|
||||
return brush_point_influence(scene,
|
||||
brush,
|
||||
view_positions[point_],
|
||||
extension_sample,
|
||||
params.multi_frame_falloff);
|
||||
});
|
||||
Array<bool> selection_array(curves.points_num());
|
||||
point_mask.to_bools(selection_array);
|
||||
const VArray<bool> selection_varray = VArray<bool>::ForSpan(selection_array);
|
||||
|
||||
Array<float2> view_positions = calculate_view_positions(params, selection);
|
||||
bke::CurvesGeometry &curves = params.drawing.strokes_for_write();
|
||||
bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
|
||||
const OffsetIndices points_by_curve = curves.points_by_curve();
|
||||
const VArray<bool> cyclic = curves.cyclic();
|
||||
const int iterations = 2;
|
||||
|
||||
const VArray<float> influences = VArray<float>::ForFunc(
|
||||
view_positions.size(), [&](const int64_t point_) {
|
||||
return brush_point_influence(
|
||||
scene, brush, view_positions[point_], extension_sample, params.multi_frame_falloff);
|
||||
});
|
||||
Array<bool> selection_array(curves.points_num());
|
||||
selection.to_bools(selection_array);
|
||||
const VArray<bool> selection_varray = VArray<bool>::ForSpan(selection_array);
|
||||
|
||||
bool changed = false;
|
||||
if (sculpt_mode_flag & GP_SCULPT_FLAGMODE_APPLY_POSITION) {
|
||||
MutableSpan<float3> positions = curves.positions_for_write();
|
||||
geometry::smooth_curve_attribute(curves.curves_range(),
|
||||
points_by_curve,
|
||||
selection_varray,
|
||||
cyclic,
|
||||
iterations,
|
||||
influences,
|
||||
false,
|
||||
false,
|
||||
positions);
|
||||
params.drawing.tag_positions_changed();
|
||||
changed = true;
|
||||
}
|
||||
if (sculpt_mode_flag & GP_SCULPT_FLAGMODE_APPLY_STRENGTH) {
|
||||
MutableSpan<float> opacities = params.drawing.opacities_for_write();
|
||||
geometry::smooth_curve_attribute(curves.curves_range(),
|
||||
points_by_curve,
|
||||
selection_varray,
|
||||
cyclic,
|
||||
iterations,
|
||||
influences,
|
||||
true,
|
||||
false,
|
||||
opacities);
|
||||
changed = true;
|
||||
}
|
||||
if (sculpt_mode_flag & GP_SCULPT_FLAGMODE_APPLY_THICKNESS) {
|
||||
const MutableSpan<float> radii = params.drawing.radii_for_write();
|
||||
geometry::smooth_curve_attribute(curves.curves_range(),
|
||||
points_by_curve,
|
||||
selection_varray,
|
||||
cyclic,
|
||||
iterations,
|
||||
influences,
|
||||
true,
|
||||
false,
|
||||
radii);
|
||||
curves.tag_radii_changed();
|
||||
changed = true;
|
||||
}
|
||||
if (sculpt_mode_flag & GP_SCULPT_FLAGMODE_APPLY_UV) {
|
||||
bke::SpanAttributeWriter<float> rotations = attributes.lookup_or_add_for_write_span<float>(
|
||||
"rotation", bke::AttrDomain::Point);
|
||||
geometry::smooth_curve_attribute(curves.curves_range(),
|
||||
points_by_curve,
|
||||
selection_varray,
|
||||
cyclic,
|
||||
iterations,
|
||||
influences,
|
||||
true,
|
||||
false,
|
||||
rotations.span);
|
||||
rotations.finish();
|
||||
changed = true;
|
||||
}
|
||||
return changed;
|
||||
});
|
||||
bool changed = false;
|
||||
if (sculpt_mode_flag & GP_SCULPT_FLAGMODE_APPLY_POSITION) {
|
||||
MutableSpan<float3> positions = curves.positions_for_write();
|
||||
geometry::smooth_curve_attribute(curves.curves_range(),
|
||||
points_by_curve,
|
||||
selection_varray,
|
||||
cyclic,
|
||||
iterations,
|
||||
influences,
|
||||
false,
|
||||
false,
|
||||
positions);
|
||||
params.drawing.tag_positions_changed();
|
||||
changed = true;
|
||||
}
|
||||
if (sculpt_mode_flag & GP_SCULPT_FLAGMODE_APPLY_STRENGTH) {
|
||||
MutableSpan<float> opacities = params.drawing.opacities_for_write();
|
||||
geometry::smooth_curve_attribute(curves.curves_range(),
|
||||
points_by_curve,
|
||||
selection_varray,
|
||||
cyclic,
|
||||
iterations,
|
||||
influences,
|
||||
true,
|
||||
false,
|
||||
opacities);
|
||||
changed = true;
|
||||
}
|
||||
if (sculpt_mode_flag & GP_SCULPT_FLAGMODE_APPLY_THICKNESS) {
|
||||
const MutableSpan<float> radii = params.drawing.radii_for_write();
|
||||
geometry::smooth_curve_attribute(curves.curves_range(),
|
||||
points_by_curve,
|
||||
selection_varray,
|
||||
cyclic,
|
||||
iterations,
|
||||
influences,
|
||||
true,
|
||||
false,
|
||||
radii);
|
||||
curves.tag_radii_changed();
|
||||
changed = true;
|
||||
}
|
||||
if (sculpt_mode_flag & GP_SCULPT_FLAGMODE_APPLY_UV) {
|
||||
bke::SpanAttributeWriter<float> rotations =
|
||||
attributes.lookup_or_add_for_write_span<float>("rotation", bke::AttrDomain::Point);
|
||||
geometry::smooth_curve_attribute(curves.curves_range(),
|
||||
points_by_curve,
|
||||
selection_varray,
|
||||
cyclic,
|
||||
iterations,
|
||||
influences,
|
||||
true,
|
||||
false,
|
||||
rotations.span);
|
||||
rotations.finish();
|
||||
changed = true;
|
||||
}
|
||||
return changed;
|
||||
});
|
||||
this->stroke_extended(extension_sample);
|
||||
}
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ class StrengthOperation : public GreasePencilStrokeOperationCommon {
|
||||
void StrengthOperation::on_stroke_begin(const bContext &C, const InputSample &start_sample)
|
||||
{
|
||||
this->init_stroke(C, start_sample);
|
||||
this->init_auto_masking(C, start_sample);
|
||||
}
|
||||
|
||||
void StrengthOperation::on_stroke_extended(const bContext &C, const InputSample &extension_sample)
|
||||
@@ -43,30 +44,22 @@ void StrengthOperation::on_stroke_extended(const bContext &C, const InputSample
|
||||
const Brush &brush = *BKE_paint_brush(&paint);
|
||||
const bool invert = this->is_inverted(brush);
|
||||
|
||||
const bool is_masking = GPENCIL_ANY_SCULPT_MASK(
|
||||
eGP_Sculpt_SelectMaskFlag(scene.toolsettings->gpencil_selectmode_sculpt));
|
||||
this->foreach_editable_drawing_with_automask(
|
||||
C, [&](const GreasePencilStrokeParams ¶ms, const IndexMask &point_mask) {
|
||||
Array<float2> view_positions = calculate_view_positions(params, point_mask);
|
||||
MutableSpan<float> opacities = params.drawing.opacities_for_write();
|
||||
|
||||
this->foreach_editable_drawing(C, [&](const GreasePencilStrokeParams ¶ms) {
|
||||
IndexMaskMemory selection_memory;
|
||||
const IndexMask selection = point_selection_mask(params, is_masking, selection_memory);
|
||||
if (selection.is_empty()) {
|
||||
return false;
|
||||
}
|
||||
point_mask.foreach_index(GrainSize(4096), [&](const int64_t point_i) {
|
||||
float &opacity = opacities[point_i];
|
||||
const float influence = brush_point_influence(
|
||||
scene, brush, view_positions[point_i], extension_sample, params.multi_frame_falloff);
|
||||
/* Brush influence mapped to opacity by a factor of 0.125. */
|
||||
const float delta_opacity = (invert ? -influence : influence) * 0.125f;
|
||||
opacity = std::clamp(opacity + delta_opacity, 0.0f, 1.0f);
|
||||
});
|
||||
|
||||
Array<float2> view_positions = calculate_view_positions(params, selection);
|
||||
MutableSpan<float> opacities = params.drawing.opacities_for_write();
|
||||
|
||||
selection.foreach_index(GrainSize(4096), [&](const int64_t point_i) {
|
||||
float &opacity = opacities[point_i];
|
||||
const float influence = brush_point_influence(
|
||||
scene, brush, view_positions[point_i], extension_sample, params.multi_frame_falloff);
|
||||
/* Brush influence mapped to opacity by a factor of 0.125. */
|
||||
const float delta_opacity = (invert ? -influence : influence) * 0.125f;
|
||||
opacity = std::clamp(opacity + delta_opacity, 0.0f, 1.0f);
|
||||
});
|
||||
|
||||
return true;
|
||||
});
|
||||
return true;
|
||||
});
|
||||
this->stroke_extended(extension_sample);
|
||||
}
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ class ThicknessOperation : public GreasePencilStrokeOperationCommon {
|
||||
void ThicknessOperation::on_stroke_begin(const bContext &C, const InputSample &start_sample)
|
||||
{
|
||||
this->init_stroke(C, start_sample);
|
||||
this->init_auto_masking(C, start_sample);
|
||||
}
|
||||
|
||||
void ThicknessOperation::on_stroke_extended(const bContext &C, const InputSample &extension_sample)
|
||||
@@ -43,33 +44,25 @@ void ThicknessOperation::on_stroke_extended(const bContext &C, const InputSample
|
||||
const Brush &brush = *BKE_paint_brush(&paint);
|
||||
const bool invert = this->is_inverted(brush);
|
||||
|
||||
const bool is_masking = GPENCIL_ANY_SCULPT_MASK(
|
||||
eGP_Sculpt_SelectMaskFlag(scene.toolsettings->gpencil_selectmode_sculpt));
|
||||
this->foreach_editable_drawing_with_automask(
|
||||
C, [&](const GreasePencilStrokeParams ¶ms, const IndexMask &point_mask) {
|
||||
Array<float2> view_positions = calculate_view_positions(params, point_mask);
|
||||
bke::CurvesGeometry &curves = params.drawing.strokes_for_write();
|
||||
BLI_assert(view_positions.size() == curves.points_num());
|
||||
MutableSpan<float> radii = params.drawing.radii_for_write();
|
||||
|
||||
this->foreach_editable_drawing(C, [&](const GreasePencilStrokeParams ¶ms) {
|
||||
IndexMaskMemory selection_memory;
|
||||
const IndexMask selection = point_selection_mask(params, is_masking, selection_memory);
|
||||
if (selection.is_empty()) {
|
||||
return false;
|
||||
}
|
||||
point_mask.foreach_index(GrainSize(4096), [&](const int64_t point_i) {
|
||||
float &radius = radii[point_i];
|
||||
const float influence = brush_point_influence(
|
||||
scene, brush, view_positions[point_i], extension_sample, params.multi_frame_falloff);
|
||||
/* Factor 1/1000 is used to map arbitrary influence value to a sensible radius. */
|
||||
const float delta_radius = (invert ? -influence : influence) * 0.001f;
|
||||
radius = std::max(radius + delta_radius, 0.0f);
|
||||
});
|
||||
|
||||
Array<float2> view_positions = calculate_view_positions(params, selection);
|
||||
bke::CurvesGeometry &curves = params.drawing.strokes_for_write();
|
||||
BLI_assert(view_positions.size() == curves.points_num());
|
||||
MutableSpan<float> radii = params.drawing.radii_for_write();
|
||||
|
||||
selection.foreach_index(GrainSize(4096), [&](const int64_t point_i) {
|
||||
float &radius = radii[point_i];
|
||||
const float influence = brush_point_influence(
|
||||
scene, brush, view_positions[point_i], extension_sample, params.multi_frame_falloff);
|
||||
/* Factor 1/1000 is used to map arbitrary influence value to a sensible radius. */
|
||||
const float delta_radius = (invert ? -influence : influence) * 0.001f;
|
||||
radius = std::max(radius + delta_radius, 0.0f);
|
||||
});
|
||||
|
||||
curves.tag_radii_changed();
|
||||
return true;
|
||||
});
|
||||
curves.tag_radii_changed();
|
||||
return true;
|
||||
});
|
||||
this->stroke_extended(extension_sample);
|
||||
}
|
||||
|
||||
|
||||
@@ -42,6 +42,7 @@ static float2 rotate_by_angle(const float2 &vec, const float angle)
|
||||
void TwistOperation::on_stroke_begin(const bContext &C, const InputSample &start_sample)
|
||||
{
|
||||
this->init_stroke(C, start_sample);
|
||||
this->init_auto_masking(C, start_sample);
|
||||
}
|
||||
|
||||
void TwistOperation::on_stroke_extended(const bContext &C, const InputSample &extension_sample)
|
||||
@@ -51,25 +52,19 @@ void TwistOperation::on_stroke_extended(const bContext &C, const InputSample &ex
|
||||
const Brush &brush = *BKE_paint_brush(&paint);
|
||||
const bool invert = this->is_inverted(brush);
|
||||
|
||||
const bool is_masking = GPENCIL_ANY_SCULPT_MASK(
|
||||
eGP_Sculpt_SelectMaskFlag(scene.toolsettings->gpencil_selectmode_sculpt));
|
||||
|
||||
this->foreach_editable_drawing(
|
||||
C, [&](const GreasePencilStrokeParams ¶ms, const DeltaProjectionFunc &projection_fn) {
|
||||
IndexMaskMemory selection_memory;
|
||||
const IndexMask selection = point_selection_mask(params, is_masking, selection_memory);
|
||||
if (selection.is_empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this->foreach_editable_drawing_with_automask(
|
||||
C,
|
||||
[&](const GreasePencilStrokeParams ¶ms,
|
||||
const IndexMask &point_mask,
|
||||
const DeltaProjectionFunc &projection_fn) {
|
||||
bke::crazyspace::GeometryDeformation deformation = get_drawing_deformation(params);
|
||||
Array<float2> view_positions = calculate_view_positions(params, selection);
|
||||
Array<float2> view_positions = calculate_view_positions(params, point_mask);
|
||||
bke::CurvesGeometry &curves = params.drawing.strokes_for_write();
|
||||
MutableSpan<float3> positions = curves.positions_for_write();
|
||||
|
||||
const float2 mouse_pos = extension_sample.mouse_position;
|
||||
|
||||
selection.foreach_index(GrainSize(4096), [&](const int64_t point_i) {
|
||||
point_mask.foreach_index(GrainSize(4096), [&](const int64_t point_i) {
|
||||
const float2 &co = view_positions[point_i];
|
||||
const float influence = brush_point_influence(
|
||||
scene, brush, co, extension_sample, params.multi_frame_falloff);
|
||||
|
||||
@@ -38,7 +38,7 @@ void VertexAverageOperation::on_stroke_extended(const bContext &C,
|
||||
const float radius = brush_radius(scene, brush, extension_sample.pressure);
|
||||
const float radius_squared = radius * radius;
|
||||
|
||||
const bool is_masking = GPENCIL_ANY_VERTEX_MASK(
|
||||
const bool use_selection_masking = GPENCIL_ANY_VERTEX_MASK(
|
||||
eGP_vertex_SelectMaskFlag(scene.toolsettings->gpencil_selectmode_vertex));
|
||||
|
||||
const bool do_points = do_vertex_color_points(brush);
|
||||
@@ -49,7 +49,8 @@ void VertexAverageOperation::on_stroke_extended(const bContext &C,
|
||||
int color_count = 0;
|
||||
this->foreach_editable_drawing(C, [&](const GreasePencilStrokeParams ¶ms) {
|
||||
IndexMaskMemory memory;
|
||||
const IndexMask point_selection = point_selection_mask(params, is_masking, memory);
|
||||
const IndexMask point_selection = point_mask_for_stroke_operation(
|
||||
params, use_selection_masking, memory);
|
||||
if (!point_selection.is_empty() && do_points) {
|
||||
const Array<float2> view_positions = calculate_view_positions(params, point_selection);
|
||||
const VArray<ColorGeometry4f> vertex_colors = params.drawing.vertex_colors();
|
||||
@@ -64,7 +65,8 @@ void VertexAverageOperation::on_stroke_extended(const bContext &C,
|
||||
}
|
||||
});
|
||||
}
|
||||
const IndexMask fill_selection = fill_selection_mask(params, is_masking, memory);
|
||||
const IndexMask fill_selection = fill_mask_for_stroke_operation(
|
||||
params, use_selection_masking, memory);
|
||||
if (!fill_selection.is_empty() && do_fill) {
|
||||
const OffsetIndices<int> points_by_curve = params.drawing.strokes().points_by_curve();
|
||||
const Array<float2> view_positions = calculate_view_positions(params, point_selection);
|
||||
@@ -94,7 +96,8 @@ void VertexAverageOperation::on_stroke_extended(const bContext &C,
|
||||
|
||||
this->foreach_editable_drawing(C, GrainSize(1), [&](const GreasePencilStrokeParams ¶ms) {
|
||||
IndexMaskMemory memory;
|
||||
const IndexMask point_selection = point_selection_mask(params, is_masking, memory);
|
||||
const IndexMask point_selection = point_mask_for_stroke_operation(
|
||||
params, use_selection_masking, memory);
|
||||
if (!point_selection.is_empty() && do_points) {
|
||||
const Array<float2> view_positions = calculate_view_positions(params, point_selection);
|
||||
MutableSpan<ColorGeometry4f> vertex_colors = params.drawing.vertex_colors_for_write();
|
||||
@@ -108,7 +111,8 @@ void VertexAverageOperation::on_stroke_extended(const bContext &C,
|
||||
});
|
||||
}
|
||||
|
||||
const IndexMask fill_selection = fill_selection_mask(params, is_masking, memory);
|
||||
const IndexMask fill_selection = fill_mask_for_stroke_operation(
|
||||
params, use_selection_masking, memory);
|
||||
if (!fill_selection.is_empty() && do_fill) {
|
||||
const OffsetIndices<int> points_by_curve = params.drawing.strokes().points_by_curve();
|
||||
const Array<float2> view_positions = calculate_view_positions(params, point_selection);
|
||||
|
||||
@@ -38,16 +38,18 @@ void VertexBlurOperation::on_stroke_extended(const bContext &C,
|
||||
const float radius = brush_radius(scene, brush, extension_sample.pressure);
|
||||
const float radius_squared = radius * radius;
|
||||
|
||||
const bool is_masking = GPENCIL_ANY_VERTEX_MASK(
|
||||
const bool use_selection_masking = GPENCIL_ANY_VERTEX_MASK(
|
||||
eGP_vertex_SelectMaskFlag(scene.toolsettings->gpencil_selectmode_vertex));
|
||||
|
||||
this->foreach_editable_drawing(C, GrainSize(1), [&](const GreasePencilStrokeParams ¶ms) {
|
||||
IndexMaskMemory memory;
|
||||
const IndexMask stroke_selection = stroke_selection_mask(params, is_masking, memory);
|
||||
const IndexMask stroke_selection = curve_mask_for_stroke_operation(
|
||||
params, use_selection_masking, memory);
|
||||
if (stroke_selection.is_empty()) {
|
||||
return false;
|
||||
}
|
||||
const IndexMask point_selection = point_selection_mask(params, is_masking, memory);
|
||||
const IndexMask point_selection = point_mask_for_stroke_operation(
|
||||
params, use_selection_masking, memory);
|
||||
const Array<float2> view_positions = calculate_view_positions(params, point_selection);
|
||||
const OffsetIndices<int> points_by_curve = params.drawing.strokes().points_by_curve();
|
||||
MutableSpan<ColorGeometry4f> vertex_colors = params.drawing.vertex_colors_for_write();
|
||||
|
||||
@@ -39,7 +39,7 @@ void VertexPaintOperation::on_stroke_extended(const bContext &C,
|
||||
const Brush &brush = *BKE_paint_brush(&paint);
|
||||
const bool invert = this->is_inverted(brush);
|
||||
|
||||
const bool is_masking = GPENCIL_ANY_VERTEX_MASK(
|
||||
const bool use_selection_masking = GPENCIL_ANY_VERTEX_MASK(
|
||||
eGP_vertex_SelectMaskFlag(scene.toolsettings->gpencil_selectmode_vertex));
|
||||
|
||||
const bool do_points = do_vertex_color_points(brush);
|
||||
@@ -51,7 +51,8 @@ void VertexPaintOperation::on_stroke_extended(const bContext &C,
|
||||
|
||||
this->foreach_editable_drawing(C, GrainSize(1), [&](const GreasePencilStrokeParams ¶ms) {
|
||||
IndexMaskMemory memory;
|
||||
const IndexMask point_selection = point_selection_mask(params, is_masking, memory);
|
||||
const IndexMask point_selection = point_mask_for_stroke_operation(
|
||||
params, use_selection_masking, memory);
|
||||
if (!point_selection.is_empty() && do_points) {
|
||||
Array<float2> view_positions = calculate_view_positions(params, point_selection);
|
||||
MutableSpan<ColorGeometry4f> vertex_colors = params.drawing.vertex_colors_for_write();
|
||||
@@ -79,7 +80,8 @@ void VertexPaintOperation::on_stroke_extended(const bContext &C,
|
||||
}
|
||||
}
|
||||
|
||||
const IndexMask fill_selection = fill_selection_mask(params, is_masking, memory);
|
||||
const IndexMask fill_selection = fill_mask_for_stroke_operation(
|
||||
params, use_selection_masking, memory);
|
||||
if (!fill_selection.is_empty() && do_fill) {
|
||||
const OffsetIndices<int> points_by_curve = params.drawing.strokes().points_by_curve();
|
||||
Array<float2> view_positions = calculate_view_positions(params, point_selection);
|
||||
|
||||
@@ -36,7 +36,7 @@ void VertexReplaceOperation::on_stroke_extended(const bContext &C,
|
||||
Paint &paint = *BKE_paint_get_active_from_context(&C);
|
||||
const Brush &brush = *BKE_paint_brush(&paint);
|
||||
|
||||
const bool is_masking = GPENCIL_ANY_VERTEX_MASK(
|
||||
const bool use_selection_masking = GPENCIL_ANY_VERTEX_MASK(
|
||||
eGP_vertex_SelectMaskFlag(scene.toolsettings->gpencil_selectmode_vertex));
|
||||
|
||||
const bool do_points = do_vertex_color_points(brush);
|
||||
@@ -48,7 +48,8 @@ void VertexReplaceOperation::on_stroke_extended(const bContext &C,
|
||||
|
||||
this->foreach_editable_drawing(C, GrainSize(1), [&](const GreasePencilStrokeParams ¶ms) {
|
||||
IndexMaskMemory memory;
|
||||
const IndexMask point_selection = point_selection_mask(params, is_masking, memory);
|
||||
const IndexMask point_selection = point_mask_for_stroke_operation(
|
||||
params, use_selection_masking, memory);
|
||||
if (!point_selection.is_empty() && do_points) {
|
||||
Array<float2> view_positions = calculate_view_positions(params, point_selection);
|
||||
MutableSpan<ColorGeometry4f> vertex_colors = params.drawing.vertex_colors_for_write();
|
||||
@@ -61,7 +62,8 @@ void VertexReplaceOperation::on_stroke_extended(const bContext &C,
|
||||
});
|
||||
}
|
||||
|
||||
const IndexMask fill_selection = fill_selection_mask(params, is_masking, memory);
|
||||
const IndexMask fill_selection = fill_mask_for_stroke_operation(
|
||||
params, use_selection_masking, memory);
|
||||
if (!fill_selection.is_empty() && do_fill) {
|
||||
const OffsetIndices<int> points_by_curve = params.drawing.strokes().points_by_curve();
|
||||
Array<float2> view_positions = calculate_view_positions(params, point_selection);
|
||||
|
||||
@@ -71,7 +71,7 @@ void VertexSmearOperation::init_color_grid(const bContext &C, const float2 start
|
||||
const Scene &scene = *CTX_data_scene(&C);
|
||||
Paint &paint = *BKE_paint_get_active_from_context(&C);
|
||||
const Brush &brush = *BKE_paint_brush(&paint);
|
||||
const bool is_masking = GPENCIL_ANY_VERTEX_MASK(
|
||||
const bool use_selection_masking = GPENCIL_ANY_VERTEX_MASK(
|
||||
eGP_vertex_SelectMaskFlag(scene.toolsettings->gpencil_selectmode_vertex));
|
||||
const float radius = brush_radius(scene, brush, 1.0f);
|
||||
|
||||
@@ -89,7 +89,8 @@ void VertexSmearOperation::init_color_grid(const bContext &C, const float2 start
|
||||
/* Initialize grid values. */
|
||||
this->foreach_editable_drawing(C, [&](const GreasePencilStrokeParams ¶ms) {
|
||||
IndexMaskMemory memory;
|
||||
const IndexMask point_selection = point_selection_mask(params, is_masking, memory);
|
||||
const IndexMask point_selection = point_mask_for_stroke_operation(
|
||||
params, use_selection_masking, memory);
|
||||
if (point_selection.is_empty()) {
|
||||
return false;
|
||||
}
|
||||
@@ -154,12 +155,13 @@ void VertexSmearOperation::on_stroke_extended(const bContext &C,
|
||||
const Brush &brush = *BKE_paint_brush(&paint);
|
||||
const float radius = brush_radius(scene, brush, extension_sample.pressure);
|
||||
|
||||
const bool is_masking = GPENCIL_ANY_VERTEX_MASK(
|
||||
const bool use_selection_masking = GPENCIL_ANY_VERTEX_MASK(
|
||||
eGP_vertex_SelectMaskFlag(scene.toolsettings->gpencil_selectmode_vertex));
|
||||
|
||||
this->foreach_editable_drawing(C, GrainSize(1), [&](const GreasePencilStrokeParams ¶ms) {
|
||||
IndexMaskMemory memory;
|
||||
const IndexMask point_selection = point_selection_mask(params, is_masking, memory);
|
||||
const IndexMask point_selection = point_mask_for_stroke_operation(
|
||||
params, use_selection_masking, memory);
|
||||
if (point_selection.is_empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user