diff options
Diffstat (limited to 'source/blender/editors/gpencil')
21 files changed, 1369 insertions, 311 deletions
diff --git a/source/blender/editors/gpencil/CMakeLists.txt b/source/blender/editors/gpencil/CMakeLists.txt index 47ae90acb74..bff7310e9f7 100644 --- a/source/blender/editors/gpencil/CMakeLists.txt +++ b/source/blender/editors/gpencil/CMakeLists.txt @@ -36,10 +36,12 @@ set(SRC annotate_paint.c drawgpencil.c editaction_gpencil.c + gpencil_add_blank.c gpencil_add_lineart.c gpencil_add_monkey.c gpencil_add_stroke.c gpencil_armature.c + gpencil_bake_animation.c gpencil_convert.c gpencil_data.c gpencil_edit.c diff --git a/source/blender/editors/gpencil/annotate_paint.c b/source/blender/editors/gpencil/annotate_paint.c index e9817f82090..c155587e95a 100644 --- a/source/blender/editors/gpencil/annotate_paint.c +++ b/source/blender/editors/gpencil/annotate_paint.c @@ -660,10 +660,14 @@ static short annotation_stroke_addpoint(tGPsdata *p, View3D *v3d = p->area->spacedata.first; view3d_region_operator_needs_opengl(p->win, p->region); - ED_view3d_autodist_init(p->depsgraph, - p->region, - v3d, - (ts->annotate_v3d_align & GP_PROJECT_DEPTH_STROKE) ? 1 : 0); + ED_view3d_depth_override(p->depsgraph, + p->region, + v3d, + NULL, + (ts->annotate_v3d_align & GP_PROJECT_DEPTH_STROKE) ? + V3D_DEPTH_GPENCIL_ONLY : + V3D_DEPTH_NO_GPENCIL, + false); } /* convert screen-coordinates to appropriate coordinates (and store them) */ @@ -1222,7 +1226,7 @@ static void annotation_stroke_doeraser(tGPsdata *p) if (p->flags & GP_PAINTFLAG_V3D_ERASER_DEPTH) { View3D *v3d = p->area->spacedata.first; view3d_region_operator_needs_opengl(p->win, p->region); - ED_view3d_autodist_init(p->depsgraph, p->region, v3d, 0); + ED_view3d_depth_override(p->depsgraph, p->region, v3d, NULL, V3D_DEPTH_NO_GPENCIL, false); } } @@ -1544,7 +1548,7 @@ static void annotation_paint_initstroke(tGPsdata *p, if (p->gpl == NULL) { /* tag for annotations */ p->gpd->flag |= GP_DATA_ANNOTATIONS; - p->gpl = BKE_gpencil_layer_addnew(p->gpd, DATA_("Note"), true); + p->gpl = BKE_gpencil_layer_addnew(p->gpd, DATA_("Note"), true, false); if (p->custom_color[3]) { copy_v3_v3(p->gpl->color, p->custom_color); @@ -1695,8 +1699,14 @@ static void annotation_paint_strokeend(tGPsdata *p) /* need to restore the original projection settings before packing up */ view3d_region_operator_needs_opengl(p->win, p->region); - ED_view3d_autodist_init( - p->depsgraph, p->region, v3d, (ts->annotate_v3d_align & GP_PROJECT_DEPTH_STROKE) ? 1 : 0); + ED_view3d_depth_override(p->depsgraph, + p->region, + v3d, + NULL, + (ts->annotate_v3d_align & GP_PROJECT_DEPTH_STROKE) ? + V3D_DEPTH_GPENCIL_ONLY : + V3D_DEPTH_NO_GPENCIL, + false); } /* check if doing eraser or not */ diff --git a/source/blender/editors/gpencil/gpencil_add_blank.c b/source/blender/editors/gpencil/gpencil_add_blank.c new file mode 100644 index 00000000000..3aa16e54597 --- /dev/null +++ b/source/blender/editors/gpencil/gpencil_add_blank.c @@ -0,0 +1,101 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2017 Blender Foundation + * This is a new part of Blender + */ + +/** \file + * \ingroup edgpencil + */ + +#include "BLI_math.h" +#include "BLI_utildefines.h" + +#include "DNA_gpencil_types.h" +#include "DNA_material_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" + +#include "BKE_context.h" +#include "BKE_gpencil.h" +#include "BKE_gpencil_geom.h" +#include "BKE_main.h" +#include "BKE_material.h" + +#include "DEG_depsgraph.h" + +#include "ED_gpencil.h" + +/* Definition of the most important info from a color */ +typedef struct ColorTemplate { + const char *name; + float line[4]; + float fill[4]; +} ColorTemplate; + +/* Add color an ensure duplications (matched by name) */ +static int gpencil_stroke_material(Main *bmain, Object *ob, const ColorTemplate *pct) +{ + int index; + Material *ma = BKE_gpencil_object_material_ensure_by_name(bmain, ob, pct->name, &index); + + copy_v4_v4(ma->gp_style->stroke_rgba, pct->line); + srgb_to_linearrgb_v4(ma->gp_style->stroke_rgba, ma->gp_style->stroke_rgba); + + copy_v4_v4(ma->gp_style->fill_rgba, pct->fill); + srgb_to_linearrgb_v4(ma->gp_style->fill_rgba, ma->gp_style->fill_rgba); + + return index; +} + +/* ***************************************************************** */ +/* Stroke Geometry */ + +/* ***************************************************************** */ +/* Color Data */ + +static const ColorTemplate gp_stroke_material_black = { + "Black", + {0.0f, 0.0f, 0.0f, 1.0f}, + {0.0f, 0.0f, 0.0f, 0.0f}, +}; + +/* ***************************************************************** */ +/* Blank API */ + +/* Add a Simple empty object with one layer and one color. */ +void ED_gpencil_create_blank(bContext *C, Object *ob, float UNUSED(mat[4][4])) +{ + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + bGPdata *gpd = (bGPdata *)ob->data; + + /* create colors */ + int color_black = gpencil_stroke_material(bmain, ob, &gp_stroke_material_black); + + /* set first color as active and in brushes */ + ob->actcol = color_black + 1; + + /* layers */ + bGPDlayer *layer = BKE_gpencil_layer_addnew(gpd, "GP_Layer", true, false); + + /* frames */ + BKE_gpencil_frame_addnew(layer, CFRA); + + /* update depsgraph */ + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + gpd->flag |= GP_DATA_CACHE_IS_DIRTY; +} diff --git a/source/blender/editors/gpencil/gpencil_add_lineart.c b/source/blender/editors/gpencil/gpencil_add_lineart.c index 6b28c6ec13e..ac0da0ad1db 100644 --- a/source/blender/editors/gpencil/gpencil_add_lineart.c +++ b/source/blender/editors/gpencil/gpencil_add_lineart.c @@ -96,7 +96,7 @@ void ED_gpencil_create_lineart(bContext *C, Object *ob) ob->actcol = color_black + 1; /* layers */ - bGPDlayer *lines = BKE_gpencil_layer_addnew(gpd, "Lines", true); + bGPDlayer *lines = BKE_gpencil_layer_addnew(gpd, "Lines", true, false); /* frames */ BKE_gpencil_frame_addnew(lines, 0); diff --git a/source/blender/editors/gpencil/gpencil_add_monkey.c b/source/blender/editors/gpencil/gpencil_add_monkey.c index 4497d963c6d..d8734c4ae6b 100644 --- a/source/blender/editors/gpencil/gpencil_add_monkey.c +++ b/source/blender/editors/gpencil/gpencil_add_monkey.c @@ -837,8 +837,8 @@ void ED_gpencil_create_monkey(bContext *C, Object *ob, float mat[4][4]) /* layers */ /* NOTE: For now, we just add new layers, to make it easier to separate out old/new instances */ - bGPDlayer *Fills = BKE_gpencil_layer_addnew(gpd, "Fills", false); - bGPDlayer *Lines = BKE_gpencil_layer_addnew(gpd, "Lines", true); + bGPDlayer *Fills = BKE_gpencil_layer_addnew(gpd, "Fills", false, false); + bGPDlayer *Lines = BKE_gpencil_layer_addnew(gpd, "Lines", true, false); /* frames */ /* NOTE: No need to check for existing, as this will take care of it for us */ diff --git a/source/blender/editors/gpencil/gpencil_add_stroke.c b/source/blender/editors/gpencil/gpencil_add_stroke.c index 26237636526..e95496b51ee 100644 --- a/source/blender/editors/gpencil/gpencil_add_stroke.c +++ b/source/blender/editors/gpencil/gpencil_add_stroke.c @@ -225,8 +225,8 @@ void ED_gpencil_create_stroke(bContext *C, Object *ob, float mat[4][4]) ob->actcol = color_black + 1; /* layers */ - bGPDlayer *colors = BKE_gpencil_layer_addnew(gpd, "Colors", false); - bGPDlayer *lines = BKE_gpencil_layer_addnew(gpd, "Lines", true); + bGPDlayer *colors = BKE_gpencil_layer_addnew(gpd, "Colors", false, false); + bGPDlayer *lines = BKE_gpencil_layer_addnew(gpd, "Lines", true, false); /* frames */ bGPDframe *frame_color = BKE_gpencil_frame_addnew(colors, CFRA); diff --git a/source/blender/editors/gpencil/gpencil_bake_animation.c b/source/blender/editors/gpencil/gpencil_bake_animation.c new file mode 100644 index 00000000000..30ebc9189c5 --- /dev/null +++ b/source/blender/editors/gpencil/gpencil_bake_animation.c @@ -0,0 +1,448 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2021 Blender Foundation + * This is a new part of Blender + */ + +/** \file + * \ingroup edgpencil + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_blenlib.h" +#include "BLI_ghash.h" +#include "BLI_math.h" + +#include "DNA_anim_types.h" +#include "DNA_gpencil_types.h" +#include "DNA_material_types.h" +#include "DNA_scene_types.h" +#include "DNA_screen_types.h" + +#include "BKE_anim_data.h" +#include "BKE_context.h" +#include "BKE_duplilist.h" +#include "BKE_gpencil.h" +#include "BKE_gpencil_geom.h" +#include "BKE_layer.h" +#include "BKE_main.h" +#include "BKE_material.h" +#include "BKE_object.h" +#include "BKE_report.h" +#include "BKE_scene.h" + +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_query.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "ED_gpencil.h" +#include "ED_transform_snap_object_context.h" + +#include "gpencil_intern.h" + +const EnumPropertyItem rna_gpencil_reproject_type_items[] = { + {GP_REPROJECT_KEEP, "KEEP", 0, "No Reproject", ""}, + {GP_REPROJECT_FRONT, "FRONT", 0, "Front", "Reproject the strokes using the X-Z plane"}, + {GP_REPROJECT_SIDE, "SIDE", 0, "Side", "Reproject the strokes using the Y-Z plane"}, + {GP_REPROJECT_TOP, "TOP", 0, "Top", "Reproject the strokes using the X-Y plane"}, + {GP_REPROJECT_VIEW, + "VIEW", + 0, + "View", + "Reproject the strokes to end up on the same plane, as if drawn from the current " + "viewpoint " + "using 'Cursor' Stroke Placement"}, + {GP_REPROJECT_CURSOR, + "CURSOR", + 0, + "Cursor", + "Reproject the strokes using the orientation of 3D cursor"}, + {0, NULL, 0, NULL, NULL}, +}; + +/* Check frame_end is always > start frame! */ +static void gpencil_bake_set_frame_end(struct Main *UNUSED(main), + struct Scene *UNUSED(scene), + struct PointerRNA *ptr) +{ + int frame_start = RNA_int_get(ptr, "frame_start"); + int frame_end = RNA_int_get(ptr, "frame_end"); + + if (frame_end <= frame_start) { + RNA_int_set(ptr, "frame_end", frame_start + 1); + } +} + +/* Extract mesh animation to Grease Pencil. */ +static bool gpencil_bake_grease_pencil_animation_poll(bContext *C) +{ + Object *obact = CTX_data_active_object(C); + if (CTX_data_mode_enum(C) != CTX_MODE_OBJECT) { + return false; + } + + /* Check if grease pencil or empty for dupli groups. */ + if ((obact == NULL) || ((obact->type != OB_GPENCIL) && (obact->type != OB_EMPTY))) { + return false; + } + + /* Only if the current view is 3D View. */ + ScrArea *area = CTX_wm_area(C); + return (area && area->spacetype); +} + +typedef struct GpBakeOb { + struct GpBakeOb *next, *prev; + Object *ob; +} GpBakeOb; + +/* Get list of keyframes used by selected objects. */ +static void animdata_keyframe_list_get(ListBase *ob_list, + const bool only_selected, + GHash *r_keyframes) +{ + /* Loop all objects to get the list of keyframes used. */ + LISTBASE_FOREACH (GpBakeOb *, elem, ob_list) { + Object *ob = elem->ob; + AnimData *adt = BKE_animdata_from_id(&ob->id); + if ((adt == NULL) || (adt->action == NULL)) { + continue; + } + LISTBASE_FOREACH (FCurve *, fcurve, &adt->action->curves) { + int i; + BezTriple *bezt; + for (i = 0, bezt = fcurve->bezt; i < fcurve->totvert; i++, bezt++) { + /* Keyframe number is x value of point. */ + if ((bezt->f2 & SELECT) || (!only_selected)) { + /* Insert only one key for each keyframe number. */ + int key = (int)bezt->vec[1][0]; + if (!BLI_ghash_haskey(r_keyframes, POINTER_FROM_INT(key))) { + BLI_ghash_insert(r_keyframes, POINTER_FROM_INT(key), POINTER_FROM_INT(key)); + } + } + } + } + } +} + +static void gpencil_bake_duplilist(Depsgraph *depsgraph, Scene *scene, Object *ob, ListBase *list) +{ + GpBakeOb *elem = NULL; + ListBase *lb; + DupliObject *dob; + lb = object_duplilist(depsgraph, scene, ob); + for (dob = lb->first; dob; dob = dob->next) { + if (dob->ob->type != OB_GPENCIL) { + continue; + } + + elem = MEM_callocN(sizeof(GpBakeOb), __func__); + elem->ob = dob->ob; + BLI_addtail(list, elem); + } + + free_object_duplilist(lb); +} + +static void gpencil_bake_ob_list(bContext *C, Depsgraph *depsgraph, Scene *scene, ListBase *list) +{ + GpBakeOb *elem = NULL; + + /* Add active object. In some files this could not be in selected array. */ + Object *obact = CTX_data_active_object(C); + + if (obact->type == OB_GPENCIL) { + elem = MEM_callocN(sizeof(GpBakeOb), __func__); + elem->ob = obact; + BLI_addtail(list, elem); + } + /* Add duplilist. */ + else if (obact->type == OB_EMPTY) { + gpencil_bake_duplilist(depsgraph, scene, obact, list); + } + + /* Add other selected objects. */ + CTX_DATA_BEGIN (C, Object *, ob, selected_objects) { + if (ob == obact) { + continue; + } + /* Add selected objects.*/ + if (ob->type == OB_GPENCIL) { + elem = MEM_callocN(sizeof(GpBakeOb), __func__); + elem->ob = ob; + BLI_addtail(list, elem); + } + + /* Add duplilist. */ + if (ob->type == OB_EMPTY) { + gpencil_bake_duplilist(depsgraph, scene, ob, list); + } + } + CTX_DATA_END; +} + +static void gpencil_bake_free_ob_list(ListBase *list) +{ + LISTBASE_FOREACH_MUTABLE (GpBakeOb *, elem, list) { + MEM_SAFE_FREE(elem); + } +} + +static int gpencil_bake_grease_pencil_animation_exec(bContext *C, wmOperator *op) +{ + Main *bmain = CTX_data_main(C); + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + Scene *scene = CTX_data_scene(C); + ARegion *region = CTX_wm_region(C); + View3D *v3d = CTX_wm_view3d(C); + + ListBase ob_selected_list = {NULL, NULL}; + gpencil_bake_ob_list(C, depsgraph, scene, &ob_selected_list); + + /* Grab all relevant settings. */ + const int step = RNA_int_get(op->ptr, "step"); + + const int frame_start = (scene->r.sfra > RNA_int_get(op->ptr, "frame_start")) ? + scene->r.sfra : + RNA_int_get(op->ptr, "frame_start"); + + const int frame_end = (scene->r.efra < RNA_int_get(op->ptr, "frame_end")) ? + scene->r.efra : + RNA_int_get(op->ptr, "frame_end"); + + const bool only_selected = RNA_boolean_get(op->ptr, "only_selected"); + const int frame_offset = RNA_int_get(op->ptr, "frame_target") - frame_start; + const int project_type = RNA_enum_get(op->ptr, "project_type"); + + /* Create a new grease pencil object. */ + Object *ob_gpencil = NULL; + ushort local_view_bits = (v3d && v3d->localvd) ? v3d->local_view_uuid : 0; + ob_gpencil = ED_gpencil_add_object(C, scene->cursor.location, local_view_bits); + float invmat[4][4]; + invert_m4_m4(invmat, ob_gpencil->obmat); + + bGPdata *gpd_dst = (bGPdata *)ob_gpencil->data; + gpd_dst->draw_mode = GP_DRAWMODE_2D; + + /* Set cursor to indicate working. */ + WM_cursor_wait(true); + + GP_SpaceConversion gsc = {NULL}; + SnapObjectContext *sctx = NULL; + if (project_type != GP_REPROJECT_KEEP) { + /* Init space conversion stuff. */ + gpencil_point_conversion_init(C, &gsc); + /* Move the grease pencil object to conversion data. */ + gsc.ob = ob_gpencil; + + /* Init snap context for geometry projection. */ + sctx = ED_transform_snap_object_context_create_view3d(scene, 0, region, CTX_wm_view3d(C)); + } + + /* Loop all frame range. */ + int oldframe = (int)DEG_get_ctime(depsgraph); + int key = -1; + + /* Get list of keyframes. */ + GHash *keyframe_list = BLI_ghash_int_new(__func__); + if (only_selected) { + animdata_keyframe_list_get(&ob_selected_list, only_selected, keyframe_list); + } + + for (int i = frame_start; i < frame_end + 1; i++) { + key++; + /* Jump if not step limit but include last frame always. */ + if ((key % step != 0) && (i != frame_end)) { + continue; + } + + /* Check if frame is in the list of frames to be exported. */ + if ((only_selected) && (!BLI_ghash_haskey(keyframe_list, POINTER_FROM_INT(i)))) { + continue; + } + + /* Move scene to new frame. */ + CFRA = i; + BKE_scene_graph_update_for_newframe(depsgraph); + + /* Loop all objects in the list. */ + LISTBASE_FOREACH (GpBakeOb *, elem, &ob_selected_list) { + Object *ob_eval = (Object *)DEG_get_evaluated_object(depsgraph, elem->ob); + bGPdata *gpd_src = ob_eval->data; + + LISTBASE_FOREACH (bGPDlayer *, gpl_src, &gpd_src->layers) { + /* Create destination layer. */ + char *layer_name; + layer_name = BLI_sprintfN("%s_%s", elem->ob->id.name + 2, gpl_src->info); + bGPDlayer *gpl_dst = BKE_gpencil_layer_named_get(gpd_dst, layer_name); + if (gpl_dst == NULL) { + gpl_dst = BKE_gpencil_layer_addnew(gpd_dst, layer_name, true, false); + } + MEM_freeN(layer_name); + + /* Layer Transform matrix. */ + float matrix[4][4]; + BKE_gpencil_layer_transform_matrix_get(depsgraph, elem->ob, gpl_src, matrix); + + /* Duplicate frame. */ + bGPDframe *gpf_src = BKE_gpencil_layer_frame_get(gpl_src, CFRA, GP_GETFRAME_USE_PREV); + if (gpf_src == NULL) { + continue; + } + bGPDframe *gpf_dst = BKE_gpencil_frame_duplicate(gpf_src, true); + gpf_dst->framenum = CFRA + frame_offset; + gpf_dst->flag &= ~GP_FRAME_SELECT; + BLI_addtail(&gpl_dst->frames, gpf_dst); + + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf_dst->strokes) { + /* Create material of the stroke. */ + Material *ma_src = BKE_object_material_get(elem->ob, gps->mat_nr + 1); + bool found = false; + for (int index = 0; index < ob_gpencil->totcol; index++) { + Material *ma_dst = BKE_object_material_get(ob_gpencil, index + 1); + if (ma_src == ma_dst) { + found = true; + break; + } + } + if (!found) { + BKE_object_material_slot_add(bmain, ob_gpencil); + BKE_object_material_assign( + bmain, ob_gpencil, ma_src, ob_gpencil->totcol, BKE_MAT_ASSIGN_USERPREF); + } + + /* Set new material index. */ + gps->mat_nr = BKE_gpencil_object_material_index_get(ob_gpencil, ma_src); + + /* Update point location to new object space. */ + for (int j = 0; j < gps->totpoints; j++) { + bGPDspoint *pt = &gps->points[j]; + mul_m4_v3(matrix, &pt->x); + mul_m4_v3(invmat, &pt->x); + } + + /* Reproject stroke. */ + if (project_type != GP_REPROJECT_KEEP) { + ED_gpencil_stroke_reproject( + depsgraph, &gsc, sctx, gpl_dst, gpf_dst, gps, project_type, false); + } + else { + BKE_gpencil_stroke_geometry_update(gpd_dst, gps); + } + } + } + } + } + /* Return scene frame state and DB to original state. */ + CFRA = oldframe; + BKE_scene_graph_update_for_newframe(depsgraph); + + /* Free memory. */ + gpencil_bake_free_ob_list(&ob_selected_list); + if (sctx != NULL) { + ED_transform_snap_object_context_destroy(sctx); + } + /* Free temp hash table. */ + if (keyframe_list != NULL) { + BLI_ghash_free(keyframe_list, NULL, NULL); + } + + /* Notifiers. */ + DEG_relations_tag_update(bmain); + DEG_id_tag_update(&scene->id, ID_RECALC_SELECT); + DEG_id_tag_update(&gpd_dst->id, ID_RECALC_COPY_ON_WRITE); + WM_event_add_notifier(C, NC_OBJECT | NA_ADDED, NULL); + WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene); + + /* Reset cursor. */ + WM_cursor_wait(false); + + /* done */ + return OPERATOR_FINISHED; +} + +static int gpencil_bake_grease_pencil_animation_invoke(bContext *C, + wmOperator *op, + const wmEvent *UNUSED(event)) +{ + PropertyRNA *prop; + Scene *scene = CTX_data_scene(C); + + prop = RNA_struct_find_property(op->ptr, "frame_start"); + if (!RNA_property_is_set(op->ptr, prop)) { + const int frame_start = RNA_property_int_get(op->ptr, prop); + if (frame_start < scene->r.sfra) { + RNA_property_int_set(op->ptr, prop, scene->r.sfra); + } + } + + prop = RNA_struct_find_property(op->ptr, "frame_end"); + if (!RNA_property_is_set(op->ptr, prop)) { + const int frame_end = RNA_property_int_get(op->ptr, prop); + if (frame_end > scene->r.efra) { + RNA_property_int_set(op->ptr, prop, scene->r.efra); + } + } + + /* Show popup dialog to allow editing. */ + return WM_operator_props_dialog_popup(C, op, 250); +} + +void GPENCIL_OT_bake_grease_pencil_animation(wmOperatorType *ot) +{ + PropertyRNA *prop; + + /* identifiers */ + ot->name = "Bake Object Transform to Grease Pencil"; + ot->idname = "GPENCIL_OT_bake_grease_pencil_animation"; + ot->description = "Bake grease pencil object transform to grease pencil keyframes"; + + /* callbacks */ + ot->invoke = gpencil_bake_grease_pencil_animation_invoke; + ot->exec = gpencil_bake_grease_pencil_animation_exec; + ot->poll = gpencil_bake_grease_pencil_animation_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + prop = RNA_def_int( + ot->srna, "frame_start", 1, 1, 100000, "Start Frame", "The start frame", 1, 100000); + + prop = RNA_def_int( + ot->srna, "frame_end", 250, 1, 100000, "End Frame", "The end frame of animation", 1, 100000); + RNA_def_property_update_runtime(prop, gpencil_bake_set_frame_end); + + prop = RNA_def_int(ot->srna, "step", 1, 1, 100, "Step", "Step between generated frames", 1, 100); + + RNA_def_boolean( + ot->srna, "only_selected", 0, "Only Selected Keyframes", "Convert only selected keyframes"); + RNA_def_int( + ot->srna, "frame_target", 1, 1, 100000, "Target Frame", "Destination frame", 1, 100000); + + RNA_def_enum(ot->srna, + "project_type", + rna_gpencil_reproject_type_items, + GP_REPROJECT_KEEP, + "Projection Type", + ""); +} diff --git a/source/blender/editors/gpencil/gpencil_convert.c b/source/blender/editors/gpencil/gpencil_convert.c index ac75ae44c8a..8ab413e907c 100644 --- a/source/blender/editors/gpencil/gpencil_convert.c +++ b/source/blender/editors/gpencil/gpencil_convert.c @@ -1857,7 +1857,7 @@ static int image_to_gpencil_exec(bContext *C, wmOperator *op) /* Add layer and frame. */ bGPdata *gpd = (bGPdata *)ob->data; - bGPDlayer *gpl = BKE_gpencil_layer_addnew(gpd, "Image Layer", true); + bGPDlayer *gpl = BKE_gpencil_layer_addnew(gpd, "Image Layer", true, false); bGPDframe *gpf = BKE_gpencil_frame_addnew(gpl, CFRA); done = BKE_gpencil_from_image(sima, gpd, gpf, size, is_mask); diff --git a/source/blender/editors/gpencil/gpencil_data.c b/source/blender/editors/gpencil/gpencil_data.c index fd2758c8a08..d9a807d17ab 100644 --- a/source/blender/editors/gpencil/gpencil_data.c +++ b/source/blender/editors/gpencil/gpencil_data.c @@ -129,7 +129,7 @@ static int gpencil_data_add_exec(bContext *C, wmOperator *op) gpd->flag |= GP_DATA_ANNOTATIONS; /* add new layer (i.e. a "note") */ - BKE_gpencil_layer_addnew(*gpd_ptr, DATA_("Note"), true); + BKE_gpencil_layer_addnew(*gpd_ptr, DATA_("Note"), true, false); /* notifiers */ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); @@ -231,7 +231,7 @@ static int gpencil_layer_add_exec(bContext *C, wmOperator *op) /* mark as annotation */ (*gpd_ptr)->flag |= GP_DATA_ANNOTATIONS; - BKE_gpencil_layer_addnew(*gpd_ptr, DATA_("Note"), true); + BKE_gpencil_layer_addnew(*gpd_ptr, DATA_("Note"), true, false); gpd = *gpd_ptr; } else { @@ -239,7 +239,7 @@ static int gpencil_layer_add_exec(bContext *C, wmOperator *op) Object *ob = CTX_data_active_object(C); if ((ob != NULL) && (ob->type == OB_GPENCIL)) { gpd = (bGPdata *)ob->data; - bGPDlayer *gpl = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true); + bGPDlayer *gpl = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true, false); /* Add a new frame to make it visible in Dopesheet. */ if (gpl != NULL) { gpl->actframe = BKE_gpencil_layer_frame_get(gpl, CFRA, GP_GETFRAME_ADD_NEW); @@ -524,7 +524,6 @@ enum { static bool gpencil_layer_duplicate_object_poll(bContext *C) { - ViewLayer *view_layer = CTX_data_view_layer(C); Object *ob = CTX_data_active_object(C); if ((ob == NULL) || (ob->type != OB_GPENCIL)) { return false; @@ -537,90 +536,75 @@ static bool gpencil_layer_duplicate_object_poll(bContext *C) return false; } - /* check there are more grease pencil objects */ - LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) { - if ((base->object != ob) && (base->object->type == OB_GPENCIL)) { - return true; - } - } - - return false; + return true; } static int gpencil_layer_duplicate_object_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); - Scene *scene = CTX_data_scene(C); - char name[MAX_ID_NAME - 2]; - RNA_string_get(op->ptr, "object", name); - - if (name[0] == '\0') { - return OPERATOR_CANCELLED; - } - - Object *ob_dst = (Object *)BKE_scene_object_find_by_name(scene, name); - - int mode = RNA_enum_get(op->ptr, "mode"); + const bool only_active = RNA_boolean_get(op->ptr, "only_active"); + const int mode = RNA_enum_get(op->ptr, "mode"); Object *ob_src = CTX_data_active_object(C); bGPdata *gpd_src = (bGPdata *)ob_src->data; - bGPDlayer *gpl_src = BKE_gpencil_layer_active_get(gpd_src); + bGPDlayer *gpl_active = BKE_gpencil_layer_active_get(gpd_src); - /* Sanity checks. */ - if (ELEM(NULL, gpd_src, gpl_src, ob_dst)) { - return OPERATOR_CANCELLED; - } - /* Cannot copy itself and check destination type. */ - if ((ob_src == ob_dst) || (ob_dst->type != OB_GPENCIL)) { - return OPERATOR_CANCELLED; - } - - bGPdata *gpd_dst = (bGPdata *)ob_dst->data; - - /* Create new layer. */ - bGPDlayer *gpl_dst = BKE_gpencil_layer_addnew(gpd_dst, gpl_src->info, true); - /* Need to copy some variables (not all). */ - gpl_dst->onion_flag = gpl_src->onion_flag; - gpl_dst->thickness = gpl_src->thickness; - gpl_dst->line_change = gpl_src->line_change; - copy_v4_v4(gpl_dst->tintcolor, gpl_src->tintcolor); - gpl_dst->opacity = gpl_src->opacity; - - /* Create all frames. */ - LISTBASE_FOREACH (bGPDframe *, gpf_src, &gpl_src->frames) { - - if ((mode == GP_LAYER_COPY_OBJECT_ACT_FRAME) && (gpf_src != gpl_src->actframe)) { + CTX_DATA_BEGIN (C, Object *, ob, selected_objects) { + if ((ob == ob_src) || (ob->type != OB_GPENCIL)) { continue; } + bGPdata *gpd_dst = (bGPdata *)ob->data; + LISTBASE_FOREACH_BACKWARD (bGPDlayer *, gpl_src, &gpd_src->layers) { + if ((only_active) && (gpl_src != gpl_active)) { + continue; + } + /* Create new layer (adding at head of the list). */ + bGPDlayer *gpl_dst = BKE_gpencil_layer_addnew(gpd_dst, gpl_src->info, true, true); + /* Need to copy some variables (not all). */ + gpl_dst->onion_flag = gpl_src->onion_flag; + gpl_dst->thickness = gpl_src->thickness; + gpl_dst->line_change = gpl_src->line_change; + copy_v4_v4(gpl_dst->tintcolor, gpl_src->tintcolor); + gpl_dst->opacity = gpl_src->opacity; + + /* Create all frames. */ + LISTBASE_FOREACH (bGPDframe *, gpf_src, &gpl_src->frames) { + + if ((mode == GP_LAYER_COPY_OBJECT_ACT_FRAME) && (gpf_src != gpl_src->actframe)) { + continue; + } - /* Create new frame. */ - bGPDframe *gpf_dst = BKE_gpencil_frame_addnew(gpl_dst, gpf_src->framenum); + /* Create new frame. */ + bGPDframe *gpf_dst = BKE_gpencil_frame_addnew(gpl_dst, gpf_src->framenum); - /* Copy strokes. */ - LISTBASE_FOREACH (bGPDstroke *, gps_src, &gpf_src->strokes) { + /* Copy strokes. */ + LISTBASE_FOREACH (bGPDstroke *, gps_src, &gpf_src->strokes) { - /* Make copy of source stroke. */ - bGPDstroke *gps_dst = BKE_gpencil_stroke_duplicate(gps_src, true, true); + /* Make copy of source stroke. */ + bGPDstroke *gps_dst = BKE_gpencil_stroke_duplicate(gps_src, true, true); - /* Check if material is in destination object, - * otherwise add the slot with the material. */ - Material *ma_src = BKE_object_material_get(ob_src, gps_src->mat_nr + 1); - if (ma_src != NULL) { - int idx = BKE_gpencil_object_material_ensure(bmain, ob_dst, ma_src); + /* Check if material is in destination object, + * otherwise add the slot with the material. */ + Material *ma_src = BKE_object_material_get(ob_src, gps_src->mat_nr + 1); + if (ma_src != NULL) { + int idx = BKE_gpencil_object_material_ensure(bmain, ob, ma_src); - /* Reassign the stroke material to the right slot in destination object. */ - gps_dst->mat_nr = idx; - } + /* Reassign the stroke material to the right slot in destination object. */ + gps_dst->mat_nr = idx; + } - /* Add new stroke to frame. */ - BLI_addtail(&gpf_dst->strokes, gps_dst); + /* Add new stroke to frame. */ + BLI_addtail(&gpf_dst->strokes, gps_dst); + } + } } + /* notifiers */ + DEG_id_tag_update(&gpd_dst->id, + ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE); + DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE); } + CTX_DATA_END; - /* notifiers */ - DEG_id_tag_update(&gpd_dst->id, - ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE); - DEG_id_tag_update(&ob_dst->id, ID_RECALC_COPY_ON_WRITE); WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); return OPERATOR_FINISHED; @@ -628,6 +612,8 @@ static int gpencil_layer_duplicate_object_exec(bContext *C, wmOperator *op) void GPENCIL_OT_layer_duplicate_object(wmOperatorType *ot) { + PropertyRNA *prop; + static const EnumPropertyItem copy_mode[] = { {GP_LAYER_COPY_OBJECT_ALL_FRAME, "ALL", 0, "All Frames", ""}, {GP_LAYER_COPY_OBJECT_ACT_FRAME, "ACTIVE", 0, "Active Frame", ""}, @@ -637,7 +623,7 @@ void GPENCIL_OT_layer_duplicate_object(wmOperatorType *ot) /* identifiers */ ot->name = "Duplicate Layer to New Object"; ot->idname = "GPENCIL_OT_layer_duplicate_object"; - ot->description = "Make a copy of the active Grease Pencil layer to new object"; + ot->description = "Make a copy of the active Grease Pencil layer to selected object"; /* callbacks */ ot->exec = gpencil_layer_duplicate_object_exec; @@ -646,11 +632,14 @@ void GPENCIL_OT_layer_duplicate_object(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - ot->prop = RNA_def_string( - ot->srna, "object", NULL, MAX_ID_NAME - 2, "Object", "Name of the destination object"); - RNA_def_property_flag(ot->prop, PROP_HIDDEN | PROP_SKIP_SAVE); + ot->prop = RNA_def_enum(ot->srna, "mode", copy_mode, GP_LAYER_COPY_OBJECT_ALL_FRAME, "Mode", ""); - RNA_def_enum(ot->srna, "mode", copy_mode, GP_LAYER_COPY_OBJECT_ALL_FRAME, "Mode", ""); + prop = RNA_def_boolean(ot->srna, + "only_active", + true, + "Only Active", + "Copy only active Layer, uncheck to append all layers"); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); } /* ********************* Duplicate Frame ************************** */ @@ -1443,7 +1432,7 @@ static int gpencil_layer_change_exec(bContext *C, wmOperator *op) /* Get layer or create new one */ if (layer_num == -1) { /* Create layer */ - gpl = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true); + gpl = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true, false); } else { /* Try to get layer */ @@ -1555,6 +1544,7 @@ static int gpencil_stroke_arrange_exec(bContext *C, wmOperator *op) const int direction = RNA_enum_get(op->ptr, "direction"); const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); + bGPDstroke *gps_target = NULL; bool changed = false; CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) { @@ -1569,7 +1559,6 @@ static int gpencil_stroke_arrange_exec(bContext *C, wmOperator *op) if (gpf == NULL) { continue; } - bool gpf_lock = false; /* verify if any selected stroke is in the extreme of the stack and select to move */ for (gps = gpf->strokes.first; gps; gps = gps->next) { /* only if selected */ @@ -1582,18 +1571,19 @@ static int gpencil_stroke_arrange_exec(bContext *C, wmOperator *op) if (ED_gpencil_stroke_material_editable(ob, gpl, gps) == false) { continue; } + bool gpf_lock = false; /* some stroke is already at front*/ if (ELEM(direction, GP_STROKE_MOVE_TOP, GP_STROKE_MOVE_UP)) { if (gps == gpf->strokes.last) { gpf_lock = true; - continue; + gps_target = gps; } } /* Some stroke is already at bottom. */ if (ELEM(direction, GP_STROKE_MOVE_BOTTOM, GP_STROKE_MOVE_DOWN)) { if (gps == gpf->strokes.first) { gpf_lock = true; - continue; + gps_target = gps; } } /* add to list (if not locked) */ @@ -1602,47 +1592,74 @@ static int gpencil_stroke_arrange_exec(bContext *C, wmOperator *op) } } } + + const int target_index = (gps_target) ? BLI_findindex(&gpf->strokes, gps_target) : -1; + int prev_index = target_index; /* Now do the movement of the stroke */ - if (!gpf_lock) { - switch (direction) { - /* Bring to Front */ - case GP_STROKE_MOVE_TOP: - LISTBASE_FOREACH (LinkData *, link, &selected) { - gps = link->data; - BLI_remlink(&gpf->strokes, gps); + switch (direction) { + /* Bring to Front */ + case GP_STROKE_MOVE_TOP: + LISTBASE_FOREACH (LinkData *, link, &selected) { + gps = link->data; + BLI_remlink(&gpf->strokes, gps); + if (gps_target) { + BLI_insertlinkbefore(&gpf->strokes, gps_target, gps); + } + else { BLI_addtail(&gpf->strokes, gps); - changed = true; } - break; - /* Bring Forward */ - case GP_STROKE_MOVE_UP: - LISTBASE_FOREACH_BACKWARD (LinkData *, link, &selected) { - gps = link->data; - BLI_listbase_link_move(&gpf->strokes, gps, 1); - changed = true; + changed = true; + } + break; + /* Bring Forward */ + case GP_STROKE_MOVE_UP: + LISTBASE_FOREACH_BACKWARD (LinkData *, link, &selected) { + gps = link->data; + if (gps_target) { + int gps_index = BLI_findindex(&gpf->strokes, gps); + if (gps_index + 1 >= prev_index) { + prev_index = gps_index; + continue; + } + prev_index = gps_index; + } + BLI_listbase_link_move(&gpf->strokes, gps, 1); + changed = true; + } + break; + /* Send Backward */ + case GP_STROKE_MOVE_DOWN: + LISTBASE_FOREACH (LinkData *, link, &selected) { + gps = link->data; + if (gps_target) { + int gps_index = BLI_findindex(&gpf->strokes, gps); + if (gps_index - 1 <= prev_index) { + prev_index = gps_index; + continue; + } + prev_index = gps_index; } - break; - /* Send Backward */ - case GP_STROKE_MOVE_DOWN: - LISTBASE_FOREACH (LinkData *, link, &selected) { - gps = link->data; - BLI_listbase_link_move(&gpf->strokes, gps, -1); - changed = true; + BLI_listbase_link_move(&gpf->strokes, gps, -1); + changed = true; + } + break; + /* Send to Back */ + case GP_STROKE_MOVE_BOTTOM: + LISTBASE_FOREACH_BACKWARD (LinkData *, link, &selected) { + gps = link->data; + BLI_remlink(&gpf->strokes, gps); + if (gps_target) { + BLI_insertlinkafter(&gpf->strokes, gps_target, gps); } - break; - /* Send to Back */ - case GP_STROKE_MOVE_BOTTOM: - LISTBASE_FOREACH_BACKWARD (LinkData *, link, &selected) { - gps = link->data; - BLI_remlink(&gpf->strokes, gps); + else { BLI_addhead(&gpf->strokes, gps); - changed = true; } - break; - default: - BLI_assert(0); - break; - } + changed = true; + } + break; + default: + BLI_assert(0); + break; } BLI_freelistN(&selected); } @@ -3561,6 +3578,79 @@ void GPENCIL_OT_set_active_material(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } +/* ********************* Append Materials in a new object ************************** */ +static bool gpencil_materials_copy_to_object_poll(bContext *C) +{ + Object *ob = CTX_data_active_object(C); + if ((ob == NULL) || (ob->type != OB_GPENCIL)) { + return false; + } + short *totcolp = BKE_object_material_len_p(ob); + if (*totcolp == 0) { + return false; + } + + return true; +} + +static int gpencil_materials_copy_to_object_exec(bContext *C, wmOperator *op) +{ + Main *bmain = CTX_data_main(C); + const bool only_active = RNA_boolean_get(op->ptr, "only_active"); + Object *ob_src = CTX_data_active_object(C); + Material *ma_active = BKE_gpencil_material(ob_src, ob_src->actcol); + + CTX_DATA_BEGIN (C, Object *, ob, selected_objects) { + if ((ob == ob_src) || (ob->type != OB_GPENCIL)) { + continue; + } + /* Duplicate materials. */ + for (int i = 0; i < ob_src->totcol; i++) { + Material *ma_src = BKE_object_material_get(ob_src, i + 1); + if (only_active && ma_src != ma_active) { + continue; + } + + if (ma_src != NULL) { + BKE_gpencil_object_material_ensure(bmain, ob, ma_src); + } + } + + /* notifiers */ + DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE); + } + CTX_DATA_END; + + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_materials_copy_to_object(wmOperatorType *ot) +{ + PropertyRNA *prop; + + /* identifiers */ + ot->name = "Copy Materials to Selected Object"; + ot->idname = "GPENCIL_OT_materials_copy_to_object"; + ot->description = "Append Materials of the active Grease Pencil to other object"; + + /* callbacks */ + ot->exec = gpencil_materials_copy_to_object_exec; + ot->poll = gpencil_materials_copy_to_object_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + prop = RNA_def_boolean(ot->srna, + "only_active", + true, + "Only Active", + "Append only active material, uncheck to append all materials"); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); +} + /* Parent GPencil object to Lattice */ bool ED_gpencil_add_lattice_modifier(const bContext *C, ReportList *reports, @@ -3717,3 +3807,51 @@ void GPENCIL_OT_layer_mask_remove(wmOperatorType *ot) ot->exec = gpencil_layer_mask_remove_exec; ot->poll = gpencil_active_layer_poll; } + +static int gpencil_layer_mask_move_exec(bContext *C, wmOperator *op) +{ + bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPDlayer *gpl = BKE_gpencil_layer_active_get(gpd); + const int direction = RNA_enum_get(op->ptr, "type"); + + /* sanity checks */ + if (ELEM(NULL, gpd, gpl)) { + return OPERATOR_CANCELLED; + } + if (gpl->act_mask > 0) { + bGPDlayer_Mask *mask = BLI_findlink(&gpl->mask_layers, gpl->act_mask - 1); + if (mask != NULL) { + BLI_assert(ELEM(direction, -1, 0, 1)); /* we use value below */ + if (BLI_listbase_link_move(&gpl->mask_layers, mask, direction)) { + gpl->act_mask += direction; + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + } + } + } + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_layer_mask_move(wmOperatorType *ot) +{ + static const EnumPropertyItem slot_move[] = { + {GP_LAYER_MOVE_UP, "UP", 0, "Up", ""}, + {GP_LAYER_MOVE_DOWN, "DOWN", 0, "Down", ""}, + {0, NULL, 0, NULL, NULL}, + }; + + /* identifiers */ + ot->name = "Move Grease Pencil Layer Mask"; + ot->idname = "GPENCIL_OT_layer_mask_move"; + ot->description = "Move the active Grease Pencil mask layer up/down in the list"; + + /* api callbacks */ + ot->exec = gpencil_layer_mask_move_exec; + ot->poll = gpencil_active_layer_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + ot->prop = RNA_def_enum(ot->srna, "type", slot_move, 0, "Type", ""); +} diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c index 4bbd475dd2c..f29f5187015 100644 --- a/source/blender/editors/gpencil/gpencil_edit.c +++ b/source/blender/editors/gpencil/gpencil_edit.c @@ -1677,7 +1677,7 @@ static int gpencil_strokes_paste_exec(bContext *C, wmOperator *op) if (gpl == NULL) { /* no active layer - let's just create one */ - gpl = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true); + gpl = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true, false); } else if ((BKE_gpencil_layer_is_editable(gpl) == false) && (type == GP_COPY_TO_ACTIVE)) { BKE_report( @@ -1818,18 +1818,13 @@ static int gpencil_move_to_layer_exec(bContext *C, wmOperator *op) { Object *ob = CTX_data_active_object(C); bGPdata *gpd = (bGPdata *)ob->data; - Scene *scene = CTX_data_scene(C); bGPDlayer *target_layer = NULL; ListBase strokes = {NULL, NULL}; int layer_num = RNA_int_get(op->ptr, "layer"); const bool use_autolock = (bool)(gpd->flag & GP_DATA_AUTOLOCK_LAYERS); + const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); - if (GPENCIL_MULTIEDIT_SESSIONS_ON(gpd)) { - BKE_report(op->reports, RPT_ERROR, "Operator not supported in multiframe edition"); - return OPERATOR_CANCELLED; - } - - /* if autolock enabled, disabled now */ + /* If autolock enabled, disabled now. */ if (use_autolock) { gpd->flag &= ~GP_DATA_AUTOLOCK_LAYERS; } @@ -1840,7 +1835,7 @@ static int gpencil_move_to_layer_exec(bContext *C, wmOperator *op) } else { /* Create a new layer. */ - target_layer = BKE_gpencil_layer_addnew(gpd, "GP_Layer", true); + target_layer = BKE_gpencil_layer_addnew(gpd, "GP_Layer", true, false); } if (target_layer == NULL) { @@ -1852,53 +1847,59 @@ static int gpencil_move_to_layer_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - /* Extract all strokes to move to this layer - * NOTE: We need to do this in a two-pass system to avoid conflicts with strokes - * getting repeatedly moved - */ - CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) { - bGPDframe *gpf = gpl->actframe; - - /* skip if no frame with strokes, or if this is the layer we're moving strokes to */ - if ((gpl == target_layer) || (gpf == NULL)) { + /* Extract all strokes to move to this layer. */ + CTX_DATA_BEGIN (C, bGPDlayer *, gpl_src, editable_gpencil_layers) { + /* Skip if this is the layer we're moving strokes to. */ + if (gpl_src == target_layer) { continue; } + bGPDframe *init_gpf = (is_multiedit) ? gpl_src->frames.first : gpl_src->actframe; + for (bGPDframe *gpf_src = init_gpf; gpf_src; gpf_src = gpf_src->next) { + if ((gpf_src == gpl_src->actframe) || + ((gpf_src->flag & GP_FRAME_SELECT) && (is_multiedit))) { + if (gpf_src == NULL) { + continue; + } - /* make copies of selected strokes, and deselect these once we're done */ - LISTBASE_FOREACH_MUTABLE (bGPDstroke *, gps, &gpf->strokes) { + bGPDstroke *gpsn = NULL; + BLI_listbase_clear(&strokes); + for (bGPDstroke *gps = gpf_src->strokes.first; gps; gps = gpsn) { + gpsn = gps->next; + /* Skip strokes that are invalid for current view. */ + if (ED_gpencil_stroke_can_use(C, gps) == false) { + continue; + } + /* Check if the color is editable. */ + if (ED_gpencil_stroke_material_editable(ob, gpl_src, gps) == false) { + continue; + } - /* skip strokes that are invalid for current view */ - if (ED_gpencil_stroke_can_use(C, gps) == false) { - continue; - } + if (gps->flag & GP_STROKE_SELECT) { + BLI_remlink(&gpf_src->strokes, gps); + BLI_addtail(&strokes, gps); + } + } + /* Paste them all in one go. */ + if (strokes.first) { + bGPDframe *gpf_dst = BKE_gpencil_layer_frame_get( + target_layer, gpf_src->framenum, GP_GETFRAME_ADD_NEW); - /* Check if the color is editable. */ - if (ED_gpencil_stroke_material_editable(ob, gpl, gps) == false) { - continue; + BLI_movelisttolist(&gpf_dst->strokes, &strokes); + BLI_assert((strokes.first == strokes.last) && (strokes.first == NULL)); + } } - - /* TODO: Don't just move entire strokes - instead, only copy the selected portions... */ - if (gps->flag & GP_STROKE_SELECT) { - BLI_remlink(&gpf->strokes, gps); - BLI_addtail(&strokes, gps); + /* If not multi-edit, exit loop. */ + if (!is_multiedit) { + break; } } - - /* if new layer and autolock, lock old layer */ + /* If new layer and autolock, lock old layer. */ if ((layer_num == -1) && (use_autolock)) { - gpl->flag |= GP_LAYER_LOCKED; + gpl_src->flag |= GP_LAYER_LOCKED; } } CTX_DATA_END; - /* Paste them all in one go */ - if (strokes.first) { - bGPDframe *gpf = BKE_gpencil_layer_frame_get(target_layer, CFRA, GP_GETFRAME_ADD_NEW); - - BLI_movelisttolist(&gpf->strokes, &strokes); - BLI_assert((strokes.first == strokes.last) && (strokes.first == NULL)); - } - /* back autolock status */ if (use_autolock) { gpd->flag |= GP_DATA_AUTOLOCK_LAYERS; @@ -2778,7 +2779,7 @@ void GPENCIL_OT_dissolve(wmOperatorType *ot) /* Poll callback for snap operators */ /* NOTE: For now, we only allow these in the 3D view, as other editors do not - * define a cursor or gridstep which can be used + * define a cursor or grid-step which can be used. */ static bool gpencil_snap_poll(bContext *C) { @@ -3442,7 +3443,7 @@ void GPENCIL_OT_stroke_caps_set(wmOperatorType *ot) {GP_STROKE_CAPS_TOGGLE_BOTH, "TOGGLE", 0, "Both", ""}, {GP_STROKE_CAPS_TOGGLE_START, "START", 0, "Start", ""}, {GP_STROKE_CAPS_TOGGLE_END, "END", 0, "End", ""}, - {GP_STROKE_CAPS_TOGGLE_DEFAULT, "TOGGLE", 0, "Default", "Set as default rounded"}, + {GP_STROKE_CAPS_TOGGLE_DEFAULT, "DEFAULT", 0, "Default", "Set as default rounded"}, {0, NULL, 0, NULL, NULL}, }; @@ -3762,6 +3763,7 @@ static int gpencil_strokes_reproject_exec(bContext *C, wmOperator *op) const eGP_ReprojectModes mode = RNA_enum_get(op->ptr, "type"); const bool keep_original = RNA_boolean_get(op->ptr, "keep_original"); const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd); + const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); /* Init snap context for geometry projection. */ SnapObjectContext *sctx = NULL; @@ -3774,36 +3776,55 @@ static int gpencil_strokes_reproject_exec(bContext *C, wmOperator *op) int cfra_prv = INT_MIN; /* Go through each editable + selected stroke, adjusting each of its points one by one... */ - GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) { - bool curve_select = false; - if (is_curve_edit && gps->editcurve != NULL) { - curve_select = gps->editcurve->flag & GP_CURVE_SELECT; - } + CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) { + bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe; - if (gps->flag & GP_STROKE_SELECT || curve_select) { + for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) { + if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) { + if (gpf == NULL) { + continue; + } + for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + /* skip strokes that are invalid for current view */ + if (ED_gpencil_stroke_can_use(C, gps) == false) { + continue; + } + bool curve_select = false; + if (is_curve_edit && gps->editcurve != NULL) { + curve_select = gps->editcurve->flag & GP_CURVE_SELECT; + } - /* update frame to get the new location of objects */ - if ((mode == GP_REPROJECT_SURFACE) && (cfra_prv != gpf_->framenum)) { - cfra_prv = gpf_->framenum; - CFRA = gpf_->framenum; - BKE_scene_graph_update_for_newframe(depsgraph); - } + if (gps->flag & GP_STROKE_SELECT || curve_select) { - ED_gpencil_stroke_reproject(depsgraph, &gsc, sctx, gpl, gpf_, gps, mode, keep_original); + /* update frame to get the new location of objects */ + if ((mode == GP_REPROJECT_SURFACE) && (cfra_prv != gpf->framenum)) { + cfra_prv = gpf->framenum; + CFRA = gpf->framenum; + BKE_scene_graph_update_for_newframe(depsgraph); + } - if (is_curve_edit && gps->editcurve != NULL) { - BKE_gpencil_stroke_editcurve_update(gpd, gpl, gps); - /* Update the selection from the stroke to the curve. */ - BKE_gpencil_editcurve_stroke_sync_selection(gpd, gps, gps->editcurve); + ED_gpencil_stroke_reproject(depsgraph, &gsc, sctx, gpl, gpf, gps, mode, keep_original); - gps->flag |= GP_STROKE_NEEDS_CURVE_UPDATE; - BKE_gpencil_stroke_geometry_update(gpd, gps); - } + if (is_curve_edit && gps->editcurve != NULL) { + BKE_gpencil_stroke_editcurve_update(gpd, gpl, gps); + /* Update the selection from the stroke to the curve. */ + BKE_gpencil_editcurve_stroke_sync_selection(gpd, gps, gps->editcurve); - changed = true; + gps->flag |= GP_STROKE_NEEDS_CURVE_UPDATE; + BKE_gpencil_stroke_geometry_update(gpd, gps); + } + + changed = true; + } + } + } + /* If not multi-edit, exit loop. */ + if (!is_multiedit) { + break; + } } } - GP_EDITABLE_STROKES_END(gpstroke_iter); + CTX_DATA_END; /* return frame state and DB to original state */ CFRA = oldframe; @@ -3832,7 +3853,8 @@ void GPENCIL_OT_reproject(wmOperatorType *ot) "VIEW", 0, "View", - "Reproject the strokes to end up on the same plane, as if drawn from the current viewpoint " + "Reproject the strokes to end up on the same plane, as if drawn from the current " + "viewpoint " "using 'Cursor' Stroke Placement"}, {GP_REPROJECT_SURFACE, "SURFACE", @@ -3851,7 +3873,8 @@ void GPENCIL_OT_reproject(wmOperatorType *ot) ot->name = "Reproject Strokes"; ot->idname = "GPENCIL_OT_reproject"; ot->description = - "Reproject the selected strokes from the current viewpoint as if they had been newly drawn " + "Reproject the selected strokes from the current viewpoint as if they had been newly " + "drawn " "(e.g. to fix problems from accidental 3D cursor movement or accidental viewport changes, " "or for matching deforming geometry)"; @@ -3951,7 +3974,7 @@ static void gpencil_smooth_stroke(bContext *C, wmOperator *op) } if (smooth_thickness) { /* thickness need to repeat process several times */ - for (int r2 = 0; r2 < r * 20; r2++) { + for (int r2 = 0; r2 < 20; r2++) { BKE_gpencil_stroke_smooth_thickness(gps, i, factor); } } @@ -3981,6 +4004,11 @@ static int gpencil_count_subdivision_cuts(bGPDstroke *gps) } } + if ((gps->flag & GP_STROKE_CYCLIC) && (gps->points[0].flag & GP_SPOINT_SELECT) && + (gps->points[gps->totpoints - 1].flag & GP_SPOINT_SELECT)) { + totnewpoints++; + } + return totnewpoints; } @@ -4079,6 +4107,47 @@ static void gpencil_stroke_subdivide(bGPDstroke *gps, const int cuts) } } } + + /* Subdivide between last and first point. */ + if (gps->flag & GP_STROKE_CYCLIC) { + bGPDspoint *pt = &temp_points[oldtotpoints - 1]; + bGPDspoint *next = &temp_points[0]; + if ((pt->flag & GP_SPOINT_SELECT) && (next->flag & GP_SPOINT_SELECT)) { + bGPDspoint *pt_final = &gps->points[i2]; + if (gps->dvert != NULL) { + dvert_final = &gps->dvert[i2]; + } + /* Interpolate all values */ + interp_v3_v3v3(&pt_final->x, &pt->x, &next->x, 0.5f); + pt_final->pressure = interpf(pt->pressure, next->pressure, 0.5f); + pt_final->strength = interpf(pt->strength, next->strength, 0.5f); + CLAMP(pt_final->strength, GPENCIL_STRENGTH_MIN, 1.0f); + interp_v4_v4v4(pt_final->vert_color, pt->vert_color, next->vert_color, 0.5f); + pt_final->time = interpf(pt->time, next->time, 0.5f); + pt_final->flag |= GP_SPOINT_SELECT; + + /* interpolate weights */ + if (gps->dvert != NULL) { + dvert = &temp_dverts[oldtotpoints - 1]; + dvert_next = &temp_dverts[0]; + dvert_final = &gps->dvert[i2]; + + dvert_final->totweight = dvert->totweight; + dvert_final->dw = MEM_dupallocN(dvert->dw); + + /* interpolate weight values */ + for (int d = 0; d < dvert->totweight; d++) { + MDeformWeight *dw_a = &dvert->dw[d]; + if (dvert_next->totweight > d) { + MDeformWeight *dw_b = &dvert_next->dw[d]; + MDeformWeight *dw_final = &dvert_final->dw[d]; + dw_final->weight = interpf(dw_a->weight, dw_b->weight, 0.5f); + } + } + } + } + } + /* free temp memory */ MEM_SAFE_FREE(temp_points); MEM_SAFE_FREE(temp_dverts); @@ -4162,7 +4231,8 @@ void GPENCIL_OT_stroke_subdivide(wmOperatorType *ot) ot->name = "Subdivide Stroke"; ot->idname = "GPENCIL_OT_stroke_subdivide"; ot->description = - "Subdivide between continuous selected points of the stroke adding a point half way between " + "Subdivide between continuous selected points of the stroke adding a point half way " + "between " "them"; /* api callbacks */ @@ -4474,6 +4544,9 @@ static int gpencil_stroke_separate_exec(bContext *C, wmOperator *op) eGP_SeparateModes mode = RNA_enum_get(op->ptr, "mode"); + const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd_src); + const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd_src); + /* sanity checks */ if (ELEM(NULL, gpd_src)) { return OPERATOR_CANCELLED; @@ -4484,8 +4557,22 @@ static int gpencil_stroke_separate_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd_src); - const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd_src); + /* Cancel if nothing selected. */ + if (ELEM(mode, GP_SEPARATE_POINT, GP_SEPARATE_STROKE)) { + bool has_selected = false; + CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) { + if (ED_gpencil_layer_has_selected_stroke(gpl, is_multiedit)) { + has_selected = true; + break; + } + } + CTX_DATA_END; + + if (!has_selected) { + BKE_report(op->reports, RPT_ERROR, "Nothing selected"); + return OPERATOR_CANCELLED; + } + } /* Create a new object. */ /* Take into account user preferences for duplicating actions. */ @@ -4530,7 +4617,7 @@ static int gpencil_stroke_separate_exec(bContext *C, wmOperator *op) if (gps->flag & GP_STROKE_SELECT) { /* add layer if not created before */ if (gpl_dst == NULL) { - gpl_dst = BKE_gpencil_layer_addnew(gpd_dst, gpl->info, false); + gpl_dst = BKE_gpencil_layer_addnew(gpd_dst, gpl->info, false, false); } /* add frame if not created before */ @@ -4979,17 +5066,27 @@ static int gpencil_cutter_lasso_select(bContext *C, /* init space conversion stuff */ gpencil_point_conversion_init(C, &gsc); - /* deselect all strokes first */ - CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) { - int i; - for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { - pt->flag &= ~GP_SPOINT_SELECT; - } + /* Deselect all strokes. */ + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { + bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe; + for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) { + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { + if (gps->flag & GP_STROKE_SELECT) { + int i; + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + pt->flag &= ~GP_SPOINT_SELECT; + } - gps->flag &= ~GP_STROKE_SELECT; - BKE_gpencil_stroke_select_index_reset(gps); + gps->flag &= ~GP_STROKE_SELECT; + BKE_gpencil_stroke_select_index_reset(gps); + } + } + /* if not multiedit, exit loop. */ + if (!is_multiedit) { + break; + } + } } - CTX_DATA_END; /* Select points */ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { @@ -5250,3 +5347,181 @@ void GPENCIL_OT_stroke_merge_by_distance(wmOperatorType *ot) RNA_def_property_flag(prop, PROP_SKIP_SAVE); } /** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Stroke Normalize Operator + * \{ */ + +typedef enum eGP_NormalizeMode { + GP_NORMALIZE_THICKNESS = 0, + GP_NORMALIZE_OPACITY, +} eGP_NormalizeMode; + +static bool gpencil_stroke_normalize_poll(bContext *C) +{ + Object *ob = CTX_data_active_object(C); + if ((ob == NULL) || (ob->type != OB_GPENCIL)) { + return false; + } + bGPdata *gpd = (bGPdata *)ob->data; + if (gpd == NULL) { + return false; + } + + bGPDlayer *gpl = BKE_gpencil_layer_active_get(gpd); + + return ((gpl != NULL) && (ob->mode == OB_MODE_EDIT_GPENCIL)); +} + +static void gpencil_stroke_normalize_ui(bContext *UNUSED(C), wmOperator *op) +{ + uiLayout *layout = op->layout; + uiLayout *row; + + const eGP_NormalizeMode mode = RNA_enum_get(op->ptr, "mode"); + + uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, false); + row = uiLayoutRow(layout, true); + uiItemR(row, op->ptr, "mode", 0, NULL, ICON_NONE); + + if (mode == GP_NORMALIZE_THICKNESS) { + row = uiLayoutRow(layout, true); + uiItemR(row, op->ptr, "value", 0, NULL, ICON_NONE); + } + else if (mode == GP_NORMALIZE_OPACITY) { + row = uiLayoutRow(layout, true); + uiItemR(row, op->ptr, "factor", 0, NULL, ICON_NONE); + } +} + +static int gpencil_stroke_normalize_exec(bContext *C, wmOperator *op) +{ + bGPdata *gpd = ED_gpencil_data_get_active(C); + + /* Sanity checks. */ + if (ELEM(NULL, gpd)) { + return OPERATOR_CANCELLED; + } + + const eGP_NormalizeMode mode = RNA_enum_get(op->ptr, "mode"); + const int value = RNA_int_get(op->ptr, "value"); + const float factor = RNA_float_get(op->ptr, "factor"); + + /* Go through each editable + selected stroke. */ + const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); + const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd); + + CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) { + bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe; + + for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) { + if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) { + + if (gpf == NULL) { + continue; + } + + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { + + /* Skip strokes that are invalid for current view. */ + if (ED_gpencil_stroke_can_use(C, gps) == false) { + continue; + } + + bool selected = (is_curve_edit) ? gps->editcurve->flag |= GP_CURVE_SELECT : + (gps->flag & GP_STROKE_SELECT); + if (!selected) { + continue; + } + + float stroke_thickness_inv = 1.0f / max_ii(gps->thickness, 1); + /* Fill opacity need to be managed before. */ + if (mode == GP_NORMALIZE_OPACITY) { + gps->fill_opacity_fac = factor; + CLAMP(gps->fill_opacity_fac, 0.0f, 1.0f); + } + + /* Loop all Polyline points. */ + if (!is_curve_edit) { + for (int i = 0; i < gps->totpoints; i++) { + bGPDspoint *pt = &gps->points[i]; + if (mode == GP_NORMALIZE_THICKNESS) { + pt->pressure = max_ff((float)value * stroke_thickness_inv, 0.0f); + } + else if (mode == GP_NORMALIZE_OPACITY) { + pt->strength = factor; + CLAMP(pt->strength, 0.0f, 1.0f); + } + } + } + else { + /* Loop all Bezier points. */ + for (int i = 0; i < gps->editcurve->tot_curve_points; i++) { + bGPDcurve_point *gpc_pt = &gps->editcurve->curve_points[i]; + if (mode == GP_NORMALIZE_THICKNESS) { + gpc_pt->pressure = max_ff((float)value * stroke_thickness_inv, 0.0f); + } + else if (mode == GP_NORMALIZE_OPACITY) { + gpc_pt->strength = factor; + CLAMP(gpc_pt->strength, 0.0f, 1.0f); + } + } + + gps->flag |= GP_STROKE_NEEDS_CURVE_UPDATE; + BKE_gpencil_stroke_geometry_update(gpd, gps); + } + } + /* If not multiedit, exit loop. */ + if (!is_multiedit) { + break; + } + } + } + } + CTX_DATA_END; + + /* 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); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_stroke_normalize(wmOperatorType *ot) +{ + static const EnumPropertyItem prop_gpencil_normalize_modes[] = { + {GP_NORMALIZE_THICKNESS, + "THICKNESS", + 0, + "Thickness", + "Normalizes the stroke thickness by making all points use the same thickness value"}, + {GP_NORMALIZE_OPACITY, + "OPACITY", + 0, + "Opacity", + "Normalizes the stroke opacity by making all points use the same opacity value"}, + {0, NULL, 0, NULL, NULL}, + }; + + /* identifiers */ + ot->name = "Normalize Stroke"; + ot->idname = "GPENCIL_OT_stroke_normalize"; + ot->description = "Normalize stroke attributes"; + + /* api callbacks */ + ot->exec = gpencil_stroke_normalize_exec; + ot->poll = gpencil_stroke_normalize_poll; + ot->ui = gpencil_stroke_normalize_ui; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* props */ + ot->prop = RNA_def_enum( + ot->srna, "mode", prop_gpencil_normalize_modes, 0, "Mode", "Attribute to be normalized"); + RNA_def_float(ot->srna, "factor", 1.0f, 0.0f, 1.0f, "Factor", "", 0.0f, 1.0f); + RNA_def_int(ot->srna, "value", 10, 0, 1000, "Value", "Value", 0, 1000); +} + +/** \} */ diff --git a/source/blender/editors/gpencil/gpencil_fill.c b/source/blender/editors/gpencil/gpencil_fill.c index 4749f40fac5..f74e211dd65 100644 --- a/source/blender/editors/gpencil/gpencil_fill.c +++ b/source/blender/editors/gpencil/gpencil_fill.c @@ -1029,9 +1029,15 @@ static void gpencil_invert_image(tGPDfill *tgpf) /* Red->Green */ else if (color[0] == 1.0f) { set_pixel(ibuf, v, fill_col[1]); - /* Add thickness of 2 pixels to avoid too thin lines. */ - int offset = (v % ibuf->x < center) ? 1 : -1; - set_pixel(ibuf, v + offset, fill_col[1]); + /* Add thickness of 2 pixels to avoid too thin lines, but avoid extremes of the pixel line. + */ + int row = v / ibuf->x; + int lowpix = row * ibuf->x; + int highpix = lowpix + ibuf->x - 1; + if ((v > lowpix) && (v < highpix)) { + int offset = (v % ibuf->x < center) ? 1 : -1; + set_pixel(ibuf, v + offset, fill_col[1]); + } } else { /* Set to Transparent. */ @@ -1241,6 +1247,7 @@ static bool dilate_shape(ImBuf *ibuf) static void gpencil_get_outline_points(tGPDfill *tgpf, const bool dilate) { ImBuf *ibuf; + Brush *brush = tgpf->brush; float rgba[4]; void *lock; int v[2]; @@ -1273,7 +1280,9 @@ static void gpencil_get_outline_points(tGPDfill *tgpf, const bool dilate) /* Dilate. */ if (dilate) { - dilate_shape(ibuf); + for (int i = 0; i < brush->gpencil_settings->dilate_pixels; i++) { + dilate_shape(ibuf); + } } for (int idx = imagesize - 1; idx != 0; idx--) { @@ -1363,7 +1372,8 @@ static void gpencil_get_depth_array(tGPDfill *tgpf) if (ts->gpencil_v3d_align & GP_PROJECT_DEPTH_VIEW) { /* need to restore the original projection settings before packing up */ view3d_region_operator_needs_opengl(tgpf->win, tgpf->region); - ED_view3d_autodist_init(tgpf->depsgraph, tgpf->region, tgpf->v3d, 0); + ED_view3d_depth_override( + tgpf->depsgraph, tgpf->region, tgpf->v3d, NULL, V3D_DEPTH_NO_GPENCIL, false); /* Since strokes are so fine, when using their depth we need a margin * otherwise they might get missed. */ @@ -1679,7 +1689,7 @@ static tGPDfill *gpencil_session_init_fill(bContext *C, wmOperator *op) tgpf->gpd = gpd; tgpf->gpl = BKE_gpencil_layer_active_get(gpd); if (tgpf->gpl == NULL) { - tgpf->gpl = BKE_gpencil_layer_addnew(tgpf->gpd, DATA_("GP_Layer"), true); + tgpf->gpl = BKE_gpencil_layer_addnew(tgpf->gpd, DATA_("GP_Layer"), true, false); } tgpf->lock_axis = ts->gp_sculpt.lock_axis; diff --git a/source/blender/editors/gpencil/gpencil_intern.h b/source/blender/editors/gpencil/gpencil_intern.h index c6f74c39beb..276e8c81e0f 100644 --- a/source/blender/editors/gpencil/gpencil_intern.h +++ b/source/blender/editors/gpencil/gpencil_intern.h @@ -421,6 +421,7 @@ void GPENCIL_OT_layer_duplicate_object(struct wmOperatorType *ot); void GPENCIL_OT_layer_mask_add(struct wmOperatorType *ot); void GPENCIL_OT_layer_mask_remove(struct wmOperatorType *ot); +void GPENCIL_OT_layer_mask_move(struct wmOperatorType *ot); void GPENCIL_OT_hide(struct wmOperatorType *ot); void GPENCIL_OT_reveal(struct wmOperatorType *ot); @@ -443,6 +444,7 @@ void GPENCIL_OT_frame_clean_duplicate(struct wmOperatorType *ot); void GPENCIL_OT_convert(struct wmOperatorType *ot); void GPENCIL_OT_bake_mesh_animation(struct wmOperatorType *ot); +void GPENCIL_OT_bake_grease_pencil_animation(struct wmOperatorType *ot); void GPENCIL_OT_image_to_grease_pencil(struct wmOperatorType *ot); void GPENCIL_OT_trace_image(struct wmOperatorType *ot); @@ -486,6 +488,7 @@ void GPENCIL_OT_stroke_trim(struct wmOperatorType *ot); void GPENCIL_OT_stroke_merge_by_distance(struct wmOperatorType *ot); void GPENCIL_OT_stroke_merge_material(struct wmOperatorType *ot); void GPENCIL_OT_stroke_reset_vertex_color(struct wmOperatorType *ot); +void GPENCIL_OT_stroke_normalize(struct wmOperatorType *ot); void GPENCIL_OT_material_to_vertex_color(struct wmOperatorType *ot); void GPENCIL_OT_extract_palette_vertex(struct wmOperatorType *ot); @@ -537,6 +540,7 @@ void GPENCIL_OT_material_lock_unused(struct wmOperatorType *ot); void GPENCIL_OT_material_select(struct wmOperatorType *ot); void GPENCIL_OT_material_set(struct wmOperatorType *ot); void GPENCIL_OT_set_active_material(struct wmOperatorType *ot); +void GPENCIL_OT_materials_copy_to_object(struct wmOperatorType *ot); /* convert old 2.7 files to 2.8 */ void GPENCIL_OT_convert_old_files(struct wmOperatorType *ot); @@ -748,4 +752,7 @@ struct GP_EditableStrokes_Iter { } \ (void)0 +/* Reused items for bake operators. */ +extern const EnumPropertyItem rna_gpencil_reproject_type_items[]; + /* ****************************************************** */ diff --git a/source/blender/editors/gpencil/gpencil_interpolate.c b/source/blender/editors/gpencil/gpencil_interpolate.c index bfa1ee6bcaf..0062e363cdf 100644 --- a/source/blender/editors/gpencil/gpencil_interpolate.c +++ b/source/blender/editors/gpencil/gpencil_interpolate.c @@ -84,7 +84,8 @@ typedef struct tGPDinterpolate_layer { /** interpolate factor */ float factor; - /* Hash tablets to create temp relationship between strokes. */ + /* List of strokes and Hash tablets to create temp relationship between strokes. */ + struct ListBase selected_strokes; struct GHash *used_strokes; struct GHash *pair_strokes; @@ -282,6 +283,7 @@ static void gpencil_stroke_pair_table(bContext *C, const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); /* Create hash tablets with relationship between strokes. */ + BLI_listbase_clear(&tgpil->selected_strokes); tgpil->used_strokes = BLI_ghash_ptr_new(__func__); tgpil->pair_strokes = BLI_ghash_ptr_new(__func__); @@ -315,7 +317,8 @@ static void gpencil_stroke_pair_table(bContext *C, if (ELEM(NULL, gps_from, gps_to)) { continue; } - /* Insert the pair entry in the hash table. */ + /* Insert the pair entry in the hash table and the list of strokes to keep order. */ + BLI_addtail(&tgpil->selected_strokes, BLI_genericNodeN(gps_from)); BLI_ghash_insert(tgpil->pair_strokes, gps_from, gps_to); } } @@ -405,10 +408,13 @@ static void gpencil_interpolate_update_strokes(bContext *C, tGPDinterpolate *tgp /* Clear previous interpolations. */ gpencil_interpolate_free_tagged_strokes(tgpil->interFrame); - GHashIterator gh_iter; - GHASH_ITER (gh_iter, tgpil->pair_strokes) { - bGPDstroke *gps_from = (bGPDstroke *)BLI_ghashIterator_getKey(&gh_iter); - bGPDstroke *gps_to = (bGPDstroke *)BLI_ghashIterator_getValue(&gh_iter); + LISTBASE_FOREACH (LinkData *, link, &tgpil->selected_strokes) { + bGPDstroke *gps_from = link->data; + if (!BLI_ghash_haskey(tgpil->pair_strokes, gps_from)) { + continue; + } + bGPDstroke *gps_to = (bGPDstroke *)BLI_ghash_lookup(tgpil->pair_strokes, gps_from); + /* Create new stroke. */ bGPDstroke *new_stroke = BKE_gpencil_stroke_duplicate(gps_from, true, true); new_stroke->flag |= GP_STROKE_TAG; @@ -527,10 +533,12 @@ static void gpencil_interpolate_set_points(bContext *C, tGPDinterpolate *tgpi) gpencil_stroke_pair_table(C, tgpi, tgpil); /* Create new strokes data with interpolated points reading original stroke. */ - GHashIterator gh_iter; - GHASH_ITER (gh_iter, tgpil->pair_strokes) { - bGPDstroke *gps_from = (bGPDstroke *)BLI_ghashIterator_getKey(&gh_iter); - bGPDstroke *gps_to = (bGPDstroke *)BLI_ghashIterator_getValue(&gh_iter); + LISTBASE_FOREACH (LinkData *, link, &tgpil->selected_strokes) { + bGPDstroke *gps_from = link->data; + if (!BLI_ghash_haskey(tgpil->pair_strokes, gps_from)) { + continue; + } + bGPDstroke *gps_to = (bGPDstroke *)BLI_ghash_lookup(tgpil->pair_strokes, gps_from); /* If destination stroke is smaller, resize new_stroke to size of gps_to stroke. */ if (gps_from->totpoints > gps_to->totpoints) { @@ -658,6 +666,9 @@ static void gpencil_interpolate_exit(bContext *C, wmOperator *op) MEM_SAFE_FREE(tgpil->nextFrame); MEM_SAFE_FREE(tgpil->interFrame); + /* Free list of strokes. */ + BLI_freelistN(&tgpil->selected_strokes); + /* Free Hash tablets. */ if (tgpil->used_strokes != NULL) { BLI_ghash_free(tgpil->used_strokes, NULL, NULL); @@ -1292,9 +1303,9 @@ static int gpencil_interpolate_seq_exec(bContext *C, wmOperator *op) bGPDframe *nextFrame = BKE_gpencil_frame_duplicate(gpf_next, true); /* Create a table with source and target pair of strokes. */ + ListBase selected_strokes = {NULL}; GHash *used_strokes = BLI_ghash_ptr_new(__func__); GHash *pair_strokes = BLI_ghash_ptr_new(__func__); - LISTBASE_FOREACH (bGPDstroke *, gps_from, &prevFrame->strokes) { bGPDstroke *gps_to = NULL; /* Only selected. */ @@ -1342,7 +1353,9 @@ static int gpencil_interpolate_seq_exec(bContext *C, wmOperator *op) } } - /* Insert the pair entry in the hash table. */ + /* Insert the pair entry in the hash table and in the list of strokes to keep same order. + */ + BLI_addtail(&selected_strokes, BLI_genericNodeN(gps_from)); BLI_ghash_insert(pair_strokes, gps_from, gps_to); } @@ -1369,11 +1382,12 @@ static int gpencil_interpolate_seq_exec(bContext *C, wmOperator *op) } /* Apply the factor to all pair of strokes. */ - GHashIterator gh_iter; - GHASH_ITER (gh_iter, pair_strokes) { - bGPDstroke *gps_from = (bGPDstroke *)BLI_ghashIterator_getKey(&gh_iter); - bGPDstroke *gps_to = (bGPDstroke *)BLI_ghashIterator_getValue(&gh_iter); - + LISTBASE_FOREACH (LinkData *, link, &selected_strokes) { + bGPDstroke *gps_from = link->data; + if (!BLI_ghash_haskey(pair_strokes, gps_from)) { + continue; + } + bGPDstroke *gps_to = (bGPDstroke *)BLI_ghash_lookup(pair_strokes, gps_from); /* Create new stroke. */ bGPDstroke *new_stroke = BKE_gpencil_stroke_duplicate(gps_from, true, true); new_stroke->flag |= GP_STROKE_TAG; @@ -1394,6 +1408,8 @@ static int gpencil_interpolate_seq_exec(bContext *C, wmOperator *op) } } + BLI_freelistN(&selected_strokes); + /* Free Hash tablets. */ if (used_strokes != NULL) { BLI_ghash_free(used_strokes, NULL, NULL); @@ -1419,34 +1435,31 @@ static void gpencil_interpolate_seq_ui(bContext *C, wmOperator *op) { uiLayout *layout = op->layout; uiLayout *col, *row; - PointerRNA ptr; - - RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr); const eGP_Interpolate_Type type = RNA_enum_get(op->ptr, "type"); uiLayoutSetPropSep(layout, true); uiLayoutSetPropDecorate(layout, false); row = uiLayoutRow(layout, true); - uiItemR(row, &ptr, "step", 0, NULL, ICON_NONE); + uiItemR(row, op->ptr, "step", 0, NULL, ICON_NONE); row = uiLayoutRow(layout, true); - uiItemR(row, &ptr, "layers", 0, NULL, ICON_NONE); + uiItemR(row, op->ptr, "layers", 0, NULL, ICON_NONE); if (CTX_data_mode_enum(C) == CTX_MODE_EDIT_GPENCIL) { row = uiLayoutRow(layout, true); - uiItemR(row, &ptr, "interpolate_selected_only", 0, NULL, ICON_NONE); + uiItemR(row, op->ptr, "interpolate_selected_only", 0, NULL, ICON_NONE); } row = uiLayoutRow(layout, true); - uiItemR(row, &ptr, "flip", 0, NULL, ICON_NONE); + uiItemR(row, op->ptr, "flip", 0, NULL, ICON_NONE); col = uiLayoutColumn(layout, true); - uiItemR(col, &ptr, "smooth_factor", 0, NULL, ICON_NONE); - uiItemR(col, &ptr, "smooth_steps", 0, NULL, ICON_NONE); + uiItemR(col, op->ptr, "smooth_factor", 0, NULL, ICON_NONE); + uiItemR(col, op->ptr, "smooth_steps", 0, NULL, ICON_NONE); row = uiLayoutRow(layout, true); - uiItemR(row, &ptr, "type", 0, NULL, ICON_NONE); + uiItemR(row, op->ptr, "type", 0, NULL, ICON_NONE); if (type == GP_IPO_CURVEMAP) { /* Get an RNA pointer to ToolSettings to give to the custom curve. */ @@ -1460,16 +1473,16 @@ static void gpencil_interpolate_seq_ui(bContext *C, wmOperator *op) } else if (type != GP_IPO_LINEAR) { row = uiLayoutRow(layout, false); - uiItemR(row, &ptr, "easing", 0, NULL, ICON_NONE); + uiItemR(row, op->ptr, "easing", 0, NULL, ICON_NONE); if (type == GP_IPO_BACK) { row = uiLayoutRow(layout, false); - uiItemR(row, &ptr, "back", 0, NULL, ICON_NONE); + uiItemR(row, op->ptr, "back", 0, NULL, ICON_NONE); } else if (type == GP_IPO_ELASTIC) { row = uiLayoutRow(layout, false); - uiItemR(row, &ptr, "amplitude", 0, NULL, ICON_NONE); + uiItemR(row, op->ptr, "amplitude", 0, NULL, ICON_NONE); row = uiLayoutRow(layout, false); - uiItemR(row, &ptr, "period", 0, NULL, ICON_NONE); + uiItemR(row, op->ptr, "period", 0, NULL, ICON_NONE); } } } @@ -1690,7 +1703,7 @@ static bool gpencil_interpolate_reverse_poll(bContext *C) if (area == NULL) { return false; } - if ((area->spacetype != SPACE_VIEW3D) && (area->spacetype != SPACE_ACTION)) { + if (!ELEM(area->spacetype, SPACE_VIEW3D, SPACE_ACTION)) { return false; } diff --git a/source/blender/editors/gpencil/gpencil_mesh.c b/source/blender/editors/gpencil/gpencil_mesh.c index b7ed77801c0..55468dffab0 100644 --- a/source/blender/editors/gpencil/gpencil_mesh.c +++ b/source/blender/editors/gpencil/gpencil_mesh.c @@ -402,25 +402,6 @@ static int gpencil_bake_mesh_animation_invoke(bContext *C, void GPENCIL_OT_bake_mesh_animation(wmOperatorType *ot) { - static const EnumPropertyItem reproject_type[] = { - {GP_REPROJECT_KEEP, "KEEP", 0, "No Reproject", ""}, - {GP_REPROJECT_FRONT, "FRONT", 0, "Front", "Reproject the strokes using the X-Z plane"}, - {GP_REPROJECT_SIDE, "SIDE", 0, "Side", "Reproject the strokes using the Y-Z plane"}, - {GP_REPROJECT_TOP, "TOP", 0, "Top", "Reproject the strokes using the X-Y plane"}, - {GP_REPROJECT_VIEW, - "VIEW", - 0, - "View", - "Reproject the strokes to end up on the same plane, as if drawn from the current viewpoint " - "using 'Cursor' Stroke Placement"}, - {GP_REPROJECT_CURSOR, - "CURSOR", - 0, - "Cursor", - "Reproject the strokes using the orientation of 3D cursor"}, - {0, NULL, 0, NULL, NULL}, - }; - static const EnumPropertyItem target_object_modes[] = { {GP_TARGET_OB_NEW, "NEW", 0, "New Object", ""}, {GP_TARGET_OB_SELECTED, "SELECTED", 0, "Selected Object", ""}, @@ -491,5 +472,10 @@ void GPENCIL_OT_bake_mesh_animation(wmOperatorType *ot) RNA_def_int( ot->srna, "frame_target", 1, 1, 100000, "Target Frame", "Destination frame", 1, 100000); - RNA_def_enum(ot->srna, "project_type", reproject_type, GP_REPROJECT_VIEW, "Projection Type", ""); + RNA_def_enum(ot->srna, + "project_type", + rna_gpencil_reproject_type_items, + GP_REPROJECT_VIEW, + "Projection Type", + ""); } diff --git a/source/blender/editors/gpencil/gpencil_ops.c b/source/blender/editors/gpencil/gpencil_ops.c index 1a6cb5670c4..35640cf3b66 100644 --- a/source/blender/editors/gpencil/gpencil_ops.c +++ b/source/blender/editors/gpencil/gpencil_ops.c @@ -601,6 +601,7 @@ void ED_operatortypes_gpencil(void) WM_operatortype_append(GPENCIL_OT_layer_mask_add); WM_operatortype_append(GPENCIL_OT_layer_mask_remove); + WM_operatortype_append(GPENCIL_OT_layer_mask_move); WM_operatortype_append(GPENCIL_OT_hide); WM_operatortype_append(GPENCIL_OT_reveal); @@ -621,6 +622,7 @@ void ED_operatortypes_gpencil(void) WM_operatortype_append(GPENCIL_OT_convert); WM_operatortype_append(GPENCIL_OT_bake_mesh_animation); + WM_operatortype_append(GPENCIL_OT_bake_grease_pencil_animation); WM_operatortype_append(GPENCIL_OT_image_to_grease_pencil); #ifdef WITH_POTRACE @@ -647,9 +649,11 @@ void ED_operatortypes_gpencil(void) WM_operatortype_append(GPENCIL_OT_stroke_merge_by_distance); WM_operatortype_append(GPENCIL_OT_stroke_merge_material); WM_operatortype_append(GPENCIL_OT_stroke_reset_vertex_color); + WM_operatortype_append(GPENCIL_OT_stroke_normalize); WM_operatortype_append(GPENCIL_OT_material_to_vertex_color); WM_operatortype_append(GPENCIL_OT_extract_palette_vertex); + WM_operatortype_append(GPENCIL_OT_materials_copy_to_object); WM_operatortype_append(GPENCIL_OT_transform_fill); WM_operatortype_append(GPENCIL_OT_reset_transform_fill); diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c index 1217a3a7e8f..e40748e5f6e 100644 --- a/source/blender/editors/gpencil/gpencil_paint.c +++ b/source/blender/editors/gpencil/gpencil_paint.c @@ -1332,7 +1332,7 @@ static float view3d_point_depth(const RegionView3D *rv3d, const float co[3]) /* only erase stroke points that are visible */ static bool gpencil_stroke_eraser_is_occluded( - tGPsdata *p, bGPDlayer *gpl, const bGPDspoint *pt, const int x, const int y) + tGPsdata *p, bGPDlayer *gpl, bGPDspoint *pt, const int x, const int y) { Object *obact = (Object *)p->ownerPtr.data; Brush *brush = p->brush; @@ -1364,7 +1364,11 @@ static bool gpencil_stroke_eraser_is_occluded( mul_v3_m4v3(fpt, diff_mat, &pt->x); const float depth_pt = view3d_point_depth(rv3d, fpt); + /* Checked occlusion flag. */ + pt->flag |= GP_SPOINT_TEMP_TAG; if (depth_pt > depth_mval) { + /* Is occluded. */ + pt->flag |= GP_SPOINT_TEMP_TAG2; return true; } } @@ -1540,6 +1544,10 @@ static void gpencil_stroke_eraser_dostroke(tGPsdata *p, for (i = 0; i < gps->totpoints; i++) { bGPDspoint *pt = &gps->points[i]; pt->flag &= ~GP_SPOINT_TAG; + /* Occlusion already checked. */ + pt->flag &= ~GP_SPOINT_TEMP_TAG; + /* Point is occluded. */ + pt->flag &= ~GP_SPOINT_TEMP_TAG2; } /* First Pass: Loop over the points in the stroke @@ -1585,9 +1593,23 @@ static void gpencil_stroke_eraser_dostroke(tGPsdata *p, * - this assumes that linewidth is irrelevant */ if (gpencil_stroke_inside_circle(mval, radius, pc0[0], pc0[1], pc2[0], pc2[1])) { - if ((gpencil_stroke_eraser_is_occluded(p, gpl, pt0, pc0[0], pc0[1]) == false) || - (gpencil_stroke_eraser_is_occluded(p, gpl, pt1, pc1[0], pc1[1]) == false) || - (gpencil_stroke_eraser_is_occluded(p, gpl, pt2, pc2[0], pc2[1]) == false)) { + + bool is_occluded_pt0, is_occluded_pt1, is_occluded_pt2 = true; + is_occluded_pt0 = (pt0 && ((pt0->flag & GP_SPOINT_TEMP_TAG) != 0)) ? + ((pt0->flag & GP_SPOINT_TEMP_TAG2) != 0) : + gpencil_stroke_eraser_is_occluded(p, gpl, pt0, pc0[0], pc0[1]); + if (is_occluded_pt0) { + is_occluded_pt1 = ((pt1->flag & GP_SPOINT_TEMP_TAG) != 0) ? + ((pt1->flag & GP_SPOINT_TEMP_TAG2) != 0) : + gpencil_stroke_eraser_is_occluded(p, gpl, pt1, pc1[0], pc1[1]); + if (is_occluded_pt1) { + is_occluded_pt2 = ((pt2->flag & GP_SPOINT_TEMP_TAG) != 0) ? + ((pt2->flag & GP_SPOINT_TEMP_TAG2) != 0) : + gpencil_stroke_eraser_is_occluded(p, gpl, pt2, pc2[0], pc2[1]); + } + } + + if (!is_occluded_pt0 || !is_occluded_pt1 || !is_occluded_pt2) { /* Point is affected: */ /* Adjust thickness * - Influence of eraser falls off with distance from the middle of the eraser @@ -1722,7 +1744,7 @@ static void gpencil_stroke_doeraser(tGPsdata *p) if ((gp_settings != NULL) && (gp_settings->flag & GP_BRUSH_OCCLUDE_ERASER)) { View3D *v3d = p->area->spacedata.first; view3d_region_operator_needs_opengl(p->win, p->region); - ED_view3d_autodist_init(p->depsgraph, p->region, v3d, 0); + ED_view3d_depth_override(p->depsgraph, p->region, v3d, NULL, V3D_DEPTH_NO_GPENCIL, false); } } @@ -2120,7 +2142,7 @@ static void gpencil_paint_initstroke(tGPsdata *p, /* get active layer (or add a new one if non-existent) */ p->gpl = BKE_gpencil_layer_active_get(p->gpd); if (p->gpl == NULL) { - p->gpl = BKE_gpencil_layer_addnew(p->gpd, DATA_("GP_Layer"), true); + p->gpl = BKE_gpencil_layer_addnew(p->gpd, DATA_("GP_Layer"), true, false); changed = true; if (p->custom_color[3]) { copy_v3_v3(p->gpl->color, p->custom_color); @@ -2305,8 +2327,14 @@ static void gpencil_paint_strokeend(tGPsdata *p) /* need to restore the original projection settings before packing up */ view3d_region_operator_needs_opengl(p->win, p->region); - ED_view3d_autodist_init( - p->depsgraph, p->region, v3d, (ts->gpencil_v3d_align & GP_PROJECT_DEPTH_STROKE) ? 1 : 0); + ED_view3d_depth_override(p->depsgraph, + p->region, + v3d, + NULL, + (ts->gpencil_v3d_align & GP_PROJECT_DEPTH_STROKE) ? + V3D_DEPTH_GPENCIL_ONLY : + V3D_DEPTH_NO_GPENCIL, + false); } /* check if doing eraser or not */ @@ -3259,7 +3287,7 @@ static int gpencil_draw_invoke(bContext *C, wmOperator *op, const wmEvent *event gpencil_guide_event_handling(C, op, event, p); } - if (ob && (ob->type == OB_GPENCIL) && ((p->gpd->flag & GP_DATA_STROKE_PAINTMODE) == 0)) { + if ((ob->type == OB_GPENCIL) && ((p->gpd->flag & GP_DATA_STROKE_PAINTMODE) == 0)) { /* FIXME: use the mode switching operator, this misses notifiers, messages. */ /* Just set paintmode flag... */ p->gpd->flag |= GP_DATA_STROKE_PAINTMODE; @@ -3671,14 +3699,6 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) * is essential for ensuring that they can quickly return to that view */ } - else if ((event->type == EVT_BKEY) && (event->val == KM_RELEASE)) { - /* Add Blank Frame - * - Since this operator is non-modal, we can just call it here, and keep going... - * - This operator is especially useful when animating - */ - WM_operator_name_call(C, "GPENCIL_OT_blank_frame_add", WM_OP_EXEC_DEFAULT, NULL); - estate = OPERATOR_RUNNING_MODAL; - } else if ((!ELEM(p->paintmode, GP_PAINTMODE_ERASER, GP_PAINTMODE_SET_CP))) { gpencil_guide_event_handling(C, op, event, p); estate = OPERATOR_RUNNING_MODAL; @@ -3691,7 +3711,7 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) /* Exit painting mode (and/or end current stroke). * */ - if (ELEM(event->type, EVT_RETKEY, EVT_PADENTER, EVT_ESCKEY, EVT_SPACEKEY, EVT_EKEY)) { + if (ELEM(event->type, EVT_RETKEY, EVT_PADENTER, EVT_ESCKEY, EVT_SPACEKEY)) { p->status = GP_STATUS_DONE; estate = OPERATOR_FINISHED; diff --git a/source/blender/editors/gpencil/gpencil_primitive.c b/source/blender/editors/gpencil/gpencil_primitive.c index dfff0ce639e..5f02bbf0a77 100644 --- a/source/blender/editors/gpencil/gpencil_primitive.c +++ b/source/blender/editors/gpencil/gpencil_primitive.c @@ -314,7 +314,7 @@ static void gpencil_primitive_set_initdata(bContext *C, tGPDprimitive *tgpi) /* if layer doesn't exist, create a new one */ if (gpl == NULL) { - gpl = BKE_gpencil_layer_addnew(tgpi->gpd, DATA_("Primitives"), true); + gpl = BKE_gpencil_layer_addnew(tgpi->gpd, DATA_("Primitives"), true, false); } tgpi->gpl = gpl; @@ -785,10 +785,14 @@ static void gpencil_primitive_update_strokes(bContext *C, tGPDprimitive *tgpi) /* need to restore the original projection settings before packing up */ view3d_region_operator_needs_opengl(tgpi->win, tgpi->region); - ED_view3d_autodist_init(tgpi->depsgraph, - tgpi->region, - tgpi->v3d, - (ts->gpencil_v3d_align & GP_PROJECT_DEPTH_STROKE) ? 1 : 0); + ED_view3d_depth_override(tgpi->depsgraph, + tgpi->region, + tgpi->v3d, + NULL, + (ts->gpencil_v3d_align & GP_PROJECT_DEPTH_STROKE) ? + V3D_DEPTH_GPENCIL_ONLY : + V3D_DEPTH_NO_GPENCIL, + false); depth_arr = MEM_mallocN(sizeof(float) * gps->totpoints, "depth_points"); tGPspoint *ptc = &points2D[0]; @@ -1532,24 +1536,22 @@ static void gpencil_primitive_strength(tGPDprimitive *tgpi, bool reset) Brush *brush = tgpi->brush; BrushGpencilSettings *brush_settings = brush->gpencil_settings; - if (brush) { - if (reset) { - brush_settings->draw_strength = tgpi->brush_strength; - tgpi->brush_strength = 0.0f; - } - else { - if (tgpi->brush_strength == 0.0f) { - tgpi->brush_strength = brush_settings->draw_strength; - } - float move[2]; - sub_v2_v2v2(move, tgpi->mval, tgpi->mvalo); - float adjust = (move[1] > 0.0f) ? 0.01f : -0.01f; - brush_settings->draw_strength += adjust * fabsf(len_manhattan_v2(move)); + if (reset) { + brush_settings->draw_strength = tgpi->brush_strength; + tgpi->brush_strength = 0.0f; + } + else { + if (tgpi->brush_strength == 0.0f) { + tgpi->brush_strength = brush_settings->draw_strength; } - - /* limit low limit because below 0.2f the stroke is invisible */ - CLAMP(brush_settings->draw_strength, 0.2f, 1.0f); + float move[2]; + sub_v2_v2v2(move, tgpi->mval, tgpi->mvalo); + float adjust = (move[1] > 0.0f) ? 0.01f : -0.01f; + brush_settings->draw_strength += adjust * fabsf(len_manhattan_v2(move)); } + + /* limit low limit because below 0.2f the stroke is invisible */ + CLAMP(brush_settings->draw_strength, 0.2f, 1.0f); } /* brush size */ diff --git a/source/blender/editors/gpencil/gpencil_select.c b/source/blender/editors/gpencil/gpencil_select.c index aab08e9c8c4..e1776988186 100644 --- a/source/blender/editors/gpencil/gpencil_select.c +++ b/source/blender/editors/gpencil/gpencil_select.c @@ -505,7 +505,7 @@ void GPENCIL_OT_select_alternate(wmOperatorType *ot) /* properties */ RNA_def_boolean(ot->srna, "unselect_ends", - true, + false, "Unselect Ends", "Do not select the first and last point of the stroke"); } diff --git a/source/blender/editors/gpencil/gpencil_trace_ops.c b/source/blender/editors/gpencil/gpencil_trace_ops.c index d2e5fa3db32..cd75f9313ad 100644 --- a/source/blender/editors/gpencil/gpencil_trace_ops.c +++ b/source/blender/editors/gpencil/gpencil_trace_ops.c @@ -207,7 +207,7 @@ static void trace_initialize_job_data(TraceJob *trace_job) trace_job->gpd = (bGPdata *)trace_job->ob_gpencil->data; trace_job->gpl = BKE_gpencil_layer_active_get(trace_job->gpd); if (trace_job->gpl == NULL) { - trace_job->gpl = BKE_gpencil_layer_addnew(trace_job->gpd, DATA_("Trace"), true); + trace_job->gpl = BKE_gpencil_layer_addnew(trace_job->gpd, DATA_("Trace"), true, false); } } diff --git a/source/blender/editors/gpencil/gpencil_trace_utils.c b/source/blender/editors/gpencil/gpencil_trace_utils.c index ada777d43f3..12c38fb2744 100644 --- a/source/blender/editors/gpencil/gpencil_trace_utils.c +++ b/source/blender/editors/gpencil/gpencil_trace_utils.c @@ -281,7 +281,6 @@ void ED_gpencil_trace_data_to_strokes(Main *bmain, mat_mask_idx = ob->totcol - 1; } - potrace_path_t *path = st->plist; int n, *tag; potrace_dpoint_t(*c)[3]; @@ -289,7 +288,7 @@ void ED_gpencil_trace_data_to_strokes(Main *bmain, * good results using the Potrace data. */ const float scalef = 0.008f * scale; /* Draw each curve. */ - path = st->plist; + potrace_path_t *path = st->plist; while (path != NULL) { n = path->curve.n; tag = path->curve.tag; @@ -308,9 +307,16 @@ void ED_gpencil_trace_data_to_strokes(Main *bmain, if (gps->totpoints == 0) { add_point(gps, scalef, offset, c[n - 1][2].x, c[n - 1][2].y); } + else { + add_point(gps, scalef, offset, last[0], last[1]); + } + add_point(gps, scalef, offset, c[i][1].x, c[i][1].y); add_point(gps, scalef, offset, c[i][2].x, c[i][2].y); + + last[0] = c[i][2].x; + last[1] = c[i][2].y; break; } case POTRACE_CURVETO: { diff --git a/source/blender/editors/gpencil/gpencil_utils.c b/source/blender/editors/gpencil/gpencil_utils.c index 574670de7ca..c9ef340b9d3 100644 --- a/source/blender/editors/gpencil/gpencil_utils.c +++ b/source/blender/editors/gpencil/gpencil_utils.c @@ -543,6 +543,38 @@ bool gpencil_stroke_inside_circle(const float mval[2], int rad, int x0, int y0, } /* ******************************************************** */ +/* Selection Validity Testing */ + +bool ED_gpencil_frame_has_selected_stroke(const bGPDframe *gpf) +{ + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { + if (gps->flag & GP_STROKE_SELECT) { + return true; + } + } + + return false; +} + +bool ED_gpencil_layer_has_selected_stroke(const bGPDlayer *gpl, const bool is_multiedit) +{ + bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe; + for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) { + if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) { + if (ED_gpencil_frame_has_selected_stroke(gpf)) { + return true; + } + } + /* If not multiedit, exit loop. */ + if (!is_multiedit) { + break; + } + } + + return false; +} + +/* ******************************************************** */ /* Stroke Validity Testing */ /* Check whether given stroke can be edited given the supplied context */ @@ -648,7 +680,7 @@ void gpencil_point_conversion_init(bContext *C, GP_SpaceConversion *r_gsc) view3d_operator_needs_opengl(C); view3d_region_operator_needs_opengl(win, region); - ED_view3d_autodist_init(depsgraph, region, v3d, 0); + ED_view3d_depth_override(depsgraph, region, v3d, NULL, V3D_DEPTH_NO_GPENCIL, false); /* for camera view set the subrect */ if (rv3d->persp == RV3D_CAMOB) { @@ -1253,7 +1285,11 @@ void ED_gpencil_stroke_reproject(Depsgraph *depsgraph, float location[3] = {0.0f, 0.0f, 0.0f}; float normal[3] = {0.0f, 0.0f, 0.0f}; - ED_view3d_win_to_ray(region, xy, &ray_start[0], &ray_normal[0]); + BLI_assert(gps->flag & GP_STROKE_3DSPACE); + BLI_assert(gsc->area && gsc->area->spacetype == SPACE_VIEW3D); + const View3D *v3d = gsc->area->spacedata.first; + ED_view3d_win_to_ray_clipped( + depsgraph, region, v3d, xy, &ray_start[0], &ray_normal[0], true); if (ED_transform_snap_object_project_ray(sctx, depsgraph, &(const struct SnapObjectParams){ |