diff --git a/source/blender/blenkernel/BKE_brush.h b/source/blender/blenkernel/BKE_brush.h index 52f86ef9c41..2c0034ad234 100644 --- a/source/blender/blenkernel/BKE_brush.h +++ b/source/blender/blenkernel/BKE_brush.h @@ -69,7 +69,7 @@ BrushPainter *brush_painter_new(struct Brush *brush); void brush_painter_require_imbuf(BrushPainter *painter, short flt, short texonly, int size); int brush_painter_paint(BrushPainter *painter, BrushFunc func, float *pos, - double time, void *user); + double time, float pressure, void *user); void brush_painter_break_stroke(BrushPainter *painter); void brush_painter_free(BrushPainter *painter); diff --git a/source/blender/blenkernel/intern/brush.c b/source/blender/blenkernel/intern/brush.c index 5ade66e4566..36e8c2137cc 100644 --- a/source/blender/blenkernel/intern/brush.c +++ b/source/blender/blenkernel/intern/brush.c @@ -299,6 +299,13 @@ void brush_check_exists(Brush **brush) /* Brush Sampling */ +/*static float taylor_approx_cos(float f) +{ + f = f*f; + f = 1.0f - f/2.0f + f*f/24.0f; + return f; +}*/ + float brush_sample_falloff(Brush *brush, float dist) { float a, outer, inner; @@ -310,10 +317,12 @@ float brush_sample_falloff(Brush *brush, float dist) return brush->alpha; } else if ((dist < outer) && (inner < outer)) { - /* formula used by sculpt: - 0.5f * (cos(3*(dist - inner)/(outer - inner)) + 1); */ a = sqrt((dist - inner)/(outer - inner)); return (1 - a)*brush->alpha; + + /* formula used by sculpt, with taylor approx + a = 0.5f*(taylor_approx_cos(3.0f*(dist - inner)/(outer - inner)) + 1.0f); + return a*brush->alpha; */ } else return 0.0f; @@ -470,6 +479,8 @@ struct BrushPainter { double accumtime; /* accumulated time since last paint op (airbrush) */ double lasttime; /* time of last update */ + float lastpressure; + short firsttouch; /* first paint op */ float startsize; @@ -720,24 +731,37 @@ void brush_painter_break_stroke(BrushPainter *painter) painter->firsttouch= 1; } -int brush_painter_paint(BrushPainter *painter, BrushFunc func, float *pos, double time, void *user) +static void brush_apply_pressure(BrushPainter *painter, Brush *brush, float pressure) +{ + if (brush->flag & BRUSH_ALPHA_PRESSURE) + brush->alpha = MAX2(0.0, painter->startalpha*pressure); + if (brush->flag & BRUSH_SIZE_PRESSURE) + brush->size = MAX2(1.0, painter->startsize*pressure); + if (brush->flag & BRUSH_RAD_PRESSURE) + brush->innerradius = MAX2(0.0, painter->startinnerradius*pressure); + if (brush->flag & BRUSH_SPACING_PRESSURE) + brush->spacing = MAX2(1.0, painter->startspacing*(1.5f-pressure)); +} + +int brush_painter_paint(BrushPainter *painter, BrushFunc func, float *pos, double time, float pressure, void *user) { Brush *brush= painter->brush; int totpaintops= 0; + if (pressure == 0.0f) + pressure = 1.0f; /* zero pressure == not using tablet */ + if (painter->firsttouch) { /* paint exactly once on first touch */ painter->startpaintpos[0]= pos[0]; painter->startpaintpos[1]= pos[1]; + brush_apply_pressure(painter, brush, pressure); if (painter->cache.enabled) brush_painter_refresh_cache(painter, pos); totpaintops += func(user, painter->cache.ibuf, pos, pos); - painter->lastpaintpos[0]= pos[0]; - painter->lastpaintpos[1]= pos[1]; painter->lasttime= time; - painter->firsttouch= 0; } #if 0 @@ -760,8 +784,10 @@ int brush_painter_paint(BrushPainter *painter, BrushFunc func, float *pos, doubl paintpos[0]= painter->lastmousepos[0] + dmousepos[0]*step; paintpos[1]= painter->lastmousepos[1] + dmousepos[1]*step; - if (painter->cache.enabled) brush_painter_refresh_cache(painter); - totpaintops += func(user, painter->cache.ibuf, painter->lastpaintpos, paintpos); + if (painter->cache.enabled) + brush_painter_refresh_cache(painter); + totpaintops += func(user, painter->cache.ibuf, + painter->lastpaintpos, paintpos); painter->lastpaintpos[0]= paintpos[0]; painter->lastpaintpos[1]= paintpos[1]; @@ -774,15 +800,18 @@ int brush_painter_paint(BrushPainter *painter, BrushFunc func, float *pos, doubl #endif else { float startdistance, spacing, step, paintpos[2], dmousepos[2]; - float brushsize = MAX2(1.0, brush->size); + float t, len, press; - /* compute brush spacing adapted to brush size */ - spacing= brushsize*brush->spacing*0.01f; + /* compute brush spacing adapted to brush size, spacing may depend + on pressure, so update it */ + brush_apply_pressure(painter, brush, painter->lastpressure); + spacing= MAX2(1.0, brush->size)*brush->spacing*0.01f; /* setup starting distance, direction vector and accumulated distance */ startdistance= painter->accumdistance; Vec2Subf(dmousepos, pos, painter->lastmousepos); - painter->accumdistance += Normalise2(dmousepos); + len= Normalise2(dmousepos); + painter->accumdistance += len; /* do paint op over unpainted distance */ while (painter->accumdistance >= spacing) { @@ -790,8 +819,14 @@ int brush_painter_paint(BrushPainter *painter, BrushFunc func, float *pos, doubl paintpos[0]= painter->lastmousepos[0] + dmousepos[0]*step; paintpos[1]= painter->lastmousepos[1] + dmousepos[1]*step; + t = step/len; + press= (1.0-t)*painter->lastpressure + t*pressure; + brush_apply_pressure(painter, brush, press); + spacing= MAX2(1.0, brush->size)*brush->spacing*0.01f; + if (painter->cache.enabled) brush_painter_refresh_cache(painter, paintpos); + totpaintops += func(user, painter->cache.ibuf, painter->lastpaintpos, paintpos); @@ -815,6 +850,7 @@ int brush_painter_paint(BrushPainter *painter, BrushFunc func, float *pos, doubl painter->accumtime -= painttime; while (painter->accumtime >= brush->rate) { + brush_apply_pressure(painter, brush, pressure); if (painter->cache.enabled) brush_painter_refresh_cache(painter, paintpos); totpaintops += @@ -828,6 +864,12 @@ int brush_painter_paint(BrushPainter *painter, BrushFunc func, float *pos, doubl painter->lastmousepos[0]= pos[0]; painter->lastmousepos[1]= pos[1]; + painter->lastpressure= pressure; + + brush->alpha = painter->startalpha; + brush->size = painter->startsize; + brush->innerradius = painter->startinnerradius; + brush->spacing = painter->startspacing; return totpaintops; } diff --git a/source/blender/makesdna/DNA_brush_types.h b/source/blender/makesdna/DNA_brush_types.h index 41df45a3d9f..40347c9834f 100644 --- a/source/blender/makesdna/DNA_brush_types.h +++ b/source/blender/makesdna/DNA_brush_types.h @@ -64,9 +64,13 @@ typedef struct Brush { } Brush; /* Brush.flag */ -#define BRUSH_AIRBRUSH 1 -#define BRUSH_TORUS 2 -#define BRUSH_FIXED_TEX 4 +#define BRUSH_AIRBRUSH 1 +#define BRUSH_TORUS 2 +#define BRUSH_ALPHA_PRESSURE 4 +#define BRUSH_SIZE_PRESSURE 8 +#define BRUSH_RAD_PRESSURE 16 +#define BRUSH_SPACING_PRESSURE 32 +#define BRUSH_FIXED_TEX 64 /* Brush.blend */ #define BRUSH_BLEND_MIX 0 diff --git a/source/blender/src/buttons_editing.c b/source/blender/src/buttons_editing.c index d33a0528fa8..3e03b3deaba 100644 --- a/source/blender/src/buttons_editing.c +++ b/source/blender/src/buttons_editing.c @@ -4274,10 +4274,14 @@ static void editing_panel_mesh_paint(void) uiBlockBeginAlign(block); uiDefButF(block, COL, B_VPCOLSLI, "", 0,yco,200,19, brush->rgb, 0, 0, 0, 0, ""); - uiDefButF(block, NUMSLI, B_NOP, "Opacity ", 0,yco-20,200,19, &brush->alpha, 0.0, 1.0, 0, 0, "The amount of pressure on the brush"); - uiDefButI(block, NUMSLI, B_NOP, "Size ", 0,yco-40,200,19, &brush->size, 1, 200, 0, 0, "The size of the brush"); - uiDefButF(block, NUMSLI, B_NOP, "Falloff ", 0,yco-60,200,19, &brush->innerradius, 0.0, 1.0, 0, 0, "The fall off radius of the brush"); - uiDefButF(block, NUMSLI, B_NOP, "Spacing ",0,yco-80,200,19, &brush->spacing, 1.0, 100.0, 0, 0, "Repeating paint on %% of brush diameter"); + uiDefButF(block, NUMSLI, B_NOP, "Opacity ", 0,yco-20,180,19, &brush->alpha, 0.0, 1.0, 0, 0, "The amount of pressure on the brush"); + uiDefButBitS(block, TOG|BIT, BRUSH_ALPHA_PRESSURE, B_NOP, "P", 180,yco-20,20,19, &brush->flag, 0, 0, 0, 0, "Enables pressure sensitivity for tablets"); + uiDefButI(block, NUMSLI, B_NOP, "Size ", 0,yco-40,180,19, &brush->size, 1, 200, 0, 0, "The size of the brush"); + uiDefButBitS(block, TOG|BIT, BRUSH_SIZE_PRESSURE, B_NOP, "P", 180,yco-40,20,19, &brush->flag, 0, 0, 0, 0, "Enables pressure sensitivity for tablets"); + uiDefButF(block, NUMSLI, B_NOP, "Falloff ", 0,yco-60,180,19, &brush->innerradius, 0.0, 1.0, 0, 0, "The fall off radius of the brush"); + uiDefButBitS(block, TOG|BIT, BRUSH_RAD_PRESSURE, B_NOP, "P", 180,yco-60,20,19, &brush->flag, 0, 0, 0, 0, "Enables pressure sensitivity for tablets"); + uiDefButF(block, NUMSLI, B_NOP, "Spacing ",0,yco-80,180,19, &brush->spacing, 1.0, 100.0, 0, 0, "Repeating paint on %% of brush diameter"); + uiDefButBitS(block, TOG|BIT, BRUSH_SPACING_PRESSURE, B_NOP, "P", 180,yco-80,20,19, &brush->flag, 0, 0, 0, 0, "Enables pressure sensitivity for tablets"); uiBlockEndAlign(block); yco -= 110; diff --git a/source/blender/src/drawimage.c b/source/blender/src/drawimage.c index be60509852f..3958f0d0c3a 100644 --- a/source/blender/src/drawimage.c +++ b/source/blender/src/drawimage.c @@ -1075,10 +1075,14 @@ static void image_panel_paint(short cntrl) // IMAGE_HANDLER_PROPERTIES uiBlockBeginAlign(block); uiDefButF(block, COL, B_VPCOLSLI, "", 0,yco,200,19, brush->rgb, 0, 0, 0, 0, ""); - uiDefButF(block, NUMSLI, B_SIMANOTHING, "Opacity ", 0,yco-20,200,19, &brush->alpha, 0.0, 1.0, 0, 0, "The amount of pressure on the brush"); - uiDefButI(block, NUMSLI, B_SIMANOTHING, "Size ", 0,yco-40,200,19, &brush->size, 1, 200, 0, 0, "The size of the brush"); - uiDefButF(block, NUMSLI, B_SIMANOTHING, "Falloff ", 0,yco-60,200,19, &brush->innerradius, 0.0, 1.0, 0, 0, "The fall off radius of the brush"); - uiDefButF(block, NUMSLI, B_SIMANOTHING, "Spacing ",0,yco-80,200,19, &brush->spacing, 1.0, 100.0, 0, 0, "Repeating paint on %% of brush diameter"); + uiDefButF(block, NUMSLI, B_SIMANOTHING, "Opacity ", 0,yco-20,180,19, &brush->alpha, 0.0, 1.0, 0, 0, "The amount of pressure on the brush"); + uiDefButBitS(block, TOG|BIT, BRUSH_ALPHA_PRESSURE, B_SIMANOTHING, "P", 180,yco-20,20,19, &brush->flag, 0, 0, 0, 0, "Enables pressure sensitivity for tablets"); + uiDefButI(block, NUMSLI, B_SIMANOTHING, "Size ", 0,yco-40,180,19, &brush->size, 1, 200, 0, 0, "The size of the brush"); + uiDefButBitS(block, TOG|BIT, BRUSH_SIZE_PRESSURE, B_SIMANOTHING, "P", 180,yco-40,20,19, &brush->flag, 0, 0, 0, 0, "Enables pressure sensitivity for tablets"); + uiDefButF(block, NUMSLI, B_SIMANOTHING, "Falloff ", 0,yco-60,180,19, &brush->innerradius, 0.0, 1.0, 0, 0, "The fall off radius of the brush"); + uiDefButBitS(block, TOG|BIT, BRUSH_RAD_PRESSURE, B_SIMANOTHING, "P", 180,yco-60,20,19, &brush->flag, 0, 0, 0, 0, "Enables pressure sensitivity for tablets"); + uiDefButF(block, NUMSLI, B_SIMANOTHING, "Spacing ",0,yco-80,180,19, &brush->spacing, 1.0, 100.0, 0, 0, "Repeating paint on %% of brush diameter"); + uiDefButBitS(block, TOG|BIT, BRUSH_SPACING_PRESSURE, B_SIMANOTHING, "P", 180,yco-80,20,19, &brush->flag, 0, 0, 0, 0, "Enables pressure sensitivity for tablets"); uiBlockEndAlign(block); yco -= 110; diff --git a/source/blender/src/imagepaint.c b/source/blender/src/imagepaint.c index dc7db64836a..64a61f0acf0 100644 --- a/source/blender/src/imagepaint.c +++ b/source/blender/src/imagepaint.c @@ -2,8 +2,7 @@ * $Id$ * imagepaint.c * - * Functions to edit the "2D UV/Image " - * and handle user events sent to it. + * Functions to paint images in 2D and 3D. * * ***** BEGIN GPL/BL DUAL LICENSE BLOCK ***** * @@ -82,6 +81,8 @@ #include "BDR_imagepaint.h" #include "BDR_vpaint.h" +#include "GHOST_Types.h" + #include "blendef.h" #include "mydevice.h" @@ -517,7 +518,7 @@ static void imapaint_canvas_free(ImagePaintState *s) imb_freerectfloatImBuf(s->clonecanvas); } -static int imapaint_do_paint(ImagePaintState *s, BrushPainter *painter, Image *image, short texpaint, float *uv, double time, int update) +static int imapaint_do_paint(ImagePaintState *s, BrushPainter *painter, Image *image, short texpaint, float *uv, double time, int update, float pressure) { float pos[2]; @@ -526,7 +527,7 @@ static int imapaint_do_paint(ImagePaintState *s, BrushPainter *painter, Image *i brush_painter_require_imbuf(painter, ((image->ibuf->rect_float)? 1: 0), 0, 0); - if (brush_painter_paint(painter, imapaint_paint_op, pos, time, s)) { + if (brush_painter_paint(painter, imapaint_paint_op, pos, time, pressure, s)) { if (update) imapaint_image_update(image, texpaint); return 1; @@ -534,7 +535,7 @@ static int imapaint_do_paint(ImagePaintState *s, BrushPainter *painter, Image *i else return 0; } -static void imapaint_do(ImagePaintState *s, BrushPainter *painter, short texpaint, short *prevmval, short *mval, double time) +static void imapaint_do(ImagePaintState *s, BrushPainter *painter, short texpaint, short *prevmval, short *mval, double time, float pressure) { Image *newimage = NULL; float fwuv[2], bkuv[2], newuv[2]; @@ -564,7 +565,8 @@ static void imapaint_do(ImagePaintState *s, BrushPainter *painter, short texpain if (breakstroke) { texpaint_pick_uv(s->ob, s->me, s->faceindex, mval, fwuv); - redraw |= imapaint_do_paint(s, painter, s->image, texpaint, fwuv, time, 1); + redraw |= imapaint_do_paint(s, painter, s->image, texpaint, fwuv, + time, 1, pressure); imapaint_clear_partial_redraw(); brush_painter_break_stroke(painter); } @@ -577,8 +579,10 @@ static void imapaint_do(ImagePaintState *s, BrushPainter *painter, short texpain /* paint in new image */ if (newimage) { if (breakstroke) - redraw|= imapaint_do_paint(s, painter, newimage, texpaint, bkuv, time, 0); - redraw|= imapaint_do_paint(s, painter, newimage, texpaint, newuv, time, 1); + redraw|= imapaint_do_paint(s, painter, newimage, texpaint, + bkuv, time, 0, pressure); + redraw|= imapaint_do_paint(s, painter, newimage, texpaint, newuv, + time, 1, pressure); } /* update state */ @@ -589,7 +593,8 @@ static void imapaint_do(ImagePaintState *s, BrushPainter *painter, short texpain } else { imapaint_compute_uvco(mval, newuv); - redraw |= imapaint_do_paint(s, painter, s->image, texpaint, newuv, time, 1); + redraw |= imapaint_do_paint(s, painter, s->image, texpaint, newuv, + time, 1, pressure); } if (redraw) { @@ -605,6 +610,8 @@ void imagepaint_paint(short mousebutton, short texpaint) ToolSettings *settings= G.scene->toolsettings; short prevmval[2], mval[2]; double time; + float pressure; + const GHOST_TabletData *td; /* initialize state */ memset(&s, 0, sizeof(s)); @@ -641,30 +648,36 @@ void imagepaint_paint(short mousebutton, short texpaint) painter= brush_painter_new(s.brush); getmouseco_areawin(mval); + td= get_tablet_data(); + pressure= (td)? td->Pressure: 1.0f; time= PIL_check_seconds_timer(); prevmval[0]= mval[0]; prevmval[1]= mval[1]; - imapaint_do(&s, painter, texpaint, prevmval, mval, time); - - //get_tablet_data(); + imapaint_do(&s, painter, texpaint, prevmval, mval, time, pressure); /* paint loop */ - while(get_mbut() & mousebutton) { + do { getmouseco_areawin(mval); + if(td) { + td= get_tablet_data(); + pressure= (td)? td->Pressure: 1.0f; + } time= PIL_check_seconds_timer(); if((mval[0] != prevmval[0]) || (mval[1] != prevmval[1])) { - imapaint_do(&s, painter, texpaint, prevmval, mval, time); + imapaint_do(&s, painter, texpaint, prevmval, mval, time, pressure); prevmval[0]= mval[0]; prevmval[1]= mval[1]; - //s.brush->size = MAX2(1, MIN2(200, s.brush->size*0.9)); } else if (s.brush->flag & BRUSH_AIRBRUSH) - imapaint_do(&s, painter, texpaint, prevmval, mval, time); + imapaint_do(&s, painter, texpaint, prevmval, mval, time, pressure); else BIF_wait_for_statechange(); - } + + /* do mouse checking at the end, so don't check twice, and potentially + miss a short tap */ + } while(get_mbut() & mousebutton); /* clean up */ settings->imapaint.flag &= ~IMAGEPAINT_DRAWING; @@ -679,8 +692,6 @@ void imagepaint_paint(short mousebutton, short texpaint) persp(PERSP_WIN); } - - /* todo: BIF_undo_push("Image paint"); */ } void imagepaint_pick(short mousebutton) diff --git a/source/blender/src/vpaint.c b/source/blender/src/vpaint.c index 380763834ac..cf4c5e9b9fc 100644 --- a/source/blender/src/vpaint.c +++ b/source/blender/src/vpaint.c @@ -547,6 +547,9 @@ void sample_vpaint() /* frontbuf */ brush->rgb[0]= cp[0]/255.0f; brush->rgb[1]= cp[1]/255.0f; brush->rgb[2]= cp[2]/255.0f; + + allqueue(REDRAWVIEW3D, 0); + allqueue(REDRAWIMAGE, 0); } }