Basic pressure sensitivity support in imagepaint, for opacity, size,

falloff and spacing. Click the button labeled "P" next to the sliders.
Works best for opacity, other options can give poor quality strokes in
some cases. Also performance needs to be improved.

Remember, works only on X11 and Mac, no Windows support for tablets yet.
This commit is contained in:
2006-09-24 19:29:25 +00:00
parent 7f7f35d1ca
commit ef7200741b
7 changed files with 111 additions and 43 deletions

View File

@@ -69,7 +69,7 @@ BrushPainter *brush_painter_new(struct Brush *brush);
void brush_painter_require_imbuf(BrushPainter *painter, short flt, void brush_painter_require_imbuf(BrushPainter *painter, short flt,
short texonly, int size); short texonly, int size);
int brush_painter_paint(BrushPainter *painter, BrushFunc func, float *pos, 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_break_stroke(BrushPainter *painter);
void brush_painter_free(BrushPainter *painter); void brush_painter_free(BrushPainter *painter);

View File

@@ -299,6 +299,13 @@ void brush_check_exists(Brush **brush)
/* Brush Sampling */ /* 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 brush_sample_falloff(Brush *brush, float dist)
{ {
float a, outer, inner; float a, outer, inner;
@@ -310,10 +317,12 @@ float brush_sample_falloff(Brush *brush, float dist)
return brush->alpha; return brush->alpha;
} }
else if ((dist < outer) && (inner < outer)) { 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)); a = sqrt((dist - inner)/(outer - inner));
return (1 - a)*brush->alpha; 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 else
return 0.0f; return 0.0f;
@@ -470,6 +479,8 @@ struct BrushPainter {
double accumtime; /* accumulated time since last paint op (airbrush) */ double accumtime; /* accumulated time since last paint op (airbrush) */
double lasttime; /* time of last update */ double lasttime; /* time of last update */
float lastpressure;
short firsttouch; /* first paint op */ short firsttouch; /* first paint op */
float startsize; float startsize;
@@ -720,24 +731,37 @@ void brush_painter_break_stroke(BrushPainter *painter)
painter->firsttouch= 1; 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; Brush *brush= painter->brush;
int totpaintops= 0; int totpaintops= 0;
if (pressure == 0.0f)
pressure = 1.0f; /* zero pressure == not using tablet */
if (painter->firsttouch) { if (painter->firsttouch) {
/* paint exactly once on first touch */ /* paint exactly once on first touch */
painter->startpaintpos[0]= pos[0]; painter->startpaintpos[0]= pos[0];
painter->startpaintpos[1]= pos[1]; painter->startpaintpos[1]= pos[1];
brush_apply_pressure(painter, brush, pressure);
if (painter->cache.enabled) if (painter->cache.enabled)
brush_painter_refresh_cache(painter, pos); brush_painter_refresh_cache(painter, pos);
totpaintops += func(user, painter->cache.ibuf, pos, pos); totpaintops += func(user, painter->cache.ibuf, pos, pos);
painter->lastpaintpos[0]= pos[0];
painter->lastpaintpos[1]= pos[1];
painter->lasttime= time; painter->lasttime= time;
painter->firsttouch= 0; painter->firsttouch= 0;
} }
#if 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[0]= painter->lastmousepos[0] + dmousepos[0]*step;
paintpos[1]= painter->lastmousepos[1] + dmousepos[1]*step; paintpos[1]= painter->lastmousepos[1] + dmousepos[1]*step;
if (painter->cache.enabled) brush_painter_refresh_cache(painter); if (painter->cache.enabled)
totpaintops += func(user, painter->cache.ibuf, painter->lastpaintpos, paintpos); brush_painter_refresh_cache(painter);
totpaintops += func(user, painter->cache.ibuf,
painter->lastpaintpos, paintpos);
painter->lastpaintpos[0]= paintpos[0]; painter->lastpaintpos[0]= paintpos[0];
painter->lastpaintpos[1]= paintpos[1]; painter->lastpaintpos[1]= paintpos[1];
@@ -774,15 +800,18 @@ int brush_painter_paint(BrushPainter *painter, BrushFunc func, float *pos, doubl
#endif #endif
else { else {
float startdistance, spacing, step, paintpos[2], dmousepos[2]; 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 */ /* compute brush spacing adapted to brush size, spacing may depend
spacing= brushsize*brush->spacing*0.01f; 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 */ /* setup starting distance, direction vector and accumulated distance */
startdistance= painter->accumdistance; startdistance= painter->accumdistance;
Vec2Subf(dmousepos, pos, painter->lastmousepos); Vec2Subf(dmousepos, pos, painter->lastmousepos);
painter->accumdistance += Normalise2(dmousepos); len= Normalise2(dmousepos);
painter->accumdistance += len;
/* do paint op over unpainted distance */ /* do paint op over unpainted distance */
while (painter->accumdistance >= spacing) { 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[0]= painter->lastmousepos[0] + dmousepos[0]*step;
paintpos[1]= painter->lastmousepos[1] + dmousepos[1]*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) if (painter->cache.enabled)
brush_painter_refresh_cache(painter, paintpos); brush_painter_refresh_cache(painter, paintpos);
totpaintops += totpaintops +=
func(user, painter->cache.ibuf, painter->lastpaintpos, paintpos); 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; painter->accumtime -= painttime;
while (painter->accumtime >= brush->rate) { while (painter->accumtime >= brush->rate) {
brush_apply_pressure(painter, brush, pressure);
if (painter->cache.enabled) if (painter->cache.enabled)
brush_painter_refresh_cache(painter, paintpos); brush_painter_refresh_cache(painter, paintpos);
totpaintops += totpaintops +=
@@ -828,6 +864,12 @@ int brush_painter_paint(BrushPainter *painter, BrushFunc func, float *pos, doubl
painter->lastmousepos[0]= pos[0]; painter->lastmousepos[0]= pos[0];
painter->lastmousepos[1]= pos[1]; 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; return totpaintops;
} }

View File

@@ -64,9 +64,13 @@ typedef struct Brush {
} Brush; } Brush;
/* Brush.flag */ /* Brush.flag */
#define BRUSH_AIRBRUSH 1 #define BRUSH_AIRBRUSH 1
#define BRUSH_TORUS 2 #define BRUSH_TORUS 2
#define BRUSH_FIXED_TEX 4 #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 */ /* Brush.blend */
#define BRUSH_BLEND_MIX 0 #define BRUSH_BLEND_MIX 0

View File

@@ -4274,10 +4274,14 @@ static void editing_panel_mesh_paint(void)
uiBlockBeginAlign(block); uiBlockBeginAlign(block);
uiDefButF(block, COL, B_VPCOLSLI, "", 0,yco,200,19, brush->rgb, 0, 0, 0, 0, ""); 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"); 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");
uiDefButI(block, NUMSLI, B_NOP, "Size ", 0,yco-40,200,19, &brush->size, 1, 200, 0, 0, "The size of 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");
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"); uiDefButI(block, NUMSLI, B_NOP, "Size ", 0,yco-40,180,19, &brush->size, 1, 200, 0, 0, "The size 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"); 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); uiBlockEndAlign(block);
yco -= 110; yco -= 110;

View File

@@ -1075,10 +1075,14 @@ static void image_panel_paint(short cntrl) // IMAGE_HANDLER_PROPERTIES
uiBlockBeginAlign(block); uiBlockBeginAlign(block);
uiDefButF(block, COL, B_VPCOLSLI, "", 0,yco,200,19, brush->rgb, 0, 0, 0, 0, ""); 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"); 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");
uiDefButI(block, NUMSLI, B_SIMANOTHING, "Size ", 0,yco-40,200,19, &brush->size, 1, 200, 0, 0, "The size of 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");
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"); uiDefButI(block, NUMSLI, B_SIMANOTHING, "Size ", 0,yco-40,180,19, &brush->size, 1, 200, 0, 0, "The size 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"); 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); uiBlockEndAlign(block);
yco -= 110; yco -= 110;

View File

@@ -2,8 +2,7 @@
* $Id$ * $Id$
* imagepaint.c * imagepaint.c
* *
* Functions to edit the "2D UV/Image " * Functions to paint images in 2D and 3D.
* and handle user events sent to it.
* *
* ***** BEGIN GPL/BL DUAL LICENSE BLOCK ***** * ***** BEGIN GPL/BL DUAL LICENSE BLOCK *****
* *
@@ -82,6 +81,8 @@
#include "BDR_imagepaint.h" #include "BDR_imagepaint.h"
#include "BDR_vpaint.h" #include "BDR_vpaint.h"
#include "GHOST_Types.h"
#include "blendef.h" #include "blendef.h"
#include "mydevice.h" #include "mydevice.h"
@@ -517,7 +518,7 @@ static void imapaint_canvas_free(ImagePaintState *s)
imb_freerectfloatImBuf(s->clonecanvas); 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]; 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); 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) if (update)
imapaint_image_update(image, texpaint); imapaint_image_update(image, texpaint);
return 1; return 1;
@@ -534,7 +535,7 @@ static int imapaint_do_paint(ImagePaintState *s, BrushPainter *painter, Image *i
else return 0; 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; Image *newimage = NULL;
float fwuv[2], bkuv[2], newuv[2]; float fwuv[2], bkuv[2], newuv[2];
@@ -564,7 +565,8 @@ static void imapaint_do(ImagePaintState *s, BrushPainter *painter, short texpain
if (breakstroke) { if (breakstroke) {
texpaint_pick_uv(s->ob, s->me, s->faceindex, mval, fwuv); 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(); imapaint_clear_partial_redraw();
brush_painter_break_stroke(painter); brush_painter_break_stroke(painter);
} }
@@ -577,8 +579,10 @@ static void imapaint_do(ImagePaintState *s, BrushPainter *painter, short texpain
/* paint in new image */ /* paint in new image */
if (newimage) { if (newimage) {
if (breakstroke) if (breakstroke)
redraw|= imapaint_do_paint(s, painter, newimage, texpaint, bkuv, time, 0); redraw|= imapaint_do_paint(s, painter, newimage, texpaint,
redraw|= imapaint_do_paint(s, painter, newimage, texpaint, newuv, time, 1); bkuv, time, 0, pressure);
redraw|= imapaint_do_paint(s, painter, newimage, texpaint, newuv,
time, 1, pressure);
} }
/* update state */ /* update state */
@@ -589,7 +593,8 @@ static void imapaint_do(ImagePaintState *s, BrushPainter *painter, short texpain
} }
else { else {
imapaint_compute_uvco(mval, newuv); 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) { if (redraw) {
@@ -605,6 +610,8 @@ void imagepaint_paint(short mousebutton, short texpaint)
ToolSettings *settings= G.scene->toolsettings; ToolSettings *settings= G.scene->toolsettings;
short prevmval[2], mval[2]; short prevmval[2], mval[2];
double time; double time;
float pressure;
const GHOST_TabletData *td;
/* initialize state */ /* initialize state */
memset(&s, 0, sizeof(s)); memset(&s, 0, sizeof(s));
@@ -641,30 +648,36 @@ void imagepaint_paint(short mousebutton, short texpaint)
painter= brush_painter_new(s.brush); painter= brush_painter_new(s.brush);
getmouseco_areawin(mval); getmouseco_areawin(mval);
td= get_tablet_data();
pressure= (td)? td->Pressure: 1.0f;
time= PIL_check_seconds_timer(); time= PIL_check_seconds_timer();
prevmval[0]= mval[0]; prevmval[0]= mval[0];
prevmval[1]= mval[1]; prevmval[1]= mval[1];
imapaint_do(&s, painter, texpaint, prevmval, mval, time); imapaint_do(&s, painter, texpaint, prevmval, mval, time, pressure);
//get_tablet_data();
/* paint loop */ /* paint loop */
while(get_mbut() & mousebutton) { do {
getmouseco_areawin(mval); getmouseco_areawin(mval);
if(td) {
td= get_tablet_data();
pressure= (td)? td->Pressure: 1.0f;
}
time= PIL_check_seconds_timer(); time= PIL_check_seconds_timer();
if((mval[0] != prevmval[0]) || (mval[1] != prevmval[1])) { 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[0]= mval[0];
prevmval[1]= mval[1]; prevmval[1]= mval[1];
//s.brush->size = MAX2(1, MIN2(200, s.brush->size*0.9));
} }
else if (s.brush->flag & BRUSH_AIRBRUSH) 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 else
BIF_wait_for_statechange(); 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 */ /* clean up */
settings->imapaint.flag &= ~IMAGEPAINT_DRAWING; settings->imapaint.flag &= ~IMAGEPAINT_DRAWING;
@@ -679,8 +692,6 @@ void imagepaint_paint(short mousebutton, short texpaint)
persp(PERSP_WIN); persp(PERSP_WIN);
} }
/* todo: BIF_undo_push("Image paint"); */
} }
void imagepaint_pick(short mousebutton) void imagepaint_pick(short mousebutton)

View File

@@ -547,6 +547,9 @@ void sample_vpaint() /* frontbuf */
brush->rgb[0]= cp[0]/255.0f; brush->rgb[0]= cp[0]/255.0f;
brush->rgb[1]= cp[1]/255.0f; brush->rgb[1]= cp[1]/255.0f;
brush->rgb[2]= cp[2]/255.0f; brush->rgb[2]= cp[2]/255.0f;
allqueue(REDRAWVIEW3D, 0);
allqueue(REDRAWIMAGE, 0);
} }
} }