GPencil: New option to Merge All layers in active one

This new option allows to combine all layers in the active one. Also the merge down option has been improved.

Reviewed By: mendio, pablo vazquez (UI)

Differential Revision: https://developer.blender.org/D13054
This commit is contained in:
Antonio Vazquez 2021-11-04 19:40:18 +01:00
parent 1c6d3d614a
commit 556c71a84a
Notes: blender-bot 2023-02-15 09:26:34 +01:00
Referenced by issue #88449, Blender LTS: Maintenance Task 2.93
4 changed files with 171 additions and 65 deletions

View File

@ -110,7 +110,8 @@ class GPENCIL_MT_layer_context_menu(Menu):
layout.separator()
layout.operator("gpencil.layer_merge", icon='SORT_ASC', text="Merge Down")
layout.operator("gpencil.layer_merge", icon='SORT_ASC', text="Merge Down").mode = 'ACTIVE'
layout.operator("gpencil.layer_merge", text="Merge All").mode = 'ALL'
layout.separator()
layout.operator("gpencil.layer_duplicate_object", text="Copy Layer to Selected").only_active = True

View File

@ -1321,79 +1321,103 @@ void GPENCIL_OT_layer_isolate(wmOperatorType *ot)
}
/* ********************** Merge Layer with the next layer **************************** */
enum {
GP_LAYER_MERGE_ACTIVE = 0,
GP_LAYER_MERGE_ALL = 1,
};
static void apply_layer_settings(bGPDlayer *gpl)
{
/* Apply layer attributes. */
LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
gps->fill_opacity_fac *= gpl->opacity;
gps->vert_color_fill[3] *= gpl->opacity;
for (int p = 0; p < gps->totpoints; p++) {
bGPDspoint *pt = &gps->points[p];
float factor = (((float)gps->thickness * pt->pressure) + (float)gpl->line_change) /
((float)gps->thickness * pt->pressure);
pt->pressure *= factor;
pt->strength *= gpl->opacity;
/* Layer transformation. */
mul_v3_m4v3(&pt->x, gpl->layer_mat, &pt->x);
zero_v3(gpl->location);
zero_v3(gpl->rotation);
copy_v3_fl(gpl->scale, 1.0f);
}
}
}
gpl->line_change = 0;
gpl->opacity = 1.0f;
unit_m4(gpl->layer_mat);
invert_m4_m4(gpl->layer_invmat, gpl->layer_mat);
}
static int gpencil_merge_layer_exec(bContext *C, wmOperator *op)
{
bGPdata *gpd = ED_gpencil_data_get_active(C);
bGPDlayer *gpl_src = BKE_gpencil_layer_active_get(gpd);
bGPDlayer *gpl_dst = gpl_src->prev;
bGPDlayer *gpl_active = BKE_gpencil_layer_active_get(gpd);
bGPDlayer *gpl_dst = gpl_active->prev;
const int mode = RNA_enum_get(op->ptr, "mode");
if (ELEM(NULL, gpd, gpl_dst, gpl_src)) {
BKE_report(op->reports, RPT_ERROR, "No layers to merge");
if (mode == GP_LAYER_MERGE_ACTIVE) {
if (ELEM(NULL, gpd, gpl_dst, gpl_active)) {
BKE_report(op->reports, RPT_ERROR, "No layers to merge");
return OPERATOR_CANCELLED;
}
}
else {
if (ELEM(NULL, gpd, gpl_active)) {
BKE_report(op->reports, RPT_ERROR, "No layers to flatten");
return OPERATOR_CANCELLED;
}
}
if (mode == GP_LAYER_MERGE_ACTIVE) {
/* Apply destination layer attributes. */
apply_layer_settings(gpl_active);
ED_gpencil_layer_merge(gpd, gpl_active, gpl_dst, false);
}
else if (mode == GP_LAYER_MERGE_ALL) {
/* Apply layer attributes to all layers. */
LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
apply_layer_settings(gpl);
}
gpl_dst = gpl_active;
/* Merge layers on top of active layer. */
if (gpd->layers.last != gpl_dst) {
LISTBASE_FOREACH_BACKWARD_MUTABLE (bGPDlayer *, gpl, &gpd->layers) {
if (gpl == gpl_dst) {
break;
}
ED_gpencil_layer_merge(gpd, gpl, gpl->prev, false);
}
}
/* Merge layers below active layer. */
LISTBASE_FOREACH_BACKWARD_MUTABLE (bGPDlayer *, gpl, &gpd->layers) {
if (gpl == gpl_dst) {
continue;
}
ED_gpencil_layer_merge(gpd, gpl, gpl_dst, true);
}
/* Set general layers settings to default values. */
gpl_active->blend_mode = eGplBlendMode_Regular;
gpl_active->flag &= ~GP_LAYER_LOCKED;
gpl_active->flag &= ~GP_LAYER_HIDE;
gpl_active->flag |= GP_LAYER_USE_LIGHTS;
gpl_active->onion_flag |= GP_LAYER_ONIONSKIN;
}
else {
return OPERATOR_CANCELLED;
}
/* Collect frames of gpl_dst in hash table to avoid O(n^2) lookups. */
GHash *gh_frames_dst = BLI_ghash_int_new_ex(__func__, 64);
LISTBASE_FOREACH (bGPDframe *, gpf_dst, &gpl_dst->frames) {
BLI_ghash_insert(gh_frames_dst, POINTER_FROM_INT(gpf_dst->framenum), gpf_dst);
/* Clear any invalid mask. Some other layer could be using the merged layer. */
LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
BKE_gpencil_layer_mask_cleanup(gpd, gpl);
}
/* Read all frames from merge layer and add any missing in destination layer,
* copying all previous strokes to keep the image equals.
* Need to do it in a separated loop to avoid strokes accumulation. */
LISTBASE_FOREACH (bGPDframe *, gpf_src, &gpl_src->frames) {
/* Try to find frame in destination layer hash table. */
bGPDframe *gpf_dst = BLI_ghash_lookup(gh_frames_dst, POINTER_FROM_INT(gpf_src->framenum));
if (!gpf_dst) {
gpf_dst = BKE_gpencil_layer_frame_get(gpl_dst, gpf_src->framenum, GP_GETFRAME_ADD_COPY);
/* Use same frame type. */
gpf_dst->key_type = gpf_src->key_type;
BLI_ghash_insert(gh_frames_dst, POINTER_FROM_INT(gpf_src->framenum), gpf_dst);
}
}
/* Read all frames from merge layer and add strokes. */
LISTBASE_FOREACH (bGPDframe *, gpf_src, &gpl_src->frames) {
/* Try to find frame in destination layer hash table. */
bGPDframe *gpf_dst = BLI_ghash_lookup(gh_frames_dst, POINTER_FROM_INT(gpf_src->framenum));
/* Apply layer transformation. */
LISTBASE_FOREACH (bGPDstroke *, gps_src, &gpf_src->strokes) {
for (int p = 0; p < gps_src->totpoints; p++) {
bGPDspoint *pt = &gps_src->points[p];
mul_v3_m4v3(&pt->x, gpl_src->layer_mat, &pt->x);
}
}
/* Add to tail all strokes. */
if (gpf_dst) {
BLI_movelisttolist(&gpf_dst->strokes, &gpf_src->strokes);
}
}
/* Add Masks to destination layer. */
LISTBASE_FOREACH (bGPDlayer_Mask *, mask, &gpl_src->mask_layers) {
/* Don't add merged layers or missing layer names. */
if (!BKE_gpencil_layer_named_get(gpd, mask->name) || STREQ(mask->name, gpl_src->info) ||
STREQ(mask->name, gpl_dst->info)) {
continue;
}
if (!BKE_gpencil_layer_mask_named_get(gpl_dst, mask->name)) {
bGPDlayer_Mask *mask_new = MEM_dupallocN(mask);
BLI_addtail(&gpl_dst->mask_layers, mask_new);
gpl_dst->act_mask++;
}
}
/* Set destination layer as active. */
BKE_gpencil_layer_active_set(gpd, gpl_dst);
/* Now delete next layer */
BKE_gpencil_layer_delete(gpd, gpl_src);
BLI_ghash_free(gh_frames_dst, NULL, NULL);
/* Reorder masking. */
BKE_gpencil_layer_mask_sort(gpd, gpl_dst);
/* notifiers */
DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
@ -1404,10 +1428,16 @@ static int gpencil_merge_layer_exec(bContext *C, wmOperator *op)
void GPENCIL_OT_layer_merge(wmOperatorType *ot)
{
static const EnumPropertyItem merge_modes[] = {
{GP_LAYER_MERGE_ACTIVE, "ACTIVE", 0, "Active", "Combine active layer into the layer below"},
{GP_LAYER_MERGE_ALL, "ALL", 0, "All", "Combine all layers into the active layer"},
{0, NULL, 0, NULL, NULL},
};
/* identifiers */
ot->name = "Merge Down";
ot->idname = "GPENCIL_OT_layer_merge";
ot->description = "Merge the current layer with the layer below";
ot->description = "Combine Layers";
/* callbacks */
ot->exec = gpencil_merge_layer_exec;
@ -1415,6 +1445,8 @@ void GPENCIL_OT_layer_merge(wmOperatorType *ot)
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
ot->prop = RNA_def_enum(ot->srna, "mode", merge_modes, GP_LAYER_MERGE_ACTIVE, "Mode", "");
}
/* ********************** Change Layer ***************************** */

View File

@ -3415,3 +3415,71 @@ void ED_gpencil_stroke_close_by_distance(bGPDstroke *gps, const float threshold)
BKE_gpencil_stroke_close(gps);
}
}
/* Merge two layers. */
void ED_gpencil_layer_merge(bGPdata *gpd,
bGPDlayer *gpl_src,
bGPDlayer *gpl_dst,
const bool reverse)
{
/* Collect frames of gpl_dst in hash table to avoid O(n^2) lookups. */
GHash *gh_frames_dst = BLI_ghash_int_new_ex(__func__, 64);
LISTBASE_FOREACH (bGPDframe *, gpf_dst, &gpl_dst->frames) {
BLI_ghash_insert(gh_frames_dst, POINTER_FROM_INT(gpf_dst->framenum), gpf_dst);
}
/* Read all frames from merge layer and add any missing in destination layer,
* copying all previous strokes to keep the image equals.
* Need to do it in a separated loop to avoid strokes accumulation. */
LISTBASE_FOREACH (bGPDframe *, gpf_src, &gpl_src->frames) {
/* Try to find frame in destination layer hash table. */
bGPDframe *gpf_dst = BLI_ghash_lookup(gh_frames_dst, POINTER_FROM_INT(gpf_src->framenum));
if (!gpf_dst) {
gpf_dst = BKE_gpencil_layer_frame_get(gpl_dst, gpf_src->framenum, GP_GETFRAME_ADD_COPY);
/* Use same frame type. */
gpf_dst->key_type = gpf_src->key_type;
BLI_ghash_insert(gh_frames_dst, POINTER_FROM_INT(gpf_src->framenum), gpf_dst);
}
}
/* Read all frames from merge layer and add strokes. */
LISTBASE_FOREACH (bGPDframe *, gpf_src, &gpl_src->frames) {
/* Try to find frame in destination layer hash table. */
bGPDframe *gpf_dst = BLI_ghash_lookup(gh_frames_dst, POINTER_FROM_INT(gpf_src->framenum));
/* Add to tail all strokes. */
if (gpf_dst) {
if (reverse) {
BLI_movelisttolist_reverse(&gpf_dst->strokes, &gpf_src->strokes);
}
else {
BLI_movelisttolist(&gpf_dst->strokes, &gpf_src->strokes);
}
}
}
/* Add Masks to destination layer. */
LISTBASE_FOREACH (bGPDlayer_Mask *, mask, &gpl_src->mask_layers) {
/* Don't add merged layers or missing layer names. */
if (!BKE_gpencil_layer_named_get(gpd, mask->name) || STREQ(mask->name, gpl_src->info) ||
STREQ(mask->name, gpl_dst->info)) {
continue;
}
if (!BKE_gpencil_layer_mask_named_get(gpl_dst, mask->name)) {
bGPDlayer_Mask *mask_new = MEM_dupallocN(mask);
BLI_addtail(&gpl_dst->mask_layers, mask_new);
gpl_dst->act_mask++;
}
}
/* Set destination layer as active. */
BKE_gpencil_layer_active_set(gpd, gpl_dst);
/* Now delete merged layer. */
BKE_gpencil_layer_delete(gpd, gpl_src);
BLI_ghash_free(gh_frames_dst, NULL, NULL);
/* Reorder masking. */
if (gpl_dst->mask_layers.first) {
BKE_gpencil_layer_mask_sort(gpd, gpl_dst);
}
}

View File

@ -205,6 +205,11 @@ void ED_gpencil_select_frame(struct bGPDlayer *gpl, int selx, short select_mode)
bool ED_gpencil_layer_frames_delete(struct bGPDlayer *gpl);
void ED_gpencil_layer_frames_duplicate(struct bGPDlayer *gpl);
void ED_gpencil_layer_merge(struct bGPdata *gpd,
struct bGPDlayer *gpl_src,
struct bGPDlayer *gpl_dst,
const bool reverse);
void ED_gpencil_layer_frames_keytype_set(struct bGPDlayer *gpl, short type);
void ED_gpencil_layer_snap_frames(struct bGPDlayer *gpl, struct Scene *scene, short mode);