Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/editors/gpencil/gpencil_brush.c')
-rw-r--r--source/blender/editors/gpencil/gpencil_brush.c695
1 files changed, 462 insertions, 233 deletions
diff --git a/source/blender/editors/gpencil/gpencil_brush.c b/source/blender/editors/gpencil/gpencil_brush.c
index bd9bfcf7025..52642fb2570 100644
--- a/source/blender/editors/gpencil/gpencil_brush.c
+++ b/source/blender/editors/gpencil/gpencil_brush.c
@@ -48,6 +48,7 @@
#include "BLT_translation.h"
+#include "DNA_meshdata_types.h"
#include "DNA_scene_types.h"
#include "DNA_screen_types.h"
#include "DNA_space_types.h"
@@ -60,6 +61,9 @@
#include "BKE_library.h"
#include "BKE_report.h"
#include "BKE_screen.h"
+#include "BKE_object_deform.h"
+#include "BKE_colortools.h"
+#include "BKE_material.h"
#include "UI_interface.h"
@@ -80,6 +84,9 @@
#include "GPU_immediate_util.h"
#include "GPU_state.h"
+#include "DEG_depsgraph.h"
+#include "DEG_depsgraph_query.h"
+
#include "gpencil_intern.h"
/* ************************************************ */
@@ -89,7 +96,9 @@
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;
@@ -114,6 +123,10 @@ typedef struct tGP_BrushEditData {
/* Start of new sculpt stroke */
bool first;
+ /* Is multiframe editing enabled, and are we using falloff for that? */
+ bool is_multiframe;
+ bool use_multiframe_falloff;
+
/* Current frame */
int cfra;
@@ -128,6 +141,13 @@ typedef struct tGP_BrushEditData {
/* - effect vector (e.g. 2D/3D translation for grab brush) */
float dvec[3];
+ /* - multiframe falloff factor */
+ float mf_falloff;
+
+ /* active vertex group */
+ int vrgroup;
+
+
/* brush geometry (bounding box) */
rcti brush_rect;
@@ -147,12 +167,34 @@ typedef struct tGP_BrushEditData {
/* Callback for performing some brush operation on a single point */
-typedef bool (*GP_BrushApplyCb)(tGP_BrushEditData *gso, bGPDstroke *gps, int i,
+typedef bool (*GP_BrushApplyCb)(tGP_BrushEditData *gso, bGPDstroke *gps, int pt_index,
const int radius, const int co[2]);
/* ************************************************ */
/* Utility Functions */
+/* apply lock axis reset */
+static void gpsculpt_compute_lock_axis(tGP_BrushEditData *gso, bGPDspoint *pt, const float save_pt[3])
+{
+ if (gso->sa->spacetype != SPACE_VIEW3D) {
+ return;
+ }
+
+ ToolSettings *ts = gso->scene->toolsettings;
+ int axis = ts->gp_sculpt.lock_axis;
+
+ /* lock axis control */
+ if (axis == 1) {
+ pt->x = save_pt[0];
+ }
+ if (axis == 2) {
+ pt->y = save_pt[1];
+ }
+ if (axis == 3) {
+ pt->z = save_pt[2];
+ }
+}
+
/* Context ---------------------------------------- */
/* Get the sculpting settings */
@@ -162,10 +204,18 @@ static GP_BrushEdit_Settings *gpsculpt_get_settings(Scene *scene)
}
/* Get the active brush */
-static GP_EditBrush_Data *gpsculpt_get_brush(Scene *scene)
+static GP_EditBrush_Data *gpsculpt_get_brush(Scene *scene, bool is_weight_mode)
{
GP_BrushEdit_Settings *gset = &scene->toolsettings->gp_sculpt;
- return &gset->brush[gset->brushtype];
+ GP_EditBrush_Data *brush = NULL;
+ if (is_weight_mode) {
+ brush = &gset->brush[gset->weighttype];
+ }
+ else {
+ brush = &gset->brush[gset->brushtype];
+ }
+
+ return brush;
}
/* Brush Operations ------------------------------- */
@@ -181,6 +231,14 @@ static bool gp_brush_invert_check(tGP_BrushEditData *gso)
invert ^= true;
}
+ /* set temporary status */
+ if (invert) {
+ gso->brush->flag |= GP_EDITBRUSH_FLAG_TMP_INVERT;
+ }
+ else {
+ gso->brush->flag &= ~GP_EDITBRUSH_FLAG_TMP_INVERT;
+ }
+
return invert;
}
@@ -208,6 +266,9 @@ static float gp_brush_influence_calc(tGP_BrushEditData *gso, const int radius, c
influence *= fac;
}
+ /* apply multiframe falloff */
+ influence *= gso->mf_falloff;
+
/* return influence */
return influence;
}
@@ -222,29 +283,34 @@ static float gp_brush_influence_calc(tGP_BrushEditData *gso, const int radius, c
/* Smooth Brush */
/* A simple (but slower + inaccurate) smooth-brush implementation to test the algorithm for stroke smoothing */
-static bool gp_brush_smooth_apply(tGP_BrushEditData *gso, bGPDstroke *gps, int i,
- const int radius, const int co[2])
+static bool gp_brush_smooth_apply(
+ tGP_BrushEditData *gso, bGPDstroke *gps, int pt_index,
+ const int radius, const int co[2])
{
- GP_EditBrush_Data *brush = gso->brush;
+ // GP_EditBrush_Data *brush = gso->brush;
float inf = gp_brush_influence_calc(gso, radius, co);
- bool affect_pressure = (brush->flag & GP_EDITBRUSH_FLAG_SMOOTH_PRESSURE) != 0;
/* need one flag enabled by default */
- if ((gso->settings->flag & (GP_BRUSHEDIT_FLAG_APPLY_POSITION |
- GP_BRUSHEDIT_FLAG_APPLY_STRENGTH |
- GP_BRUSHEDIT_FLAG_APPLY_THICKNESS)) == 0)
+ if ((gso->settings->flag &
+ (GP_BRUSHEDIT_FLAG_APPLY_POSITION |
+ GP_BRUSHEDIT_FLAG_APPLY_STRENGTH |
+ GP_BRUSHEDIT_FLAG_APPLY_THICKNESS |
+ GP_BRUSHEDIT_FLAG_APPLY_UV)) == 0)
{
gso->settings->flag |= GP_BRUSHEDIT_FLAG_APPLY_POSITION;
}
/* perform smoothing */
if (gso->settings->flag & GP_BRUSHEDIT_FLAG_APPLY_POSITION) {
- gp_smooth_stroke(gps, i, inf, affect_pressure);
+ BKE_gpencil_smooth_stroke(gps, pt_index, inf);
}
if (gso->settings->flag & GP_BRUSHEDIT_FLAG_APPLY_STRENGTH) {
- gp_smooth_stroke_strength(gps, i, inf);
+ BKE_gpencil_smooth_stroke_strength(gps, pt_index, inf);
}
if (gso->settings->flag & GP_BRUSHEDIT_FLAG_APPLY_THICKNESS) {
- gp_smooth_stroke_thickness(gps, i, inf);
+ BKE_gpencil_smooth_stroke_thickness(gps, pt_index, inf);
+ }
+ if (gso->settings->flag & GP_BRUSHEDIT_FLAG_APPLY_UV) {
+ BKE_gpencil_smooth_stroke_uv(gps, pt_index, inf);
}
return true;
@@ -254,10 +320,11 @@ static bool gp_brush_smooth_apply(tGP_BrushEditData *gso, bGPDstroke *gps, int i
/* Line Thickness Brush */
/* Make lines thicker or thinner by the specified amounts */
-static bool gp_brush_thickness_apply(tGP_BrushEditData *gso, bGPDstroke *gps, int i,
- const int radius, const int co[2])
+static bool gp_brush_thickness_apply(
+ tGP_BrushEditData *gso, bGPDstroke *gps, int pt_index,
+ const int radius, const int co[2])
{
- bGPDspoint *pt = gps->points + i;
+ bGPDspoint *pt = gps->points + pt_index;
float inf;
/* Compute strength of effect
@@ -294,28 +361,29 @@ static bool gp_brush_thickness_apply(tGP_BrushEditData *gso, bGPDstroke *gps, in
/* Make color more or less transparent by the specified amounts */
static bool gp_brush_strength_apply(
- tGP_BrushEditData *gso, bGPDstroke *gps, int i,
+ tGP_BrushEditData *gso, bGPDstroke *gps, int pt_index,
const int radius, const int co[2])
{
- bGPDspoint *pt = gps->points + i;
+ 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.
+ * - 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) / 10.0f;
+ inf = gp_brush_influence_calc(gso, radius, co) / 20.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 */
+ /* make line more transparent - reduce alpha factor */
pt->strength -= inf;
}
else {
- /* make line thicker - increase stroke pressure */
+ /* 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);
@@ -382,8 +450,9 @@ static void gp_brush_grab_stroke_init(tGP_BrushEditData *gso, bGPDstroke *gps)
}
/* store references to stroke points in the initial stage */
-static bool gp_brush_grab_store_points(tGP_BrushEditData *gso, bGPDstroke *gps, int i,
- const int radius, const int co[2])
+static bool gp_brush_grab_store_points(
+ 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);
@@ -392,7 +461,7 @@ static bool gp_brush_grab_store_points(tGP_BrushEditData *gso, bGPDstroke *gps,
BLI_assert(data->size < data->capacity);
/* insert this point into the set of affected points */
- data->points[data->size] = i;
+ data->points[data->size] = pt_index;
data->weights[data->size] = inf;
data->size++;
@@ -431,7 +500,7 @@ static void gp_brush_grab_calc_dvec(tGP_BrushEditData *gso)
/* Apply grab transform to all relevant points of the affected strokes */
static void gp_brush_grab_apply_cached(
- tGP_BrushEditData *gso, bGPDstroke *gps, bool parented, float diff_mat[4][4])
+ tGP_BrushEditData *gso, bGPDstroke *gps, float diff_mat[4][4])
{
tGPSB_Grab_StrokeData *data = BLI_ghash_lookup(gso->stroke_customdata, gps);
int i;
@@ -443,23 +512,21 @@ static void gp_brush_grab_apply_cached(
/* adjust the amount of displacement to apply */
mul_v3_v3fl(delta, gso->dvec, data->weights[i]);
- if (!parented) {
- /* apply */
- add_v3_v3(&pt->x, delta);
- }
- else {
- float fpt[3];
- /* apply transformation */
- mul_v3_m4v3(fpt, diff_mat, &pt->x);
- /* apply */
- add_v3_v3(fpt, delta);
- copy_v3_v3(&pt->x, fpt);
- /* 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);
- }
+ 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);
}
}
@@ -480,10 +547,14 @@ static void gp_brush_grab_stroke_free(void *ptr)
/* Push Brush */
/* NOTE: Depends on gp_brush_grab_calc_dvec() */
-static bool gp_brush_push_apply(tGP_BrushEditData *gso, bGPDstroke *gps, int i,
- const int radius, const int co[2])
+static bool gp_brush_push_apply(
+ tGP_BrushEditData *gso, bGPDstroke *gps, int pt_index,
+ const int radius, const int co[2])
{
- bGPDspoint *pt = gps->points + i;
+ bGPDspoint *pt = gps->points + pt_index;
+ float save_pt[3];
+ copy_v3_v3(save_pt, &pt->x);
+
float inf = gp_brush_influence_calc(gso, radius, co);
float delta[3] = {0.0f};
@@ -493,6 +564,9 @@ static bool gp_brush_push_apply(tGP_BrushEditData *gso, bGPDstroke *gps, int i,
/* apply */
add_v3_v3(&pt->x, delta);
+ /* compute lock axis */
+ gpsculpt_compute_lock_axis(gso, pt, save_pt);
+
/* done */
return true;
}
@@ -512,7 +586,8 @@ static void gp_brush_calc_midpoint(tGP_BrushEditData *gso)
float *rvec = ED_view3d_cursor3d_get(gso->scene, v3d)->location;
float zfac = ED_view3d_calc_zfac(rv3d, rvec, NULL);
- float mval_f[2] = {UNPACK2(gso->mval)};
+ float mval_f[2];
+ copy_v2fl_v2i(mval_f, gso->mval);
float mval_prj[2];
float dvec[3];
@@ -536,12 +611,15 @@ static void gp_brush_calc_midpoint(tGP_BrushEditData *gso)
}
/* Shrink distance between midpoint and this point... */
-static bool gp_brush_pinch_apply(tGP_BrushEditData *gso, bGPDstroke *gps, int i,
- const int radius, const int co[2])
+static bool gp_brush_pinch_apply(
+ tGP_BrushEditData *gso, bGPDstroke *gps, int pt_index,
+ const int radius, const int co[2])
{
- bGPDspoint *pt = gps->points + i;
+ bGPDspoint *pt = gps->points + pt_index;
float fac, inf;
float 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
@@ -571,6 +649,9 @@ static bool gp_brush_pinch_apply(tGP_BrushEditData *gso, bGPDstroke *gps, int i,
/* 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);
+
/* done */
return true;
}
@@ -582,11 +663,14 @@ static bool gp_brush_pinch_apply(tGP_BrushEditData *gso, bGPDstroke *gps, int i,
* convert the rotated point and convert it into "data" space
*/
-static bool gp_brush_twist_apply(tGP_BrushEditData *gso, bGPDstroke *gps, int i,
- const int radius, const int co[2])
+static bool gp_brush_twist_apply(
+ tGP_BrushEditData *gso, bGPDstroke *gps, int pt_index,
+ const int radius, const int co[2])
{
- bGPDspoint *pt = gps->points + i;
+ bGPDspoint *pt = gps->points + pt_index;
float angle, inf;
+ float save_pt[3];
+ copy_v3_v3(save_pt, &pt->x);
/* Angle to rotate by */
inf = gp_brush_influence_calc(gso, radius, co);
@@ -615,6 +699,9 @@ static bool gp_brush_twist_apply(tGP_BrushEditData *gso, bGPDstroke *gps, int i,
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};
@@ -654,10 +741,13 @@ static bool gp_brush_twist_apply(tGP_BrushEditData *gso, bGPDstroke *gps, int i,
/* Randomize Brush */
/* Apply some random jitter to the point */
-static bool gp_brush_randomize_apply(tGP_BrushEditData *gso, bGPDstroke *gps, int i,
- const int radius, const int co[2])
+static bool gp_brush_randomize_apply(
+ tGP_BrushEditData *gso, bGPDstroke *gps, int pt_index,
+ const int radius, const int co[2])
{
- bGPDspoint *pt = gps->points + i;
+ bGPDspoint *pt = gps->points + pt_index;
+ float save_pt[3];
+ copy_v3_v3(save_pt, &pt->x);
/* Amount of jitter to apply depends on the distance of the point to the cursor,
* as well as the strength of the brush
@@ -667,7 +757,8 @@ static bool gp_brush_randomize_apply(tGP_BrushEditData *gso, bGPDstroke *gps, in
/* need one flag enabled by default */
if ((gso->settings->flag & (GP_BRUSHEDIT_FLAG_APPLY_POSITION |
GP_BRUSHEDIT_FLAG_APPLY_STRENGTH |
- GP_BRUSHEDIT_FLAG_APPLY_THICKNESS)) == 0)
+ GP_BRUSHEDIT_FLAG_APPLY_THICKNESS |
+ GP_BRUSHEDIT_FLAG_APPLY_UV)) == 0)
{
gso->settings->flag |= GP_BRUSHEDIT_FLAG_APPLY_POSITION;
}
@@ -710,6 +801,8 @@ static bool gp_brush_randomize_apply(tGP_BrushEditData *gso, bGPDstroke *gps, in
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 {
@@ -749,11 +842,76 @@ static bool gp_brush_randomize_apply(tGP_BrushEditData *gso, bGPDstroke *gps, in
/* only limit lower value */
CLAMP_MIN(pt->pressure, 0.0f);
}
+ /* apply random to UV (use pressure) */
+ if (gso->settings->flag & GP_BRUSHEDIT_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);
+ }
/* 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])
+{
+ 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;
+ }
+ }
+ /* get current weight */
+ float curweight = 0.0f;
+ for (int i = 0; i < dvert->totweight; i++) {
+ MDeformWeight *gpw = &dvert->dw[i];
+ if (gpw->def_nr == gso->vrgroup) {
+ curweight = gpw->weight;
+ break;
+ }
+ }
+
+ if (gp_brush_invert_check(gso)) {
+ /* reduce weight */
+ curweight -= inf;
+ }
+ else {
+ /* increase weight */
+ curweight += inf;
+ }
+
+ CLAMP(curweight, 0.0f, 1.0f);
+ BKE_gpencil_vgroup_add_point_weight(dvert, gso->vrgroup, curweight);
+
+ /* weight should stay within [0.0, 1.0] */
+ if (pt->pressure < 0.0f)
+ pt->pressure = 0.0f;
+
+ return true;
+}
+
+
+
/* ************************************************ */
/* Non Callback-Based Brushes */
@@ -827,7 +985,7 @@ static void gp_brush_clone_init(bContext *C, tGP_BrushEditData *gso)
/* Init colormap for mapping between the pasted stroke's source colour(names)
* and the final colours that will be used here instead...
*/
- data->new_colors = gp_copybuf_validate_colormap(gso->gpd);
+ data->new_colors = gp_copybuf_validate_colormap(C);
}
/* Free custom data used for "clone" brush */
@@ -857,9 +1015,12 @@ static void gp_brush_clone_add(bContext *C, tGP_BrushEditData *gso)
{
tGPSB_CloneBrushData *data = gso->customdata;
- Scene *scene = gso->scene;
+ Object *ob = CTX_data_active_object(C);
bGPDlayer *gpl = CTX_data_active_gpencil_layer(C);
- bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, CFRA, true);
+ Depsgraph *depsgraph = CTX_data_depsgraph(C);
+ int cfra_eval = (int)DEG_get_ctime(depsgraph);
+
+ bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, cfra_eval, true);
bGPDstroke *gps;
float delta[3];
@@ -882,17 +1043,22 @@ static void gp_brush_clone_add(bContext *C, tGP_BrushEditData *gso)
new_stroke = MEM_dupallocN(gps);
new_stroke->points = MEM_dupallocN(gps->points);
+ 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 */
- BLI_assert(new_stroke->colorname[0] != '\0');
- new_stroke->palcolor = BLI_ghash_lookup(data->new_colors, new_stroke->colorname);
-
- BLI_assert(new_stroke->palcolor != NULL);
- BLI_strncpy(new_stroke->colorname, new_stroke->palcolor->info, sizeof(new_stroke->colorname));
+ Material *ma = BLI_ghash_lookup(data->new_colors, &new_stroke->mat_nr);
+ if ((ma) && (BKE_object_material_slot_find_index(ob, ma) > 0)) {
+ gps->mat_nr = BKE_object_material_slot_find_index(ob, ma) - 1;
+ CLAMP_MIN(gps->mat_nr, 0);
+ }
+ else {
+ gps->mat_nr = 0; /* only if the color is not found */
+ }
/* Adjust all the stroke's points, so that the strokes
* get pasted relative to where the cursor is now
@@ -977,57 +1143,6 @@ static bool gpsculpt_brush_apply_clone(bContext *C, tGP_BrushEditData *gso)
}
/* ************************************************ */
-/* Cursor drawing */
-
-/* Helper callback for drawing the cursor itself */
-static void gp_brush_drawcursor(bContext *C, int x, int y, void *UNUSED(customdata))
-{
- GP_EditBrush_Data *brush = gpsculpt_get_brush(CTX_data_scene(C));
-
- if (brush) {
- GPUVertFormat *format = immVertexFormat();
- uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
-
- GPU_line_smooth(true);
- GPU_blend(true);
-
- /* Inner Ring: Light color for action of the brush */
- /* TODO: toggle between add and remove? */
- immUniformColor4ub(255, 255, 255, 200);
- imm_draw_circle_wire_2d(pos, x, y, brush->size, 40);
-
- /* Outer Ring: Dark color for contrast on light backgrounds (e.g. gray on white) */
- immUniformColor3ub(30, 30, 30);
- imm_draw_circle_wire_2d(pos, x, y, brush->size + 1, 40);
-
- immUnbindProgram();
-
- GPU_blend(false);
- GPU_line_smooth(false);
- }
-}
-
-/* Turn brush cursor in on/off */
-static void gpencil_toggle_brush_cursor(bContext *C, bool enable)
-{
- GP_BrushEdit_Settings *gset = gpsculpt_get_settings(CTX_data_scene(C));
-
- if (gset->paintcursor && !enable) {
- /* clear cursor */
- WM_paint_cursor_end(CTX_wm_manager(C), gset->paintcursor);
- gset->paintcursor = NULL;
- }
- else if (enable) {
- /* enable cursor */
- gset->paintcursor = WM_paint_cursor_activate(CTX_wm_manager(C),
- NULL,
- gp_brush_drawcursor, NULL);
- }
-}
-
-
-/* ************************************************ */
/* Header Info for GPencil Sculpt */
static void gpsculpt_brush_header_set(bContext *C, tGP_BrushEditData *gso)
@@ -1054,17 +1169,40 @@ 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);
+
+ const bool is_weight_mode = ob->mode == OB_MODE_GPENCIL_WEIGHT;
+ /* set the brush using the tool */
+ GP_BrushEdit_Settings *gset = &ts->gp_sculpt;
+ eGP_EditBrush_Types mode = RNA_enum_get(op->ptr, "mode");
+ const bool keep_brush = RNA_boolean_get(op->ptr, "keep_brush");
+
+ if (!keep_brush) {
+ if (is_weight_mode) {
+ gset->weighttype = mode;
+ }
+ else {
+ gset->brushtype = mode;
+ }
+ }
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->brush = gpsculpt_get_brush(scene);
+ gso->brush = gpsculpt_get_brush(scene, is_weight_mode);
- gso->brush_type = gso->settings->brushtype;
+ if (is_weight_mode) {
+ gso->brush_type = gso->settings->weighttype;
+ }
+ else {
+ gso->brush_type = gso->settings->brushtype;
+ }
/* Random generator, only init once. */
uint rng_seed = (uint)(PIL_check_seconds_timer_i() & UINT_MAX);
@@ -1078,10 +1216,31 @@ static bool gpsculpt_brush_init(bContext *C, wmOperator *op)
gso->cfra = INT_MAX; /* NOTE: So that first stroke will get handled in init_stroke() */
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_BRUSHEDIT_FLAG_FRAME_FALLOFF) != 0;
+
+ /* init multiedit falloff curve data before doing anything,
+ * so we won't have to do it again later
+ */
+ if (gso->is_multiframe) {
+ curvemapping_initialize(ts->gp_sculpt.cur_falloff);
+ }
+
/* initialise custom data for brushes */
switch (gso->brush_type) {
case GP_EDITBRUSH_TYPE_CLONE:
@@ -1133,9 +1292,10 @@ static bool gpsculpt_brush_init(bContext *C, wmOperator *op)
gpsculpt_brush_header_set(C, gso);
/* setup cursor drawing */
- WM_cursor_modal_set(CTX_wm_window(C), BC_CROSSCURSOR);
- gpencil_toggle_brush_cursor(C, true);
-
+ //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;
}
@@ -1179,7 +1339,12 @@ static void gpsculpt_brush_exit(bContext *C, wmOperator *op)
/* disable cursor and headerprints */
ED_workspace_status_text(C, NULL);
WM_cursor_modal_restore(win);
- gpencil_toggle_brush_cursor(C, false);
+ if (gso->sa->spacetype != SPACE_VIEW3D) {
+ ED_gpencil_toggle_brush_cursor(C, false, NULL);
+ }
+
+ /* disable temp invert flag */
+ gso->brush->flag &= ~GP_EDITBRUSH_FLAG_TMP_INVERT;
/* free operator data */
MEM_freeN(gso);
@@ -1197,13 +1362,13 @@ static bool gpsculpt_brush_poll(bContext *C)
static void gpsculpt_brush_init_stroke(tGP_BrushEditData *gso)
{
- Scene *scene = gso->scene;
bGPdata *gpd = gso->gpd;
+
bGPDlayer *gpl;
- int cfra = CFRA;
+ 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 == gso->cfra))
+ if ((gpd == NULL) || (cfra_eval == gso->cfra))
return;
/* go through each layer, and ensure that we've got a valid frame to use */
@@ -1217,21 +1382,21 @@ static void gpsculpt_brush_init_stroke(tGP_BrushEditData *gso)
* spent too much time editing the wrong frame...
*/
// XXX: should this be allowed when framelock is enabled?
- if (gpf->framenum != cfra) {
- BKE_gpencil_frame_addcopy(gpl, cfra);
+ 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;
+ gso->cfra = cfra_eval;
}
/* Apply ----------------------------------------------- */
/* Apply brush operation to points in this stroke */
static bool gpsculpt_brush_do_stroke(
- tGP_BrushEditData *gso, bGPDstroke *gps, bool parented,
+ tGP_BrushEditData *gso, bGPDstroke *gps,
float diff_mat[4][4], GP_BrushApplyCb apply)
{
GP_SpaceConversion *gsc = &gso->gsc;
@@ -1246,14 +1411,9 @@ static bool gpsculpt_brush_do_stroke(
bool changed = false;
if (gps->totpoints == 1) {
- if (!parented) {
- gp_point_to_xy(gsc, gps, gps->points, &pc1[0], &pc1[1]);
- }
- else {
- bGPDspoint pt_temp;
- gp_point_to_parent_space(gps->points, diff_mat, &pt_temp);
- gp_point_to_xy(gsc, gps, &pt_temp, &pc1[0], &pc1[1]);
- }
+ 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])) {
@@ -1280,19 +1440,12 @@ static bool gpsculpt_brush_do_stroke(
continue;
}
}
- if (!parented) {
- gp_point_to_xy(gsc, gps, pt1, &pc1[0], &pc1[1]);
- gp_point_to_xy(gsc, gps, pt2, &pc2[0], &pc2[1]);
- }
- else {
- bGPDspoint npt;
- gp_point_to_parent_space(pt1, diff_mat, &npt);
- gp_point_to_xy(gsc, gps, &npt, &pc1[0], &pc1[1]);
-
- gp_point_to_parent_space(pt2, diff_mat, &npt);
- gp_point_to_xy(gsc, gps, &npt, &pc2[0], &pc2[1]);
- }
+ 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])) ||
@@ -1342,9 +1495,114 @@ static bool gpsculpt_brush_do_stroke(
return changed;
}
+/* Apply sculpt brushes to strokes in the given frame */
+static bool gpsculpt_brush_do_frame(
+ bContext *C, tGP_BrushEditData *gso,
+ bGPDlayer *gpl, bGPDframe *gpf,
+ float diff_mat[4][4])
+{
+ bool changed = false;
+ 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_EDITBRUSH_TYPE_SMOOTH: /* Smooth strokes */
+ {
+ changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_smooth_apply);
+ break;
+ }
+
+ case GP_EDITBRUSH_TYPE_THICKNESS: /* Adjust stroke thickness */
+ {
+ changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_thickness_apply);
+ break;
+ }
+
+ case GP_EDITBRUSH_TYPE_STRENGTH: /* Adjust stroke color strength */
+ {
+ changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_strength_apply);
+ break;
+ }
+
+ case GP_EDITBRUSH_TYPE_GRAB: /* Grab points */
+ {
+ if (gso->first) {
+ /* First time this brush stroke is being applied:
+ * 1) Prepare data buffers (init/clear) for this stroke
+ * 2) Use the points now under the cursor
+ */
+ gp_brush_grab_stroke_init(gso, gps);
+ changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_grab_store_points);
+ }
+ else {
+ /* Apply effect to the stored points */
+ gp_brush_grab_apply_cached(gso, gps, diff_mat);
+ changed |= true;
+ }
+ break;
+ }
+
+ case GP_EDITBRUSH_TYPE_PUSH: /* Push points */
+ {
+ changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_push_apply);
+ break;
+ }
+
+ case GP_EDITBRUSH_TYPE_PINCH: /* Pinch points */
+ {
+ changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_pinch_apply);
+ break;
+ }
+
+ case GP_EDITBRUSH_TYPE_TWIST: /* Twist points around midpoint */
+ {
+ changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_twist_apply);
+ break;
+ }
+
+ case GP_EDITBRUSH_TYPE_RANDOMIZE: /* Apply jitter */
+ {
+ changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_randomize_apply);
+ break;
+ }
+
+ case GP_EDITBRUSH_TYPE_WEIGHT: /* Adjust vertex group weight */
+ {
+ changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_weight_apply);
+ break;
+ }
+
+
+ default:
+ printf("ERROR: Unknown type of GPencil Sculpt brush - %u\n", gso->brush_type);
+ break;
+ }
+ /* Triangulation must be calculated if changed */
+ if (changed) {
+ gps->flag |= GP_STROKE_RECALC_CACHES;
+ gps->tot_triangles = 0;
+ }
+ }
+
+ return changed;
+}
+
/* Perform two-pass brushes which modify the existing strokes */
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 */
@@ -1378,104 +1636,53 @@ static bool gpsculpt_brush_apply_standard(bContext *C, tGP_BrushEditData *gso)
/* Find visible strokes, and perform operations on those if hit */
- float diff_mat[4][4];
- bool parented = false;
-
CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
{
- bGPDframe *gpf = gpl->actframe;
- if (gpf == NULL)
+ /* If no active frame, don't do anything... */
+ if (gpl->actframe == NULL) {
continue;
-
- /* calculate difference matrix if parent object */
- if (gpl->parent != NULL) {
- ED_gpencil_parent_location(gpl, diff_mat);
- parented = true;
}
- else {
- parented = false;
- }
-
- for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) {
- /* skip strokes that are invalid for current view */
- if (ED_gpencil_stroke_can_use(C, gps) == false)
- continue;
- /* check if the color is editable */
- if (ED_gpencil_stroke_color_use(gpl, gps) == false) {
- continue;
- }
- switch (gso->brush_type) {
- case GP_EDITBRUSH_TYPE_SMOOTH: /* Smooth strokes */
- {
- changed |= gpsculpt_brush_do_stroke(gso, gps, parented, diff_mat, gp_brush_smooth_apply);
- break;
- }
+ /* calculate difference matrix */
+ float diff_mat[4][4];
+ ED_gpencil_parent_location(depsgraph, obact, gpd, gpl, diff_mat);
- case GP_EDITBRUSH_TYPE_THICKNESS: /* Adjust stroke thickness */
- {
- changed |= gpsculpt_brush_do_stroke(gso, gps, parented, diff_mat, gp_brush_thickness_apply);
- break;
- }
+ /* Active Frame or MultiFrame? */
+ if (gso->is_multiframe) {
+ /* init multiframe falloff options */
+ int f_init = 0;
+ int f_end = 0;
- case GP_EDITBRUSH_TYPE_STRENGTH: /* Adjust stroke color strength */
- {
- changed |= gpsculpt_brush_do_stroke(gso, gps, parented, diff_mat, gp_brush_strength_apply);
- break;
- }
+ if (gso->use_multiframe_falloff) {
+ BKE_gpencil_get_range_selected(gpl, &f_init, &f_end);
+ }
- case GP_EDITBRUSH_TYPE_GRAB: /* Grab points */
- {
- if (gso->first) {
- /* First time this brush stroke is being applied:
- * 1) Prepare data buffers (init/clear) for this stroke
- * 2) Use the points now under the cursor
- */
- gp_brush_grab_stroke_init(gso, gps);
- changed |= gpsculpt_brush_do_stroke(gso, gps, parented, diff_mat, gp_brush_grab_store_points);
+ 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 {
- /* Apply effect to the stored points */
- gp_brush_grab_apply_cached(gso, gps, parented, diff_mat);
- changed |= true;
+ /* No falloff */
+ gso->mf_falloff = 1.0f;
}
- break;
- }
-
- case GP_EDITBRUSH_TYPE_PUSH: /* Push points */
- {
- changed |= gpsculpt_brush_do_stroke(gso, gps, parented, diff_mat, gp_brush_push_apply);
- break;
- }
-
- case GP_EDITBRUSH_TYPE_PINCH: /* Pinch points */
- {
- changed |= gpsculpt_brush_do_stroke(gso, gps, parented, diff_mat, gp_brush_pinch_apply);
- break;
- }
- case GP_EDITBRUSH_TYPE_TWIST: /* Twist points around midpoint */
- {
- changed |= gpsculpt_brush_do_stroke(gso, gps, parented, diff_mat, gp_brush_twist_apply);
- break;
+ /* affect strokes in this frame */
+ changed |= gpsculpt_brush_do_frame(C, gso, gpl, gpf, diff_mat);
}
-
- case GP_EDITBRUSH_TYPE_RANDOMIZE: /* Apply jitter */
- {
- changed |= gpsculpt_brush_do_stroke(gso, gps, parented, diff_mat, gp_brush_randomize_apply);
- break;
- }
-
- default:
- printf("ERROR: Unknown type of GPencil Sculpt brush - %u\n", gso->brush_type);
- break;
- }
- /* Triangulation must be calculated if changed */
- if (changed) {
- gps->flag |= GP_STROKE_RECALC_CACHES;
- gps->tot_triangles = 0;
}
}
+ else {
+ /* Apply to active frame's strokes */
+ gso->mf_falloff = 1.0f;
+ changed |= gpsculpt_brush_do_frame(C, gso, gpl, gpl->actframe, diff_mat);
+ }
}
CTX_DATA_END;
@@ -1529,6 +1736,7 @@ static void gpsculpt_brush_apply(bContext *C, wmOperator *op, PointerRNA *itempt
/* Updates */
if (changed) {
+ DEG_id_tag_update(&gso->gpd->id, OB_RECALC_DATA);
WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
}
@@ -1852,6 +2060,7 @@ static int gpsculpt_brush_modal(bContext *C, wmOperator *op, const wmEvent *even
/* Redraw toolsettings (brush settings)? */
if (redraw_toolsettings) {
+ DEG_id_tag_update(&gso->gpd->id, OB_RECALC_DATA);
WM_event_add_notifier(C, NC_SCENE | ND_TOOLSETTINGS, NULL);
}
@@ -1860,6 +2069,19 @@ static int gpsculpt_brush_modal(bContext *C, wmOperator *op, const wmEvent *even
/* Operator --------------------------------------------- */
+static const EnumPropertyItem prop_gpencil_sculpt_brush_items[] = {
+ {GP_EDITBRUSH_TYPE_SMOOTH, "SMOOTH", 0, "Smooth", "Smooth stroke points" },
+ {GP_EDITBRUSH_TYPE_THICKNESS, "THICKNESS", 0, "Thickness", "Adjust thickness of strokes" },
+ {GP_EDITBRUSH_TYPE_STRENGTH, "STRENGTH", 0, "Strength", "Adjust color strength of strokes" },
+ {GP_EDITBRUSH_TYPE_GRAB, "GRAB", 0, "Grab", "Translate the set of points initially within the brush circle" },
+ {GP_EDITBRUSH_TYPE_PUSH, "PUSH", 0, "Push", "Move points out of the way, as if combing them" },
+ {GP_EDITBRUSH_TYPE_TWIST, "TWIST", 0, "Twist", "Rotate points around the midpoint of the brush" },
+ {GP_EDITBRUSH_TYPE_PINCH, "PINCH", 0, "Pinch", "Pull points towards the midpoint of the brush" },
+ {GP_EDITBRUSH_TYPE_RANDOMIZE, "RANDOMIZE", 0, "Randomize", "Introduce jitter/randomness into strokes" },
+ {GP_EDITBRUSH_TYPE_CLONE, "CLONE", 0, "Clone", "Paste copies of the strokes stored on the clipboard" },
+ {GP_EDITBRUSH_TYPE_WEIGHT, "WEIGHT", 0, "Weight", "Weight Paint" },
+ {0, NULL, 0, NULL, NULL }
+};
void GPENCIL_OT_brush_paint(wmOperatorType *ot)
{
@@ -1879,13 +2101,20 @@ void GPENCIL_OT_brush_paint(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING;
/* properties */
+ ot->prop = RNA_def_enum(ot->srna, "mode", prop_gpencil_sculpt_brush_items, 0, "Mode", "Brush mode");
+ RNA_def_property_flag(ot->prop, PROP_HIDDEN | PROP_SKIP_SAVE);
+
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_SKIP_SAVE);
+ RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
+
+ prop = RNA_def_boolean(ot->srna, "keep_brush", false, "Keep Brush",
+ "Keep current brush activated");
+ RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
}
/* ************************************************ */