diff options
author | Falk David <falkdavid@gmx.de> | 2020-11-13 23:43:00 +0300 |
---|---|---|
committer | Falk David <falkdavid@gmx.de> | 2020-11-13 23:43:00 +0300 |
commit | 0be88c7d15d2ad1af284c6283370173647ae74eb (patch) | |
tree | 5fff573c512e284547ebe0c921ecffdae2c377c4 /source/blender/editors/transform/transform_convert_gpencil.c | |
parent | 9d28353b525ecfbcca1501be72e4276dfb2bbc2a (diff) |
GPencil: Merge GSoC curve edit mode
Differential Revision: https://developer.blender.org/D8660
This patch is the result of the GSoC 2020 "Editing Grease Pencil Strokes
Using Curves" project. It adds a submode to greasepencil edit mode that
allows for the transformation of greasepencil strokes using bezier
curves. More information about the project can be found
here: https://wiki.blender.org/wiki/User:Filedescriptor/GSoC_2020.
Diffstat (limited to 'source/blender/editors/transform/transform_convert_gpencil.c')
-rw-r--r-- | source/blender/editors/transform/transform_convert_gpencil.c | 436 |
1 files changed, 409 insertions, 27 deletions
diff --git a/source/blender/editors/transform/transform_convert_gpencil.c b/source/blender/editors/transform/transform_convert_gpencil.c index 84885dd8c49..0a742ec4470 100644 --- a/source/blender/editors/transform/transform_convert_gpencil.c +++ b/source/blender/editors/transform/transform_convert_gpencil.c @@ -31,7 +31,9 @@ #include "BKE_colortools.h" #include "BKE_context.h" +#include "BKE_curve.h" #include "BKE_gpencil.h" +#include "BKE_gpencil_curve.h" #include "BKE_gpencil_geom.h" #include "ED_gpencil.h" @@ -63,33 +65,351 @@ static void createTransGPencil_center_get(bGPDstroke *gps, float r_center[3]) } } -void createTransGPencil(bContext *C, TransInfo *t) +static short get_bezt_sel_triple_flag(BezTriple *bezt, const bool handles_visible) { - if (t->data_container_len == 0) { +#define SEL_F1 (1 << 0) +#define SEL_F2 (1 << 1) +#define SEL_F3 (1 << 2) +#define SEL_ALL ((1 << 0) | (1 << 1) | (1 << 2)) + + short flag = 0; + + if (handles_visible) { + flag = ((bezt->f1 & SELECT) ? SEL_F1 : 0) | ((bezt->f2 & SELECT) ? SEL_F2 : 0) | + ((bezt->f3 & SELECT) ? SEL_F3 : 0); + } + else { + if (bezt->f2 & SELECT) { + flag = SEL_ALL; + } + } + + /* Special case for auto & aligned handles */ + if (flag != SEL_ALL && flag & SEL_F2) { + if (ELEM(bezt->h1, HD_AUTO, HD_ALIGN) && ELEM(bezt->h2, HD_AUTO, HD_ALIGN)) { + flag = SEL_ALL; + } + } + +#undef SEL_F1 +#undef SEL_F2 +#undef SEL_F3 + return flag; +} + +static void createTransGPencil_curves(bContext *C, + TransInfo *t, + Depsgraph *depsgraph, + ToolSettings *ts, + Object *obact, + bGPdata *gpd, + const int cfra_scene, + const bool is_multiedit, + const bool use_multiframe_falloff, + const bool is_prop_edit, + const bool is_prop_edit_connected, + const bool is_scale_thickness) +{ +#define SEL_F1 (1 << 0) +#define SEL_F2 (1 << 1) +#define SEL_F3 (1 << 2) + + View3D *v3d = t->view; + const bool handle_only_selected_visible = (v3d->overlay.handle_display == CURVE_HANDLE_SELECTED); + const bool handle_all_visible = (v3d->overlay.handle_display == CURVE_HANDLE_ALL); + + TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); + tc->data_len = 0; + + /* Number of selected curve points */ + uint32_t tot_curve_points = 0, tot_sel_curve_points = 0, tot_points = 0, tot_sel_points = 0; + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { + /* Only editable and visible layers are considered. */ + if (BKE_gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) { + 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))) { + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { + /* 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_color_use(obact, gpl, gps) == false) { + continue; + } + /* Check if stroke has an editcurve */ + if (gps->editcurve == NULL) { + continue; + } + + bGPDcurve *gpc = gps->editcurve; + for (int i = 0; i < gpc->tot_curve_points; i++) { + bGPDcurve_point *gpc_pt = &gpc->curve_points[i]; + BezTriple *bezt = &gpc_pt->bezt; + if (bezt->hide) { + continue; + } + + const bool handles_visible = (handle_all_visible || + (handle_only_selected_visible && + (gpc_pt->flag & GP_CURVE_POINT_SELECT))); + + const short sel_flag = get_bezt_sel_triple_flag(bezt, handles_visible); + if (sel_flag & (SEL_F1 | SEL_F2 | SEL_F3)) { + if (sel_flag & SEL_F1) { + tot_sel_points++; + } + if (sel_flag & SEL_F2) { + tot_sel_points++; + } + if (sel_flag & SEL_F3) { + tot_sel_points++; + } + tot_sel_curve_points++; + } + + if (is_prop_edit) { + tot_points += 3; + tot_curve_points++; + } + } + } + } + + /* If not multiedit out of loop. */ + if (!is_multiedit) { + break; + } + } + } + } + + if (((is_prop_edit && !is_prop_edit_connected) ? tot_curve_points : tot_sel_points) == 0) { + tc->data_len = 0; return; } - Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - bGPdata *gpd = ED_gpencil_data_get_active(C); - ToolSettings *ts = CTX_data_tool_settings(C); + int data_len_pt = 0; - bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); - bool use_multiframe_falloff = (ts->gp_sculpt.flag & GP_SCULPT_SETT_FLAG_FRAME_FALLOFF) != 0; + if (is_prop_edit) { + tc->data_len = tot_points; + data_len_pt = tot_curve_points; + } + else { + tc->data_len = tot_sel_points; + data_len_pt = tot_sel_curve_points; + } - Object *obact = CTX_data_active_object(C); - TransData *td = NULL; - float mtx[3][3], smtx[3][3]; + if (tc->data_len == 0) { + return; + } - const Scene *scene = CTX_data_scene(C); - const int cfra_scene = CFRA; + transform_around_single_fallback_ex(t, data_len_pt); - const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0; - const bool is_prop_edit_connected = (t->flag & T_PROP_CONNECTED) != 0; - const bool is_scale_thickness = ((t->mode == TFM_GPENCIL_SHRINKFATTEN) || - (ts->gp_sculpt.flag & GP_SCULPT_SETT_FLAG_SCALE_THICKNESS)); + tc->data = MEM_callocN(tc->data_len * sizeof(TransData), __func__); + TransData *td = tc->data; - TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); + const bool use_around_origins_for_handles_test = ((t->around == V3D_AROUND_LOCAL_ORIGINS) && + transform_mode_use_local_origins(t)); + + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { + /* Only editable and visible layers are considered. */ + if (BKE_gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) { + const int cfra = (gpl->flag & GP_LAYER_FRAMELOCK) ? gpl->actframe->framenum : cfra_scene; + bGPDframe *gpf = gpl->actframe; + bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe; + float diff_mat[4][4], mtx[3][3]; + float smtx[3][3]; + + /* Init multiframe falloff options. */ + int f_init = 0; + int f_end = 0; + + if (use_multiframe_falloff) { + BKE_gpencil_frame_range_selected(gpl, &f_init, &f_end); + } + + if ((gpf->framenum != cfra) && (!is_multiedit)) { + gpf = BKE_gpencil_frame_addcopy(gpl, cfra); + /* in some weird situations (framelock enabled) return NULL */ + if (gpf == NULL) { + continue; + } + if (!is_multiedit) { + init_gpf = gpf; + } + } + + /* Calculate difference matrix. */ + BKE_gpencil_parent_matrix_get(depsgraph, obact, gpl, diff_mat); + copy_m3_m4(mtx, diff_mat); + pseudoinverse_m3_m3(smtx, mtx, PSEUDOINVERSE_EPSILON); + + for (gpf = init_gpf; gpf; gpf = gpf->next) { + if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) { + /* If multi-frame and falloff, recalculate and save value. */ + float falloff = 1.0f; /* by default no falloff */ + if ((is_multiedit) && (use_multiframe_falloff)) { + /* Falloff depends on distance to active frame + * (relative to the overall frame range). */ + falloff = BKE_gpencil_multiframe_falloff_calc( + gpf, gpl->actframe->framenum, f_init, f_end, ts->gp_sculpt.cur_falloff); + } + + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { + /* 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_color_use(obact, gpl, gps) == false) { + continue; + } + /* Check if stroke has an editcurve */ + if (gps->editcurve == NULL) { + continue; + } + TransData *head, *tail; + head = tail = td; + + gps->runtime.multi_frame_falloff = falloff; + bool need_handle_recalc = false; + + bGPDcurve *gpc = gps->editcurve; + const bool is_cyclic = gps->flag & GP_STROKE_CYCLIC; + for (int i = 0; i < gpc->tot_curve_points; i++) { + bGPDcurve_point *gpc_pt = &gpc->curve_points[i]; + BezTriple *bezt = &gpc_pt->bezt; + if (bezt->hide) { + continue; + } + + TransDataCurveHandleFlags *hdata = NULL; + bool bezt_use = false; + const bool handles_visible = (handle_all_visible || + (handle_only_selected_visible && + (gpc_pt->flag & GP_CURVE_POINT_SELECT))); + const short sel_flag = get_bezt_sel_triple_flag(bezt, handles_visible); + /* Iterate over bezier triple */ + for (int j = 0; j < 3; j++) { + bool is_ctrl_point = (j == 1); + bool sel = sel_flag & (1 << j); + + if (is_prop_edit || sel) { + copy_v3_v3(td->iloc, bezt->vec[j]); + td->loc = bezt->vec[j]; + bool rotate_around_ctrl = !handles_visible || + (t->around == V3D_AROUND_LOCAL_ORIGINS) || + (bezt->f2 & SELECT); + copy_v3_v3(td->center, bezt->vec[rotate_around_ctrl ? 1 : j]); + + if (!handles_visible || is_ctrl_point) { + if (bezt->f2 & SELECT) { + td->flag = TD_SELECTED; + } + else { + td->flag = 0; + } + } + else if (handles_visible) { + if (BEZT_ISSEL_IDX(bezt, j)) { + td->flag = TD_SELECTED; + } + else { + td->flag = 0; + } + } + + td->ext = NULL; + if (is_ctrl_point) { + if (t->mode != TFM_MIRROR) { + if (t->mode != TFM_GPENCIL_OPACITY) { + if (is_scale_thickness) { + td->val = &(gpc_pt->pressure); + td->ival = gpc_pt->pressure; + } + } + else { + td->val = &(gpc_pt->strength); + td->ival = gpc_pt->strength; + } + } + } + else { + td->val = NULL; + } + + if (hdata == NULL) { + if (is_ctrl_point && ((sel_flag & SEL_F1 & SEL_F3) == 0)) { + hdata = initTransDataCurveHandles(td, bezt); + } + else if (!is_ctrl_point) { + hdata = initTransDataCurveHandles(td, bezt); + } + } + + td->extra = gps; + td->ob = obact; + + copy_m3_m3(td->smtx, smtx); + copy_m3_m3(td->mtx, mtx); + copy_m3_m3(td->axismtx, mtx); + + td++; + tail++; + } + + bezt_use |= sel; + } + + /* Update the handle types so transformation is possible */ + if (bezt_use && !ELEM(t->mode, TFM_GPENCIL_OPACITY, TFM_GPENCIL_SHRINKFATTEN)) { + BKE_nurb_bezt_handle_test( + bezt, SELECT, handles_visible, use_around_origins_for_handles_test); + need_handle_recalc = true; + } + } + if (is_prop_edit && (head != tail)) { + calc_distanceCurveVerts(head, tail - 1, is_cyclic); + } + + if (need_handle_recalc) { + BKE_gpencil_editcurve_recalculate_handles(gps); + } + } + } + + /* If not multiedit out of loop. */ + if (!is_multiedit) { + break; + } + } + } + } +#undef SEL_F1 +#undef SEL_F2 +#undef SEL_F3 +} + +static void createTransGPencil_strokes(bContext *C, + TransInfo *t, + Depsgraph *depsgraph, + ToolSettings *ts, + Object *obact, + bGPdata *gpd, + const int cfra_scene, + const bool is_multiedit, + const bool use_multiframe_falloff, + const bool is_prop_edit, + const bool is_prop_edit_connected, + const bool is_scale_thickness) +{ + TransData *td = NULL; + float mtx[3][3], smtx[3][3]; + + TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); /* == Grease Pencil Strokes to Transform Data == * Grease Pencil stroke points can be a mixture of 2D (screen-space), * or 3D coordinates. However, they're always saved as 3D points. @@ -98,15 +418,6 @@ void createTransGPencil(bContext *C, TransInfo *t) */ tc->data_len = 0; - if (gpd == NULL) { - return; - } - - /* initialize falloff curve */ - if (is_multiedit) { - BKE_curvemapping_init(ts->gp_sculpt.cur_falloff); - } - /* First Pass: Count the number of data-points required for the strokes, * (and additional info about the configuration - e.g. 2D/3D?). */ @@ -368,6 +679,71 @@ void createTransGPencil(bContext *C, TransInfo *t) } } +void createTransGPencil(bContext *C, TransInfo *t) +{ + if (t->data_container_len == 0) { + return; + } + + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + const Scene *scene = CTX_data_scene(C); + ToolSettings *ts = scene->toolsettings; + Object *obact = CTX_data_active_object(C); + bGPdata *gpd = obact->data; + BLI_assert(gpd != NULL); + + const int cfra_scene = CFRA; + + const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); + const bool use_multiframe_falloff = (ts->gp_sculpt.flag & GP_SCULPT_SETT_FLAG_FRAME_FALLOFF) != + 0; + + const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0; + const bool is_prop_edit_connected = (t->flag & T_PROP_CONNECTED) != 0; + const bool is_scale_thickness = ((t->mode == TFM_GPENCIL_SHRINKFATTEN) || + (ts->gp_sculpt.flag & GP_SCULPT_SETT_FLAG_SCALE_THICKNESS)); + + const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd); + + /* initialize falloff curve */ + if (is_multiedit) { + BKE_curvemapping_init(ts->gp_sculpt.cur_falloff); + } + + if (gpd == NULL) { + return; + } + + if (is_curve_edit) { + createTransGPencil_curves(C, + t, + depsgraph, + ts, + obact, + gpd, + cfra_scene, + is_multiedit, + use_multiframe_falloff, + is_prop_edit, + is_prop_edit_connected, + is_scale_thickness); + } + else { + createTransGPencil_strokes(C, + t, + depsgraph, + ts, + obact, + gpd, + cfra_scene, + is_multiedit, + use_multiframe_falloff, + is_prop_edit, + is_prop_edit_connected, + is_scale_thickness); + } +} + /* force recalculation of triangles during transformation */ void recalcData_gpencil_strokes(TransInfo *t) { @@ -375,13 +751,19 @@ void recalcData_gpencil_strokes(TransInfo *t) GHash *strokes = BLI_ghash_ptr_new(__func__); TransData *td = tc->data; + bGPdata *gpd = td->ob->data; + const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd); for (int i = 0; i < tc->data_len; i++, td++) { bGPDstroke *gps = td->extra; if ((gps != NULL) && (!BLI_ghash_haskey(strokes, gps))) { BLI_ghash_insert(strokes, gps, gps); + if (is_curve_edit && gps->editcurve != NULL) { + BKE_gpencil_editcurve_recalculate_handles(gps); + gps->flag |= GP_STROKE_NEEDS_CURVE_UPDATE; + } /* Calc geometry data. */ - BKE_gpencil_stroke_geometry_update(gps); + BKE_gpencil_stroke_geometry_update(gpd, gps); } } BLI_ghash_free(strokes, NULL, NULL); |