diff --git a/source/blender/editors/gpencil/gpencil_brush.c b/source/blender/editors/gpencil/gpencil_brush.c index 4312bed657e..7b8cfc58a5f 100644 --- a/source/blender/editors/gpencil/gpencil_brush.c +++ b/source/blender/editors/gpencil/gpencil_brush.c @@ -134,6 +134,9 @@ typedef struct tGP_BrushEditData { /* - effect vector (e.g. 2D/3D translation for grab brush) */ float dvec[3]; + /* rotation for evaluated data */ + float rot_eval; + /* - multiframe falloff factor */ float mf_falloff; @@ -157,8 +160,12 @@ typedef struct tGP_BrushEditData { } tGP_BrushEditData; /* Callback for performing some brush operation on a single point */ -typedef bool (*GP_BrushApplyCb)( - tGP_BrushEditData *gso, bGPDstroke *gps, int pt_index, const int radius, const int co[2]); +typedef bool (*GP_BrushApplyCb)(tGP_BrushEditData *gso, + bGPDstroke *gps, + float rotation, + int pt_index, + const int radius, + const int co[2]); /* ************************************************ */ /* Utility Functions */ @@ -294,6 +301,20 @@ static float gp_brush_influence_calc(tGP_BrushEditData *gso, const int radius, c return influence; } +/* Force recal filling data */ +static void gp_recalc_geometry(bGPDstroke *gps) +{ + bGPDstroke *gps_orig = gps->runtime.gps_orig; + if (gps_orig) { + gps_orig->flag |= GP_STROKE_RECALC_GEOMETRY; + gps_orig->tot_triangles = 0; + } + else { + gps->flag |= GP_STROKE_RECALC_GEOMETRY; + gps->tot_triangles = 0; + } +} + /* ************************************************ */ /* Brush Callbacks */ /* This section defines the callbacks used by each brush to perform their magic. @@ -305,8 +326,12 @@ static float gp_brush_influence_calc(tGP_BrushEditData *gso, const int radius, c /* A simple (but slower + inaccurate) * smooth-brush implementation to test the algorithm for stroke smoothing. */ -static bool gp_brush_smooth_apply( - tGP_BrushEditData *gso, bGPDstroke *gps, int pt_index, const int radius, const int co[2]) +static bool gp_brush_smooth_apply(tGP_BrushEditData *gso, + bGPDstroke *gps, + float UNUSED(rot_eval), + int pt_index, + const int radius, + const int co[2]) { // GP_Sculpt_Data *gp_brush = gso->brush; float inf = gp_brush_influence_calc(gso, radius, co); @@ -331,7 +356,7 @@ static bool gp_brush_smooth_apply( BKE_gpencil_smooth_stroke_uv(gps, pt_index, inf); } - gps->flag |= GP_STROKE_RECALC_GEOMETRY; + gp_recalc_geometry(gps); return true; } @@ -340,8 +365,12 @@ static bool gp_brush_smooth_apply( /* Line Thickness Brush */ /* Make lines thicker or thinner by the specified amounts */ -static bool gp_brush_thickness_apply( - tGP_BrushEditData *gso, bGPDstroke *gps, int pt_index, const int radius, const int co[2]) +static bool gp_brush_thickness_apply(tGP_BrushEditData *gso, + bGPDstroke *gps, + float UNUSED(rot_eval), + int pt_index, + const int radius, + const int co[2]) { bGPDspoint *pt = gps->points + pt_index; float inf; @@ -380,8 +409,12 @@ static bool gp_brush_thickness_apply( /* Color Strength Brush */ /* Make color more or less transparent by the specified amounts */ -static bool gp_brush_strength_apply( - tGP_BrushEditData *gso, bGPDstroke *gps, int pt_index, const int radius, const int co[2]) +static bool gp_brush_strength_apply(tGP_BrushEditData *gso, + bGPDstroke *gps, + float UNUSED(rot_eval), + int pt_index, + const int radius, + const int co[2]) { bGPDspoint *pt = gps->points + pt_index; float inf; @@ -424,6 +457,8 @@ typedef struct tGPSB_Grab_StrokeData { int *points; /* array of influence weights for each of the included points */ float *weights; + /* angles to calc transformation */ + float *rot_eval; /* capacity of the arrays */ int capacity; @@ -451,6 +486,7 @@ static void gp_brush_grab_stroke_init(tGP_BrushEditData *gso, bGPDstroke *gps) memset(data->points, 0, sizeof(int) * data->capacity); memset(data->weights, 0, sizeof(float) * data->capacity); + memset(data->rot_eval, 0, sizeof(float) * data->capacity); } else { /* Create new instance */ @@ -461,6 +497,7 @@ static void gp_brush_grab_stroke_init(tGP_BrushEditData *gso, bGPDstroke *gps) data->points = MEM_callocN(sizeof(int) * data->capacity, "GP Stroke Grab Indices"); data->weights = MEM_callocN(sizeof(float) * data->capacity, "GP Stroke Grab Weights"); + data->rot_eval = MEM_callocN(sizeof(float) * data->capacity, "GP Stroke Grab X"); /* hook up to the cache */ BLI_ghash_insert(gso->stroke_customdata, gps, data); @@ -468,8 +505,12 @@ static void gp_brush_grab_stroke_init(tGP_BrushEditData *gso, bGPDstroke *gps) } /* store references to stroke points in the initial stage */ -static bool gp_brush_grab_store_points( - tGP_BrushEditData *gso, bGPDstroke *gps, int pt_index, const int radius, const int co[2]) +static bool gp_brush_grab_store_points(tGP_BrushEditData *gso, + bGPDstroke *gps, + float rot_eval, + int pt_index, + const int radius, + const int co[2]) { tGPSB_Grab_StrokeData *data = BLI_ghash_lookup(gso->stroke_customdata, gps); float inf = gp_brush_influence_calc(gso, radius, co); @@ -480,6 +521,7 @@ static bool gp_brush_grab_store_points( /* insert this point into the set of affected points */ data->points[data->size] = pt_index; data->weights[data->size] = inf; + data->rot_eval[data->size] = rot_eval; data->size++; /* done */ @@ -503,6 +545,16 @@ static void gp_brush_grab_calc_dvec(tGP_BrushEditData *gso) mval_f[0] = (float)(gso->mval[0] - gso->mval_prev[0]); mval_f[1] = (float)(gso->mval[1] - gso->mval_prev[1]); + /* apply evaluated data transformation */ + if (gso->rot_eval != 0.0f) { + const float cval = cos(gso->rot_eval); + const float sval = sin(gso->rot_eval); + float r[2]; + r[0] = (mval_f[0] * cval) - (mval_f[1] * sval); + r[1] = (mval_f[0] * sval) + (mval_f[1] * cval); + copy_v2_v2(mval_f, r); + } + ED_view3d_win_to_delta(gso->ar, mval_f, gso->dvec, zfac); } else { @@ -527,6 +579,10 @@ static void gp_brush_grab_apply_cached(tGP_BrushEditData *gso, bGPDspoint *pt = &gps->points[data->points[i]]; float delta[3] = {0.0f}; + /* get evaluated transformation */ + gso->rot_eval = data->rot_eval[i]; + gp_brush_grab_calc_dvec(gso); + /* adjust the amount of displacement to apply */ mul_v3_v3fl(delta, gso->dvec, data->weights[i]); @@ -545,7 +601,7 @@ static void gp_brush_grab_apply_cached(tGP_BrushEditData *gso, /* compute lock axis */ gpsculpt_compute_lock_axis(gso, pt, save_pt); } - gps->flag |= GP_STROKE_RECALC_GEOMETRY; + gp_recalc_geometry(gps); } /* free customdata used for handling this stroke */ @@ -554,8 +610,9 @@ static void gp_brush_grab_stroke_free(void *ptr) tGPSB_Grab_StrokeData *data = (tGPSB_Grab_StrokeData *)ptr; /* free arrays */ - MEM_freeN(data->points); - MEM_freeN(data->weights); + MEM_SAFE_FREE(data->points); + MEM_SAFE_FREE(data->weights); + MEM_SAFE_FREE(data->rot_eval); /* ... and this item itself, since it was also allocated */ MEM_freeN(data); @@ -564,9 +621,12 @@ static void gp_brush_grab_stroke_free(void *ptr) /* ----------------------------------------------- */ /* Push Brush */ /* NOTE: Depends on gp_brush_grab_calc_dvec() */ - -static bool gp_brush_push_apply( - tGP_BrushEditData *gso, bGPDstroke *gps, int pt_index, const int radius, const int co[2]) +static bool gp_brush_push_apply(tGP_BrushEditData *gso, + bGPDstroke *gps, + float rot_eval, + int pt_index, + const int radius, + const int co[2]) { bGPDspoint *pt = gps->points + pt_index; float save_pt[3]; @@ -593,7 +653,6 @@ static bool gp_brush_push_apply( /* ----------------------------------------------- */ /* Pinch Brush */ - /* Compute reference midpoint for the brush - this is what we'll be moving towards */ static void gp_brush_calc_midpoint(tGP_BrushEditData *gso) { @@ -630,8 +689,12 @@ static void gp_brush_calc_midpoint(tGP_BrushEditData *gso) } /* Shrink distance between midpoint and this point... */ -static bool gp_brush_pinch_apply( - tGP_BrushEditData *gso, bGPDstroke *gps, int pt_index, const int radius, const int co[2]) +static bool gp_brush_pinch_apply(tGP_BrushEditData *gso, + bGPDstroke *gps, + float UNUSED(rot_eval), + int pt_index, + const int radius, + const int co[2]) { bGPDspoint *pt = gps->points + pt_index; float fac, inf; @@ -673,7 +736,7 @@ static bool gp_brush_pinch_apply( /* compute lock axis */ gpsculpt_compute_lock_axis(gso, pt, save_pt); - gps->flag |= GP_STROKE_RECALC_GEOMETRY; + gp_recalc_geometry(gps); /* done */ return true; @@ -681,13 +744,16 @@ static bool gp_brush_pinch_apply( /* ----------------------------------------------- */ /* Twist Brush - Rotate Around midpoint */ - /* Take the screenspace coordinates of the point, rotate this around the brush midpoint, * convert the rotated point and convert it into "data" space */ -static bool gp_brush_twist_apply( - tGP_BrushEditData *gso, bGPDstroke *gps, int pt_index, const int radius, const int co[2]) +static bool gp_brush_twist_apply(tGP_BrushEditData *gso, + bGPDstroke *gps, + float UNUSED(rot_eval), + int pt_index, + const int radius, + const int co[2]) { bGPDspoint *pt = gps->points + pt_index; float angle, inf; @@ -758,7 +824,7 @@ static bool gp_brush_twist_apply( } } - gps->flag |= GP_STROKE_RECALC_GEOMETRY; + gp_recalc_geometry(gps); /* done */ return true; @@ -766,10 +832,13 @@ static bool gp_brush_twist_apply( /* ----------------------------------------------- */ /* Randomize Brush */ - /* Apply some random jitter to the point */ -static bool gp_brush_randomize_apply( - tGP_BrushEditData *gso, bGPDstroke *gps, int pt_index, const int radius, const int co[2]) +static bool gp_brush_randomize_apply(tGP_BrushEditData *gso, + bGPDstroke *gps, + float UNUSED(rot_eval), + int pt_index, + const int radius, + const int co[2]) { bGPDspoint *pt = gps->points + pt_index; float save_pt[3]; @@ -812,17 +881,6 @@ static bool gp_brush_randomize_apply( mul_v2_fl(svec, fac); } -#if 0 - printf("%f %f (%f), nco = {%f %f}, co = %d %d\n", - svec[0], - svec[1], - fac, - nco[0], - nco[1], - co[0], - co[1]); -#endif - /* convert to dataspace */ if (gps->flag & GP_STROKE_3DSPACE) { /* 3D: Project to 3D space */ @@ -886,17 +944,20 @@ static bool gp_brush_randomize_apply( CLAMP(pt->uv_rot, -M_PI_2, M_PI_2); } - gps->flag |= GP_STROKE_RECALC_GEOMETRY; + gp_recalc_geometry(gps); /* done */ return true; } /* Weight Paint Brush */ - /* Change weight paint for vertex groups */ -static bool gp_brush_weight_apply( - tGP_BrushEditData *gso, bGPDstroke *gps, int pt_index, const int radius, const int co[2]) +static bool gp_brush_weight_apply(tGP_BrushEditData *gso, + bGPDstroke *gps, + float UNUSED(rot_eval), + int pt_index, + const int radius, + const int co[2]) { /* create dvert */ BKE_gpencil_dvert_ensure(gps); @@ -948,7 +1009,6 @@ static bool gp_brush_weight_apply( /* ************************************************ */ /* Non Callback-Based Brushes */ - /* Clone Brush ------------------------------------- */ /* How this brush currently works: * - If this is start of the brush stroke, paste immediately under the cursor @@ -1130,6 +1190,7 @@ static void gp_brush_clone_adjust(tGP_BrushEditData *gso) size_t snum; /* Compute the amount of movement to apply (overwrites dvec) */ + gso->rot_eval = 0.0f; gp_brush_grab_calc_dvec(gso); /* For each of the stored strokes, apply the offset to each point */ @@ -1443,6 +1504,58 @@ static void gpsculpt_brush_init_stroke(tGP_BrushEditData *gso) /* Apply ----------------------------------------------- */ +/* Get angle of the segment relative to the original segment before any transformation + * For strokes with one point only this is impossible to calculate because there isn't a + * valid reference point. + */ +static float gpsculpt_rotation_eval_get(GP_SpaceConversion *gsc, + bGPDstroke *gps_eval, + bGPDspoint *pt_eval, + int idx_eval) +{ + + bGPDstroke *gps_orig = gps_eval->runtime.gps_orig; + bGPDspoint *pt_orig = &gps_orig->points[pt_eval->runtime.idx_orig]; + bGPDspoint *pt_prev_eval = NULL; + bGPDspoint *pt_orig_prev = NULL; + if (idx_eval != 0) { + pt_prev_eval = &gps_eval->points[idx_eval - 1]; + } + else { + if (gps_eval->totpoints > 1) { + pt_prev_eval = &gps_eval->points[idx_eval + 1]; + } + else { + return 0.0f; + } + } + + if (pt_eval->runtime.idx_orig != 0) { + pt_orig_prev = &gps_orig->points[pt_eval->runtime.idx_orig - 1]; + } + else { + if (gps_orig->totpoints > 1) { + pt_orig_prev = &gps_orig->points[pt_eval->runtime.idx_orig + 1]; + } + else { + return 0.0f; + } + } + + /* create 2D vectors of the stroke segments */ + float v_orig_a[2], v_orig_b[2], v_eval_a[2], v_eval_b[2]; + + gp_point_3d_to_xy(gsc, GP_STROKE_3DSPACE, &pt_orig->x, v_orig_a); + gp_point_3d_to_xy(gsc, GP_STROKE_3DSPACE, &pt_orig_prev->x, v_orig_b); + sub_v2_v2(v_orig_a, v_orig_b); + + gp_point_3d_to_xy(gsc, GP_STROKE_3DSPACE, &pt_eval->x, v_eval_a); + gp_point_3d_to_xy(gsc, GP_STROKE_3DSPACE, &pt_prev_eval->x, v_eval_b); + sub_v2_v2(v_eval_a, v_eval_b); + + return angle_v2v2(v_orig_a, v_eval_a); +} + /* Apply brush operation to points in this stroke */ static bool gpsculpt_brush_do_stroke(tGP_BrushEditData *gso, bGPDstroke *gps, @@ -1457,14 +1570,16 @@ static bool gpsculpt_brush_do_stroke(tGP_BrushEditData *gso, gso->gp_brush->size; bGPDspoint *pt1, *pt2; + bGPDspoint *pt = NULL; int pc1[2] = {0}; int pc2[2] = {0}; int i; bool include_last = false; bool changed = false; - + float rot_eval = 0.0f; if (gps->totpoints == 1) { bGPDspoint pt_temp; + pt = &gps->points[0]; gp_point_to_parent_space(gps->points, diff_mat, &pt_temp); gp_point_to_xy(gsc, gps, &pt_temp, &pc1[0], &pc1[1]); @@ -1475,7 +1590,10 @@ static bool gpsculpt_brush_do_stroke(tGP_BrushEditData *gso, round_v2i_v2fl(mval_i, gso->mval); if (len_v2v2_int(mval_i, pc1) <= radius) { /* apply operation to this point */ - changed = apply(gso, gps, 0, radius, pc1); + if (pt->runtime.pt_orig != NULL) { + rot_eval = gpsculpt_rotation_eval_get(&gso->gsc, gps, pt, 0); + changed = apply(gso, gps->runtime.gps_orig, rot_eval, pt->runtime.idx_orig, radius, pc1); + } } } } @@ -1516,7 +1634,11 @@ static bool gpsculpt_brush_do_stroke(tGP_BrushEditData *gso, bool ok = false; /* To each point individually... */ - ok = apply(gso, gps, i, radius, pc1); + pt = &gps->points[i]; + if (pt->runtime.pt_orig != NULL) { + rot_eval = gpsculpt_rotation_eval_get(&gso->gsc, gps, pt, i); + ok = apply(gso, gps->runtime.gps_orig, rot_eval, pt->runtime.idx_orig, radius, pc1); + } /* Only do the second point if this is the last segment, * and it is unlikely that the point will get handled @@ -1527,8 +1649,12 @@ static bool gpsculpt_brush_do_stroke(tGP_BrushEditData *gso, * the line linking the points was! */ if (i + 1 == gps->totpoints - 1) { - ok |= apply(gso, gps, i + 1, radius, pc2); - include_last = false; + pt = &gps->points[i + 1]; + if (pt->runtime.pt_orig != NULL) { + rot_eval = gpsculpt_rotation_eval_get(&gso->gsc, gps, pt, i + 1); + ok |= apply(gso, gps->runtime.gps_orig, rot_eval, pt->runtime.idx_orig, radius, pc2); + include_last = false; + } } else { include_last = true; @@ -1542,8 +1668,13 @@ static bool gpsculpt_brush_do_stroke(tGP_BrushEditData *gso, * but it would've qualified since it did with the previous step * (but wasn't added then, to avoid double-ups). */ - changed |= apply(gso, gps, i, radius, pc1); - include_last = false; + pt = &gps->points[i]; + if (pt->runtime.pt_orig != NULL) { + rot_eval = gpsculpt_rotation_eval_get(&gso->gsc, gps, pt, i); + changed |= apply( + gso, gps->runtime.gps_orig, rot_eval, pt->runtime.idx_orig, radius, pc1); + include_last = false; + } } } } @@ -1590,18 +1721,20 @@ static bool gpsculpt_brush_do_frame( case GP_SCULPT_TYPE_GRAB: /* Grab points */ { - if (gso->first) { - /* First time this brush stroke is being applied: - * 1) Prepare data buffers (init/clear) for this stroke - * 2) Use the points now under the cursor - */ - gp_brush_grab_stroke_init(gso, gps); - changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_grab_store_points); - } - else { - /* Apply effect to the stored points */ - gp_brush_grab_apply_cached(gso, gps, diff_mat); - changed |= true; + if (gps->runtime.gps_orig != NULL) { + if (gso->first) { + /* First time this brush stroke is being applied: + * 1) Prepare data buffers (init/clear) for this stroke + * 2) Use the points now under the cursor + */ + gp_brush_grab_stroke_init(gso, gps->runtime.gps_orig); + changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_grab_store_points); + } + else { + /* Apply effect to the stored points */ + gp_brush_grab_apply_cached(gso, gps->runtime.gps_orig, diff_mat); + changed |= true; + } } break; } @@ -1641,10 +1774,7 @@ static bool gpsculpt_brush_do_frame( break; } /* Triangulation must be calculated if changed */ - if (changed) { - gps->flag |= GP_STROKE_RECALC_GEOMETRY; - gps->tot_triangles = 0; - } + gp_recalc_geometry(gps); } return changed; @@ -1656,6 +1786,7 @@ static bool gpsculpt_brush_apply_standard(bContext *C, tGP_BrushEditData *gso) ToolSettings *ts = CTX_data_tool_settings(C); Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); Object *obact = gso->object; + Object *ob_eval = DEG_get_evaluated_object(depsgraph, obact); bGPdata *gpd = gso->gpd; bool changed = false; @@ -1665,6 +1796,7 @@ static bool gpsculpt_brush_apply_standard(bContext *C, tGP_BrushEditData *gso) case GP_SCULPT_TYPE_PUSH: /* Push points */ { /* calculate amount of displacement to apply */ + gso->rot_eval = 0.0f; gp_brush_grab_calc_dvec(gso); break; } @@ -1680,6 +1812,7 @@ static bool gpsculpt_brush_apply_standard(bContext *C, tGP_BrushEditData *gso) case GP_SCULPT_TYPE_RANDOMIZE: /* Random jitter */ { /* compute the displacement vector for the cursor (in data space) */ + gso->rot_eval = 0.0f; gp_brush_grab_calc_dvec(gso); break; } @@ -1694,6 +1827,12 @@ static bool gpsculpt_brush_apply_standard(bContext *C, tGP_BrushEditData *gso) if (gpl->actframe == NULL) { continue; } + /* Get evaluated frames array data */ + int idx_eval = BLI_findindex(&gpd->layers, gpl); + bGPDframe *gpf_eval = &ob_eval->runtime.gpencil_evaluated_frames[idx_eval]; + if (gpf_eval == NULL) { + continue; + } /* calculate difference matrix */ float diff_mat[4][4]; @@ -1724,14 +1863,15 @@ static bool gpsculpt_brush_apply_standard(bContext *C, tGP_BrushEditData *gso) } /* affect strokes in this frame */ - changed |= gpsculpt_brush_do_frame(C, gso, gpl, gpf, diff_mat); + changed |= gpsculpt_brush_do_frame( + C, gso, gpl, (gpf == gpl->actframe) ? gpf_eval : gpf, diff_mat); } } } else { /* Apply to active frame's strokes */ gso->mf_falloff = 1.0f; - changed |= gpsculpt_brush_do_frame(C, gso, gpl, gpl->actframe, diff_mat); + changed |= gpsculpt_brush_do_frame(C, gso, gpl, gpf_eval, diff_mat); } } CTX_DATA_END;