Fix #134490: Grease Pencil: Clone brush in sculpt mode not working
The main issue was that the code was using `foreach_editable_drawing_with_automask` which shouldn't have been the case since there is no "masking" to be done when pasting strokes. Additionally the code was computing the view position of every pasted point and then computing the offset to move the points to the mouse position. Instead, we compute the center of the 3d bounds of the pasted strokes and then project only the center to the view. From there we can offset all the points to move them under the mouse cursor. It's a bit weird to do this projection of the center to the view and I believe we mainly do it to reuse the `DeltaProjectionFunc`. There are cases where the projection of the center to the view will fail which can lead to unexpected behavior on the user end. For now we just leave the pasted strokes where they were copied from, which isn't ideal. But this can be improved later. Pull Request: https://projects.blender.org/blender/blender/pulls/134589
This commit is contained in:
@@ -201,6 +201,11 @@ class GreasePencilStrokeOperationCommon : public GreasePencilStrokeOperation {
|
||||
const IndexMask &points,
|
||||
const DeltaProjectionFunc &projection_fn)> fn) const;
|
||||
|
||||
void foreach_editable_drawing(
|
||||
const bContext &C,
|
||||
FunctionRef<bool(const GreasePencilStrokeParams ¶ms,
|
||||
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;
|
||||
|
||||
@@ -529,6 +529,49 @@ 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);
|
||||
|
||||
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];
|
||||
const Layer &layer = grease_pencil.layer(info.layer_index);
|
||||
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::foreach_editable_drawing(
|
||||
const bContext &C,
|
||||
const GrainSize grain_size,
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
#include "BKE_grease_pencil.hh"
|
||||
#include "BKE_paint.hh"
|
||||
|
||||
#include "BLI_bounds.hh"
|
||||
|
||||
#include "ED_curves.hh"
|
||||
#include "ED_grease_pencil.hh"
|
||||
#include "ED_view3d.hh"
|
||||
@@ -30,11 +32,6 @@ class CloneOperation : public GreasePencilStrokeOperationCommon {
|
||||
void on_stroke_done(const bContext & /*C*/) override {}
|
||||
};
|
||||
|
||||
static float2 arithmetic_mean(Span<float2> values)
|
||||
{
|
||||
return std::accumulate(values.begin(), values.end(), float2(0)) / values.size();
|
||||
}
|
||||
|
||||
void CloneOperation::on_stroke_begin(const bContext &C, const InputSample &start_sample)
|
||||
{
|
||||
Main &bmain = *CTX_data_main(&C);
|
||||
@@ -49,11 +46,8 @@ 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_with_automask(
|
||||
C,
|
||||
[&](const GreasePencilStrokeParams ¶ms,
|
||||
const IndexMask & /*point_mask*/,
|
||||
const DeltaProjectionFunc &projection_fn) {
|
||||
this->foreach_editable_drawing(
|
||||
C, [&](const GreasePencilStrokeParams ¶ms, const DeltaProjectionFunc &projection_fn) {
|
||||
/* Only insert on the active layer. */
|
||||
if (¶ms.layer != grease_pencil.get_active_layer()) {
|
||||
return false;
|
||||
@@ -68,7 +62,6 @@ void CloneOperation::on_stroke_begin(const bContext &C, const InputSample &start
|
||||
return false;
|
||||
}
|
||||
|
||||
const bke::crazyspace::GeometryDeformation deformation = get_drawing_deformation(params);
|
||||
bke::CurvesGeometry &curves = params.drawing.strokes_for_write();
|
||||
const OffsetIndices<int> pasted_points_by_curve = curves.points_by_curve().slice(
|
||||
pasted_curves);
|
||||
@@ -78,15 +71,27 @@ void CloneOperation::on_stroke_begin(const bContext &C, const InputSample &start
|
||||
return false;
|
||||
}
|
||||
|
||||
Array<float2> view_positions = calculate_view_positions(params, pasted_points);
|
||||
const float2 center = arithmetic_mean(
|
||||
view_positions.as_mutable_span().slice(pasted_points));
|
||||
const float2 &mouse_delta = start_sample.mouse_position - center;
|
||||
const Bounds<float3> bounds = *bounds::min_max(curves.positions().slice(pasted_points));
|
||||
const float4x4 transform = params.layer.to_world_space(params.ob_eval);
|
||||
/* FIXME: Projecting the center of the bounds to the view can sometimes fail. This might
|
||||
* result in unexpected behavior on the user end. Figure out a way to not rely on view
|
||||
* space here and compute the translation offset in layer space instead. */
|
||||
float2 view_center(0.0f);
|
||||
if (ED_view3d_project_float_global(¶ms.region,
|
||||
math::transform_point(transform, bounds.center()),
|
||||
view_center,
|
||||
V3D_PROJ_TEST_NOP) != V3D_PROJ_RET_OK)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const float2 &mouse_delta = start_sample.mouse_position - view_center;
|
||||
const bke::crazyspace::GeometryDeformation deformation = get_drawing_deformation(params);
|
||||
|
||||
MutableSpan<float3> positions = curves.positions_for_write();
|
||||
threading::parallel_for(pasted_points, 4096, [&](const IndexRange range) {
|
||||
for (const int point_i : range) {
|
||||
positions[point_i] = compute_orig_delta(
|
||||
positions[point_i] += compute_orig_delta(
|
||||
projection_fn, deformation, point_i, mouse_delta);
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user