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 27628f08b55..d2ba81387eb 100644 --- a/source/blender/editors/grease_pencil/intern/grease_pencil_edit.cc +++ b/source/blender/editors/grease_pencil/intern/grease_pencil_edit.cc @@ -30,6 +30,7 @@ #include "BKE_curves_utils.hh" #include "BKE_deform.hh" #include "BKE_grease_pencil.hh" +#include "BKE_instances.hh" #include "BKE_lib_id.hh" #include "BKE_main.hh" #include "BKE_material.h" @@ -53,10 +54,12 @@ #include "ED_view3d.hh" #include "GEO_join_geometries.hh" +#include "GEO_realize_instances.hh" #include "GEO_reorder.hh" #include "GEO_set_curve_type.hh" #include "GEO_smooth_curves.hh" #include "GEO_subdivide_curves.hh" + #include "UI_interface_c.hh" #include "UI_resources.hh" @@ -2181,6 +2184,8 @@ static void GREASE_PENCIL_OT_separate(wmOperatorType *ot) /* Global clipboard for Grease Pencil curves. */ static struct Clipboard { bke::CurvesGeometry curves; + /* Object transform of stored curves. */ + float4x4 transform; /* We store the material uid's of the copied curves, so we can match those when pasting the * clipboard into another object. */ Vector> materials; @@ -2208,6 +2213,7 @@ static int grease_pencil_paste_strokes_exec(bContext *C, wmOperator *op) const bke::AttrDomain selection_domain = ED_grease_pencil_selection_domain_get( scene.toolsettings); GreasePencil &grease_pencil = *static_cast(object->data); + const bool keep_world_transform = RNA_boolean_get(op->ptr, "keep_world_transform"); const bool paste_on_back = RNA_boolean_get(op->ptr, "paste_back"); /* Get active layer in the target object. */ @@ -2243,7 +2249,9 @@ static int grease_pencil_paste_strokes_exec(bContext *C, wmOperator *op) selection_in_target.finish(); }); - clipboard_paste_strokes(*bmain, *object, *target_drawing, paste_on_back); + const float4x4 object_to_layer = math::invert(active_layer.to_object_space(*object)); + clipboard_paste_strokes( + *bmain, *object, *target_drawing, object_to_layer, keep_world_transform, paste_on_back); DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY); WM_event_add_notifier(C, NC_GEOM | ND_DATA, &grease_pencil); @@ -2255,8 +2263,29 @@ static int grease_pencil_paste_strokes_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } +static bke::GeometrySet join_geometries_with_transform(Span geometries, + Span transforms) +{ + BLI_assert(geometries.size() == transforms.size()); + + std::unique_ptr instances = std::make_unique(); + instances->resize(geometries.size()); + instances->transforms_for_write().copy_from(transforms); + MutableSpan handles = instances->reference_handles_for_write(); + for (const int i : geometries.index_range()) { + handles[i] = instances->add_new_reference(bke::InstanceReference{geometries[i]}); + } + + geometry::RealizeInstancesOptions options; + options.keep_original_ids = true; + options.realize_instance_attributes = false; + return realize_instances(bke::GeometrySet::from_instances(instances.release()), options); +} + static int grease_pencil_copy_strokes_exec(bContext *C, wmOperator *op) { + using bke::greasepencil::Layer; + const Scene *scene = CTX_data_scene(C); const Object *object = CTX_data_active_object(C); GreasePencil &grease_pencil = *static_cast(object->data); @@ -2268,11 +2297,14 @@ static int grease_pencil_copy_strokes_exec(bContext *C, wmOperator *op) bool anything_copied = false; int num_copied = 0; Vector set_of_copied_curves; + Vector set_of_transforms; /* Collect all selected strokes/points on all editable layers. */ const Vector drawings = retrieve_editable_drawings(*scene, grease_pencil); for (const MutableDrawingInfo &drawing_info : drawings) { const bke::CurvesGeometry &curves = drawing_info.drawing.strokes(); + const Layer &layer = *grease_pencil.layer(drawing_info.layer_index); + const float4x4 layer_to_object = layer.to_object_space(*object); if (curves.curves_num() == 0) { continue; @@ -2299,6 +2331,7 @@ static int grease_pencil_copy_strokes_exec(bContext *C, wmOperator *op) /* Add the layer selection to the set of copied curves. */ Curves *layer_curves = curves_new_nomain(std::move(copied_curves)); set_of_copied_curves.append(bke::GeometrySet::from_curves(layer_curves)); + set_of_transforms.append(layer_to_object); anything_copied = true; } @@ -2308,8 +2341,10 @@ static int grease_pencil_copy_strokes_exec(bContext *C, wmOperator *op) } /* Merge all copied curves into one CurvesGeometry object and assign it to the clipboard. */ - bke::GeometrySet joined_copied_curves = geometry::join_geometries(set_of_copied_curves, {}); + bke::GeometrySet joined_copied_curves = join_geometries_with_transform(set_of_copied_curves, + set_of_transforms); clipboard.curves = std::move(joined_copied_curves.get_curves_for_write()->geometry.wrap()); + clipboard.transform = object->object_to_world(); /* Store the session uid of the materials used by the curves in the clipboard. We use the uid to * remap the material indices when pasting. */ @@ -2363,6 +2398,11 @@ static void GREASE_PENCIL_OT_paste(wmOperatorType *ot) ot->prop = RNA_def_boolean( ot->srna, "paste_back", false, "Paste on Back", "Add pasted strokes behind all strokes"); RNA_def_property_flag(ot->prop, PROP_SKIP_SAVE); + ot->prop = RNA_def_boolean(ot->srna, + "keep_world_transform", + false, + "Keep World Transform", + "Keep the world transform of strokes from the clipboard unchanged"); } static void GREASE_PENCIL_OT_copy(wmOperatorType *ot) @@ -2430,9 +2470,13 @@ static Array clipboard_materials_remap(Main &bmain, Object &object) IndexRange clipboard_paste_strokes(Main &bmain, Object &object, bke::greasepencil::Drawing &drawing, + const float4x4 &transform, + const bool keep_world_transform, const bool paste_back) { - const bke::CurvesGeometry &clipboard_curves = ed::greasepencil::clipboard_curves(); + const Clipboard &clipboard = ensure_grease_pencil_clipboard(); + const bke::CurvesGeometry &clipboard_curves = clipboard.curves; + const float4x4 clipboard_to_world = clipboard.transform; if (clipboard_curves.curves_num() <= 0) { return {}; } @@ -2450,10 +2494,21 @@ IndexRange clipboard_paste_strokes(Main &bmain, /* Append the geometry from the clipboard to the target layer. */ Curves *clipboard_id = bke::curves_new_nomain(clipboard_curves); Curves *target_id = curves_new_nomain(std::move(drawing.strokes_for_write())); + const Array geometry_sets = { bke::GeometrySet::from_curves(paste_back ? clipboard_id : target_id), bke::GeometrySet::from_curves(paste_back ? target_id : clipboard_id)}; - bke::GeometrySet joined_curves = geometry::join_geometries(geometry_sets, {}); + + const float4x4 clipboard_transform = transform * + (keep_world_transform ? + object.world_to_object() * clipboard_to_world : + float4x4::identity()); + const Array transforms = paste_back ? + Span{clipboard_transform, + float4x4::identity()} : + Span{float4x4::identity(), clipboard_transform}; + bke::GeometrySet joined_curves = join_geometries_with_transform(geometry_sets, transforms); + drawing.strokes_for_write() = std::move(joined_curves.get_curves_for_write()->geometry.wrap()); /* Remap the material indices of the pasted curves to the target object material indices. */ diff --git a/source/blender/editors/include/ED_grease_pencil.hh b/source/blender/editors/include/ED_grease_pencil.hh index 115c5b25539..d24d170b4cb 100644 --- a/source/blender/editors/include/ED_grease_pencil.hh +++ b/source/blender/editors/include/ED_grease_pencil.hh @@ -512,11 +512,14 @@ const bke::CurvesGeometry &clipboard_curves(); /** * Paste curves from the clipboard into the drawing. * \param paste_back: Render behind existing curves by inserting curves at the front. + * \param keep_world_transform: Keep the world transform of clipboard strokes unchanged. * \return Index range of the new curves in the drawing after pasting. */ IndexRange clipboard_paste_strokes(Main &bmain, Object &object, bke::greasepencil::Drawing &drawing, + const float4x4 &transform, + bool keep_world_transform, bool paste_back); /** diff --git a/source/blender/editors/sculpt_paint/grease_pencil_sculpt_clone.cc b/source/blender/editors/sculpt_paint/grease_pencil_sculpt_clone.cc index 1064941211d..9b98554b3f9 100644 --- a/source/blender/editors/sculpt_paint/grease_pencil_sculpt_clone.cc +++ b/source/blender/editors/sculpt_paint/grease_pencil_sculpt_clone.cc @@ -37,6 +37,7 @@ void CloneOperation::on_stroke_begin(const bContext &C, const InputSample &start { Main &bmain = *CTX_data_main(&C); Object &object = *CTX_data_active_object(&C); + GreasePencil &grease_pencil = *static_cast(object.data); this->init_stroke(C, start_sample); @@ -48,8 +49,16 @@ void CloneOperation::on_stroke_begin(const bContext &C, const InputSample &start * Here we only have the GPv2 behavior that actually works for now. */ 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; + } + + /* TODO Could become a tool setting. */ + const bool keep_world_transform = false; + const float4x4 clipboard_to_layer = math::invert(params.layer.to_world_space(object)); const IndexRange pasted_curves = ed::greasepencil::clipboard_paste_strokes( - bmain, object, params.drawing, false); + bmain, object, params.drawing, clipboard_to_layer, keep_world_transform, false); if (pasted_curves.is_empty()) { return false; }