diff options
author | Campbell Barton <ideasman42@gmail.com> | 2019-04-17 07:17:24 +0300 |
---|---|---|
committer | Campbell Barton <ideasman42@gmail.com> | 2019-04-17 07:21:24 +0300 |
commit | e12c08e8d170b7ca40f204a5b0423c23a9fbc2c1 (patch) | |
tree | 8cf3453d12edb177a218ef8009357518ec6cab6a /source/blender/editors/gpencil/gpencil_brush.c | |
parent | b3dabc200a4b0399ec6b81f2ff2730d07b44fcaa (diff) |
ClangFormat: apply to source, most of intern
Apply clang format as proposed in T53211.
For details on usage and instructions for migrating branches
without conflicts, see:
https://wiki.blender.org/wiki/Tools/ClangFormat
Diffstat (limited to 'source/blender/editors/gpencil/gpencil_brush.c')
-rw-r--r-- | source/blender/editors/gpencil/gpencil_brush.c | 3502 |
1 files changed, 1736 insertions, 1766 deletions
diff --git a/source/blender/editors/gpencil/gpencil_brush.c b/source/blender/editors/gpencil/gpencil_brush.c index 8f64ec1c868..ef0ebd06d07 100644 --- a/source/blender/editors/gpencil/gpencil_brush.c +++ b/source/blender/editors/gpencil/gpencil_brush.c @@ -22,7 +22,6 @@ * \ingroup edgpencil */ - #include <stdio.h> #include <string.h> #include <stdlib.h> @@ -86,145 +85,133 @@ /* Context for brush operators */ typedef struct tGP_BrushEditData { - /* Current editor/region/etc. */ - /* NOTE: This stuff is mainly needed to handle 3D view projection stuff... */ - Depsgraph *depsgraph; - Scene *scene; - Object *object; - - ScrArea *sa; - ARegion *ar; - - /* Current GPencil datablock */ - bGPdata *gpd; + /* Current editor/region/etc. */ + /* NOTE: This stuff is mainly needed to handle 3D view projection stuff... */ + Depsgraph *depsgraph; + Scene *scene; + Object *object; - /* Brush Settings */ - GP_Sculpt_Settings *settings; - GP_Sculpt_Data *gp_brush; - GP_Sculpt_Data *gp_brush_old; + ScrArea *sa; + ARegion *ar; - eGP_Sculpt_Types brush_type; - eGP_Sculpt_Types brush_type_old; - eGP_Sculpt_Flag flag; + /* Current GPencil datablock */ + bGPdata *gpd; - /* Space Conversion Data */ - GP_SpaceConversion gsc; + /* Brush Settings */ + GP_Sculpt_Settings *settings; + GP_Sculpt_Data *gp_brush; + GP_Sculpt_Data *gp_brush_old; + eGP_Sculpt_Types brush_type; + eGP_Sculpt_Types brush_type_old; + eGP_Sculpt_Flag flag; - /* Is the brush currently painting? */ - bool is_painting; - bool is_weight_mode; + /* Space Conversion Data */ + GP_SpaceConversion gsc; - /* Start of new sculpt stroke */ - bool first; + /* Is the brush currently painting? */ + bool is_painting; + bool is_weight_mode; - /* Is multiframe editing enabled, and are we using falloff for that? */ - bool is_multiframe; - bool use_multiframe_falloff; + /* Start of new sculpt stroke */ + bool first; - /* Current frame */ - int cfra; + /* Is multiframe editing enabled, and are we using falloff for that? */ + bool is_multiframe; + bool use_multiframe_falloff; + /* Current frame */ + int cfra; - /* Brush Runtime Data: */ - /* - position and pressure - * - the *_prev variants are the previous values - */ - float mval[2], mval_prev[2]; - float pressure, pressure_prev; + /* Brush Runtime Data: */ + /* - position and pressure + * - the *_prev variants are the previous values + */ + float mval[2], mval_prev[2]; + float pressure, pressure_prev; - /* - effect vector (e.g. 2D/3D translation for grab brush) */ - float dvec[3]; + /* - effect vector (e.g. 2D/3D translation for grab brush) */ + float dvec[3]; - /* - multiframe falloff factor */ - float mf_falloff; + /* - multiframe falloff factor */ + float mf_falloff; - /* active vertex group */ - int vrgroup; + /* active vertex group */ + int vrgroup; + /* brush geometry (bounding box) */ + rcti brush_rect; - /* brush geometry (bounding box) */ - rcti brush_rect; + /* Custom data for certain brushes */ + /* - map from bGPDstroke's to structs containing custom data about those strokes */ + GHash *stroke_customdata; + /* - general customdata */ + void *customdata; - /* Custom data for certain brushes */ - /* - map from bGPDstroke's to structs containing custom data about those strokes */ - GHash *stroke_customdata; - /* - general customdata */ - void *customdata; + /* Timer for in-place accumulation of brush effect */ + wmTimer *timer; + bool timerTick; /* is this event from a timer */ - - /* Timer for in-place accumulation of brush effect */ - wmTimer *timer; - bool timerTick; /* is this event from a timer */ - - RNG *rng; + RNG *rng; } 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]); + tGP_BrushEditData *gso, bGPDstroke *gps, int pt_index, const int radius, const int co[2]); /* ************************************************ */ /* Utility Functions */ /* apply lock axis reset */ -static void gpsculpt_compute_lock_axis(tGP_BrushEditData *gso, bGPDspoint *pt, const float save_pt[3]) +static void gpsculpt_compute_lock_axis(tGP_BrushEditData *gso, + bGPDspoint *pt, + const float save_pt[3]) { - if (gso->sa->spacetype != SPACE_VIEW3D) { - return; - } - - const ToolSettings *ts = gso->scene->toolsettings; - const View3DCursor *cursor = &gso->scene->cursor; - const int axis = ts->gp_sculpt.lock_axis; - - /* lock axis control */ - switch (axis) { - case GP_LOCKAXIS_X: - { - pt->x = save_pt[0]; - break; - } - case GP_LOCKAXIS_Y: - { - pt->y = save_pt[1]; - break; - } - case GP_LOCKAXIS_Z: - { - pt->z = save_pt[2]; - break; - } - case GP_LOCKAXIS_CURSOR: - { - /* compute a plane with cursor normal and position of the point - before do the sculpt */ - const float scale[3] = { 1.0f, 1.0f, 1.0f }; - float plane_normal[3] = { 0.0f, 0.0f, 1.0f }; - float plane[4]; - float mat[4][4]; - float r_close[3]; - - loc_eul_size_to_mat4(mat, - cursor->location, - cursor->rotation_euler, - scale); - - mul_mat3_m4_v3(mat, plane_normal); - plane_from_point_normal_v3(plane, save_pt, plane_normal); - - /* find closest point to the plane with the new position */ - closest_to_plane_v3(r_close, plane, &pt->x); - copy_v3_v3(&pt->x, r_close); - break; - } - default: - { - break; - } - } + if (gso->sa->spacetype != SPACE_VIEW3D) { + return; + } + + const ToolSettings *ts = gso->scene->toolsettings; + const View3DCursor *cursor = &gso->scene->cursor; + const int axis = ts->gp_sculpt.lock_axis; + + /* lock axis control */ + switch (axis) { + case GP_LOCKAXIS_X: { + pt->x = save_pt[0]; + break; + } + case GP_LOCKAXIS_Y: { + pt->y = save_pt[1]; + break; + } + case GP_LOCKAXIS_Z: { + pt->z = save_pt[2]; + break; + } + case GP_LOCKAXIS_CURSOR: { + /* compute a plane with cursor normal and position of the point + before do the sculpt */ + const float scale[3] = {1.0f, 1.0f, 1.0f}; + float plane_normal[3] = {0.0f, 0.0f, 1.0f}; + float plane[4]; + float mat[4][4]; + float r_close[3]; + + loc_eul_size_to_mat4(mat, cursor->location, cursor->rotation_euler, scale); + + mul_mat3_m4_v3(mat, plane_normal); + plane_from_point_normal_v3(plane, save_pt, plane_normal); + + /* find closest point to the plane with the new position */ + closest_to_plane_v3(r_close, plane, &pt->x); + copy_v3_v3(&pt->x, r_close); + break; + } + default: { + break; + } + } } /* Context ---------------------------------------- */ @@ -232,22 +219,22 @@ static void gpsculpt_compute_lock_axis(tGP_BrushEditData *gso, bGPDspoint *pt, c /* Get the sculpting settings */ static GP_Sculpt_Settings *gpsculpt_get_settings(Scene *scene) { - return &scene->toolsettings->gp_sculpt; + return &scene->toolsettings->gp_sculpt; } /* Get the active brush */ static GP_Sculpt_Data *gpsculpt_get_brush(Scene *scene, bool is_weight_mode) { - GP_Sculpt_Settings *gset = &scene->toolsettings->gp_sculpt; - GP_Sculpt_Data *gp_brush = NULL; - if (is_weight_mode) { - gp_brush = &gset->brush[gset->weighttype]; - } - else { - gp_brush = &gset->brush[gset->brushtype]; - } - - return gp_brush; + GP_Sculpt_Settings *gset = &scene->toolsettings->gp_sculpt; + GP_Sculpt_Data *gp_brush = NULL; + if (is_weight_mode) { + gp_brush = &gset->brush[gset->weighttype]; + } + else { + gp_brush = &gset->brush[gset->brushtype]; + } + + return gp_brush; } /* Brush Operations ------------------------------- */ @@ -255,56 +242,56 @@ static GP_Sculpt_Data *gpsculpt_get_brush(Scene *scene, bool is_weight_mode) /* Invert behavior of brush? */ static bool gp_brush_invert_check(tGP_BrushEditData *gso) { - /* The basic setting is the brush's setting (from the panel) */ - bool invert = ((gso->gp_brush->flag & GP_SCULPT_FLAG_INVERT) != 0); - - /* During runtime, the user can hold down the Ctrl key to invert the basic behavior */ - if (gso->flag & GP_SCULPT_FLAG_INVERT) { - invert ^= true; - } - - /* set temporary status */ - if (invert) { - gso->gp_brush->flag |= GP_SCULPT_FLAG_TMP_INVERT; - } - else { - gso->gp_brush->flag &= ~GP_SCULPT_FLAG_TMP_INVERT; - } - - return invert; + /* The basic setting is the brush's setting (from the panel) */ + bool invert = ((gso->gp_brush->flag & GP_SCULPT_FLAG_INVERT) != 0); + + /* During runtime, the user can hold down the Ctrl key to invert the basic behavior */ + if (gso->flag & GP_SCULPT_FLAG_INVERT) { + invert ^= true; + } + + /* set temporary status */ + if (invert) { + gso->gp_brush->flag |= GP_SCULPT_FLAG_TMP_INVERT; + } + else { + gso->gp_brush->flag &= ~GP_SCULPT_FLAG_TMP_INVERT; + } + + return invert; } /* Compute strength of effect */ static float gp_brush_influence_calc(tGP_BrushEditData *gso, const int radius, const int co[2]) { - GP_Sculpt_Data *gp_brush = gso->gp_brush; + GP_Sculpt_Data *gp_brush = gso->gp_brush; - /* basic strength factor from brush settings */ - float influence = gp_brush->strength; + /* basic strength factor from brush settings */ + float influence = gp_brush->strength; - /* use pressure? */ - if (gp_brush->flag & GP_SCULPT_FLAG_USE_PRESSURE) { - influence *= gso->pressure; - } + /* use pressure? */ + if (gp_brush->flag & GP_SCULPT_FLAG_USE_PRESSURE) { + influence *= gso->pressure; + } - /* distance fading */ - if (gp_brush->flag & GP_SCULPT_FLAG_USE_FALLOFF) { - int mval_i[2]; - round_v2i_v2fl(mval_i, gso->mval); - float distance = (float)len_v2v2_int(mval_i, co); - float fac; + /* distance fading */ + if (gp_brush->flag & GP_SCULPT_FLAG_USE_FALLOFF) { + int mval_i[2]; + round_v2i_v2fl(mval_i, gso->mval); + float distance = (float)len_v2v2_int(mval_i, co); + float fac; - CLAMP(distance, 0.0f, (float)radius); - fac = 1.0f - (distance / (float)radius); + CLAMP(distance, 0.0f, (float)radius); + fac = 1.0f - (distance / (float)radius); - influence *= fac; - } + influence *= fac; + } - /* apply multiframe falloff */ - influence *= gso->mf_falloff; + /* apply multiframe falloff */ + influence *= gso->mf_falloff; - /* return influence */ - return influence; + /* return influence */ + return influence; } /* ************************************************ */ @@ -318,38 +305,34 @@ 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]) + tGP_BrushEditData *gso, bGPDstroke *gps, 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); - /* need one flag enabled by default */ - if ((gso->settings->flag & - (GP_SCULPT_SETT_FLAG_APPLY_POSITION | - GP_SCULPT_SETT_FLAG_APPLY_STRENGTH | - GP_SCULPT_SETT_FLAG_APPLY_THICKNESS | - GP_SCULPT_SETT_FLAG_APPLY_UV)) == 0) - { - gso->settings->flag |= GP_SCULPT_SETT_FLAG_APPLY_POSITION; - } - - /* perform smoothing */ - if (gso->settings->flag & GP_SCULPT_SETT_FLAG_APPLY_POSITION) { - BKE_gpencil_smooth_stroke(gps, pt_index, inf); - } - if (gso->settings->flag & GP_SCULPT_SETT_FLAG_APPLY_STRENGTH) { - BKE_gpencil_smooth_stroke_strength(gps, pt_index, inf); - } - if (gso->settings->flag & GP_SCULPT_SETT_FLAG_APPLY_THICKNESS) { - BKE_gpencil_smooth_stroke_thickness(gps, pt_index, inf); - } - if (gso->settings->flag & GP_SCULPT_SETT_FLAG_APPLY_UV) { - BKE_gpencil_smooth_stroke_uv(gps, pt_index, inf); - } - - gps->flag |= GP_STROKE_RECALC_GEOMETRY; - - return true; + // GP_Sculpt_Data *gp_brush = gso->brush; + float inf = gp_brush_influence_calc(gso, radius, co); + /* need one flag enabled by default */ + if ((gso->settings->flag & + (GP_SCULPT_SETT_FLAG_APPLY_POSITION | GP_SCULPT_SETT_FLAG_APPLY_STRENGTH | + GP_SCULPT_SETT_FLAG_APPLY_THICKNESS | GP_SCULPT_SETT_FLAG_APPLY_UV)) == 0) { + gso->settings->flag |= GP_SCULPT_SETT_FLAG_APPLY_POSITION; + } + + /* perform smoothing */ + if (gso->settings->flag & GP_SCULPT_SETT_FLAG_APPLY_POSITION) { + BKE_gpencil_smooth_stroke(gps, pt_index, inf); + } + if (gso->settings->flag & GP_SCULPT_SETT_FLAG_APPLY_STRENGTH) { + BKE_gpencil_smooth_stroke_strength(gps, pt_index, inf); + } + if (gso->settings->flag & GP_SCULPT_SETT_FLAG_APPLY_THICKNESS) { + BKE_gpencil_smooth_stroke_thickness(gps, pt_index, inf); + } + if (gso->settings->flag & GP_SCULPT_SETT_FLAG_APPLY_UV) { + BKE_gpencil_smooth_stroke_uv(gps, pt_index, inf); + } + + gps->flag |= GP_STROKE_RECALC_GEOMETRY; + + return true; } /* ----------------------------------------------- */ @@ -357,77 +340,73 @@ static bool gp_brush_smooth_apply( /* 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]) + 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; - - /* apply */ - // XXX: this is much too strong, and it should probably do some smoothing with the surrounding stuff - if (gp_brush_invert_check(gso)) { - /* make line thinner - reduce stroke pressure */ - pt->pressure -= inf; - } - else { - /* make line thicker - increase stroke pressure */ - pt->pressure += inf; - } - - /* Pressure should stay within [0.0, 1.0] - * However, it is nice for volumetric strokes to be able to exceed - * the upper end of this range. Therefore, we don't actually clamp - * down on the upper end. - */ - if (pt->pressure < 0.0f) - pt->pressure = 0.0f; - - return true; + 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; + + /* apply */ + // XXX: this is much too strong, and it should probably do some smoothing with the surrounding stuff + if (gp_brush_invert_check(gso)) { + /* make line thinner - reduce stroke pressure */ + pt->pressure -= inf; + } + else { + /* make line thicker - increase stroke pressure */ + pt->pressure += inf; + } + + /* Pressure should stay within [0.0, 1.0] + * However, it is nice for volumetric strokes to be able to exceed + * the upper end of this range. Therefore, we don't actually clamp + * down on the upper end. + */ + if (pt->pressure < 0.0f) + pt->pressure = 0.0f; + + return true; } - /* ----------------------------------------------- */ /* 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]) + 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, 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) / 20.0f; - - /* apply */ - if (gp_brush_invert_check(gso)) { - /* make line more transparent - reduce alpha factor */ - pt->strength -= inf; - } - else { - /* make line more opaque - increase stroke strength */ - pt->strength += inf; - } - /* smooth the strength */ - BKE_gpencil_smooth_stroke_strength(gps, pt_index, inf); - - /* Strength should stay within [0.0, 1.0] */ - CLAMP(pt->strength, 0.0f, 1.0f); - - return true; + bGPDspoint *pt = gps->points + pt_index; + float inf; + + /* Compute strength of effect + * - We divide the strength, 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) / 20.0f; + + /* apply */ + if (gp_brush_invert_check(gso)) { + /* make line more transparent - reduce alpha factor */ + pt->strength -= inf; + } + else { + /* make line more opaque - increase stroke strength */ + pt->strength += inf; + } + /* smooth the strength */ + BKE_gpencil_smooth_stroke_strength(gps, pt_index, inf); + + /* Strength should stay within [0.0, 1.0] */ + CLAMP(pt->strength, 0.0f, 1.0f); + + return true; } - /* ----------------------------------------------- */ /* Grab Brush */ @@ -438,145 +417,145 @@ static bool gp_brush_strength_apply( * the brush region. */ typedef struct tGPSB_Grab_StrokeData { - /* array of indices to corresponding points in the stroke */ - int *points; - /* array of influence weights for each of the included points */ - float *weights; - - /* capacity of the arrays */ - int capacity; - /* actual number of items currently stored */ - int size; + /* array of indices to corresponding points in the stroke */ + int *points; + /* array of influence weights for each of the included points */ + float *weights; + + /* capacity of the arrays */ + int capacity; + /* actual number of items currently stored */ + int size; } tGPSB_Grab_StrokeData; /* initialise custom data for handling this stroke */ static void gp_brush_grab_stroke_init(tGP_BrushEditData *gso, bGPDstroke *gps) { - tGPSB_Grab_StrokeData *data = NULL; + tGPSB_Grab_StrokeData *data = NULL; - BLI_assert(gps->totpoints > 0); + BLI_assert(gps->totpoints > 0); - /* Check if there are buffers already (from a prior run) */ - if (BLI_ghash_haskey(gso->stroke_customdata, gps)) { - /* Ensure that the caches are empty - * - Since we reuse these between different strokes, we don't - * want the previous invocation's data polluting the arrays - */ - data = BLI_ghash_lookup(gso->stroke_customdata, gps); - BLI_assert(data != NULL); + /* Check if there are buffers already (from a prior run) */ + if (BLI_ghash_haskey(gso->stroke_customdata, gps)) { + /* Ensure that the caches are empty + * - Since we reuse these between different strokes, we don't + * want the previous invocation's data polluting the arrays + */ + data = BLI_ghash_lookup(gso->stroke_customdata, gps); + BLI_assert(data != NULL); - data->size = 0; /* minimum requirement - so that we can repopulate again */ + data->size = 0; /* minimum requirement - so that we can repopulate again */ - memset(data->points, 0, sizeof(int) * data->capacity); - memset(data->weights, 0, sizeof(float) * data->capacity); - } - else { - /* Create new instance */ - data = MEM_callocN(sizeof(tGPSB_Grab_StrokeData), "GP Stroke Grab Data"); + memset(data->points, 0, sizeof(int) * data->capacity); + memset(data->weights, 0, sizeof(float) * data->capacity); + } + else { + /* Create new instance */ + data = MEM_callocN(sizeof(tGPSB_Grab_StrokeData), "GP Stroke Grab Data"); - data->capacity = gps->totpoints; - data->size = 0; + data->capacity = gps->totpoints; + data->size = 0; - 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->points = MEM_callocN(sizeof(int) * data->capacity, "GP Stroke Grab Indices"); + data->weights = MEM_callocN(sizeof(float) * data->capacity, "GP Stroke Grab Weights"); - /* hook up to the cache */ - BLI_ghash_insert(gso->stroke_customdata, gps, data); - } + /* hook up to the cache */ + BLI_ghash_insert(gso->stroke_customdata, gps, data); + } } /* 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]) + 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); - float inf = gp_brush_influence_calc(gso, radius, co); + tGPSB_Grab_StrokeData *data = BLI_ghash_lookup(gso->stroke_customdata, gps); + float inf = gp_brush_influence_calc(gso, radius, co); - BLI_assert(data != NULL); - BLI_assert(data->size < data->capacity); + BLI_assert(data != NULL); + BLI_assert(data->size < data->capacity); - /* insert this point into the set of affected points */ - data->points[data->size] = pt_index; - data->weights[data->size] = inf; - data->size++; + /* insert this point into the set of affected points */ + data->points[data->size] = pt_index; + data->weights[data->size] = inf; + data->size++; - /* done */ - return true; + /* done */ + return true; } /* Compute effect vector for grab brush */ static void gp_brush_grab_calc_dvec(tGP_BrushEditData *gso) { - /* Convert mouse-movements to movement vector */ - // TODO: incorporate pressure into this? - // XXX: screen-space strokes in 3D space will suffer! - if (gso->sa->spacetype == SPACE_VIEW3D) { - RegionView3D *rv3d = gso->ar->regiondata; - float *rvec = gso->scene->cursor.location; - float zfac = ED_view3d_calc_zfac(rv3d, rvec, NULL); - - float mval_f[2]; - - /* convert from 2D screenspace to 3D... */ - mval_f[0] = (float)(gso->mval[0] - gso->mval_prev[0]); - mval_f[1] = (float)(gso->mval[1] - gso->mval_prev[1]); - - ED_view3d_win_to_delta(gso->ar, mval_f, gso->dvec, zfac); - } - else { - /* 2D - just copy */ - // XXX: view2d? - gso->dvec[0] = (float)(gso->mval[0] - gso->mval_prev[0]); - gso->dvec[1] = (float)(gso->mval[1] - gso->mval_prev[1]); - gso->dvec[2] = 0.0f; /* unused */ - } + /* Convert mouse-movements to movement vector */ + // TODO: incorporate pressure into this? + // XXX: screen-space strokes in 3D space will suffer! + if (gso->sa->spacetype == SPACE_VIEW3D) { + RegionView3D *rv3d = gso->ar->regiondata; + float *rvec = gso->scene->cursor.location; + float zfac = ED_view3d_calc_zfac(rv3d, rvec, NULL); + + float mval_f[2]; + + /* convert from 2D screenspace to 3D... */ + mval_f[0] = (float)(gso->mval[0] - gso->mval_prev[0]); + mval_f[1] = (float)(gso->mval[1] - gso->mval_prev[1]); + + ED_view3d_win_to_delta(gso->ar, mval_f, gso->dvec, zfac); + } + else { + /* 2D - just copy */ + // XXX: view2d? + gso->dvec[0] = (float)(gso->mval[0] - gso->mval_prev[0]); + gso->dvec[1] = (float)(gso->mval[1] - gso->mval_prev[1]); + gso->dvec[2] = 0.0f; /* unused */ + } } /* Apply grab transform to all relevant points of the affected strokes */ -static void gp_brush_grab_apply_cached( - tGP_BrushEditData *gso, bGPDstroke *gps, float diff_mat[4][4]) +static void gp_brush_grab_apply_cached(tGP_BrushEditData *gso, + bGPDstroke *gps, + float diff_mat[4][4]) { - tGPSB_Grab_StrokeData *data = BLI_ghash_lookup(gso->stroke_customdata, gps); - int i; - - /* Apply dvec to all of the stored points */ - for (i = 0; i < data->size; i++) { - bGPDspoint *pt = &gps->points[data->points[i]]; - float delta[3] = {0.0f}; - - /* adjust the amount of displacement to apply */ - mul_v3_v3fl(delta, gso->dvec, data->weights[i]); - - float fpt[3]; - float save_pt[3]; - copy_v3_v3(save_pt, &pt->x); - /* apply transformation */ - mul_v3_m4v3(fpt, diff_mat, &pt->x); - /* apply */ - add_v3_v3v3(&pt->x, fpt, delta); - /* undo transformation to the init parent position */ - float inverse_diff_mat[4][4]; - invert_m4_m4(inverse_diff_mat, diff_mat); - mul_m4_v3(inverse_diff_mat, &pt->x); - - /* compute lock axis */ - gpsculpt_compute_lock_axis(gso, pt, save_pt); - } - gps->flag |= GP_STROKE_RECALC_GEOMETRY; + tGPSB_Grab_StrokeData *data = BLI_ghash_lookup(gso->stroke_customdata, gps); + int i; + + /* Apply dvec to all of the stored points */ + for (i = 0; i < data->size; i++) { + bGPDspoint *pt = &gps->points[data->points[i]]; + float delta[3] = {0.0f}; + + /* adjust the amount of displacement to apply */ + mul_v3_v3fl(delta, gso->dvec, data->weights[i]); + + float fpt[3]; + float save_pt[3]; + copy_v3_v3(save_pt, &pt->x); + /* apply transformation */ + mul_v3_m4v3(fpt, diff_mat, &pt->x); + /* apply */ + add_v3_v3v3(&pt->x, fpt, delta); + /* undo transformation to the init parent position */ + float inverse_diff_mat[4][4]; + invert_m4_m4(inverse_diff_mat, diff_mat); + mul_m4_v3(inverse_diff_mat, &pt->x); + + /* compute lock axis */ + gpsculpt_compute_lock_axis(gso, pt, save_pt); + } + gps->flag |= GP_STROKE_RECALC_GEOMETRY; } /* free customdata used for handling this stroke */ static void gp_brush_grab_stroke_free(void *ptr) { - tGPSB_Grab_StrokeData *data = (tGPSB_Grab_StrokeData *)ptr; + tGPSB_Grab_StrokeData *data = (tGPSB_Grab_StrokeData *)ptr; - /* free arrays */ - MEM_freeN(data->points); - MEM_freeN(data->weights); + /* free arrays */ + MEM_freeN(data->points); + MEM_freeN(data->weights); - /* ... and this item itself, since it was also allocated */ - MEM_freeN(data); + /* ... and this item itself, since it was also allocated */ + MEM_freeN(data); } /* ----------------------------------------------- */ @@ -584,29 +563,28 @@ static void gp_brush_grab_stroke_free(void *ptr) /* 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]) + tGP_BrushEditData *gso, bGPDstroke *gps, int pt_index, const int radius, const int co[2]) { - bGPDspoint *pt = gps->points + pt_index; - float save_pt[3]; - copy_v3_v3(save_pt, &pt->x); + 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 delta[3] = {0.0f}; + float inf = gp_brush_influence_calc(gso, radius, co); + float delta[3] = {0.0f}; - /* adjust the amount of displacement to apply */ - mul_v3_v3fl(delta, gso->dvec, inf); + /* adjust the amount of displacement to apply */ + mul_v3_v3fl(delta, gso->dvec, inf); - /* apply */ - add_v3_v3(&pt->x, delta); + /* apply */ + add_v3_v3(&pt->x, delta); - /* compute lock axis */ - gpsculpt_compute_lock_axis(gso, pt, save_pt); + /* compute lock axis */ + gpsculpt_compute_lock_axis(gso, pt, save_pt); - gps->flag |= GP_STROKE_RECALC_GEOMETRY; + gps->flag |= GP_STROKE_RECALC_GEOMETRY; - /* done */ - return true; + /* done */ + return true; } /* ----------------------------------------------- */ @@ -615,84 +593,83 @@ static bool gp_brush_push_apply( /* Compute reference midpoint for the brush - this is what we'll be moving towards */ static void gp_brush_calc_midpoint(tGP_BrushEditData *gso) { - if (gso->sa->spacetype == SPACE_VIEW3D) { - /* Convert mouse position to 3D space - * See: gpencil_paint.c :: gp_stroke_convertcoords() - */ - RegionView3D *rv3d = gso->ar->regiondata; - const float *rvec = gso->scene->cursor.location; - float zfac = ED_view3d_calc_zfac(rv3d, rvec, NULL); - - float mval_f[2]; - copy_v2_v2(mval_f, gso->mval); - float mval_prj[2]; - float dvec[3]; - - - if (ED_view3d_project_float_global(gso->ar, rvec, mval_prj, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK) { - sub_v2_v2v2(mval_f, mval_prj, mval_f); - ED_view3d_win_to_delta(gso->ar, mval_f, dvec, zfac); - sub_v3_v3v3(gso->dvec, rvec, dvec); - } - else { - zero_v3(gso->dvec); - } - } - else { - /* Just 2D coordinates */ - // XXX: fix View2D offsets later - gso->dvec[0] = (float)gso->mval[0]; - gso->dvec[1] = (float)gso->mval[1]; - gso->dvec[2] = 0.0f; - } + if (gso->sa->spacetype == SPACE_VIEW3D) { + /* Convert mouse position to 3D space + * See: gpencil_paint.c :: gp_stroke_convertcoords() + */ + RegionView3D *rv3d = gso->ar->regiondata; + const float *rvec = gso->scene->cursor.location; + float zfac = ED_view3d_calc_zfac(rv3d, rvec, NULL); + + float mval_f[2]; + copy_v2_v2(mval_f, gso->mval); + float mval_prj[2]; + float dvec[3]; + + if (ED_view3d_project_float_global(gso->ar, rvec, mval_prj, V3D_PROJ_TEST_NOP) == + V3D_PROJ_RET_OK) { + sub_v2_v2v2(mval_f, mval_prj, mval_f); + ED_view3d_win_to_delta(gso->ar, mval_f, dvec, zfac); + sub_v3_v3v3(gso->dvec, rvec, dvec); + } + else { + zero_v3(gso->dvec); + } + } + else { + /* Just 2D coordinates */ + // XXX: fix View2D offsets later + gso->dvec[0] = (float)gso->mval[0]; + gso->dvec[1] = (float)gso->mval[1]; + gso->dvec[2] = 0.0f; + } } /* 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]) + tGP_BrushEditData *gso, bGPDstroke *gps, int pt_index, const int radius, const int co[2]) { - bGPDspoint *pt = gps->points + pt_index; - float fac, inf; - float vec[3]; - float save_pt[3]; - copy_v3_v3(save_pt, &pt->x); - - /* Scale down standard influence value to get it more manageable... - * - No damping = Unmanageable at > 0.5 strength - * - Div 10 = Not enough effect - * - Div 5 = Happy medium... (by trial and error) - */ - inf = gp_brush_influence_calc(gso, radius, co) / 5.0f; - - /* 1) Make this point relative to the cursor/midpoint (dvec) */ - sub_v3_v3v3(vec, &pt->x, gso->dvec); - - /* 2) Shrink the distance by pulling the point towards the midpoint - * (0.0 = at midpoint, 1 = at edge of brush region) - * OR - * Increase the distance (if inverting the brush action!) - */ - if (gp_brush_invert_check(gso)) { - /* Inflate (inverse) */ - fac = 1.0f + (inf * inf); /* squared to temper the effect... */ - } - else { - /* Shrink (default) */ - fac = 1.0f - (inf * inf); /* squared to temper the effect... */ - } - mul_v3_fl(vec, fac); - - /* 3) Translate back to original space, with the shrinkage applied */ - add_v3_v3v3(&pt->x, gso->dvec, vec); - - /* compute lock axis */ - gpsculpt_compute_lock_axis(gso, pt, save_pt); - - gps->flag |= GP_STROKE_RECALC_GEOMETRY; - - /* done */ - return true; + bGPDspoint *pt = gps->points + pt_index; + float fac, inf; + float vec[3]; + float save_pt[3]; + copy_v3_v3(save_pt, &pt->x); + + /* Scale down standard influence value to get it more manageable... + * - No damping = Unmanageable at > 0.5 strength + * - Div 10 = Not enough effect + * - Div 5 = Happy medium... (by trial and error) + */ + inf = gp_brush_influence_calc(gso, radius, co) / 5.0f; + + /* 1) Make this point relative to the cursor/midpoint (dvec) */ + sub_v3_v3v3(vec, &pt->x, gso->dvec); + + /* 2) Shrink the distance by pulling the point towards the midpoint + * (0.0 = at midpoint, 1 = at edge of brush region) + * OR + * Increase the distance (if inverting the brush action!) + */ + if (gp_brush_invert_check(gso)) { + /* Inflate (inverse) */ + fac = 1.0f + (inf * inf); /* squared to temper the effect... */ + } + else { + /* Shrink (default) */ + fac = 1.0f - (inf * inf); /* squared to temper the effect... */ + } + mul_v3_fl(vec, fac); + + /* 3) Translate back to original space, with the shrinkage applied */ + add_v3_v3v3(&pt->x, gso->dvec, vec); + + /* compute lock axis */ + gpsculpt_compute_lock_axis(gso, pt, save_pt); + + gps->flag |= GP_STROKE_RECALC_GEOMETRY; + + /* done */ + return true; } /* ----------------------------------------------- */ @@ -703,268 +680,259 @@ static bool gp_brush_pinch_apply( */ static bool gp_brush_twist_apply( - tGP_BrushEditData *gso, bGPDstroke *gps, int pt_index, - 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 + pt_index; - float angle, inf; - float save_pt[3]; - copy_v3_v3(save_pt, &pt->x); - - /* Angle to rotate by */ - inf = gp_brush_influence_calc(gso, radius, co); - angle = DEG2RADF(1.0f) * inf; - - if (gp_brush_invert_check(gso)) { - /* invert angle that we rotate by */ - angle *= -1; - } - - /* Rotate in 2D or 3D space? */ - if (gps->flag & GP_STROKE_3DSPACE) { - /* Perform rotation in 3D space... */ - RegionView3D *rv3d = gso->ar->regiondata; - float rmat[3][3]; - float axis[3]; - float vec[3]; - - /* Compute rotation matrix - rotate around view vector by angle */ - negate_v3_v3(axis, rv3d->persinv[2]); - normalize_v3(axis); - - axis_angle_normalized_to_mat3(rmat, axis, angle); - - /* Rotate point (no matrix-space transforms needed, as GP points are in world space) */ - sub_v3_v3v3(vec, &pt->x, gso->dvec); /* make relative to center - * (center is stored in dvec) */ - mul_m3_v3(rmat, vec); - add_v3_v3v3(&pt->x, vec, gso->dvec); /* restore */ - - /* compute lock axis */ - gpsculpt_compute_lock_axis(gso, pt, save_pt); - } - else { - const float axis[3] = {0.0f, 0.0f, 1.0f}; - float vec[3] = {0.0f}; - float rmat[3][3]; - - /* Express position of point relative to cursor, ready to rotate */ - // XXX: There is still some offset here, but it's close to working as expected... - vec[0] = (float)(co[0] - gso->mval[0]); - vec[1] = (float)(co[1] - gso->mval[1]); - - /* rotate point */ - axis_angle_normalized_to_mat3(rmat, axis, angle); - mul_m3_v3(rmat, vec); - - /* Convert back to screen-coordinates */ - vec[0] += (float)gso->mval[0]; - vec[1] += (float)gso->mval[1]; - - /* Map from screen-coordinates to final coordinate space */ - if (gps->flag & GP_STROKE_2DSPACE) { - View2D *v2d = gso->gsc.v2d; - UI_view2d_region_to_view(v2d, vec[0], vec[1], &pt->x, &pt->y); - } - else { - // XXX - copy_v2_v2(&pt->x, vec); - } - } - - gps->flag |= GP_STROKE_RECALC_GEOMETRY; - - /* done */ - return true; + bGPDspoint *pt = gps->points + pt_index; + float angle, inf; + float save_pt[3]; + copy_v3_v3(save_pt, &pt->x); + + /* Angle to rotate by */ + inf = gp_brush_influence_calc(gso, radius, co); + angle = DEG2RADF(1.0f) * inf; + + if (gp_brush_invert_check(gso)) { + /* invert angle that we rotate by */ + angle *= -1; + } + + /* Rotate in 2D or 3D space? */ + if (gps->flag & GP_STROKE_3DSPACE) { + /* Perform rotation in 3D space... */ + RegionView3D *rv3d = gso->ar->regiondata; + float rmat[3][3]; + float axis[3]; + float vec[3]; + + /* Compute rotation matrix - rotate around view vector by angle */ + negate_v3_v3(axis, rv3d->persinv[2]); + normalize_v3(axis); + + axis_angle_normalized_to_mat3(rmat, axis, angle); + + /* Rotate point (no matrix-space transforms needed, as GP points are in world space) */ + sub_v3_v3v3(vec, &pt->x, gso->dvec); /* make relative to center + * (center is stored in dvec) */ + mul_m3_v3(rmat, vec); + add_v3_v3v3(&pt->x, vec, gso->dvec); /* restore */ + + /* compute lock axis */ + gpsculpt_compute_lock_axis(gso, pt, save_pt); + } + else { + const float axis[3] = {0.0f, 0.0f, 1.0f}; + float vec[3] = {0.0f}; + float rmat[3][3]; + + /* Express position of point relative to cursor, ready to rotate */ + // XXX: There is still some offset here, but it's close to working as expected... + vec[0] = (float)(co[0] - gso->mval[0]); + vec[1] = (float)(co[1] - gso->mval[1]); + + /* rotate point */ + axis_angle_normalized_to_mat3(rmat, axis, angle); + mul_m3_v3(rmat, vec); + + /* Convert back to screen-coordinates */ + vec[0] += (float)gso->mval[0]; + vec[1] += (float)gso->mval[1]; + + /* Map from screen-coordinates to final coordinate space */ + if (gps->flag & GP_STROKE_2DSPACE) { + View2D *v2d = gso->gsc.v2d; + UI_view2d_region_to_view(v2d, vec[0], vec[1], &pt->x, &pt->y); + } + else { + // XXX + copy_v2_v2(&pt->x, vec); + } + } + + gps->flag |= GP_STROKE_RECALC_GEOMETRY; + + /* done */ + return true; } - /* ----------------------------------------------- */ /* 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]) + tGP_BrushEditData *gso, bGPDstroke *gps, int pt_index, const int radius, const int co[2]) { - 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, - * as well as the strength of the brush - */ - const float inf = gp_brush_influence_calc(gso, radius, co) / 2.0f; - const float fac = BLI_rng_get_float(gso->rng) * inf; - /* need one flag enabled by default */ - if ((gso->settings->flag & - (GP_SCULPT_SETT_FLAG_APPLY_POSITION | - GP_SCULPT_SETT_FLAG_APPLY_STRENGTH | - GP_SCULPT_SETT_FLAG_APPLY_THICKNESS | - GP_SCULPT_SETT_FLAG_APPLY_UV)) == 0) - { - gso->settings->flag |= GP_SCULPT_SETT_FLAG_APPLY_POSITION; - } - - /* apply random to position */ - if (gso->settings->flag & GP_SCULPT_SETT_FLAG_APPLY_POSITION) { - /* Jitter is applied perpendicular to the mouse movement vector - * - We compute all effects in screenspace (since it's easier) - * and then project these to get the points/distances in - * view-space as needed. - */ - float mvec[2], svec[2]; - - /* mouse movement in ints -> floats */ - mvec[0] = (float)(gso->mval[0] - gso->mval_prev[0]); - mvec[1] = (float)(gso->mval[1] - gso->mval_prev[1]); - - /* rotate mvec by 90 degrees... */ - svec[0] = -mvec[1]; - svec[1] = mvec[0]; - - /* scale the displacement by the random displacement, and apply */ - if (BLI_rng_get_float(gso->rng) > 0.5f) { - mul_v2_fl(svec, -fac); - } - else { - mul_v2_fl(svec, fac); - } - - //printf("%f %f (%f), nco = {%f %f}, co = %d %d\n", svec[0], svec[1], fac, nco[0], nco[1], co[0], co[1]); - - /* convert to dataspace */ - if (gps->flag & GP_STROKE_3DSPACE) { - /* 3D: Project to 3D space */ - if (gso->sa->spacetype == SPACE_VIEW3D) { - bool flip; - RegionView3D *rv3d = gso->ar->regiondata; - float zfac = ED_view3d_calc_zfac(rv3d, &pt->x, &flip); - if (flip == false) { - float dvec[3]; - ED_view3d_win_to_delta(gso->gsc.ar, svec, dvec, zfac); - add_v3_v3(&pt->x, dvec); - /* compute lock axis */ - gpsculpt_compute_lock_axis(gso, pt, save_pt); - } - } - else { - /* ERROR */ - BLI_assert(!"3D stroke being sculpted in non-3D view"); - } - } - else { - /* 2D: As-is */ - // XXX: v2d scaling/offset? - float nco[2]; - nco[0] = (float)co[0] + svec[0]; - nco[1] = (float)co[1] + svec[1]; - - copy_v2_v2(&pt->x, nco); - } - } - /* apply random to strength */ - if (gso->settings->flag & GP_SCULPT_SETT_FLAG_APPLY_STRENGTH) { - if (BLI_rng_get_float(gso->rng) > 0.5f) { - pt->strength += fac; - } - else { - pt->strength -= fac; - } - CLAMP_MIN(pt->strength, 0.0f); - CLAMP_MAX(pt->strength, 1.0f); - } - /* apply random to thickness (use pressure) */ - if (gso->settings->flag & GP_SCULPT_SETT_FLAG_APPLY_THICKNESS) { - if (BLI_rng_get_float(gso->rng) > 0.5f) { - pt->pressure += fac; - } - else { - pt->pressure -= fac; - } - /* only limit lower value */ - CLAMP_MIN(pt->pressure, 0.0f); - } - /* apply random to UV (use pressure) */ - if (gso->settings->flag & GP_SCULPT_SETT_FLAG_APPLY_UV) { - if (BLI_rng_get_float(gso->rng) > 0.5f) { - pt->uv_rot += fac; - } - else { - pt->uv_rot -= fac; - } - CLAMP(pt->uv_rot, -M_PI_2, M_PI_2); - } - - gps->flag |= GP_STROKE_RECALC_GEOMETRY; - - /* done */ - return true; + 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, + * as well as the strength of the brush + */ + const float inf = gp_brush_influence_calc(gso, radius, co) / 2.0f; + const float fac = BLI_rng_get_float(gso->rng) * inf; + /* need one flag enabled by default */ + if ((gso->settings->flag & + (GP_SCULPT_SETT_FLAG_APPLY_POSITION | GP_SCULPT_SETT_FLAG_APPLY_STRENGTH | + GP_SCULPT_SETT_FLAG_APPLY_THICKNESS | GP_SCULPT_SETT_FLAG_APPLY_UV)) == 0) { + gso->settings->flag |= GP_SCULPT_SETT_FLAG_APPLY_POSITION; + } + + /* apply random to position */ + if (gso->settings->flag & GP_SCULPT_SETT_FLAG_APPLY_POSITION) { + /* Jitter is applied perpendicular to the mouse movement vector + * - We compute all effects in screenspace (since it's easier) + * and then project these to get the points/distances in + * view-space as needed. + */ + float mvec[2], svec[2]; + + /* mouse movement in ints -> floats */ + mvec[0] = (float)(gso->mval[0] - gso->mval_prev[0]); + mvec[1] = (float)(gso->mval[1] - gso->mval_prev[1]); + + /* rotate mvec by 90 degrees... */ + svec[0] = -mvec[1]; + svec[1] = mvec[0]; + + /* scale the displacement by the random displacement, and apply */ + if (BLI_rng_get_float(gso->rng) > 0.5f) { + mul_v2_fl(svec, -fac); + } + else { + mul_v2_fl(svec, fac); + } + + //printf("%f %f (%f), nco = {%f %f}, co = %d %d\n", svec[0], svec[1], fac, nco[0], nco[1], co[0], co[1]); + + /* convert to dataspace */ + if (gps->flag & GP_STROKE_3DSPACE) { + /* 3D: Project to 3D space */ + if (gso->sa->spacetype == SPACE_VIEW3D) { + bool flip; + RegionView3D *rv3d = gso->ar->regiondata; + float zfac = ED_view3d_calc_zfac(rv3d, &pt->x, &flip); + if (flip == false) { + float dvec[3]; + ED_view3d_win_to_delta(gso->gsc.ar, svec, dvec, zfac); + add_v3_v3(&pt->x, dvec); + /* compute lock axis */ + gpsculpt_compute_lock_axis(gso, pt, save_pt); + } + } + else { + /* ERROR */ + BLI_assert(!"3D stroke being sculpted in non-3D view"); + } + } + else { + /* 2D: As-is */ + // XXX: v2d scaling/offset? + float nco[2]; + nco[0] = (float)co[0] + svec[0]; + nco[1] = (float)co[1] + svec[1]; + + copy_v2_v2(&pt->x, nco); + } + } + /* apply random to strength */ + if (gso->settings->flag & GP_SCULPT_SETT_FLAG_APPLY_STRENGTH) { + if (BLI_rng_get_float(gso->rng) > 0.5f) { + pt->strength += fac; + } + else { + pt->strength -= fac; + } + CLAMP_MIN(pt->strength, 0.0f); + CLAMP_MAX(pt->strength, 1.0f); + } + /* apply random to thickness (use pressure) */ + if (gso->settings->flag & GP_SCULPT_SETT_FLAG_APPLY_THICKNESS) { + if (BLI_rng_get_float(gso->rng) > 0.5f) { + pt->pressure += fac; + } + else { + pt->pressure -= fac; + } + /* only limit lower value */ + CLAMP_MIN(pt->pressure, 0.0f); + } + /* apply random to UV (use pressure) */ + if (gso->settings->flag & GP_SCULPT_SETT_FLAG_APPLY_UV) { + if (BLI_rng_get_float(gso->rng) > 0.5f) { + pt->uv_rot += fac; + } + else { + pt->uv_rot -= fac; + } + CLAMP(pt->uv_rot, -M_PI_2, M_PI_2); + } + + gps->flag |= GP_STROKE_RECALC_GEOMETRY; + + /* 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]) + tGP_BrushEditData *gso, bGPDstroke *gps, int pt_index, const int radius, const int co[2]) { - /* create dvert */ - BKE_gpencil_dvert_ensure(gps); - - bGPDspoint *pt = gps->points + pt_index; - MDeformVert *dvert = gps->dvert + 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; - } - } - else { - bDeformGroup *defgroup = BLI_findlink(&gso->object->defbase, gso->vrgroup); - if (defgroup->flag & DG_LOCK_WEIGHT) { - return false; - } - } - /* get current weight */ - MDeformWeight *dw = defvert_verify_index(dvert, gso->vrgroup); - float curweight = dw ? dw->weight : 0.0f; - - if (gp_brush_invert_check(gso)) { - /* reduce weight */ - curweight -= inf; - } - else { - /* increase weight */ - curweight += inf; - } - - /* verify target weight */ - CLAMP_MAX(curweight, gso->gp_brush->weight); - - CLAMP(curweight, 0.0f, 1.0f); - if (dw) { - dw->weight = curweight; - } - - /* weight should stay within [0.0, 1.0] */ - if (pt->pressure < 0.0f) - pt->pressure = 0.0f; - - return true; + /* create dvert */ + BKE_gpencil_dvert_ensure(gps); + + bGPDspoint *pt = gps->points + pt_index; + MDeformVert *dvert = gps->dvert + 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; + } + } + else { + bDeformGroup *defgroup = BLI_findlink(&gso->object->defbase, gso->vrgroup); + if (defgroup->flag & DG_LOCK_WEIGHT) { + return false; + } + } + /* get current weight */ + MDeformWeight *dw = defvert_verify_index(dvert, gso->vrgroup); + float curweight = dw ? dw->weight : 0.0f; + + if (gp_brush_invert_check(gso)) { + /* reduce weight */ + curweight -= inf; + } + else { + /* increase weight */ + curweight += inf; + } + + /* verify target weight */ + CLAMP_MAX(curweight, gso->gp_brush->weight); + + CLAMP(curweight, 0.0f, 1.0f); + if (dw) { + dw->weight = curweight; + } + + /* weight should stay within [0.0, 1.0] */ + if (pt->pressure < 0.0f) + pt->pressure = 0.0f; + + return true; } - - /* ************************************************ */ /* Non Callback-Based Brushes */ @@ -980,217 +948,218 @@ static bool gp_brush_weight_apply( /* Custom state data for clone brush */ typedef struct tGPSB_CloneBrushData { - /* midpoint of the strokes on the clipboard */ - float buffer_midpoint[3]; + /* midpoint of the strokes on the clipboard */ + float buffer_midpoint[3]; - /* number of strokes in the paste buffer (and/or to be created each time) */ - size_t totitems; + /* number of strokes in the paste buffer (and/or to be created each time) */ + size_t totitems; - /* for "stamp" mode, the currently pasted brushes */ - bGPDstroke **new_strokes; + /* for "stamp" mode, the currently pasted brushes */ + bGPDstroke **new_strokes; - /* mapping from colors referenced per stroke, to the new colours in the "pasted" strokes */ - GHash *new_colors; + /* mapping from colors referenced per stroke, to the new colours in the "pasted" strokes */ + GHash *new_colors; } tGPSB_CloneBrushData; /* Initialise "clone" brush data */ static void gp_brush_clone_init(bContext *C, tGP_BrushEditData *gso) { - tGPSB_CloneBrushData *data; - bGPDstroke *gps; - - /* init custom data */ - gso->customdata = data = MEM_callocN(sizeof(tGPSB_CloneBrushData), "CloneBrushData"); - - /* compute midpoint of strokes on clipboard */ - for (gps = gp_strokes_copypastebuf.first; gps; gps = gps->next) { - if (ED_gpencil_stroke_can_use(C, gps)) { - const float dfac = 1.0f / ((float)gps->totpoints); - float mid[3] = {0.0f}; - - bGPDspoint *pt; - int i; - - /* compute midpoint of this stroke */ - for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { - float co[3]; - - mul_v3_v3fl(co, &pt->x, dfac); - add_v3_v3(mid, co); - } - - /* combine this stroke's data with the main data */ - add_v3_v3(data->buffer_midpoint, mid); - data->totitems++; - } - } - - /* Divide the midpoint by the number of strokes, to finish averaging it */ - if (data->totitems > 1) { - mul_v3_fl(data->buffer_midpoint, 1.0f / (float)data->totitems); - } - - /* Create a buffer for storing the current strokes */ - if (1 /*gso->brush->mode == GP_EDITBRUSH_CLONE_MODE_STAMP*/) { - data->new_strokes = MEM_callocN(sizeof(bGPDstroke *) * data->totitems, "cloned strokes ptr array"); - } - - /* Init colormap for mapping between the pasted stroke's source color (names) - * and the final colours that will be used here instead. - */ - data->new_colors = gp_copybuf_validate_colormap(C); + tGPSB_CloneBrushData *data; + bGPDstroke *gps; + + /* init custom data */ + gso->customdata = data = MEM_callocN(sizeof(tGPSB_CloneBrushData), "CloneBrushData"); + + /* compute midpoint of strokes on clipboard */ + for (gps = gp_strokes_copypastebuf.first; gps; gps = gps->next) { + if (ED_gpencil_stroke_can_use(C, gps)) { + const float dfac = 1.0f / ((float)gps->totpoints); + float mid[3] = {0.0f}; + + bGPDspoint *pt; + int i; + + /* compute midpoint of this stroke */ + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + float co[3]; + + mul_v3_v3fl(co, &pt->x, dfac); + add_v3_v3(mid, co); + } + + /* combine this stroke's data with the main data */ + add_v3_v3(data->buffer_midpoint, mid); + data->totitems++; + } + } + + /* Divide the midpoint by the number of strokes, to finish averaging it */ + if (data->totitems > 1) { + mul_v3_fl(data->buffer_midpoint, 1.0f / (float)data->totitems); + } + + /* Create a buffer for storing the current strokes */ + if (1 /*gso->brush->mode == GP_EDITBRUSH_CLONE_MODE_STAMP*/) { + data->new_strokes = MEM_callocN(sizeof(bGPDstroke *) * data->totitems, + "cloned strokes ptr array"); + } + + /* Init colormap for mapping between the pasted stroke's source color (names) + * and the final colours that will be used here instead. + */ + data->new_colors = gp_copybuf_validate_colormap(C); } /* Free custom data used for "clone" brush */ static void gp_brush_clone_free(tGP_BrushEditData *gso) { - tGPSB_CloneBrushData *data = gso->customdata; - - /* free strokes array */ - if (data->new_strokes) { - MEM_freeN(data->new_strokes); - data->new_strokes = NULL; - } - - /* free copybuf colormap */ - if (data->new_colors) { - BLI_ghash_free(data->new_colors, NULL, NULL); - data->new_colors = NULL; - } - - /* free the customdata itself */ - MEM_freeN(data); - gso->customdata = NULL; + tGPSB_CloneBrushData *data = gso->customdata; + + /* free strokes array */ + if (data->new_strokes) { + MEM_freeN(data->new_strokes); + data->new_strokes = NULL; + } + + /* free copybuf colormap */ + if (data->new_colors) { + BLI_ghash_free(data->new_colors, NULL, NULL); + data->new_colors = NULL; + } + + /* free the customdata itself */ + MEM_freeN(data); + gso->customdata = NULL; } /* Create new copies of the strokes on the clipboard */ static void gp_brush_clone_add(bContext *C, tGP_BrushEditData *gso) { - tGPSB_CloneBrushData *data = gso->customdata; - - Object *ob = CTX_data_active_object(C); - bGPDlayer *gpl = CTX_data_active_gpencil_layer(C); - Depsgraph *depsgraph = CTX_data_depsgraph(C); - int cfra_eval = (int)DEG_get_ctime(depsgraph); - - bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, cfra_eval, GP_GETFRAME_ADD_NEW); - bGPDstroke *gps; - - float delta[3]; - size_t strokes_added = 0; - - /* Compute amount to offset the points by */ - /* NOTE: This assumes that screenspace strokes are NOT used in the 3D view... */ - - gp_brush_calc_midpoint(gso); /* this puts the cursor location into gso->dvec */ - sub_v3_v3v3(delta, gso->dvec, data->buffer_midpoint); - - /* Copy each stroke into the layer */ - for (gps = gp_strokes_copypastebuf.first; gps; gps = gps->next) { - if (ED_gpencil_stroke_can_use(C, gps)) { - bGPDstroke *new_stroke; - bGPDspoint *pt; - int i; - - /* Make a new stroke */ - new_stroke = MEM_dupallocN(gps); - - new_stroke->points = MEM_dupallocN(gps->points); - if (gps->dvert != NULL) { - new_stroke->dvert = MEM_dupallocN(gps->dvert); - BKE_gpencil_stroke_weights_duplicate(gps, new_stroke); - } - new_stroke->triangles = MEM_dupallocN(gps->triangles); - - new_stroke->next = new_stroke->prev = NULL; - BLI_addtail(&gpf->strokes, new_stroke); - - /* Fix color references */ - Material *ma = BLI_ghash_lookup(data->new_colors, &new_stroke->mat_nr); - gps->mat_nr = BKE_gpencil_object_material_get_index(ob, ma); - if (!ma || gps->mat_nr) { - gps->mat_nr = 0; - } - /* Adjust all the stroke's points, so that the strokes - * get pasted relative to where the cursor is now - */ - for (i = 0, pt = new_stroke->points; i < new_stroke->totpoints; i++, pt++) { - /* assume that the delta can just be applied, and then everything works */ - add_v3_v3(&pt->x, delta); - } - - /* Store ref for later */ - if ((data->new_strokes) && (strokes_added < data->totitems)) { - data->new_strokes[strokes_added] = new_stroke; - strokes_added++; - } - } - } + tGPSB_CloneBrushData *data = gso->customdata; + + Object *ob = CTX_data_active_object(C); + bGPDlayer *gpl = CTX_data_active_gpencil_layer(C); + Depsgraph *depsgraph = CTX_data_depsgraph(C); + int cfra_eval = (int)DEG_get_ctime(depsgraph); + + bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, cfra_eval, GP_GETFRAME_ADD_NEW); + bGPDstroke *gps; + + float delta[3]; + size_t strokes_added = 0; + + /* Compute amount to offset the points by */ + /* NOTE: This assumes that screenspace strokes are NOT used in the 3D view... */ + + gp_brush_calc_midpoint(gso); /* this puts the cursor location into gso->dvec */ + sub_v3_v3v3(delta, gso->dvec, data->buffer_midpoint); + + /* Copy each stroke into the layer */ + for (gps = gp_strokes_copypastebuf.first; gps; gps = gps->next) { + if (ED_gpencil_stroke_can_use(C, gps)) { + bGPDstroke *new_stroke; + bGPDspoint *pt; + int i; + + /* Make a new stroke */ + new_stroke = MEM_dupallocN(gps); + + new_stroke->points = MEM_dupallocN(gps->points); + if (gps->dvert != NULL) { + new_stroke->dvert = MEM_dupallocN(gps->dvert); + BKE_gpencil_stroke_weights_duplicate(gps, new_stroke); + } + new_stroke->triangles = MEM_dupallocN(gps->triangles); + + new_stroke->next = new_stroke->prev = NULL; + BLI_addtail(&gpf->strokes, new_stroke); + + /* Fix color references */ + Material *ma = BLI_ghash_lookup(data->new_colors, &new_stroke->mat_nr); + gps->mat_nr = BKE_gpencil_object_material_get_index(ob, ma); + if (!ma || gps->mat_nr) { + gps->mat_nr = 0; + } + /* Adjust all the stroke's points, so that the strokes + * get pasted relative to where the cursor is now + */ + for (i = 0, pt = new_stroke->points; i < new_stroke->totpoints; i++, pt++) { + /* assume that the delta can just be applied, and then everything works */ + add_v3_v3(&pt->x, delta); + } + + /* Store ref for later */ + if ((data->new_strokes) && (strokes_added < data->totitems)) { + data->new_strokes[strokes_added] = new_stroke; + strokes_added++; + } + } + } } /* Move newly-added strokes around - "Stamp" mode of the Clone brush */ static void gp_brush_clone_adjust(tGP_BrushEditData *gso) { - tGPSB_CloneBrushData *data = gso->customdata; - size_t snum; - - /* Compute the amount of movement to apply (overwrites dvec) */ - gp_brush_grab_calc_dvec(gso); - - /* For each of the stored strokes, apply the offset to each point */ - /* NOTE: Again this assumes that in the 3D view, we only have 3d space and not screenspace strokes... */ - for (snum = 0; snum < data->totitems; snum++) { - bGPDstroke *gps = data->new_strokes[snum]; - bGPDspoint *pt; - int i; - - for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { - if (gso->gp_brush->flag & GP_SCULPT_FLAG_USE_FALLOFF) { - /* "Smudge" Effect when falloff is enabled */ - float delta[3] = {0.0f}; - int sco[2] = {0}; - float influence; - - /* compute influence on point */ - gp_point_to_xy(&gso->gsc, gps, pt, &sco[0], &sco[1]); - influence = gp_brush_influence_calc(gso, gso->gp_brush->size, sco); - - /* adjust the amount of displacement to apply */ - mul_v3_v3fl(delta, gso->dvec, influence); - - /* apply */ - add_v3_v3(&pt->x, delta); - } - else { - /* Just apply the offset - All points move perfectly in sync with the cursor */ - add_v3_v3(&pt->x, gso->dvec); - } - } - } + tGPSB_CloneBrushData *data = gso->customdata; + size_t snum; + + /* Compute the amount of movement to apply (overwrites dvec) */ + gp_brush_grab_calc_dvec(gso); + + /* For each of the stored strokes, apply the offset to each point */ + /* NOTE: Again this assumes that in the 3D view, we only have 3d space and not screenspace strokes... */ + for (snum = 0; snum < data->totitems; snum++) { + bGPDstroke *gps = data->new_strokes[snum]; + bGPDspoint *pt; + int i; + + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + if (gso->gp_brush->flag & GP_SCULPT_FLAG_USE_FALLOFF) { + /* "Smudge" Effect when falloff is enabled */ + float delta[3] = {0.0f}; + int sco[2] = {0}; + float influence; + + /* compute influence on point */ + gp_point_to_xy(&gso->gsc, gps, pt, &sco[0], &sco[1]); + influence = gp_brush_influence_calc(gso, gso->gp_brush->size, sco); + + /* adjust the amount of displacement to apply */ + mul_v3_v3fl(delta, gso->dvec, influence); + + /* apply */ + add_v3_v3(&pt->x, delta); + } + else { + /* Just apply the offset - All points move perfectly in sync with the cursor */ + add_v3_v3(&pt->x, gso->dvec); + } + } + } } /* Entrypoint for applying "clone" brush */ static bool gpsculpt_brush_apply_clone(bContext *C, tGP_BrushEditData *gso) { - /* Which "mode" are we operating in? */ - if (gso->first) { - /* Create initial clones */ - gp_brush_clone_add(C, gso); - } - else { - /* Stamp or Continuous Mode */ - if (1 /*gso->brush->mode == GP_EDITBRUSH_CLONE_MODE_STAMP*/) { - /* Stamp - Proceed to translate the newly added strokes */ - gp_brush_clone_adjust(gso); - } - else { - /* Continuous - Just keep pasting everytime we move */ - /* TODO: The spacing of repeat should be controlled using a "stepsize" or similar property? */ - gp_brush_clone_add(C, gso); - } - } - - return true; + /* Which "mode" are we operating in? */ + if (gso->first) { + /* Create initial clones */ + gp_brush_clone_add(C, gso); + } + else { + /* Stamp or Continuous Mode */ + if (1 /*gso->brush->mode == GP_EDITBRUSH_CLONE_MODE_STAMP*/) { + /* Stamp - Proceed to translate the newly added strokes */ + gp_brush_clone_adjust(gso); + } + else { + /* Continuous - Just keep pasting everytime we move */ + /* TODO: The spacing of repeat should be controlled using a "stepsize" or similar property? */ + gp_brush_clone_add(C, gso); + } + } + + return true; } /* ************************************************ */ @@ -1198,18 +1167,19 @@ static bool gpsculpt_brush_apply_clone(bContext *C, tGP_BrushEditData *gso) static void gpsculpt_brush_header_set(bContext *C, tGP_BrushEditData *gso) { - const char *brush_name = NULL; - char str[UI_MAX_DRAW_STR] = ""; + const char *brush_name = NULL; + char str[UI_MAX_DRAW_STR] = ""; - RNA_enum_name(rna_enum_gpencil_sculpt_brush_items, gso->brush_type, &brush_name); + RNA_enum_name(rna_enum_gpencil_sculpt_brush_items, gso->brush_type, &brush_name); - BLI_snprintf(str, sizeof(str), - IFACE_("GPencil Sculpt: %s Stroke | LMB to paint | RMB/Escape to Exit" - " | Ctrl to Invert Action | Wheel Up/Down for Size " - " | Shift-Wheel Up/Down for Strength"), - (brush_name) ? brush_name : "<?>"); + BLI_snprintf(str, + sizeof(str), + IFACE_("GPencil Sculpt: %s Stroke | LMB to paint | RMB/Escape to Exit" + " | Ctrl to Invert Action | Wheel Up/Down for Size " + " | Shift-Wheel Up/Down for Strength"), + (brush_name) ? brush_name : "<?>"); - ED_workspace_status_text(C, str); + ED_workspace_status_text(C, str); } /* ************************************************ */ @@ -1219,586 +1189,576 @@ static void gpsculpt_brush_header_set(bContext *C, tGP_BrushEditData *gso) static bool gpsculpt_brush_init(bContext *C, wmOperator *op) { - Scene *scene = CTX_data_scene(C); - ToolSettings *ts = CTX_data_tool_settings(C); - Object *ob = CTX_data_active_object(C); + Scene *scene = CTX_data_scene(C); + ToolSettings *ts = CTX_data_tool_settings(C); + Object *ob = CTX_data_active_object(C); - const bool is_weight_mode = ob->mode == OB_MODE_WEIGHT_GPENCIL; - /* set the brush using the tool */ + const bool is_weight_mode = ob->mode == OB_MODE_WEIGHT_GPENCIL; + /* set the brush using the tool */ #if 0 - GP_Sculpt_Settings *gset = &ts->gp_sculpt; - eGP_Sculpt_Types mode = is_weight_mode ? gset->weighttype : gset->brushtype; + GP_Sculpt_Settings *gset = &ts->gp_sculpt; + eGP_Sculpt_Types mode = is_weight_mode ? gset->weighttype : gset->brushtype; #endif - tGP_BrushEditData *gso; - - /* setup operator data */ - gso = MEM_callocN(sizeof(tGP_BrushEditData), "tGP_BrushEditData"); - op->customdata = gso; - - gso->depsgraph = CTX_data_depsgraph(C); - /* store state */ - gso->settings = gpsculpt_get_settings(scene); - gso->gp_brush = gpsculpt_get_brush(scene, is_weight_mode); - gso->is_weight_mode = is_weight_mode; - - if (is_weight_mode) { - gso->brush_type = gso->settings->weighttype; - } - else { - gso->brush_type = gso->settings->brushtype; - } - - /* Random generator, only init once. */ - uint rng_seed = (uint)(PIL_check_seconds_timer_i() & UINT_MAX); - rng_seed ^= POINTER_AS_UINT(gso); - gso->rng = BLI_rng_new(rng_seed); - - gso->is_painting = false; - gso->first = true; - - gso->gpd = ED_gpencil_data_get_active(C); - gso->cfra = INT_MAX; /* NOTE: So that first stroke will get handled in init_stroke() */ - - /* some brushes cannot use pressure for radius */ - if (ELEM(gso->brush_type, GP_SCULPT_TYPE_GRAB, GP_SCULPT_TYPE_CLONE)) { - gso->gp_brush->flag &= ~GP_SCULPT_FLAG_PRESSURE_RADIUS; - } - - 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->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_SCULPT_SETT_FLAG_FRAME_FALLOFF) != 0; - - /* Init multi-edit 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 */ - switch (gso->brush_type) { - case GP_SCULPT_TYPE_CLONE: - { - bGPDstroke *gps; - bool found = false; - - /* check that there are some usable strokes in the buffer */ - for (gps = gp_strokes_copypastebuf.first; gps; gps = gps->next) { - if (ED_gpencil_stroke_can_use(C, gps)) { - found = true; - break; - } - } - - if (found == false) { - /* STOP HERE! Nothing to paste! */ - BKE_report(op->reports, RPT_ERROR, - "Copy some strokes to the clipboard before using the Clone brush to paste copies of them"); - - MEM_freeN(gso); - op->customdata = NULL; - return false; - } - else { - /* initialise customdata */ - gp_brush_clone_init(C, gso); - } - break; - } - - case GP_SCULPT_TYPE_GRAB: - { - /* initialise the cache needed for this brush */ - gso->stroke_customdata = BLI_ghash_ptr_new("GP Grab Brush - Strokes Hash"); - break; - } - - /* Others - No customdata needed */ - default: - break; - } - - - /* setup space conversions */ - gp_point_conversion_init(C, &gso->gsc); - - /* update header */ - gpsculpt_brush_header_set(C, gso); - - /* setup cursor drawing */ - //WM_cursor_modal_set(CTX_wm_window(C), BC_CROSSCURSOR); - if (gso->sa->spacetype != SPACE_VIEW3D) { - ED_gpencil_toggle_brush_cursor(C, true, NULL); - } - return true; + tGP_BrushEditData *gso; + + /* setup operator data */ + gso = MEM_callocN(sizeof(tGP_BrushEditData), "tGP_BrushEditData"); + op->customdata = gso; + + gso->depsgraph = CTX_data_depsgraph(C); + /* store state */ + gso->settings = gpsculpt_get_settings(scene); + gso->gp_brush = gpsculpt_get_brush(scene, is_weight_mode); + gso->is_weight_mode = is_weight_mode; + + if (is_weight_mode) { + gso->brush_type = gso->settings->weighttype; + } + else { + gso->brush_type = gso->settings->brushtype; + } + + /* Random generator, only init once. */ + uint rng_seed = (uint)(PIL_check_seconds_timer_i() & UINT_MAX); + rng_seed ^= POINTER_AS_UINT(gso); + gso->rng = BLI_rng_new(rng_seed); + + gso->is_painting = false; + gso->first = true; + + gso->gpd = ED_gpencil_data_get_active(C); + gso->cfra = INT_MAX; /* NOTE: So that first stroke will get handled in init_stroke() */ + + /* some brushes cannot use pressure for radius */ + if (ELEM(gso->brush_type, GP_SCULPT_TYPE_GRAB, GP_SCULPT_TYPE_CLONE)) { + gso->gp_brush->flag &= ~GP_SCULPT_FLAG_PRESSURE_RADIUS; + } + + 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->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_SCULPT_SETT_FLAG_FRAME_FALLOFF) != 0; + + /* Init multi-edit 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 */ + switch (gso->brush_type) { + case GP_SCULPT_TYPE_CLONE: { + bGPDstroke *gps; + bool found = false; + + /* check that there are some usable strokes in the buffer */ + for (gps = gp_strokes_copypastebuf.first; gps; gps = gps->next) { + if (ED_gpencil_stroke_can_use(C, gps)) { + found = true; + break; + } + } + + if (found == false) { + /* STOP HERE! Nothing to paste! */ + BKE_report(op->reports, + RPT_ERROR, + "Copy some strokes to the clipboard before using the Clone brush to paste " + "copies of them"); + + MEM_freeN(gso); + op->customdata = NULL; + return false; + } + else { + /* initialise customdata */ + gp_brush_clone_init(C, gso); + } + break; + } + + case GP_SCULPT_TYPE_GRAB: { + /* initialise the cache needed for this brush */ + gso->stroke_customdata = BLI_ghash_ptr_new("GP Grab Brush - Strokes Hash"); + break; + } + + /* Others - No customdata needed */ + default: + break; + } + + /* setup space conversions */ + gp_point_conversion_init(C, &gso->gsc); + + /* update header */ + gpsculpt_brush_header_set(C, gso); + + /* setup cursor drawing */ + //WM_cursor_modal_set(CTX_wm_window(C), BC_CROSSCURSOR); + if (gso->sa->spacetype != SPACE_VIEW3D) { + ED_gpencil_toggle_brush_cursor(C, true, NULL); + } + return true; } static void gpsculpt_brush_exit(bContext *C, wmOperator *op) { - tGP_BrushEditData *gso = op->customdata; - wmWindow *win = CTX_wm_window(C); - - /* free brush-specific data */ - switch (gso->brush_type) { - case GP_SCULPT_TYPE_GRAB: - { - /* Free per-stroke customdata - * - Keys don't need to be freed, as those are the strokes - * - Values assigned to those keys do, as they are custom structs - */ - BLI_ghash_free(gso->stroke_customdata, NULL, gp_brush_grab_stroke_free); - break; - } - - case GP_SCULPT_TYPE_CLONE: - { - /* Free customdata */ - gp_brush_clone_free(gso); - break; - } - - default: - break; - } - - /* unregister timer (only used for realtime) */ - if (gso->timer) { - WM_event_remove_timer(CTX_wm_manager(C), win, gso->timer); - } - - if (gso->rng != NULL) { - BLI_rng_free(gso->rng); - } - - /* disable cursor and headerprints */ - ED_workspace_status_text(C, NULL); - WM_cursor_modal_restore(win); - if (gso->sa->spacetype != SPACE_VIEW3D) { - ED_gpencil_toggle_brush_cursor(C, false, NULL); - } - - /* disable temp invert flag */ - gso->gp_brush->flag &= ~GP_SCULPT_FLAG_TMP_INVERT; - - /* free operator data */ - MEM_freeN(gso); - op->customdata = NULL; + tGP_BrushEditData *gso = op->customdata; + wmWindow *win = CTX_wm_window(C); + + /* free brush-specific data */ + switch (gso->brush_type) { + case GP_SCULPT_TYPE_GRAB: { + /* Free per-stroke customdata + * - Keys don't need to be freed, as those are the strokes + * - Values assigned to those keys do, as they are custom structs + */ + BLI_ghash_free(gso->stroke_customdata, NULL, gp_brush_grab_stroke_free); + break; + } + + case GP_SCULPT_TYPE_CLONE: { + /* Free customdata */ + gp_brush_clone_free(gso); + break; + } + + default: + break; + } + + /* unregister timer (only used for realtime) */ + if (gso->timer) { + WM_event_remove_timer(CTX_wm_manager(C), win, gso->timer); + } + + if (gso->rng != NULL) { + BLI_rng_free(gso->rng); + } + + /* disable cursor and headerprints */ + ED_workspace_status_text(C, NULL); + WM_cursor_modal_restore(win); + if (gso->sa->spacetype != SPACE_VIEW3D) { + ED_gpencil_toggle_brush_cursor(C, false, NULL); + } + + /* disable temp invert flag */ + gso->gp_brush->flag &= ~GP_SCULPT_FLAG_TMP_INVERT; + + /* free operator data */ + MEM_freeN(gso); + op->customdata = NULL; } /* poll callback for stroke sculpting operator(s) */ static bool gpsculpt_brush_poll(bContext *C) { - /* NOTE: this is a bit slower, but is the most accurate... */ - return CTX_DATA_COUNT(C, editable_gpencil_strokes) != 0; + /* NOTE: this is a bit slower, but is the most accurate... */ + return CTX_DATA_COUNT(C, editable_gpencil_strokes) != 0; } /* Init Sculpt Stroke ---------------------------------- */ static void gpsculpt_brush_init_stroke(tGP_BrushEditData *gso) { - bGPdata *gpd = gso->gpd; - - bGPDlayer *gpl; - int cfra_eval = (int)DEG_get_ctime(gso->depsgraph); - - /* only try to add a new frame if this is the first stroke, or the frame has changed */ - if ((gpd == NULL) || (cfra_eval == gso->cfra)) - return; - - /* go through each layer, and ensure that we've got a valid frame to use */ - for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { - /* only editable and visible layers are considered */ - if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) { - bGPDframe *gpf = gpl->actframe; - - /* Make a new frame to work on if the layer's frame and the current scene frame don't match up - * - This is useful when animating as it saves that "uh-oh" moment when you realize you've - * spent too much time editing the wrong frame... - */ - // XXX: should this be allowed when framelock is enabled? - if (gpf->framenum != cfra_eval) { - BKE_gpencil_frame_addcopy(gpl, cfra_eval); - } - } - } - - /* save off new current frame, so that next update works fine */ - gso->cfra = cfra_eval; + bGPdata *gpd = gso->gpd; + + bGPDlayer *gpl; + int cfra_eval = (int)DEG_get_ctime(gso->depsgraph); + + /* only try to add a new frame if this is the first stroke, or the frame has changed */ + if ((gpd == NULL) || (cfra_eval == gso->cfra)) + return; + + /* go through each layer, and ensure that we've got a valid frame to use */ + for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { + /* only editable and visible layers are considered */ + if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) { + bGPDframe *gpf = gpl->actframe; + + /* Make a new frame to work on if the layer's frame and the current scene frame don't match up + * - This is useful when animating as it saves that "uh-oh" moment when you realize you've + * spent too much time editing the wrong frame... + */ + // XXX: should this be allowed when framelock is enabled? + if (gpf->framenum != cfra_eval) { + BKE_gpencil_frame_addcopy(gpl, cfra_eval); + } + } + } + + /* save off new current frame, so that next update works fine */ + gso->cfra = cfra_eval; } /* Apply ----------------------------------------------- */ /* Apply brush operation to points in this stroke */ -static bool gpsculpt_brush_do_stroke( - tGP_BrushEditData *gso, bGPDstroke *gps, - float diff_mat[4][4], GP_BrushApplyCb apply) +static bool gpsculpt_brush_do_stroke(tGP_BrushEditData *gso, + bGPDstroke *gps, + float diff_mat[4][4], + GP_BrushApplyCb apply) { - GP_SpaceConversion *gsc = &gso->gsc; - rcti *rect = &gso->brush_rect; - GP_Sculpt_Data *gp_brush = gso->gp_brush; - const int radius = (gp_brush->flag & GP_SCULPT_FLAG_PRESSURE_RADIUS) ? gso->gp_brush->size * gso->pressure : gso->gp_brush->size; - - bGPDspoint *pt1, *pt2; - int pc1[2] = {0}; - int pc2[2] = {0}; - int i; - bool include_last = false; - bool changed = false; - - if (gps->totpoints == 1) { - 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 */ - if ((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1])) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) { - /* only check if point is inside */ - int mval_i[2]; - 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); - } - } - } - else { - /* Loop over the points in the stroke, checking for intersections - * - an intersection means that we touched the stroke - */ - for (i = 0; (i + 1) < gps->totpoints; i++) { - /* Get points to work with */ - pt1 = gps->points + i; - pt2 = gps->points + i + 1; - - /* Skip if neither one is selected (and we are only allowed to edit/consider selected points) */ - if (gso->settings->flag & GP_SCULPT_SETT_FLAG_SELECT_MASK) { - if (!(pt1->flag & GP_SPOINT_SELECT) && !(pt2->flag & GP_SPOINT_SELECT)) { - include_last = false; - continue; - } - } - 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]); - - /* 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])) || - ((!ELEM(V2D_IS_CLIPPED, pc2[0], pc2[1])) && BLI_rcti_isect_pt(rect, pc2[0], pc2[1]))) - { - /* Check if point segment of stroke had anything to do with - * brush region (either within stroke painted, or on its lines) - * - this assumes that linewidth is irrelevant - */ - if (gp_stroke_inside_circle(gso->mval, gso->mval_prev, radius, pc1[0], pc1[1], pc2[0], pc2[1])) { - /* Apply operation to these points */ - bool ok = false; - - /* To each point individually... */ - ok = apply(gso, gps, i, radius, pc1); - - /* Only do the second point if this is the last segment, - * and it is unlikely that the point will get handled - * otherwise. - * - * NOTE: There is a small risk here that the second point wasn't really - * actually in-range. In that case, it only got in because - * the line linking the points was! - */ - if (i + 1 == gps->totpoints - 1) { - ok |= apply(gso, gps, i + 1, radius, pc2); - include_last = false; - } - else { - include_last = true; - } - - changed |= ok; - } - else if (include_last) { - /* This case is for cases where for whatever reason the second vert (1st here) doesn't get included - * because the whole edge isn't in bounds, 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; - } - } - } - } - - return changed; + GP_SpaceConversion *gsc = &gso->gsc; + rcti *rect = &gso->brush_rect; + GP_Sculpt_Data *gp_brush = gso->gp_brush; + const int radius = (gp_brush->flag & GP_SCULPT_FLAG_PRESSURE_RADIUS) ? + gso->gp_brush->size * gso->pressure : + gso->gp_brush->size; + + bGPDspoint *pt1, *pt2; + int pc1[2] = {0}; + int pc2[2] = {0}; + int i; + bool include_last = false; + bool changed = false; + + if (gps->totpoints == 1) { + 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 */ + if ((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1])) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) { + /* only check if point is inside */ + int mval_i[2]; + 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); + } + } + } + else { + /* Loop over the points in the stroke, checking for intersections + * - an intersection means that we touched the stroke + */ + for (i = 0; (i + 1) < gps->totpoints; i++) { + /* Get points to work with */ + pt1 = gps->points + i; + pt2 = gps->points + i + 1; + + /* Skip if neither one is selected (and we are only allowed to edit/consider selected points) */ + if (gso->settings->flag & GP_SCULPT_SETT_FLAG_SELECT_MASK) { + if (!(pt1->flag & GP_SPOINT_SELECT) && !(pt2->flag & GP_SPOINT_SELECT)) { + include_last = false; + continue; + } + } + 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]); + + /* 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])) || + ((!ELEM(V2D_IS_CLIPPED, pc2[0], pc2[1])) && BLI_rcti_isect_pt(rect, pc2[0], pc2[1]))) { + /* Check if point segment of stroke had anything to do with + * brush region (either within stroke painted, or on its lines) + * - this assumes that linewidth is irrelevant + */ + if (gp_stroke_inside_circle( + gso->mval, gso->mval_prev, radius, pc1[0], pc1[1], pc2[0], pc2[1])) { + /* Apply operation to these points */ + bool ok = false; + + /* To each point individually... */ + ok = apply(gso, gps, i, radius, pc1); + + /* Only do the second point if this is the last segment, + * and it is unlikely that the point will get handled + * otherwise. + * + * NOTE: There is a small risk here that the second point wasn't really + * actually in-range. In that case, it only got in because + * the line linking the points was! + */ + if (i + 1 == gps->totpoints - 1) { + ok |= apply(gso, gps, i + 1, radius, pc2); + include_last = false; + } + else { + include_last = true; + } + + changed |= ok; + } + else if (include_last) { + /* This case is for cases where for whatever reason the second vert (1st here) doesn't get included + * because the whole edge isn't in bounds, 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; + } + } + } + } + + 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]) + bContext *C, tGP_BrushEditData *gso, bGPDlayer *gpl, bGPDframe *gpf, float diff_mat[4][4]) { - bool changed = false; - Object *ob = CTX_data_active_object(C); - - 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(ob, gpl, gps) == false) { - continue; - } - - switch (gso->brush_type) { - case GP_SCULPT_TYPE_SMOOTH: /* Smooth strokes */ - { - changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_smooth_apply); - break; - } - - case GP_SCULPT_TYPE_THICKNESS: /* Adjust stroke thickness */ - { - changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_thickness_apply); - break; - } - - case GP_SCULPT_TYPE_STRENGTH: /* Adjust stroke color strength */ - { - changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_strength_apply); - break; - } - - 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; - } - break; - } - - case GP_SCULPT_TYPE_PUSH: /* Push points */ - { - changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_push_apply); - break; - } - - case GP_SCULPT_TYPE_PINCH: /* Pinch points */ - { - changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_pinch_apply); - break; - } - - case GP_SCULPT_TYPE_TWIST: /* Twist points around midpoint */ - { - changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_twist_apply); - break; - } - - case GP_SCULPT_TYPE_RANDOMIZE: /* Apply jitter */ - { - changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_randomize_apply); - break; - } - - case GP_SCULPT_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_GEOMETRY; - gps->tot_triangles = 0; - } - } - - return changed; + bool changed = false; + Object *ob = CTX_data_active_object(C); + + 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(ob, gpl, gps) == false) { + continue; + } + + switch (gso->brush_type) { + case GP_SCULPT_TYPE_SMOOTH: /* Smooth strokes */ + { + changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_smooth_apply); + break; + } + + case GP_SCULPT_TYPE_THICKNESS: /* Adjust stroke thickness */ + { + changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_thickness_apply); + break; + } + + case GP_SCULPT_TYPE_STRENGTH: /* Adjust stroke color strength */ + { + changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_strength_apply); + break; + } + + 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; + } + break; + } + + case GP_SCULPT_TYPE_PUSH: /* Push points */ + { + changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_push_apply); + break; + } + + case GP_SCULPT_TYPE_PINCH: /* Pinch points */ + { + changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_pinch_apply); + break; + } + + case GP_SCULPT_TYPE_TWIST: /* Twist points around midpoint */ + { + changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_twist_apply); + break; + } + + case GP_SCULPT_TYPE_RANDOMIZE: /* Apply jitter */ + { + changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_randomize_apply); + break; + } + + case GP_SCULPT_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_GEOMETRY; + gps->tot_triangles = 0; + } + } + + return changed; } /* Perform two-pass brushes which modify the existing strokes */ static bool gpsculpt_brush_apply_standard(bContext *C, tGP_BrushEditData *gso) { - ToolSettings *ts = CTX_data_tool_settings(C); - Depsgraph *depsgraph = CTX_data_depsgraph(C); - Object *obact = gso->object; - bGPdata *gpd = gso->gpd; - bool changed = false; - - /* Calculate brush-specific data which applies equally to all points */ - switch (gso->brush_type) { - case GP_SCULPT_TYPE_GRAB: /* Grab points */ - case GP_SCULPT_TYPE_PUSH: /* Push points */ - { - /* calculate amount of displacement to apply */ - gp_brush_grab_calc_dvec(gso); - break; - } - - case GP_SCULPT_TYPE_PINCH: /* Pinch points */ - case GP_SCULPT_TYPE_TWIST: /* Twist points around midpoint */ - { - /* calculate midpoint of the brush (in data space) */ - gp_brush_calc_midpoint(gso); - break; - } - - case GP_SCULPT_TYPE_RANDOMIZE: /* Random jitter */ - { - /* compute the displacement vector for the cursor (in data space) */ - gp_brush_grab_calc_dvec(gso); - break; - } - - default: - break; - } - - - /* Find visible strokes, and perform operations on those if hit */ - CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers) - { - /* If no active frame, don't do anything... */ - if (gpl->actframe == NULL) { - continue; - } - - /* calculate difference matrix */ - float diff_mat[4][4]; - ED_gpencil_parent_location(depsgraph, obact, gpd, gpl, diff_mat); - - /* Active Frame or MultiFrame? */ - if (gso->is_multiframe) { - /* init multiframe falloff options */ - int f_init = 0; - int f_end = 0; - - if (gso->use_multiframe_falloff) { - BKE_gpencil_get_range_selected(gpl, &f_init, &f_end); - } - - for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) { - /* Always do active frame; Otherwise, only include selected frames */ - if ((gpf == gpl->actframe) || (gpf->flag & GP_FRAME_SELECT)) { - /* compute multiframe falloff factor */ - 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( - gpf, gpl->actframe->framenum, - f_init, f_end, - ts->gp_sculpt.cur_falloff); - } - else { - /* No falloff */ - gso->mf_falloff = 1.0f; - } - - /* affect strokes in this frame */ - changed |= gpsculpt_brush_do_frame(C, gso, gpl, 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); - } - } - CTX_DATA_END; - - return changed; + ToolSettings *ts = CTX_data_tool_settings(C); + Depsgraph *depsgraph = CTX_data_depsgraph(C); + Object *obact = gso->object; + bGPdata *gpd = gso->gpd; + bool changed = false; + + /* Calculate brush-specific data which applies equally to all points */ + switch (gso->brush_type) { + case GP_SCULPT_TYPE_GRAB: /* Grab points */ + case GP_SCULPT_TYPE_PUSH: /* Push points */ + { + /* calculate amount of displacement to apply */ + gp_brush_grab_calc_dvec(gso); + break; + } + + case GP_SCULPT_TYPE_PINCH: /* Pinch points */ + case GP_SCULPT_TYPE_TWIST: /* Twist points around midpoint */ + { + /* calculate midpoint of the brush (in data space) */ + gp_brush_calc_midpoint(gso); + break; + } + + case GP_SCULPT_TYPE_RANDOMIZE: /* Random jitter */ + { + /* compute the displacement vector for the cursor (in data space) */ + gp_brush_grab_calc_dvec(gso); + break; + } + + default: + break; + } + + /* Find visible strokes, and perform operations on those if hit */ + CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) { + /* If no active frame, don't do anything... */ + if (gpl->actframe == NULL) { + continue; + } + + /* calculate difference matrix */ + float diff_mat[4][4]; + ED_gpencil_parent_location(depsgraph, obact, gpd, gpl, diff_mat); + + /* Active Frame or MultiFrame? */ + if (gso->is_multiframe) { + /* init multiframe falloff options */ + int f_init = 0; + int f_end = 0; + + if (gso->use_multiframe_falloff) { + BKE_gpencil_get_range_selected(gpl, &f_init, &f_end); + } + + for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) { + /* Always do active frame; Otherwise, only include selected frames */ + if ((gpf == gpl->actframe) || (gpf->flag & GP_FRAME_SELECT)) { + /* compute multiframe falloff factor */ + 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( + gpf, gpl->actframe->framenum, f_init, f_end, ts->gp_sculpt.cur_falloff); + } + else { + /* No falloff */ + gso->mf_falloff = 1.0f; + } + + /* affect strokes in this frame */ + changed |= gpsculpt_brush_do_frame(C, gso, gpl, 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); + } + } + CTX_DATA_END; + + return changed; } /* Calculate settings for applying brush */ static void gpsculpt_brush_apply(bContext *C, wmOperator *op, PointerRNA *itemptr) { - tGP_BrushEditData *gso = op->customdata; - GP_Sculpt_Data *gp_brush = gso->gp_brush; - const int radius = ( - (gp_brush->flag & GP_SCULPT_FLAG_PRESSURE_RADIUS) ? - gso->gp_brush->size * gso->pressure : gso->gp_brush->size); - float mousef[2]; - int mouse[2]; - bool changed = false; - - /* Get latest mouse coordinates */ - RNA_float_get_array(itemptr, "mouse", mousef); - gso->mval[0] = mouse[0] = (int)(mousef[0]); - gso->mval[1] = mouse[1] = (int)(mousef[1]); - - gso->pressure = RNA_float_get(itemptr, "pressure"); - - if (RNA_boolean_get(itemptr, "pen_flip")) - gso->flag |= GP_SCULPT_FLAG_INVERT; - else - gso->flag &= ~GP_SCULPT_FLAG_INVERT; - - - /* Store coordinates as reference, if operator just started running */ - if (gso->first) { - gso->mval_prev[0] = gso->mval[0]; - gso->mval_prev[1] = gso->mval[1]; - gso->pressure_prev = gso->pressure; - } - - /* Update brush_rect, so that it represents the bounding rectangle of brush */ - gso->brush_rect.xmin = mouse[0] - radius; - gso->brush_rect.ymin = mouse[1] - radius; - gso->brush_rect.xmax = mouse[0] + radius; - gso->brush_rect.ymax = mouse[1] + radius; - - - /* Apply brush */ - if (gso->brush_type == GP_SCULPT_TYPE_CLONE) { - changed = gpsculpt_brush_apply_clone(C, gso); - } - else { - changed = gpsculpt_brush_apply_standard(C, gso); - } - - - /* Updates */ - if (changed) { - DEG_id_tag_update(&gso->gpd->id, ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); - } - - /* Store values for next step */ - gso->mval_prev[0] = gso->mval[0]; - gso->mval_prev[1] = gso->mval[1]; - gso->pressure_prev = gso->pressure; - gso->first = false; + tGP_BrushEditData *gso = op->customdata; + GP_Sculpt_Data *gp_brush = gso->gp_brush; + const int radius = ((gp_brush->flag & GP_SCULPT_FLAG_PRESSURE_RADIUS) ? + gso->gp_brush->size * gso->pressure : + gso->gp_brush->size); + float mousef[2]; + int mouse[2]; + bool changed = false; + + /* Get latest mouse coordinates */ + RNA_float_get_array(itemptr, "mouse", mousef); + gso->mval[0] = mouse[0] = (int)(mousef[0]); + gso->mval[1] = mouse[1] = (int)(mousef[1]); + + gso->pressure = RNA_float_get(itemptr, "pressure"); + + if (RNA_boolean_get(itemptr, "pen_flip")) + gso->flag |= GP_SCULPT_FLAG_INVERT; + else + gso->flag &= ~GP_SCULPT_FLAG_INVERT; + + /* Store coordinates as reference, if operator just started running */ + if (gso->first) { + gso->mval_prev[0] = gso->mval[0]; + gso->mval_prev[1] = gso->mval[1]; + gso->pressure_prev = gso->pressure; + } + + /* Update brush_rect, so that it represents the bounding rectangle of brush */ + gso->brush_rect.xmin = mouse[0] - radius; + gso->brush_rect.ymin = mouse[1] - radius; + gso->brush_rect.xmax = mouse[0] + radius; + gso->brush_rect.ymax = mouse[1] + radius; + + /* Apply brush */ + if (gso->brush_type == GP_SCULPT_TYPE_CLONE) { + changed = gpsculpt_brush_apply_clone(C, gso); + } + else { + changed = gpsculpt_brush_apply_standard(C, gso); + } + + /* Updates */ + if (changed) { + DEG_id_tag_update(&gso->gpd->id, ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + } + + /* Store values for next step */ + gso->mval_prev[0] = gso->mval[0]; + gso->mval_prev[1] = gso->mval[1]; + gso->pressure_prev = gso->pressure; + gso->first = false; } /* Running --------------------------------------------- */ @@ -1806,370 +1766,380 @@ static void gpsculpt_brush_apply(bContext *C, wmOperator *op, PointerRNA *itempt /* helper - a record stroke, and apply paint event */ static void gpsculpt_brush_apply_event(bContext *C, wmOperator *op, const wmEvent *event) { - tGP_BrushEditData *gso = op->customdata; - ToolSettings *ts = CTX_data_tool_settings(C); - GP_Sculpt_Settings *gset = &ts->gp_sculpt; - PointerRNA itemptr; - float mouse[2]; - int tablet = 0; - - mouse[0] = event->mval[0] + 1; - mouse[1] = event->mval[1] + 1; - - /* fill in stroke */ - RNA_collection_add(op->ptr, "stroke", &itemptr); - - RNA_float_set_array(&itemptr, "mouse", mouse); - RNA_boolean_set(&itemptr, "pen_flip", event->ctrl != false); - RNA_boolean_set(&itemptr, "is_start", gso->first); - - /* handle pressure sensitivity (which is supplied by tablets) */ - if (event->tablet_data) { - const wmTabletData *wmtab = event->tablet_data; - float pressure = wmtab->Pressure; - - tablet = (wmtab->Active != EVT_TABLET_NONE); - - /* special exception here for too high pressure values on first touch in - * windows for some tablets: clamp the values to be sane - */ - if (tablet && (pressure >= 0.99f)) { - pressure = 1.0f; - } - RNA_float_set(&itemptr, "pressure", pressure); - } - else { - RNA_float_set(&itemptr, "pressure", 1.0f); - } - - if (!gso->is_weight_mode) { - if (event->shift) { - gso->gp_brush_old = gso->gp_brush; - gso->brush_type_old = gso->brush_type; - - gso->gp_brush = &gset->brush[GP_SCULPT_TYPE_SMOOTH]; - gso->brush_type = GP_SCULPT_TYPE_SMOOTH; - } - else { - if (gso->gp_brush_old != NULL) { - gso->gp_brush = gso->gp_brush_old; - gso->brush_type = gso->brush_type_old; - } - } - } - - /* apply */ - gpsculpt_brush_apply(C, op, &itemptr); + tGP_BrushEditData *gso = op->customdata; + ToolSettings *ts = CTX_data_tool_settings(C); + GP_Sculpt_Settings *gset = &ts->gp_sculpt; + PointerRNA itemptr; + float mouse[2]; + int tablet = 0; + + mouse[0] = event->mval[0] + 1; + mouse[1] = event->mval[1] + 1; + + /* fill in stroke */ + RNA_collection_add(op->ptr, "stroke", &itemptr); + + RNA_float_set_array(&itemptr, "mouse", mouse); + RNA_boolean_set(&itemptr, "pen_flip", event->ctrl != false); + RNA_boolean_set(&itemptr, "is_start", gso->first); + + /* handle pressure sensitivity (which is supplied by tablets) */ + if (event->tablet_data) { + const wmTabletData *wmtab = event->tablet_data; + float pressure = wmtab->Pressure; + + tablet = (wmtab->Active != EVT_TABLET_NONE); + + /* special exception here for too high pressure values on first touch in + * windows for some tablets: clamp the values to be sane + */ + if (tablet && (pressure >= 0.99f)) { + pressure = 1.0f; + } + RNA_float_set(&itemptr, "pressure", pressure); + } + else { + RNA_float_set(&itemptr, "pressure", 1.0f); + } + + if (!gso->is_weight_mode) { + if (event->shift) { + gso->gp_brush_old = gso->gp_brush; + gso->brush_type_old = gso->brush_type; + + gso->gp_brush = &gset->brush[GP_SCULPT_TYPE_SMOOTH]; + gso->brush_type = GP_SCULPT_TYPE_SMOOTH; + } + else { + if (gso->gp_brush_old != NULL) { + gso->gp_brush = gso->gp_brush_old; + gso->brush_type = gso->brush_type_old; + } + } + } + + /* apply */ + gpsculpt_brush_apply(C, op, &itemptr); } /* reapply */ static int gpsculpt_brush_exec(bContext *C, wmOperator *op) { - if (!gpsculpt_brush_init(C, op)) - return OPERATOR_CANCELLED; + if (!gpsculpt_brush_init(C, op)) + return OPERATOR_CANCELLED; - RNA_BEGIN(op->ptr, itemptr, "stroke") - { - gpsculpt_brush_apply(C, op, &itemptr); - } - RNA_END; + RNA_BEGIN (op->ptr, itemptr, "stroke") { + gpsculpt_brush_apply(C, op, &itemptr); + } + RNA_END; - gpsculpt_brush_exit(C, op); + gpsculpt_brush_exit(C, op); - return OPERATOR_FINISHED; + return OPERATOR_FINISHED; } - /* start modal painting */ static int gpsculpt_brush_invoke(bContext *C, wmOperator *op, const wmEvent *event) { - tGP_BrushEditData *gso = NULL; - const bool is_modal = RNA_boolean_get(op->ptr, "wait_for_input"); - const bool is_playing = ED_screen_animation_playing(CTX_wm_manager(C)) != NULL; - bool needs_timer = false; - float brush_rate = 0.0f; - - /* the operator cannot work while play animation */ - if (is_playing) { - BKE_report(op->reports, RPT_ERROR, "Cannot sculpt while play animation"); - - return OPERATOR_CANCELLED; - } - - /* init painting data */ - if (!gpsculpt_brush_init(C, op)) - return OPERATOR_CANCELLED; - - gso = op->customdata; - - /* initialise type-specific data (used for the entire session) */ - switch (gso->brush_type) { - /* Brushes requiring timer... */ - case GP_SCULPT_TYPE_THICKNESS: - brush_rate = 0.01f; // XXX: hardcoded - needs_timer = true; - break; - - case GP_SCULPT_TYPE_STRENGTH: - brush_rate = 0.01f; // XXX: hardcoded - needs_timer = true; - break; - - case GP_SCULPT_TYPE_PINCH: - brush_rate = 0.001f; // XXX: hardcoded - needs_timer = true; - break; - - case GP_SCULPT_TYPE_TWIST: - brush_rate = 0.01f; // XXX: hardcoded - needs_timer = true; - break; - - default: - break; - } - - /* register timer for increasing influence by hovering over an area */ - if (needs_timer) { - gso->timer = WM_event_add_timer(CTX_wm_manager(C), CTX_wm_window(C), TIMER, brush_rate); - } - - /* register modal handler */ - WM_event_add_modal_handler(C, op); - - /* start drawing immediately? */ - if (is_modal == false) { - ARegion *ar = CTX_wm_region(C); - - /* ensure that we'll have a new frame to draw on */ - gpsculpt_brush_init_stroke(gso); - - /* apply first dab... */ - gso->is_painting = true; - gpsculpt_brush_apply_event(C, op, event); - - /* redraw view with feedback */ - ED_region_tag_redraw(ar); - } - - return OPERATOR_RUNNING_MODAL; + tGP_BrushEditData *gso = NULL; + const bool is_modal = RNA_boolean_get(op->ptr, "wait_for_input"); + const bool is_playing = ED_screen_animation_playing(CTX_wm_manager(C)) != NULL; + bool needs_timer = false; + float brush_rate = 0.0f; + + /* the operator cannot work while play animation */ + if (is_playing) { + BKE_report(op->reports, RPT_ERROR, "Cannot sculpt while play animation"); + + return OPERATOR_CANCELLED; + } + + /* init painting data */ + if (!gpsculpt_brush_init(C, op)) + return OPERATOR_CANCELLED; + + gso = op->customdata; + + /* initialise type-specific data (used for the entire session) */ + switch (gso->brush_type) { + /* Brushes requiring timer... */ + case GP_SCULPT_TYPE_THICKNESS: + brush_rate = 0.01f; // XXX: hardcoded + needs_timer = true; + break; + + case GP_SCULPT_TYPE_STRENGTH: + brush_rate = 0.01f; // XXX: hardcoded + needs_timer = true; + break; + + case GP_SCULPT_TYPE_PINCH: + brush_rate = 0.001f; // XXX: hardcoded + needs_timer = true; + break; + + case GP_SCULPT_TYPE_TWIST: + brush_rate = 0.01f; // XXX: hardcoded + needs_timer = true; + break; + + default: + break; + } + + /* register timer for increasing influence by hovering over an area */ + if (needs_timer) { + gso->timer = WM_event_add_timer(CTX_wm_manager(C), CTX_wm_window(C), TIMER, brush_rate); + } + + /* register modal handler */ + WM_event_add_modal_handler(C, op); + + /* start drawing immediately? */ + if (is_modal == false) { + ARegion *ar = CTX_wm_region(C); + + /* ensure that we'll have a new frame to draw on */ + gpsculpt_brush_init_stroke(gso); + + /* apply first dab... */ + gso->is_painting = true; + gpsculpt_brush_apply_event(C, op, event); + + /* redraw view with feedback */ + ED_region_tag_redraw(ar); + } + + return OPERATOR_RUNNING_MODAL; } /* painting - handle events */ static int gpsculpt_brush_modal(bContext *C, wmOperator *op, const wmEvent *event) { - tGP_BrushEditData *gso = op->customdata; - const bool is_modal = RNA_boolean_get(op->ptr, "wait_for_input"); - bool redraw_region = false; - bool redraw_toolsettings = false; - - /* The operator can be in 2 states: Painting and Idling */ - if (gso->is_painting) { - /* Painting */ - switch (event->type) { - /* Mouse Move = Apply somewhere else */ - case MOUSEMOVE: - case INBETWEEN_MOUSEMOVE: - /* apply brush effect at new position */ - gpsculpt_brush_apply_event(C, op, event); - - /* force redraw, so that the cursor will at least be valid */ - redraw_region = true; - break; - - /* Timer Tick - Only if this was our own timer */ - case TIMER: - if (event->customdata == gso->timer) { - gso->timerTick = true; - gpsculpt_brush_apply_event(C, op, event); - gso->timerTick = false; - } - break; - - /* Adjust brush settings */ - /* FIXME: Step increments and modifier keys are hardcoded here! */ - case WHEELUPMOUSE: - case PADPLUSKEY: - if (event->shift) { - /* increase strength */ - gso->gp_brush->strength += 0.05f; - CLAMP_MAX(gso->gp_brush->strength, 1.0f); - } - else { - /* increase brush size */ - gso->gp_brush->size += 3; - CLAMP_MAX(gso->gp_brush->size, 300); - } - - redraw_region = true; - redraw_toolsettings = true; - break; - - case WHEELDOWNMOUSE: - case PADMINUS: - if (event->shift) { - /* decrease strength */ - gso->gp_brush->strength -= 0.05f; - CLAMP_MIN(gso->gp_brush->strength, 0.0f); - } - else { - /* decrease brush size */ - gso->gp_brush->size -= 3; - CLAMP_MIN(gso->gp_brush->size, 1); - } - - redraw_region = true; - redraw_toolsettings = true; - break; - - /* Painting mbut release = Stop painting (back to idle) */ - case LEFTMOUSE: - //BLI_assert(event->val == KM_RELEASE); - if (is_modal) { - /* go back to idling... */ - gso->is_painting = false; - } - else { - /* end sculpt session, since we're not modal */ - gso->is_painting = false; - - gpsculpt_brush_exit(C, op); - return OPERATOR_FINISHED; - } - break; - - /* Abort painting if any of the usual things are tried */ - case MIDDLEMOUSE: - case RIGHTMOUSE: - case ESCKEY: - gpsculpt_brush_exit(C, op); - return OPERATOR_FINISHED; - } - } - else { - /* Idling */ - BLI_assert(is_modal == true); - - switch (event->type) { - /* Painting mbut press = Start painting (switch to painting state) */ - case LEFTMOUSE: - /* do initial "click" apply */ - gso->is_painting = true; - gso->first = true; - - gpsculpt_brush_init_stroke(gso); - gpsculpt_brush_apply_event(C, op, event); - break; - - /* Exit modal operator, based on the "standard" ops */ - case RIGHTMOUSE: - case ESCKEY: - gpsculpt_brush_exit(C, op); - return OPERATOR_FINISHED; - - /* MMB is often used for view manipulations */ - case MIDDLEMOUSE: - return OPERATOR_PASS_THROUGH; - - /* Mouse movements should update the brush cursor - Just redraw the active region */ - case MOUSEMOVE: - case INBETWEEN_MOUSEMOVE: - redraw_region = true; - break; - - /* Adjust brush settings */ - /* FIXME: Step increments and modifier keys are hardcoded here! */ - case WHEELUPMOUSE: - case PADPLUSKEY: - if (event->shift) { - /* increase strength */ - gso->gp_brush->strength += 0.05f; - CLAMP_MAX(gso->gp_brush->strength, 1.0f); - } - else { - /* increase brush size */ - gso->gp_brush->size += 3; - CLAMP_MAX(gso->gp_brush->size, 300); - } - - redraw_region = true; - redraw_toolsettings = true; - break; - - case WHEELDOWNMOUSE: - case PADMINUS: - if (event->shift) { - /* decrease strength */ - gso->gp_brush->strength -= 0.05f; - CLAMP_MIN(gso->gp_brush->strength, 0.0f); - } - else { - /* decrease brush size */ - gso->gp_brush->size -= 3; - CLAMP_MIN(gso->gp_brush->size, 1); - } - - redraw_region = true; - redraw_toolsettings = true; - break; - - /* Change Frame - Allowed */ - case LEFTARROWKEY: - case RIGHTARROWKEY: - case UPARROWKEY: - case DOWNARROWKEY: - return OPERATOR_PASS_THROUGH; - - /* Camera/View Gizmo's - Allowed */ - /* (See rationale in gpencil_paint.c -> gpencil_draw_modal()) */ - case PAD0: case PAD1: case PAD2: case PAD3: case PAD4: - case PAD5: case PAD6: case PAD7: case PAD8: case PAD9: - return OPERATOR_PASS_THROUGH; - - /* Unhandled event */ - default: - break; - } - } - - /* Redraw region? */ - if (redraw_region) { - ARegion *ar = CTX_wm_region(C); - ED_region_tag_redraw(ar); - } - - /* Redraw toolsettings (brush settings)? */ - if (redraw_toolsettings) { - DEG_id_tag_update(&gso->gpd->id, ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_SCENE | ND_TOOLSETTINGS, NULL); - } - - return OPERATOR_RUNNING_MODAL; + tGP_BrushEditData *gso = op->customdata; + const bool is_modal = RNA_boolean_get(op->ptr, "wait_for_input"); + bool redraw_region = false; + bool redraw_toolsettings = false; + + /* The operator can be in 2 states: Painting and Idling */ + if (gso->is_painting) { + /* Painting */ + switch (event->type) { + /* Mouse Move = Apply somewhere else */ + case MOUSEMOVE: + case INBETWEEN_MOUSEMOVE: + /* apply brush effect at new position */ + gpsculpt_brush_apply_event(C, op, event); + + /* force redraw, so that the cursor will at least be valid */ + redraw_region = true; + break; + + /* Timer Tick - Only if this was our own timer */ + case TIMER: + if (event->customdata == gso->timer) { + gso->timerTick = true; + gpsculpt_brush_apply_event(C, op, event); + gso->timerTick = false; + } + break; + + /* Adjust brush settings */ + /* FIXME: Step increments and modifier keys are hardcoded here! */ + case WHEELUPMOUSE: + case PADPLUSKEY: + if (event->shift) { + /* increase strength */ + gso->gp_brush->strength += 0.05f; + CLAMP_MAX(gso->gp_brush->strength, 1.0f); + } + else { + /* increase brush size */ + gso->gp_brush->size += 3; + CLAMP_MAX(gso->gp_brush->size, 300); + } + + redraw_region = true; + redraw_toolsettings = true; + break; + + case WHEELDOWNMOUSE: + case PADMINUS: + if (event->shift) { + /* decrease strength */ + gso->gp_brush->strength -= 0.05f; + CLAMP_MIN(gso->gp_brush->strength, 0.0f); + } + else { + /* decrease brush size */ + gso->gp_brush->size -= 3; + CLAMP_MIN(gso->gp_brush->size, 1); + } + + redraw_region = true; + redraw_toolsettings = true; + break; + + /* Painting mbut release = Stop painting (back to idle) */ + case LEFTMOUSE: + //BLI_assert(event->val == KM_RELEASE); + if (is_modal) { + /* go back to idling... */ + gso->is_painting = false; + } + else { + /* end sculpt session, since we're not modal */ + gso->is_painting = false; + + gpsculpt_brush_exit(C, op); + return OPERATOR_FINISHED; + } + break; + + /* Abort painting if any of the usual things are tried */ + case MIDDLEMOUSE: + case RIGHTMOUSE: + case ESCKEY: + gpsculpt_brush_exit(C, op); + return OPERATOR_FINISHED; + } + } + else { + /* Idling */ + BLI_assert(is_modal == true); + + switch (event->type) { + /* Painting mbut press = Start painting (switch to painting state) */ + case LEFTMOUSE: + /* do initial "click" apply */ + gso->is_painting = true; + gso->first = true; + + gpsculpt_brush_init_stroke(gso); + gpsculpt_brush_apply_event(C, op, event); + break; + + /* Exit modal operator, based on the "standard" ops */ + case RIGHTMOUSE: + case ESCKEY: + gpsculpt_brush_exit(C, op); + return OPERATOR_FINISHED; + + /* MMB is often used for view manipulations */ + case MIDDLEMOUSE: + return OPERATOR_PASS_THROUGH; + + /* Mouse movements should update the brush cursor - Just redraw the active region */ + case MOUSEMOVE: + case INBETWEEN_MOUSEMOVE: + redraw_region = true; + break; + + /* Adjust brush settings */ + /* FIXME: Step increments and modifier keys are hardcoded here! */ + case WHEELUPMOUSE: + case PADPLUSKEY: + if (event->shift) { + /* increase strength */ + gso->gp_brush->strength += 0.05f; + CLAMP_MAX(gso->gp_brush->strength, 1.0f); + } + else { + /* increase brush size */ + gso->gp_brush->size += 3; + CLAMP_MAX(gso->gp_brush->size, 300); + } + + redraw_region = true; + redraw_toolsettings = true; + break; + + case WHEELDOWNMOUSE: + case PADMINUS: + if (event->shift) { + /* decrease strength */ + gso->gp_brush->strength -= 0.05f; + CLAMP_MIN(gso->gp_brush->strength, 0.0f); + } + else { + /* decrease brush size */ + gso->gp_brush->size -= 3; + CLAMP_MIN(gso->gp_brush->size, 1); + } + + redraw_region = true; + redraw_toolsettings = true; + break; + + /* Change Frame - Allowed */ + case LEFTARROWKEY: + case RIGHTARROWKEY: + case UPARROWKEY: + case DOWNARROWKEY: + return OPERATOR_PASS_THROUGH; + + /* Camera/View Gizmo's - Allowed */ + /* (See rationale in gpencil_paint.c -> gpencil_draw_modal()) */ + case PAD0: + case PAD1: + case PAD2: + case PAD3: + case PAD4: + case PAD5: + case PAD6: + case PAD7: + case PAD8: + case PAD9: + return OPERATOR_PASS_THROUGH; + + /* Unhandled event */ + default: + break; + } + } + + /* Redraw region? */ + if (redraw_region) { + ARegion *ar = CTX_wm_region(C); + ED_region_tag_redraw(ar); + } + + /* Redraw toolsettings (brush settings)? */ + if (redraw_toolsettings) { + DEG_id_tag_update(&gso->gpd->id, ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_SCENE | ND_TOOLSETTINGS, NULL); + } + + return OPERATOR_RUNNING_MODAL; } void GPENCIL_OT_sculpt_paint(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Stroke Sculpt"; - ot->idname = "GPENCIL_OT_sculpt_paint"; - ot->description = "Apply tweaks to strokes by painting over the strokes"; // XXX - - /* api callbacks */ - ot->exec = gpsculpt_brush_exec; - ot->invoke = gpsculpt_brush_invoke; - ot->modal = gpsculpt_brush_modal; - ot->cancel = gpsculpt_brush_exit; - ot->poll = gpsculpt_brush_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING; - - /* properties */ - PropertyRNA *prop; - prop = RNA_def_collection_runtime(ot->srna, "stroke", &RNA_OperatorStrokeElement, "Stroke", ""); - RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); - - prop = RNA_def_boolean(ot->srna, "wait_for_input", true, "Wait for Input", - "Enter a mini 'sculpt-mode' if enabled, otherwise, exit after drawing a single stroke"); - RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + /* identifiers */ + ot->name = "Stroke Sculpt"; + ot->idname = "GPENCIL_OT_sculpt_paint"; + ot->description = "Apply tweaks to strokes by painting over the strokes"; // XXX + + /* api callbacks */ + ot->exec = gpsculpt_brush_exec; + ot->invoke = gpsculpt_brush_invoke; + ot->modal = gpsculpt_brush_modal; + ot->cancel = gpsculpt_brush_exit; + ot->poll = gpsculpt_brush_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING; + + /* properties */ + PropertyRNA *prop; + prop = RNA_def_collection_runtime(ot->srna, "stroke", &RNA_OperatorStrokeElement, "Stroke", ""); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + + prop = RNA_def_boolean( + ot->srna, + "wait_for_input", + true, + "Wait for Input", + "Enter a mini 'sculpt-mode' if enabled, otherwise, exit after drawing a single stroke"); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); } |