GPv3: Insert grease pencil keyframe from the dopesheet #110649
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1382,6 +1382,21 @@ void GreasePencil::add_empty_drawings(const int add_num)
|
|||
}
|
||||
}
|
||||
|
||||
amelief marked this conversation as resolved
Outdated
|
||||
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
Falk David
commented
Can use a new API here 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
Falk David
commented
I think instead of the 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
Falk David
commented
`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
Falk David
commented
Rather than asserting here, I think we can return 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
Falk David
commented
Can be directly 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
Falk David
commented
`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
Falk David
commented
This should have a comment saying that the inserted frame already points to this duplicated drawing, because the index was set to 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)
|
||||
{
|
||||
|
|
|
@ -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
Falk David
commented
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
Falk David
commented
Even if it's a bit longer, I would prefer this to be 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
Falk David
commented
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
Falk David
commented
`/* 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
Falk David
commented
the 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:
|
||||
|
|
|
@ -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
Falk David
commented
`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);
|
||||
|
|
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.