GPv3: Insert grease pencil keyframe from the dopesheet #110649

Merged
Amélie Fondevilla merged 23 commits from amelief/blender:gpv3-insert-keyframe into main 2023-08-02 14:56:24 +02:00
5 changed files with 146 additions and 2 deletions

View File

@ -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

View File

@ -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

View File

@ -1382,6 +1382,21 @@ void GreasePencil::add_empty_drawings(const int add_num)
}
}
amelief marked this conversation as resolved Outdated

Shouldn't this get the duration from the duplicate_frame?

Shouldn't this get the duration from the `duplicate_frame`?

Agree. I had to change the function so that it takes an index instead of directly the frame to be duplicated. I think now it works, though.

Agree. I had to change the function so that it takes an index instead of directly the frame to be duplicated. I think now it works, though.
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<GreasePencilDrawingBase *>(
&this->drawing_array, &this->drawing_array_num, duplicate_num);
MutableSpan<GreasePencilDrawingBase *> new_drawings = this->drawings().drop_front(prev_num);
amelief marked this conversation as resolved Outdated

Can use a new API here this->drawings(duplicated_frame.drawing_index). Tiny bit nicer.

Can use a new API here `this->drawings(duplicated_frame.drawing_index)`. Tiny bit nicer.
for (const int i : new_drawings.index_range()) {
amelief marked this conversation as resolved Outdated

I think instead of the assert here I would prefer a switch that has a TODO comment for the GP_DRAWING_REFERENCE case and does nothing.

I think instead of the `assert` here I would prefer a `switch` that has a `TODO` comment for the `GP_DRAWING_REFERENCE` case and does nothing.
new_drawings[i] = reinterpret_cast<GreasePencilDrawingBase *>(
MEM_new<bke::greasepencil::Drawing>(__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<int> 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);
amelief marked this conversation as resolved Outdated

using namespace blender::bke::greasepencil; here so we can use Drawing below.

`using namespace blender::bke::greasepencil;` here so we can use `Drawing` below.
/* 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
amelief marked this conversation as resolved Outdated

Rather than asserting here, I think we can return false. I don't think we should crash here if the frame_number isn't found.

Rather than asserting here, I think we can return `false`. I don't think we should crash here if the `frame_number` isn't found.
* 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: {
amelief marked this conversation as resolved Outdated

Can be directly Drawing &drawing = reinterpret_cast<const GreasePencilDrawing *>(drawing_base)->wrap();

Can be directly `Drawing &drawing = reinterpret_cast<const GreasePencilDrawing *>(drawing_base)->wrap();`
const Drawing &src_drawing =
reinterpret_cast<const GreasePencilDrawing *>(src_drawing_base)->wrap();
if (do_instance) {
amelief marked this conversation as resolved Outdated

drawing.add_user(); (needs a rebase with main)

`drawing.add_user();` (needs a rebase with `main`)
/* 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();
amelief marked this conversation as resolved Outdated

This should have a comment saying that the inserted frame already points to this duplicated drawing, because the index was set to int(this->drawings().size()).

This should have a comment saying that the inserted frame already points to this duplicated drawing, because the index was set to `int(this->drawings().size())`.
}
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)
{

View File

@ -12,11 +12,14 @@
#include <cstring>
#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"
amelief marked this conversation as resolved Outdated

Don't think this header needs to be included.

Don't think this header needs to be included.
#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<Layer *>(ale->data);
GreasePencil *grease_pencil = reinterpret_cast<GreasePencil *>(ale->id);
const int current_frame_number = ac->scene->r.cfra;
amelief marked this conversation as resolved Outdated

Even if it's a bit longer, I would prefer this to be current_frame_number. Makes the rest more readable imho.

Even if it's a bit longer, I would prefer this to be `current_frame_number`. Makes the rest more readable imho.
if (layer->frames().contains(current_frame_number)) {
return;
}
amelief marked this conversation as resolved Outdated

This comment can be removed with the change suggested above.

This comment can be removed with the change suggested above.
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(
amelief marked this conversation as resolved Outdated

/* There is no active frame to hold to, or it's a null frame. Therefore just insert a blank frame. */

`/* There is no active frame to hold to, or it's a null frame. Therefore just insert a 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);
}
amelief marked this conversation as resolved Outdated

the layer.add_frame function will do the tagging already. I think this is not needed.

the `layer.add_frame` function will do the tagging already. I think this is not needed.
}
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:

View File

@ -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,
amelief marked this conversation as resolved Outdated

duplicate_num and Drawing &drawing

`duplicate_num` and `Drawing &drawing`
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);