Changeset View
Changeset View
Standalone View
Standalone View
source/blender/editors/gpencil/gpencil_brush.c
| Context not available. | |||||
| #include "BKE_library.h" | #include "BKE_library.h" | ||||
| #include "BKE_report.h" | #include "BKE_report.h" | ||||
| #include "BKE_screen.h" | #include "BKE_screen.h" | ||||
| #include "BKE_object_deform.h" | |||||
| #include "BKE_colortools.h" | |||||
| #include "UI_interface.h" | #include "UI_interface.h" | ||||
| Context not available. | |||||
| /* Current editor/region/etc. */ | /* Current editor/region/etc. */ | ||||
| /* NOTE: This stuff is mainly needed to handle 3D view projection stuff... */ | /* NOTE: This stuff is mainly needed to handle 3D view projection stuff... */ | ||||
| Scene *scene; | Scene *scene; | ||||
| Object *object; | |||||
| ScrArea *sa; | ScrArea *sa; | ||||
| ARegion *ar; | ARegion *ar; | ||||
| Context not available. | |||||
| /* Start of new sculpt stroke */ | /* Start of new sculpt stroke */ | ||||
| bool first; | bool first; | ||||
| /* Is multiframe editing enabled, and are we using falloff for that? */ | |||||
| bool is_multiframe; | |||||
| bool use_multiframe_falloff; | |||||
| /* Current frame */ | /* Current frame */ | ||||
| int cfra; | int cfra; | ||||
| Context not available. | |||||
| /* - effect vector (e.g. 2D/3D translation for grab brush) */ | /* - effect vector (e.g. 2D/3D translation for grab brush) */ | ||||
| float dvec[3]; | float dvec[3]; | ||||
| /* - multiframe falloff factor */ | |||||
| float mf_falloff; | |||||
| /* active vertex group */ | |||||
| int vrgroup; | |||||
| /* brush geometry (bounding box) */ | /* brush geometry (bounding box) */ | ||||
| rcti brush_rect; | rcti brush_rect; | ||||
| Context not available. | |||||
| /* Callback for performing some brush operation on a single point */ | /* Callback for performing some brush operation on a single point */ | ||||
| typedef bool (*GP_BrushApplyCb)(tGP_BrushEditData *gso, bGPDstroke *gps, int i, | typedef bool (*GP_BrushApplyCb)(tGP_BrushEditData *gso, bGPDstroke *gps, int pt_index, | ||||
| const int radius, const int co[2]); | const int radius, const int co[2]); | ||||
| /* ************************************************ */ | /* ************************************************ */ | ||||
| /* Utility Functions */ | /* Utility Functions */ | ||||
| /* apply lock axis reset */ | |||||
| static void gpsculpt_compute_lock_axis(tGP_BrushEditData *gso, bGPDspoint *pt, const float save_pt[3]) | |||||
| { | |||||
| if (gso->sa->spacetype != SPACE_VIEW3D) { | |||||
| return; | |||||
| } | |||||
| ToolSettings *ts = gso->scene->toolsettings; | |||||
| int axis = ts->gp_sculpt.lock_axis; | |||||
| /* lock axis control */ | |||||
| if (axis == 1) { | |||||
| pt->x = save_pt[0]; | |||||
| } | |||||
| if (axis == 2) { | |||||
| pt->y = save_pt[1]; | |||||
| } | |||||
| if (axis == 3) { | |||||
| pt->z = save_pt[2]; | |||||
| } | |||||
| } | |||||
| /* Context ---------------------------------------- */ | /* Context ---------------------------------------- */ | ||||
| /* Get the sculpting settings */ | /* Get the sculpting settings */ | ||||
| Context not available. | |||||
| } | } | ||||
| /* Get the active brush */ | /* Get the active brush */ | ||||
| static GP_EditBrush_Data *gpsculpt_get_brush(Scene *scene) | static GP_EditBrush_Data *gpsculpt_get_brush(Scene *scene, bool is_weight_mode) | ||||
| { | { | ||||
| GP_BrushEdit_Settings *gset = &scene->toolsettings->gp_sculpt; | GP_BrushEdit_Settings *gset = &scene->toolsettings->gp_sculpt; | ||||
| return &gset->brush[gset->brushtype]; | GP_EditBrush_Data *brush = NULL; | ||||
| if (is_weight_mode) { | |||||
| brush = &gset->brush[gset->weighttype]; | |||||
| } | |||||
| else { | |||||
| brush = &gset->brush[gset->brushtype]; | |||||
| } | |||||
| return brush; | |||||
| } | } | ||||
| /* Brush Operations ------------------------------- */ | /* Brush Operations ------------------------------- */ | ||||
| Context not available. | |||||
| invert ^= true; | invert ^= true; | ||||
| } | } | ||||
| /* set temporary status */ | |||||
| if (invert) { | |||||
| gso->brush->flag |= GP_EDITBRUSH_FLAG_TMP_INVERT; | |||||
| } | |||||
| else { | |||||
| gso->brush->flag &= ~GP_EDITBRUSH_FLAG_TMP_INVERT; | |||||
| } | |||||
| return invert; | return invert; | ||||
| } | } | ||||
| Context not available. | |||||
| influence *= fac; | influence *= fac; | ||||
| } | } | ||||
| /* apply multiframe falloff */ | |||||
| influence *= gso->mf_falloff; | |||||
| /* return influence */ | /* return influence */ | ||||
| return influence; | return influence; | ||||
| } | } | ||||
| Context not available. | |||||
| /* Smooth Brush */ | /* Smooth Brush */ | ||||
| /* A simple (but slower + inaccurate) smooth-brush implementation to test the algorithm for stroke smoothing */ | /* 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 i, | static bool gp_brush_smooth_apply( | ||||
| const int radius, const int co[2]) | tGP_BrushEditData *gso, bGPDstroke *gps, int pt_index, | ||||
| const int radius, const int co[2]) | |||||
| { | { | ||||
| GP_EditBrush_Data *brush = gso->brush; | GP_EditBrush_Data *brush = gso->brush; | ||||
| float inf = gp_brush_influence_calc(gso, radius, co); | float inf = gp_brush_influence_calc(gso, radius, co); | ||||
| bool affect_pressure = (brush->flag & GP_EDITBRUSH_FLAG_SMOOTH_PRESSURE) != 0; | |||||
| /* need one flag enabled by default */ | /* need one flag enabled by default */ | ||||
| if ((gso->settings->flag & (GP_BRUSHEDIT_FLAG_APPLY_POSITION | | if ((gso->settings->flag & (GP_BRUSHEDIT_FLAG_APPLY_POSITION | | ||||
| GP_BRUSHEDIT_FLAG_APPLY_STRENGTH | | GP_BRUSHEDIT_FLAG_APPLY_STRENGTH | | ||||
| GP_BRUSHEDIT_FLAG_APPLY_THICKNESS)) == 0) | GP_BRUSHEDIT_FLAG_APPLY_THICKNESS | | ||||
| GP_BRUSHEDIT_FLAG_APPLY_UV)) == 0) | |||||
| { | { | ||||
| gso->settings->flag |= GP_BRUSHEDIT_FLAG_APPLY_POSITION; | gso->settings->flag |= GP_BRUSHEDIT_FLAG_APPLY_POSITION; | ||||
| } | } | ||||
| /* perform smoothing */ | /* perform smoothing */ | ||||
| if (gso->settings->flag & GP_BRUSHEDIT_FLAG_APPLY_POSITION) { | if (gso->settings->flag & GP_BRUSHEDIT_FLAG_APPLY_POSITION) { | ||||
| gp_smooth_stroke(gps, i, inf, affect_pressure); | BKE_gp_smooth_stroke(gps, pt_index, inf); | ||||
| } | } | ||||
| if (gso->settings->flag & GP_BRUSHEDIT_FLAG_APPLY_STRENGTH) { | if (gso->settings->flag & GP_BRUSHEDIT_FLAG_APPLY_STRENGTH) { | ||||
| gp_smooth_stroke_strength(gps, i, inf); | BKE_gp_smooth_stroke_strength(gps, pt_index, inf); | ||||
| } | } | ||||
| if (gso->settings->flag & GP_BRUSHEDIT_FLAG_APPLY_THICKNESS) { | if (gso->settings->flag & GP_BRUSHEDIT_FLAG_APPLY_THICKNESS) { | ||||
| gp_smooth_stroke_thickness(gps, i, inf); | BKE_gp_smooth_stroke_thickness(gps, pt_index, inf); | ||||
| } | } | ||||
| if (gso->settings->flag & GP_BRUSHEDIT_FLAG_APPLY_UV) { | |||||
| BKE_gp_smooth_stroke_uv(gps, pt_index, inf); | |||||
| } | |||||
| return true; | return true; | ||||
| } | } | ||||
| Context not available. | |||||
| /* Line Thickness Brush */ | /* Line Thickness Brush */ | ||||
| /* Make lines thicker or thinner by the specified amounts */ | /* Make lines thicker or thinner by the specified amounts */ | ||||
| static bool gp_brush_thickness_apply(tGP_BrushEditData *gso, bGPDstroke *gps, int i, | static bool gp_brush_thickness_apply( | ||||
| const int radius, const int co[2]) | tGP_BrushEditData *gso, bGPDstroke *gps, int pt_index, | ||||
| const int radius, const int co[2]) | |||||
| { | { | ||||
| bGPDspoint *pt = gps->points + i; | bGPDspoint *pt = gps->points + pt_index; | ||||
| float inf; | float inf; | ||||
| /* Compute strength of effect | /* Compute strength of effect | ||||
| Context not available. | |||||
| /* Make color more or less transparent by the specified amounts */ | /* Make color more or less transparent by the specified amounts */ | ||||
| static bool gp_brush_strength_apply( | static bool gp_brush_strength_apply( | ||||
| tGP_BrushEditData *gso, bGPDstroke *gps, int i, | tGP_BrushEditData *gso, bGPDstroke *gps, int pt_index, | ||||
| const int radius, const int co[2]) | const int radius, const int co[2]) | ||||
| { | { | ||||
| bGPDspoint *pt = gps->points + i; | bGPDspoint *pt = gps->points + pt_index; | ||||
| float inf; | float inf; | ||||
| /* Compute strength of effect | /* Compute strength of effect | ||||
| Context not available. | |||||
| } | } | ||||
| /* store references to stroke points in the initial stage */ | /* store references to stroke points in the initial stage */ | ||||
| static bool gp_brush_grab_store_points(tGP_BrushEditData *gso, bGPDstroke *gps, int i, | static bool gp_brush_grab_store_points( | ||||
| const int radius, const int co[2]) | tGP_BrushEditData *gso, bGPDstroke *gps, int pt_index, | ||||
| const int radius, const int co[2]) | |||||
| { | { | ||||
| tGPSB_Grab_StrokeData *data = BLI_ghash_lookup(gso->stroke_customdata, gps); | tGPSB_Grab_StrokeData *data = BLI_ghash_lookup(gso->stroke_customdata, gps); | ||||
| float inf = gp_brush_influence_calc(gso, radius, co); | float inf = gp_brush_influence_calc(gso, radius, co); | ||||
| Context not available. | |||||
| BLI_assert(data->size < data->capacity); | BLI_assert(data->size < data->capacity); | ||||
| /* insert this point into the set of affected points */ | /* insert this point into the set of affected points */ | ||||
| data->points[data->size] = i; | data->points[data->size] = pt_index; | ||||
| data->weights[data->size] = inf; | data->weights[data->size] = inf; | ||||
| data->size++; | data->size++; | ||||
| Context not available. | |||||
| /* Apply grab transform to all relevant points of the affected strokes */ | /* Apply grab transform to all relevant points of the affected strokes */ | ||||
| static void gp_brush_grab_apply_cached( | static void gp_brush_grab_apply_cached( | ||||
| tGP_BrushEditData *gso, bGPDstroke *gps, bool parented, float diff_mat[4][4]) | tGP_BrushEditData *gso, bGPDstroke *gps, float diff_mat[4][4]) | ||||
| { | { | ||||
| tGPSB_Grab_StrokeData *data = BLI_ghash_lookup(gso->stroke_customdata, gps); | tGPSB_Grab_StrokeData *data = BLI_ghash_lookup(gso->stroke_customdata, gps); | ||||
| int i; | int i; | ||||
| Context not available. | |||||
| /* adjust the amount of displacement to apply */ | /* adjust the amount of displacement to apply */ | ||||
| mul_v3_v3fl(delta, gso->dvec, data->weights[i]); | mul_v3_v3fl(delta, gso->dvec, data->weights[i]); | ||||
| if (!parented) { | |||||
| /* apply */ | float fpt[3]; | ||||
| add_v3_v3(&pt->x, delta); | float save_pt[3]; | ||||
| } | copy_v3_v3(save_pt, &pt->x); | ||||
| else { | /* apply transformation */ | ||||
| float fpt[3]; | mul_v3_m4v3(fpt, diff_mat, &pt->x); | ||||
| /* apply transformation */ | /* apply */ | ||||
| mul_v3_m4v3(fpt, diff_mat, &pt->x); | add_v3_v3v3(&pt->x, fpt, delta); | ||||
| /* apply */ | /* undo transformation to the init parent position */ | ||||
| add_v3_v3(fpt, delta); | float inverse_diff_mat[4][4]; | ||||
| copy_v3_v3(&pt->x, fpt); | invert_m4_m4(inverse_diff_mat, diff_mat); | ||||
| /* undo transformation to the init parent position */ | mul_m4_v3(inverse_diff_mat, &pt->x); | ||||
| float inverse_diff_mat[4][4]; | |||||
| invert_m4_m4(inverse_diff_mat, diff_mat); | /* compute lock axis */ | ||||
| mul_m4_v3(inverse_diff_mat, &pt->x); | gpsculpt_compute_lock_axis(gso, pt, save_pt); | ||||
| } | |||||
| } | } | ||||
| } | } | ||||
| Context not available. | |||||
| /* Push Brush */ | /* Push Brush */ | ||||
| /* NOTE: Depends on gp_brush_grab_calc_dvec() */ | /* NOTE: Depends on gp_brush_grab_calc_dvec() */ | ||||
| static bool gp_brush_push_apply(tGP_BrushEditData *gso, bGPDstroke *gps, int i, | static bool gp_brush_push_apply( | ||||
| const int radius, const int co[2]) | tGP_BrushEditData *gso, bGPDstroke *gps, int pt_index, | ||||
| const int radius, const int co[2]) | |||||
| { | { | ||||
| bGPDspoint *pt = gps->points + i; | bGPDspoint *pt = gps->points + pt_index; | ||||
| float save_pt[3]; | |||||
| copy_v3_v3(save_pt, &pt->x); | |||||
| float inf = gp_brush_influence_calc(gso, radius, co); | float inf = gp_brush_influence_calc(gso, radius, co); | ||||
| float delta[3] = {0.0f}; | float delta[3] = {0.0f}; | ||||
| Context not available. | |||||
| /* apply */ | /* apply */ | ||||
| add_v3_v3(&pt->x, delta); | add_v3_v3(&pt->x, delta); | ||||
| /* compute lock axis */ | |||||
| gpsculpt_compute_lock_axis(gso, pt, save_pt); | |||||
| /* done */ | /* done */ | ||||
| return true; | return true; | ||||
| } | } | ||||
| Context not available. | |||||
| float *rvec = ED_view3d_cursor3d_get(gso->scene, v3d); | float *rvec = ED_view3d_cursor3d_get(gso->scene, v3d); | ||||
| float zfac = ED_view3d_calc_zfac(rv3d, rvec, NULL); | float zfac = ED_view3d_calc_zfac(rv3d, rvec, NULL); | ||||
| float mval_f[2] = {UNPACK2(gso->mval)}; | float mval_f[2]; | ||||
| copy_v2fl_v2i(mval_f, gso->mval); | |||||
| float mval_prj[2]; | float mval_prj[2]; | ||||
| float dvec[3]; | float dvec[3]; | ||||
| Context not available. | |||||
| } | } | ||||
| /* Shrink distance between midpoint and this point... */ | /* Shrink distance between midpoint and this point... */ | ||||
| static bool gp_brush_pinch_apply(tGP_BrushEditData *gso, bGPDstroke *gps, int i, | static bool gp_brush_pinch_apply( | ||||
| const int radius, const int co[2]) | tGP_BrushEditData *gso, bGPDstroke *gps, int pt_index, | ||||
| const int radius, const int co[2]) | |||||
| { | { | ||||
| bGPDspoint *pt = gps->points + i; | bGPDspoint *pt = gps->points + pt_index; | ||||
| float fac, inf; | float fac, inf; | ||||
| float vec[3]; | float vec[3]; | ||||
| float save_pt[3]; | |||||
| copy_v3_v3(save_pt, &pt->x); | |||||
| /* Scale down standard influence value to get it more manageable... | /* Scale down standard influence value to get it more manageable... | ||||
| * - No damping = Unmanageable at > 0.5 strength | * - No damping = Unmanageable at > 0.5 strength | ||||
| * - Div 10 = Not enough effect | * - Div 10 = Not enough effect | ||||
| Context not available. | |||||
| /* 3) Translate back to original space, with the shrinkage applied */ | /* 3) Translate back to original space, with the shrinkage applied */ | ||||
| add_v3_v3v3(&pt->x, gso->dvec, vec); | add_v3_v3v3(&pt->x, gso->dvec, vec); | ||||
| /* compute lock axis */ | |||||
| gpsculpt_compute_lock_axis(gso, pt, save_pt); | |||||
| /* done */ | /* done */ | ||||
| return true; | return true; | ||||
| } | } | ||||
| Context not available. | |||||
| * convert the rotated point and convert it into "data" space | * convert the rotated point and convert it into "data" space | ||||
| */ | */ | ||||
| static bool gp_brush_twist_apply(tGP_BrushEditData *gso, bGPDstroke *gps, int i, | static bool gp_brush_twist_apply( | ||||
| const int radius, const int co[2]) | tGP_BrushEditData *gso, bGPDstroke *gps, int pt_index, | ||||
| const int radius, const int co[2]) | |||||
| { | { | ||||
| bGPDspoint *pt = gps->points + i; | bGPDspoint *pt = gps->points + pt_index; | ||||
| float angle, inf; | float angle, inf; | ||||
| float save_pt[3]; | |||||
| copy_v3_v3(save_pt, &pt->x); | |||||
| /* Angle to rotate by */ | /* Angle to rotate by */ | ||||
| inf = gp_brush_influence_calc(gso, radius, co); | inf = gp_brush_influence_calc(gso, radius, co); | ||||
| angle = DEG2RADF(1.0f) * inf; | angle = DEG2RADF(1.0f) * inf; | ||||
| Context not available. | |||||
| sub_v3_v3v3(vec, &pt->x, gso->dvec); /* make relative to center (center is stored in dvec) */ | sub_v3_v3v3(vec, &pt->x, gso->dvec); /* make relative to center (center is stored in dvec) */ | ||||
| mul_m3_v3(rmat, vec); | mul_m3_v3(rmat, vec); | ||||
| add_v3_v3v3(&pt->x, vec, gso->dvec); /* restore */ | add_v3_v3v3(&pt->x, vec, gso->dvec); /* restore */ | ||||
| /* compute lock axis */ | |||||
| gpsculpt_compute_lock_axis(gso, pt, save_pt); | |||||
| } | } | ||||
| else { | else { | ||||
| const float axis[3] = {0.0f, 0.0f, 1.0f}; | const float axis[3] = {0.0f, 0.0f, 1.0f}; | ||||
| Context not available. | |||||
| /* Randomize Brush */ | /* Randomize Brush */ | ||||
| /* Apply some random jitter to the point */ | /* Apply some random jitter to the point */ | ||||
| static bool gp_brush_randomize_apply(tGP_BrushEditData *gso, bGPDstroke *gps, int i, | static bool gp_brush_randomize_apply( | ||||
| const int radius, const int co[2]) | tGP_BrushEditData *gso, bGPDstroke *gps, int pt_index, | ||||
| const int radius, const int co[2]) | |||||
| { | { | ||||
| bGPDspoint *pt = gps->points + i; | bGPDspoint *pt = gps->points + pt_index; | ||||
| float save_pt[3]; | |||||
| copy_v3_v3(save_pt, &pt->x); | |||||
| /* Amount of jitter to apply depends on the distance of the point to the cursor, | /* Amount of jitter to apply depends on the distance of the point to the cursor, | ||||
| * as well as the strength of the brush | * as well as the strength of the brush | ||||
| */ | */ | ||||
| Context not available. | |||||
| /* need one flag enabled by default */ | /* need one flag enabled by default */ | ||||
| if ((gso->settings->flag & (GP_BRUSHEDIT_FLAG_APPLY_POSITION | | if ((gso->settings->flag & (GP_BRUSHEDIT_FLAG_APPLY_POSITION | | ||||
| GP_BRUSHEDIT_FLAG_APPLY_STRENGTH | | GP_BRUSHEDIT_FLAG_APPLY_STRENGTH | | ||||
| GP_BRUSHEDIT_FLAG_APPLY_THICKNESS)) == 0) | GP_BRUSHEDIT_FLAG_APPLY_THICKNESS | | ||||
| GP_BRUSHEDIT_FLAG_APPLY_UV)) == 0) | |||||
| { | { | ||||
| gso->settings->flag |= GP_BRUSHEDIT_FLAG_APPLY_POSITION; | gso->settings->flag |= GP_BRUSHEDIT_FLAG_APPLY_POSITION; | ||||
| } | } | ||||
| Context not available. | |||||
| float dvec[3]; | float dvec[3]; | ||||
| ED_view3d_win_to_delta(gso->gsc.ar, svec, dvec, zfac); | ED_view3d_win_to_delta(gso->gsc.ar, svec, dvec, zfac); | ||||
| add_v3_v3(&pt->x, dvec); | add_v3_v3(&pt->x, dvec); | ||||
| /* compute lock axis */ | |||||
| gpsculpt_compute_lock_axis(gso, pt, save_pt); | |||||
| } | } | ||||
| } | } | ||||
| else { | else { | ||||
| Context not available. | |||||
| /* only limit lower value */ | /* only limit lower value */ | ||||
| CLAMP_MIN(pt->pressure, 0.0f); | CLAMP_MIN(pt->pressure, 0.0f); | ||||
| } | } | ||||
| /* apply random to UV (use pressure) */ | |||||
| if (gso->settings->flag & GP_BRUSHEDIT_FLAG_APPLY_UV) { | |||||
| if (BLI_frand() > 0.5f) { | |||||
| pt->uv_rot += fac; | |||||
| } | |||||
| else { | |||||
| pt->uv_rot -= fac; | |||||
| } | |||||
| CLAMP(pt->uv_rot, -M_PI_2, M_PI_2); | |||||
| } | |||||
| /* done */ | /* done */ | ||||
| return true; | 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]) | |||||
| { | |||||
| bGPDspoint *pt = gps->points + pt_index; | |||||
| float inf; | |||||
| /* Compute strength of effect | |||||
| * - We divide the strength by 10, so that users can set "sane" values. | |||||
| * Otherwise, good default values are in the range of 0.093 | |||||
| */ | |||||
| inf = gp_brush_influence_calc(gso, radius, co) / 10.0f; | |||||
| /* need a vertex group */ | |||||
| if (gso->vrgroup == -1) { | |||||
| if (gso->object) { | |||||
| BKE_object_defgroup_add(gso->object); | |||||
| gso->vrgroup = 0; | |||||
| } | |||||
| } | |||||
| /* get current weight */ | |||||
| float curweight = 0.0f; | |||||
| for (int i = 0; i < pt->totweight; ++i) { | |||||
| bGPDweight *gpw = &pt->weights[i]; | |||||
| if (gpw->index == gso->vrgroup) { | |||||
| curweight = gpw->factor; | |||||
| break; | |||||
| } | |||||
| } | |||||
| if (gp_brush_invert_check(gso)) { | |||||
| /* reduce weight */ | |||||
| curweight -= inf; | |||||
| } | |||||
| else { | |||||
| /* increase weight */ | |||||
| curweight += inf; | |||||
| } | |||||
| CLAMP(curweight, 0.0f, 1.0f); | |||||
| BKE_gpencil_vgroup_add_point_weight(pt, gso->vrgroup, curweight); | |||||
| /* weight should stay within [0.0, 1.0] */ | |||||
| if (pt->pressure < 0.0f) | |||||
| pt->pressure = 0.0f; | |||||
| return true; | |||||
| } | |||||
| /* ************************************************ */ | /* ************************************************ */ | ||||
| /* Non Callback-Based Brushes */ | /* Non Callback-Based Brushes */ | ||||
| Context not available. | |||||
| /* Init colormap for mapping between the pasted stroke's source colour(names) | /* Init colormap for mapping between the pasted stroke's source colour(names) | ||||
| * and the final colours that will be used here instead... | * and the final colours that will be used here instead... | ||||
| */ | */ | ||||
| data->new_colors = gp_copybuf_validate_colormap(gso->gpd); | data->new_colors = gp_copybuf_validate_colormap(C); | ||||
| } | } | ||||
| /* Free custom data used for "clone" brush */ | /* Free custom data used for "clone" brush */ | ||||
| Context not available. | |||||
| new_stroke = MEM_dupallocN(gps); | new_stroke = MEM_dupallocN(gps); | ||||
| new_stroke->points = MEM_dupallocN(gps->points); | new_stroke->points = MEM_dupallocN(gps->points); | ||||
| BKE_gpencil_stroke_weights_duplicate(gps, new_stroke); | |||||
| new_stroke->triangles = MEM_dupallocN(gps->triangles); | new_stroke->triangles = MEM_dupallocN(gps->triangles); | ||||
| new_stroke->next = new_stroke->prev = NULL; | new_stroke->next = new_stroke->prev = NULL; | ||||
| Context not available. | |||||
| return true; | return true; | ||||
| } | } | ||||
| /* ************************************************ */ | |||||
| /* Cursor drawing */ | |||||
| /* Helper callback for drawing the cursor itself */ | |||||
| static void gp_brush_drawcursor(bContext *C, int x, int y, void *UNUSED(customdata)) | |||||
| { | |||||
| GP_EditBrush_Data *brush = gpsculpt_get_brush(CTX_data_scene(C)); | |||||
| if (brush) { | |||||
| Gwn_VertFormat *format = immVertexFormat(); | |||||
| unsigned int pos = GWN_vertformat_attr_add(format, "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT); | |||||
| immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); | |||||
| glEnable(GL_LINE_SMOOTH); | |||||
| glEnable(GL_BLEND); | |||||
| /* Inner Ring: Light color for action of the brush */ | |||||
| /* TODO: toggle between add and remove? */ | |||||
| immUniformColor4ub(255, 255, 255, 200); | |||||
| imm_draw_circle_wire_2d(pos, x, y, brush->size, 40); | |||||
| /* Outer Ring: Dark color for contrast on light backgrounds (e.g. gray on white) */ | |||||
| immUniformColor3ub(30, 30, 30); | |||||
| imm_draw_circle_wire_2d(pos, x, y, brush->size + 1, 40); | |||||
| immUnbindProgram(); | |||||
| glDisable(GL_BLEND); | |||||
| glDisable(GL_LINE_SMOOTH); | |||||
| } | |||||
| } | |||||
| /* Turn brush cursor in on/off */ | |||||
| static void gpencil_toggle_brush_cursor(bContext *C, bool enable) | |||||
| { | |||||
| GP_BrushEdit_Settings *gset = gpsculpt_get_settings(CTX_data_scene(C)); | |||||
| if (gset->paintcursor && !enable) { | |||||
| /* clear cursor */ | |||||
| WM_paint_cursor_end(CTX_wm_manager(C), gset->paintcursor); | |||||
| gset->paintcursor = NULL; | |||||
| } | |||||
| else if (enable) { | |||||
| /* enable cursor */ | |||||
| gset->paintcursor = WM_paint_cursor_activate(CTX_wm_manager(C), | |||||
| NULL, | |||||
| gp_brush_drawcursor, NULL); | |||||
| } | |||||
| } | |||||
| /* ************************************************ */ | /* ************************************************ */ | ||||
| /* Header Info for GPencil Sculpt */ | /* Header Info for GPencil Sculpt */ | ||||
| Context not available. | |||||
| static bool gpsculpt_brush_init(bContext *C, wmOperator *op) | static bool gpsculpt_brush_init(bContext *C, wmOperator *op) | ||||
| { | { | ||||
| Scene *scene = CTX_data_scene(C); | Scene *scene = CTX_data_scene(C); | ||||
| ToolSettings *ts = CTX_data_tool_settings(C); | |||||
| Object *ob = CTX_data_active_object(C); | |||||
| WorkSpace *workspace = CTX_wm_workspace(C); | |||||
| const bool is_weight_mode = (workspace->object_mode == OB_MODE_GPENCIL_WEIGHT); | |||||
| tGP_BrushEditData *gso; | tGP_BrushEditData *gso; | ||||
| /* setup operator data */ | /* setup operator data */ | ||||
| Context not available. | |||||
| /* store state */ | /* store state */ | ||||
| gso->settings = gpsculpt_get_settings(scene); | gso->settings = gpsculpt_get_settings(scene); | ||||
| gso->brush = gpsculpt_get_brush(scene); | gso->brush = gpsculpt_get_brush(scene, is_weight_mode); | ||||
| gso->brush_type = gso->settings->brushtype; | |||||
| if (is_weight_mode) { | |||||
| gso->brush_type = gso->settings->weighttype; | |||||
| } | |||||
| else { | |||||
| gso->brush_type = gso->settings->brushtype; | |||||
| } | |||||
| gso->is_painting = false; | gso->is_painting = false; | ||||
| gso->first = true; | gso->first = true; | ||||
| Context not available. | |||||
| gso->cfra = INT_MAX; /* NOTE: So that first stroke will get handled in init_stroke() */ | gso->cfra = INT_MAX; /* NOTE: So that first stroke will get handled in init_stroke() */ | ||||
| gso->scene = scene; | gso->scene = scene; | ||||
| gso->object = ob; | |||||
| if (ob) { | |||||
| gso->vrgroup = ob->actdef - 1; | |||||
| if (!BLI_findlink(&ob->defbase, gso->vrgroup)) { | |||||
| gso->vrgroup = -1; | |||||
| } | |||||
| } | |||||
| else { | |||||
| gso->vrgroup = - 1; | |||||
| } | |||||
| gso->sa = CTX_wm_area(C); | gso->sa = CTX_wm_area(C); | ||||
| gso->ar = CTX_wm_region(C); | gso->ar = CTX_wm_region(C); | ||||
| /* multiframe settings */ | |||||
| gso->is_multiframe = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gso->gpd); | |||||
| gso->use_multiframe_falloff = (ts->gp_sculpt.flag & GP_BRUSHEDIT_FLAG_FRAME_FALLOFF) != 0; | |||||
| /* init multiedit falloff curve data before doing anything, | |||||
| * so we won't have to do it again later | |||||
| */ | |||||
| if (gso->is_multiframe) { | |||||
| curvemapping_initialize(ts->gp_sculpt.cur_falloff); | |||||
| } | |||||
| /* initialise custom data for brushes */ | /* initialise custom data for brushes */ | ||||
| switch (gso->brush_type) { | switch (gso->brush_type) { | ||||
| case GP_EDITBRUSH_TYPE_CLONE: | case GP_EDITBRUSH_TYPE_CLONE: | ||||
| Context not available. | |||||
| gpsculpt_brush_header_set(C, gso); | gpsculpt_brush_header_set(C, gso); | ||||
| /* setup cursor drawing */ | /* setup cursor drawing */ | ||||
| WM_cursor_modal_set(CTX_wm_window(C), BC_CROSSCURSOR); | //WM_cursor_modal_set(CTX_wm_window(C), BC_CROSSCURSOR); | ||||
| gpencil_toggle_brush_cursor(C, true); | if (gso->sa->spacetype != SPACE_VIEW3D) { | ||||
| ED_gpencil_toggle_brush_cursor(C, true, NULL); | |||||
| } | |||||
| return true; | return true; | ||||
| } | } | ||||
| Context not available. | |||||
| /* disable cursor and headerprints */ | /* disable cursor and headerprints */ | ||||
| ED_area_headerprint(CTX_wm_area(C), NULL); | ED_area_headerprint(CTX_wm_area(C), NULL); | ||||
| WM_cursor_modal_restore(win); | WM_cursor_modal_restore(win); | ||||
| gpencil_toggle_brush_cursor(C, false); | if (gso->sa->spacetype != SPACE_VIEW3D) { | ||||
| ED_gpencil_toggle_brush_cursor(C, false, NULL); | |||||
| } | |||||
| /* disable temp invert flag */ | |||||
| gso->brush->flag &= ~GP_EDITBRUSH_FLAG_TMP_INVERT; | |||||
| /* free operator data */ | /* free operator data */ | ||||
| MEM_freeN(gso); | MEM_freeN(gso); | ||||
| op->customdata = NULL; | op->customdata = NULL; | ||||
| Context not available. | |||||
| /* Apply brush operation to points in this stroke */ | /* Apply brush operation to points in this stroke */ | ||||
| static bool gpsculpt_brush_do_stroke( | static bool gpsculpt_brush_do_stroke( | ||||
| tGP_BrushEditData *gso, bGPDstroke *gps, bool parented, | tGP_BrushEditData *gso, bGPDstroke *gps, | ||||
| float diff_mat[4][4], GP_BrushApplyCb apply) | float diff_mat[4][4], GP_BrushApplyCb apply) | ||||
| { | { | ||||
| GP_SpaceConversion *gsc = &gso->gsc; | GP_SpaceConversion *gsc = &gso->gsc; | ||||
| Context not available. | |||||
| bool changed = false; | bool changed = false; | ||||
| if (gps->totpoints == 1) { | if (gps->totpoints == 1) { | ||||
| if (!parented) { | bGPDspoint pt_temp; | ||||
| gp_point_to_xy(gsc, gps, gps->points, &pc1[0], &pc1[1]); | gp_point_to_parent_space(gps->points, diff_mat, &pt_temp); | ||||
| } | gp_point_to_xy(gsc, gps, &pt_temp, &pc1[0], &pc1[1]); | ||||
| else { | |||||
| bGPDspoint pt_temp; | |||||
| gp_point_to_parent_space(gps->points, diff_mat, &pt_temp); | |||||
| gp_point_to_xy(gsc, gps, &pt_temp, &pc1[0], &pc1[1]); | |||||
| } | |||||
| /* do boundbox check first */ | /* do boundbox check first */ | ||||
| if ((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1])) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) { | if ((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1])) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) { | ||||
| Context not available. | |||||
| continue; | continue; | ||||
| } | } | ||||
| } | } | ||||
| if (!parented) { | bGPDspoint npt; | ||||
| gp_point_to_xy(gsc, gps, pt1, &pc1[0], &pc1[1]); | gp_point_to_parent_space(pt1, diff_mat, &npt); | ||||
| gp_point_to_xy(gsc, gps, pt2, &pc2[0], &pc2[1]); | gp_point_to_xy(gsc, gps, &npt, &pc1[0], &pc1[1]); | ||||
| } | |||||
| else { | |||||
| bGPDspoint npt; | |||||
| gp_point_to_parent_space(pt1, diff_mat, &npt); | |||||
| gp_point_to_xy(gsc, gps, &npt, &pc1[0], &pc1[1]); | |||||
| gp_point_to_parent_space(pt2, diff_mat, &npt); | |||||
| gp_point_to_xy(gsc, gps, &npt, &pc2[0], &pc2[1]); | |||||
| } | |||||
| gp_point_to_parent_space(pt2, diff_mat, &npt); | |||||
| gp_point_to_xy(gsc, gps, &npt, &pc2[0], &pc2[1]); | |||||
| /* Check that point segment of the boundbox of the selection stroke */ | /* Check that point segment of the boundbox of the selection stroke */ | ||||
| if (((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1])) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) || | if (((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1])) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) || | ||||
| Context not available. | |||||
| return changed; | return changed; | ||||
| } | } | ||||
| /* Apply sculpt brushes to strokes in the given frame */ | |||||
| static bool gpsculpt_brush_do_frame( | |||||
| bContext *C, tGP_BrushEditData *gso, | |||||
| bGPDlayer *gpl, bGPDframe *gpf, | |||||
| float diff_mat[4][4]) | |||||
| { | |||||
| bool changed = false; | |||||
| 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; | |||||
| } | |||||
| /* check if the color is editable */ | |||||
| if (ED_gpencil_stroke_color_use(gpl, gps) == false) { | |||||
| continue; | |||||
| } | |||||
| switch (gso->brush_type) { | |||||
| case GP_EDITBRUSH_TYPE_SMOOTH: /* Smooth strokes */ | |||||
| { | |||||
| changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_smooth_apply); | |||||
| break; | |||||
| } | |||||
| case GP_EDITBRUSH_TYPE_THICKNESS: /* Adjust stroke thickness */ | |||||
| { | |||||
| changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_thickness_apply); | |||||
| break; | |||||
| } | |||||
| case GP_EDITBRUSH_TYPE_STRENGTH: /* Adjust stroke color strength */ | |||||
| { | |||||
| changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_strength_apply); | |||||
| break; | |||||
| } | |||||
| case GP_EDITBRUSH_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; | |||||
| } | |||||
| break; | |||||
| } | |||||
| case GP_EDITBRUSH_TYPE_PUSH: /* Push points */ | |||||
| { | |||||
| changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_push_apply); | |||||
| break; | |||||
| } | |||||
| case GP_EDITBRUSH_TYPE_PINCH: /* Pinch points */ | |||||
| { | |||||
| changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_pinch_apply); | |||||
| break; | |||||
| } | |||||
| case GP_EDITBRUSH_TYPE_TWIST: /* Twist points around midpoint */ | |||||
| { | |||||
| changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_twist_apply); | |||||
| break; | |||||
| } | |||||
| case GP_EDITBRUSH_TYPE_RANDOMIZE: /* Apply jitter */ | |||||
| { | |||||
| changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_randomize_apply); | |||||
| break; | |||||
| } | |||||
| case GP_EDITBRUSH_TYPE_WEIGHT: /* Adjust vertex group weight */ | |||||
| { | |||||
| changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_weight_apply); | |||||
| break; | |||||
| } | |||||
| default: | |||||
| printf("ERROR: Unknown type of GPencil Sculpt brush - %u\n", gso->brush_type); | |||||
| break; | |||||
| } | |||||
| /* Triangulation must be calculated if changed */ | |||||
| if (changed) { | |||||
| gps->flag |= GP_STROKE_RECALC_CACHES; | |||||
| gps->tot_triangles = 0; | |||||
| } | |||||
| } | |||||
| return changed; | |||||
| } | |||||
| /* Perform two-pass brushes which modify the existing strokes */ | /* Perform two-pass brushes which modify the existing strokes */ | ||||
| static bool gpsculpt_brush_apply_standard(bContext *C, tGP_BrushEditData *gso) | static bool gpsculpt_brush_apply_standard(bContext *C, tGP_BrushEditData *gso) | ||||
| { | { | ||||
| ToolSettings *ts = CTX_data_tool_settings(C); | |||||
| Object *obact = gso->object; | |||||
| bGPdata *gpd = gso->gpd; | |||||
| bool changed = false; | bool changed = false; | ||||
| /* Calculate brush-specific data which applies equally to all points */ | /* Calculate brush-specific data which applies equally to all points */ | ||||
| Context not available. | |||||
| /* Find visible strokes, and perform operations on those if hit */ | /* Find visible strokes, and perform operations on those if hit */ | ||||
| float diff_mat[4][4]; | |||||
| bool parented = false; | |||||
| CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers) | CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers) | ||||
| { | { | ||||
| bGPDframe *gpf = gpl->actframe; | /* If no active frame, don't do anything... */ | ||||
| if (gpf == NULL) | if (gpl->actframe == NULL) { | ||||
| continue; | continue; | ||||
| /* calculate difference matrix if parent object */ | |||||
| if (gpl->parent != NULL) { | |||||
| ED_gpencil_parent_location(gpl, diff_mat); | |||||
| parented = true; | |||||
| } | |||||
| else { | |||||
| parented = false; | |||||
| } | } | ||||
| for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { | /* calculate difference matrix */ | ||||
| /* skip strokes that are invalid for current view */ | float diff_mat[4][4]; | ||||
| if (ED_gpencil_stroke_can_use(C, gps) == false) | ED_gpencil_parent_location(obact, gpd, gpl, diff_mat); | ||||
| continue; | |||||
| /* check if the color is editable */ | /* Active Frame or MultiFrame? */ | ||||
| if (ED_gpencil_stroke_color_use(gpl, gps) == false) { | if (gso->is_multiframe) { | ||||
| continue; | /* init multiframe falloff options */ | ||||
| int f_init = 0; | |||||
| int f_end = 0; | |||||
| if (gso->use_multiframe_falloff) { | |||||
| BKE_gp_get_range_selected(gpl, &f_init, &f_end); | |||||
| } | } | ||||
| switch (gso->brush_type) { | for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) { | ||||
| case GP_EDITBRUSH_TYPE_SMOOTH: /* Smooth strokes */ | /* Always do active frame; Otherwise, only include selected frames */ | ||||
| { | if ((gpf == gpl->actframe) || (gpf->flag & GP_FRAME_SELECT)) { | ||||
| changed |= gpsculpt_brush_do_stroke(gso, gps, parented, diff_mat, gp_brush_smooth_apply); | /* compute multiframe falloff factor */ | ||||
| break; | if (gso->use_multiframe_falloff) { | ||||
| } | /* Faloff depends on distance to active frame (relative to the overall frame range) */ | ||||
| gso->mf_falloff = BKE_gpencil_multiframe_falloff_calc( | |||||
| case GP_EDITBRUSH_TYPE_THICKNESS: /* Adjust stroke thickness */ | gpf, gpl->actframe->framenum, | ||||
| { | f_init, f_end, | ||||
| changed |= gpsculpt_brush_do_stroke(gso, gps, parented, diff_mat, gp_brush_thickness_apply); | ts->gp_sculpt.cur_falloff); | ||||
| break; | |||||
| } | |||||
| case GP_EDITBRUSH_TYPE_STRENGTH: /* Adjust stroke color strength */ | |||||
| { | |||||
| changed |= gpsculpt_brush_do_stroke(gso, gps, parented, diff_mat, gp_brush_strength_apply); | |||||
| break; | |||||
| } | |||||
| case GP_EDITBRUSH_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, parented, diff_mat, gp_brush_grab_store_points); | |||||
| } | } | ||||
| else { | else { | ||||
| /* Apply effect to the stored points */ | /* No falloff */ | ||||
| gp_brush_grab_apply_cached(gso, gps, parented, diff_mat); | gso->mf_falloff = 1.0f; | ||||
| changed |= true; | |||||
| } | } | ||||
| break; | |||||
| } | /* affect strokes in this frame */ | ||||
| changed |= gpsculpt_brush_do_frame(C, gso, gpl, gpf, diff_mat); | |||||
| case GP_EDITBRUSH_TYPE_PUSH: /* Push points */ | |||||
| { | |||||
| changed |= gpsculpt_brush_do_stroke(gso, gps, parented, diff_mat, gp_brush_push_apply); | |||||
| break; | |||||
| } | |||||
| case GP_EDITBRUSH_TYPE_PINCH: /* Pinch points */ | |||||
| { | |||||
| changed |= gpsculpt_brush_do_stroke(gso, gps, parented, diff_mat, gp_brush_pinch_apply); | |||||
| break; | |||||
| } | |||||
| case GP_EDITBRUSH_TYPE_TWIST: /* Twist points around midpoint */ | |||||
| { | |||||
| changed |= gpsculpt_brush_do_stroke(gso, gps, parented, diff_mat, gp_brush_twist_apply); | |||||
| break; | |||||
| } | |||||
| case GP_EDITBRUSH_TYPE_RANDOMIZE: /* Apply jitter */ | |||||
| { | |||||
| changed |= gpsculpt_brush_do_stroke(gso, gps, parented, diff_mat, gp_brush_randomize_apply); | |||||
| break; | |||||
| } | } | ||||
| default: | |||||
| printf("ERROR: Unknown type of GPencil Sculpt brush - %u\n", gso->brush_type); | |||||
| break; | |||||
| } | |||||
| /* Triangulation must be calculated if changed */ | |||||
| if (changed) { | |||||
| gps->flag |= GP_STROKE_RECALC_CACHES; | |||||
| gps->tot_triangles = 0; | |||||
| } | } | ||||
| } | } | ||||
| else { | |||||
| /* Apply to active frame's strokes */ | |||||
| gso->mf_falloff = 1.0f; | |||||
| changed |= gpsculpt_brush_do_frame(C, gso, gpl, gpl->actframe, diff_mat); | |||||
| } | |||||
| } | } | ||||
| CTX_DATA_END; | CTX_DATA_END; | ||||
| Context not available. | |||||