diff --git a/scripts/startup/bl_ui/space_view3d.py b/scripts/startup/bl_ui/space_view3d.py index 31e614d2af2..61b6a51dd02 100644 --- a/scripts/startup/bl_ui/space_view3d.py +++ b/scripts/startup/bl_ui/space_view3d.py @@ -765,6 +765,12 @@ class VIEW3D_HT_header(Header): depress=(tool_settings.gpencil_selectmode_edit == 'STROKE'), ).mode = 'STROKE' + + if object_mode == 'PAINT_GREASE_PENCIL': + row = layout.row() + sub = row.row(align=True) + sub.prop(tool_settings, "use_gpencil_draw_additive", text="", icon='FREEZE') + # Grease Pencil (legacy) if obj and obj.type == 'GPENCIL' and context.gpencil_data: gpd = context.gpencil_data diff --git a/source/blender/blenkernel/BKE_grease_pencil.hh b/source/blender/blenkernel/BKE_grease_pencil.hh index cc7d4a4a0b2..8e07057c5ad 100644 --- a/source/blender/blenkernel/BKE_grease_pencil.hh +++ b/source/blender/blenkernel/BKE_grease_pencil.hh @@ -310,6 +310,11 @@ class Layer : public ::GreasePencilLayer { * drawing. */ int drawing_index_at(const int frame_number) const; + /** + * \returns the key of the active frame at \a frame_number or -1 if there is no frame. + */ + FramesMapKey frame_key_at(int frame_number) const; + /** * \returns a pointer to the active frame at \a frame_number or nullptr if there is no frame. */ @@ -329,7 +334,7 @@ class Layer : public ::GreasePencilLayer { private: GreasePencilFrame *add_frame_internal(int frame_number, int drawing_index); - FramesMapKey frame_key_at(int frame_number) const; + /** * Removes null frames starting from \a begin until \a end (excluded) or until a non-null frame * is reached. \param begin, end: Iterators into the `sorted_keys` span. \returns an iterator to diff --git a/source/blender/blenkernel/intern/grease_pencil.cc b/source/blender/blenkernel/intern/grease_pencil.cc index e886a960f21..2d06d79c8b5 100644 --- a/source/blender/blenkernel/intern/grease_pencil.cc +++ b/source/blender/blenkernel/intern/grease_pencil.cc @@ -1382,6 +1382,21 @@ void GreasePencil::add_empty_drawings(const int add_num) } } +void GreasePencil::add_duplicate_drawings(const int duplicate_num, + const blender::bke::greasepencil::Drawing &drawing) +{ + using namespace blender; + BLI_assert(duplicate_num > 0); + const int prev_num = this->drawings().size(); + grow_array( + &this->drawing_array, &this->drawing_array_num, duplicate_num); + MutableSpan new_drawings = this->drawings().drop_front(prev_num); + for (const int i : new_drawings.index_range()) { + new_drawings[i] = reinterpret_cast( + MEM_new(__func__, drawing)); + } +} + bool GreasePencil::insert_blank_frame(blender::bke::greasepencil::Layer &layer, int frame_number, int duration, @@ -1397,6 +1412,73 @@ bool GreasePencil::insert_blank_frame(blender::bke::greasepencil::Layer &layer, return true; } +static int get_frame_duration(const blender::bke::greasepencil::Layer &layer, + const int frame_number) +{ + Span sorted_keys = layer.sorted_keys(); + const int *frame_number_it = std::lower_bound( + sorted_keys.begin(), sorted_keys.end(), frame_number); + if (std::next(frame_number_it) == sorted_keys.end()) { + return 0; + } + const int next_frame_number = *(std::next(frame_number_it)); + return next_frame_number - frame_number; +} + +bool GreasePencil::insert_duplicate_frame(blender::bke::greasepencil::Layer &layer, + const int src_frame_number, + const int dst_frame_number, + const bool do_instance) +{ + using namespace blender::bke::greasepencil; + + if (!layer.frames().contains(src_frame_number)) { + return false; + } + const GreasePencilFrame &src_frame = layer.frames().lookup(src_frame_number); + + /* Create the new frame structure, with the same duration. + * If we want to make an instance of the source frame, the drawing index gets copied from the + * source frame. Otherwise, we set the drawing index to the size of the drawings array, since we + * are going to add a new drawing copied from the source drawing. */ + const int duration = src_frame.is_implicit_hold() ? 0 : + get_frame_duration(layer, src_frame_number); + const int drawing_index = do_instance ? src_frame.drawing_index : int(this->drawings().size()); + GreasePencilFrame *dst_frame = layer.add_frame(dst_frame_number, drawing_index, duration); + + if (dst_frame == nullptr) { + return false; + } + + dst_frame->type = src_frame.type; + + const GreasePencilDrawingBase *src_drawing_base = this->drawings(src_frame.drawing_index); + switch (src_drawing_base->type) { + case GP_DRAWING: { + const Drawing &src_drawing = + reinterpret_cast(src_drawing_base)->wrap(); + if (do_instance) { + /* Adds the duplicate frame as a new instance of the same drawing. We thus increase the + * user count of the corresponding drawing. */ + src_drawing.add_user(); + } + else { + /* Create a copy of the drawing, and add it at the end of the drawings array. + * Note that the frame already points to this new drawing, as the drawing index was set to + * `int(this->drawings().size())`. */ + this->add_duplicate_drawings(1, src_drawing); + } + break; + } + case GP_DRAWING_REFERENCE: + /* TODO: Duplicate drawing references is not yet implemented. + * For now, just remove the frame that we inserted. */ + layer.remove_frame(dst_frame_number); + return false; + } + return true; +} + void GreasePencil::remove_frame_at(blender::bke::greasepencil::Layer &layer, const int frame_number) { diff --git a/source/blender/editors/space_action/action_edit.cc b/source/blender/editors/space_action/action_edit.cc index 1ac112517ae..f89dec3d5eb 100644 --- a/source/blender/editors/space_action/action_edit.cc +++ b/source/blender/editors/space_action/action_edit.cc @@ -12,11 +12,14 @@ #include #include "BLI_blenlib.h" +#include "BLI_map.hh" #include "BLI_math.h" #include "BLI_utildefines.h" #include "BLT_translation.h" +#include "DEG_depsgraph.h" + #include "DNA_anim_types.h" #include "DNA_gpencil_legacy_types.h" #include "DNA_key_types.h" @@ -34,6 +37,7 @@ #include "BKE_fcurve.h" #include "BKE_global.h" #include "BKE_gpencil_legacy.h" +#include "BKE_grease_pencil.hh" #include "BKE_key.h" #include "BKE_nla.h" #include "BKE_report.h" @@ -765,6 +769,45 @@ static void insert_gpencil_key(bAnimContext *ac, } } +static void insert_grease_pencil_key(bAnimContext *ac, + bAnimListElem *ale, + const bool hold_previous) +{ + using namespace blender::bke::greasepencil; + Layer *layer = static_cast(ale->data); + GreasePencil *grease_pencil = reinterpret_cast(ale->id); + const int current_frame_number = ac->scene->r.cfra; + + if (layer->frames().contains(current_frame_number)) { + return; + } + + bool changed = false; + if (hold_previous) { + const FramesMapKey active_frame_number = layer->frame_key_at(current_frame_number); + if ((active_frame_number == -1) || (layer->frames().lookup(active_frame_number).is_null())) { + /* There is no active frame to hold to, or it's a null frame. Therefore just insert a blank + * frame. */ + changed = grease_pencil->insert_blank_frame( + *layer, current_frame_number, 0, BEZT_KEYTYPE_KEYFRAME); + } + else { + /* Duplicate the active frame. */ + changed = grease_pencil->insert_duplicate_frame( + *layer, active_frame_number, current_frame_number, false); + } + } + else { + /* Insert a blank frame. */ + changed = grease_pencil->insert_blank_frame( + *layer, current_frame_number, 0, BEZT_KEYTYPE_KEYFRAME); + } + + if (changed) { + DEG_id_tag_update(&grease_pencil->id, ID_RECALC_GEOMETRY); + } +} + static void insert_fcurve_key(bAnimContext *ac, bAnimListElem *ale, const AnimationEvalContext anim_eval_context, @@ -853,6 +896,7 @@ static void insert_action_keys(bAnimContext *ac, short mode) else { add_frame_mode = GP_GETFRAME_ADD_NEW; } + const bool grease_pencil_hold_previous = ((ts->gpencil_flags & GP_TOOL_FLAG_RETAIN_LAST) != 0); /* insert keyframes */ const AnimationEvalContext anim_eval_context = BKE_animsys_eval_context_construct( @@ -864,7 +908,7 @@ static void insert_action_keys(bAnimContext *ac, short mode) break; case ANIMTYPE_GREASE_PENCIL_LAYER: - /* GPv3: To be implemented. */ + insert_grease_pencil_key(ac, ale, grease_pencil_hold_previous); break; case ANIMTYPE_FCURVE: diff --git a/source/blender/makesdna/DNA_grease_pencil_types.h b/source/blender/makesdna/DNA_grease_pencil_types.h index c3a4ceadbd7..ca334286c66 100644 --- a/source/blender/makesdna/DNA_grease_pencil_types.h +++ b/source/blender/makesdna/DNA_grease_pencil_types.h @@ -481,10 +481,17 @@ typedef struct GreasePencil { void remove_layer(blender::bke::greasepencil::Layer &layer); void add_empty_drawings(int add_num); + void add_duplicate_drawings(int duplicate_num, + const blender::bke::greasepencil::Drawing &drawing); bool insert_blank_frame(blender::bke::greasepencil::Layer &layer, int frame_number, int duration, eBezTriple_KeyframeType keytype); + bool insert_duplicate_frame(blender::bke::greasepencil::Layer &layer, + const int src_frame_number, + const int dst_frame_number, + const bool do_instance); + void remove_frame_at(blender::bke::greasepencil::Layer &layer, int frame_number); void remove_drawing(int index);