diff options
author | Brecht Van Lommel <brechtvanlommel@pandora.be> | 2006-07-28 04:56:35 +0400 |
---|---|---|
committer | Brecht Van Lommel <brechtvanlommel@pandora.be> | 2006-07-28 04:56:35 +0400 |
commit | f893d950a1524d1069963dde251d48143063ad51 (patch) | |
tree | 30b8a54e5fd3bcdb059346a77337d68fdfca7180 /source | |
parent | 3d0f27c19d943f5bb9e3052b0c6a03313a16bbb0 (diff) |
Image Paint:
- Code for brush spacing and timing was rewritten, making spacing more even.
Example: http://users.pandora.be/blendix/brush_spacing.jpg
- Instead of Stepsize for regular brushes and Flow for airbrushes, there is
now Spacing for both, and Rate for airbrushes.
- Airbrush now works more like it does in the Gimp now, by maintaining the
spacing even if the brush moves faster than the painting rate.
- Some preparations to make brushes work in texture paint mode.
Diffstat (limited to 'source')
-rw-r--r-- | source/blender/blenkernel/intern/brush.c | 3 | ||||
-rw-r--r-- | source/blender/include/BDR_imagepaint.h | 4 | ||||
-rw-r--r-- | source/blender/include/blendef.h | 8 | ||||
-rw-r--r-- | source/blender/include/butspace.h | 5 | ||||
-rw-r--r-- | source/blender/makesdna/DNA_brush_types.h | 20 | ||||
-rw-r--r-- | source/blender/makesdna/DNA_scene_types.h | 8 | ||||
-rw-r--r-- | source/blender/src/buttons_editing.c | 91 | ||||
-rw-r--r-- | source/blender/src/drawimage.c | 31 | ||||
-rw-r--r-- | source/blender/src/editface.c | 318 | ||||
-rw-r--r-- | source/blender/src/imagepaint.c | 528 | ||||
-rw-r--r-- | source/blender/src/space.c | 4 | ||||
-rw-r--r-- | source/blender/src/vpaint.c | 21 |
12 files changed, 510 insertions, 531 deletions
diff --git a/source/blender/blenkernel/intern/brush.c b/source/blender/blenkernel/intern/brush.c index ca9d1154024..383cf5b3fe8 100644 --- a/source/blender/blenkernel/intern/brush.c +++ b/source/blender/blenkernel/intern/brush.c @@ -54,7 +54,8 @@ Brush *add_brush(char *name) brush->rgb[2]= 1.0f; brush->alpha= 0.2f; brush->size= 25; - brush->timing= 100.0f; + brush->spacing= 10.0f; + brush->rate= 0.1f; brush->innerradius= 0.5f; brush->clone.alpha= 0.5; diff --git a/source/blender/include/BDR_imagepaint.h b/source/blender/include/BDR_imagepaint.h index 8824c63071d..9ea9c9a107a 100644 --- a/source/blender/include/BDR_imagepaint.h +++ b/source/blender/include/BDR_imagepaint.h @@ -34,10 +34,10 @@ #define BDR_IMAGEPAINT_H void imagepaint_redraw_tool(void); -void imagepaint_paint(short mousebutton); void imagepaint_pick(short mousebutton); -void texturepaint_paint(); +void imagepaint_paint(short mousebutton); +void texturepaint_paint(short mousebutton); #endif /* BDR_IMAGEPAINT_H */ diff --git a/source/blender/include/blendef.h b/source/blender/include/blendef.h index 49873674917..fde70e4a871 100644 --- a/source/blender/include/blendef.h +++ b/source/blender/include/blendef.h @@ -310,16 +310,16 @@ #define B_SIMAGESAVE 364 #define B_SIMACLONEBROWSE 365 #define B_SIMACLONEDELETE 366 -#define B_SIMABRUSHCHANGE 367 #define B_SIMANOTHING 368 #define B_SIMACURVES 369 #define B_SIMARANGE 370 #define B_SIMA_USE_ALPHA 371 #define B_SIMA_SHOW_ALPHA 372 #define B_SIMA_SHOW_ZBUF 373 -#define B_BRUSHBROWSE 374 -#define B_BRUSHDELETE 375 -#define B_BRUSHLOCAL 376 +#define B_SIMABRUSHBROWSE 374 +#define B_SIMABRUSHDELETE 375 +#define B_SIMABRUSHLOCAL 376 +#define B_SIMABRUSHCHANGE 377 /* BUTS: 400 */ #define B_BUTSHOME 401 diff --git a/source/blender/include/butspace.h b/source/blender/include/butspace.h index a739ee9327f..adcf13b0f55 100644 --- a/source/blender/include/butspace.h +++ b/source/blender/include/butspace.h @@ -537,6 +537,11 @@ void curvemap_buttons(struct uiBlock *block, struct CurveMapping *cumap, char la #define B_CLR_WPAINT 2850 +#define B_BRUSHBROWSE 2851 +#define B_BRUSHDELETE 2852 +#define B_BRUSHLOCAL 2853 +#define B_BRUSHCHANGE 2854 + /* *********************** */ #define B_RADIOBUTS 3000 diff --git a/source/blender/makesdna/DNA_brush_types.h b/source/blender/makesdna/DNA_brush_types.h index ff578494339..8372e004fcb 100644 --- a/source/blender/makesdna/DNA_brush_types.h +++ b/source/blender/makesdna/DNA_brush_types.h @@ -35,23 +35,25 @@ typedef struct Brush { ID id; - short flag, blend; - int size; - float innerradius; - float timing; + short flag, blend; /* general purpose flag, blend mode */ + int size; /* brush diameter */ + float innerradius; /* inner radius after which the falloff starts */ + float spacing; /* spacing of paint operations */ + float rate; /* paint operations / second (airbrush) */ - float rgb[3]; /* color */ - float alpha; /* opacity */ + float rgb[3]; /* color */ + float alpha; /* opacity */ struct Clone { - struct Image *image; /* image for clone tool */ - float offset[2]; /* offset of clone image from canvas */ - float alpha; /* transparency for drawing of clone image */ + struct Image *image; /* image for clone tool */ + float offset[2]; /* offset of clone image from canvas */ + float alpha; /* transparency for drawing of clone image */ } clone; } Brush; /* Brush.flag */ #define BRUSH_AIRBRUSH 1 +#define BRUSH_TORUS 2 /* Brush.blend */ #define BRUSH_BLEND_MIX 0 diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index fe2b0cfa015..c71df7a655e 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -544,11 +544,9 @@ typedef struct Scene { #define FFMPEG_AUTOSPLIT_OUTPUT 2 /* toolsettings->imagepaint_flag */ -#define IMAGEPAINT_TORUS 1 -#define IMAGEPAINT_DRAWING 2 -#define IMAGEPAINT_TIMED 4 -#define IMAGEPAINT_DRAW_TOOL 8 -#define IMAGEPAINT_DRAW_TOOL_DRAWING 16 +#define IMAGEPAINT_DRAWING 1 +#define IMAGEPAINT_DRAW_TOOL 2 +#define IMAGEPAINT_DRAW_TOOL_DRAWING 4 #ifdef __cplusplus } diff --git a/source/blender/src/buttons_editing.c b/source/blender/src/buttons_editing.c index 88ed339bd7f..31fdf5b7b06 100644 --- a/source/blender/src/buttons_editing.c +++ b/source/blender/src/buttons_editing.c @@ -48,6 +48,7 @@ #include "DNA_action_types.h" #include "DNA_armature_types.h" +#include "DNA_brush_types.h" #include "DNA_camera_types.h" #include "DNA_color_types.h" #include "DNA_constraint_types.h" @@ -75,6 +76,7 @@ #include "DNA_packedFile_types.h" #include "BKE_blender.h" +#include "BKE_brush.h" #include "BKE_curve.h" #include "BKE_colortools.h" #include "BKE_depsgraph.h" @@ -3717,6 +3719,7 @@ void do_fpaintbuts(unsigned short event) TFace *activetf, *tf; int a; extern VPaint Gwp; /* from vpaint */ + ToolSettings *settings= G.scene->toolsettings; ob= OBACT; if(ob==NULL) return; @@ -3852,6 +3855,40 @@ void do_fpaintbuts(unsigned short event) allqueue(REDRAWVIEW3D, 0); DAG_object_flush_update(G.scene, ob, OB_RECALC_DATA); } + case B_BRUSHBROWSE: + if(G.buts->menunr==-2) { + activate_databrowse((ID*)settings->imapaint.brush, ID_BR, 0, B_BRUSHBROWSE, &G.buts->menunr, do_global_buttons); + break; + } + else if(G.buts->menunr < 0) break; + + if(brush_set_nr(&settings->imapaint.brush, G.buts->menunr)) { + BIF_undo_push("Browse Brush"); + allqueue(REDRAWBUTSEDIT, 0); + allqueue(REDRAWIMAGE, 0); + } + break; + case B_BRUSHDELETE: + if(brush_delete(&settings->imapaint.brush)) { + BIF_undo_push("Unlink Brush"); + allqueue(REDRAWBUTSEDIT, 0); + allqueue(REDRAWIMAGE, 0); + } + break; + case B_KEEPDATA: + brush_toggle_fake_user(settings->imapaint.brush); + allqueue(REDRAWBUTSEDIT, 0); + allqueue(REDRAWIMAGE, 0); + break; + case B_BRUSHLOCAL: + if(settings->imapaint.brush && settings->imapaint.brush->id.lib) { + if(okee("Make local")) { + make_local_brush(settings->imapaint.brush); + allqueue(REDRAWBUTSEDIT, 0); + allqueue(REDRAWIMAGE, 0); + } + } + break; } } @@ -3918,7 +3955,7 @@ static void editing_panel_mesh_paint(void) uiBlockEndAlign(block); } } - else{ + else { // if(G.f & G_VERTEXPAINT) { extern VPaint Gvp; /* from vpaint */ uiBlockBeginAlign(block); @@ -3956,10 +3993,56 @@ static void editing_panel_mesh_paint(void) uiDefButF(block, NUM, B_DIFF, "Mul:", 1061,0,112,19, &Gvp.mul, 0.1, 50.0, 10, 0, "Set the number to multiply vertex colors with"); uiDefButF(block, NUM, B_DIFF, "Gamma:", 1174,0,102,19, &Gvp.gamma, 0.1, 5.0, 10, 0, "Change the clarity of the vertex colors"); uiBlockEndAlign(block); - - } - +#if 0 + else { /* texture paint */ + ToolSettings *settings= G.scene->toolsettings; + Brush *brush= settings->imapaint.brush; + ID *id; + int yco, xco, butw; + + yco= 160; + + uiBlockBeginAlign(block); + uiDefButS(block, ROW, B_BRUSHCHANGE, "Draw", 0 ,yco,80,19, &settings->imapaint.tool, 7.0, PAINT_TOOL_DRAW, 0, 0, "Draw brush"); + uiDefButS(block, ROW, B_BRUSHCHANGE, "Soften", 80 ,yco,80,19, &settings->imapaint.tool, 7.0, PAINT_TOOL_SOFTEN, 0, 0, "Soften brush"); + uiDefButS(block, ROW, B_BRUSHCHANGE, "Smear", 160,yco,80,19, &settings->imapaint.tool, 7.0, PAINT_TOOL_SMEAR, 0, 0, "Smear brush"); + uiBlockEndAlign(block); + yco -= 30; + + uiBlockSetCol(block, TH_BUT_SETTING2); + id= (ID*)settings->imapaint.brush; + xco= std_libbuttons(block, 0, yco, 0, NULL, B_BRUSHBROWSE, ID_BR, 0, id, NULL, &(G.buts->menunr), 0, B_BRUSHLOCAL, B_BRUSHDELETE, 0, B_KEEPDATA); + uiBlockSetCol(block, TH_AUTO); + + if(brush && !brush->id.lib) { + butw= 320-(xco+10); + + uiDefButS(block, MENU, B_SIMANOTHING, "Mix %x0|Add %x1|Subtract %x2|Multiply %x3|Lighten %x4|Darken %x5", xco+10,yco,butw,19, &brush->blend, 0, 0, 0, 0, "Blending method for applying brushes"); + + uiDefButBitS(block, TOG|BIT, BRUSH_TORUS, B_SIMABRUSHCHANGE, "Wrap", xco+10,yco-25,butw,19, &brush->flag, 0, 0, 0, 0, "Enables torus wrapping"); + + uiBlockBeginAlign(block); + uiDefButBitS(block, TOG|BIT, BRUSH_AIRBRUSH, B_SIMABRUSHCHANGE, "Airbrush", xco+10,yco-50,butw,19, &brush->flag, 0, 0, 0, 0, "Keep applying paint effect while holding mouse (spray)"); + uiDefButF(block, NUM, B_SIMANOTHING, "Rate ", xco+10,yco-70,butw,19, &brush->rate, 0.01, 1.0, 0, 0, "Number of paints per second for Airbrush"); + uiBlockEndAlign(block); + + yco -= 25; + + 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"); + + if(brush->flag & BRUSH_AIRBRUSH) + uiDefButF(block, NUMSLI, B_NOP, "Flow ", 0,yco-80,200,19, &brush->spacing, 1.0, 100.0, 0, 0, "Paint Flow for Air Brush"); + else + uiDefButF(block, NUMSLI, B_NOP, "Stepsize ",0,yco-80,200,19, &brush->spacing, 1.0, 100.0, 0, 0, "Repeating Paint On %% of Brush diameter"); + uiBlockEndAlign(block); + } + } +#endif } static void editing_panel_mesh_texface(void) diff --git a/source/blender/src/drawimage.c b/source/blender/src/drawimage.c index 94d7ddd0b23..f43943ec540 100644 --- a/source/blender/src/drawimage.c +++ b/source/blender/src/drawimage.c @@ -896,6 +896,7 @@ void do_imagebuts(unsigned short event) case B_SIMABRUSHCHANGE: allqueue(REDRAWIMAGE, 0); + allqueue(REDRAWBUTSEDIT, 0); break; case B_SIMACURVES: @@ -909,33 +910,37 @@ void do_imagebuts(unsigned short event) allqueue(REDRAWIMAGE, 0); break; - case B_BRUSHBROWSE: + case B_SIMABRUSHBROWSE: if(G.sima->menunr==-2) { - activate_databrowse((ID*)settings->imapaint.brush, ID_BR, 0, B_BRUSHBROWSE, &G.sima->menunr, do_global_buttons); + activate_databrowse((ID*)settings->imapaint.brush, ID_BR, 0, B_SIMABRUSHBROWSE, &G.sima->menunr, do_global_buttons); break; } else if(G.sima->menunr < 0) break; if(brush_set_nr(&settings->imapaint.brush, G.sima->menunr)) { BIF_undo_push("Browse Brush"); + allqueue(REDRAWBUTSEDIT, 0); allqueue(REDRAWIMAGE, 0); } break; - case B_BRUSHDELETE: + case B_SIMABRUSHDELETE: if(brush_delete(&settings->imapaint.brush)) { BIF_undo_push("Unlink Brush"); allqueue(REDRAWIMAGE, 0); + allqueue(REDRAWBUTSEDIT, 0); } break; case B_KEEPDATA: brush_toggle_fake_user(settings->imapaint.brush); allqueue(REDRAWIMAGE, 0); + allqueue(REDRAWBUTSEDIT, 0); break; - case B_BRUSHLOCAL: + case B_SIMABRUSHLOCAL: if(settings->imapaint.brush && settings->imapaint.brush->id.lib) { if(okee("Make local")) { make_local_brush(settings->imapaint.brush); allqueue(REDRAWIMAGE, 0); + allqueue(REDRAWBUTSEDIT, 0); } } break; @@ -1020,19 +1025,21 @@ static void image_panel_paint(short cntrl) // IMAGE_HANDLER_PROPERTIES uiBlockSetCol(block, TH_BUT_SETTING2); id= (ID*)settings->imapaint.brush; - xco= std_libbuttons(block, 0, yco, 0, NULL, B_BRUSHBROWSE, ID_BR, 0, id, NULL, &(G.sima->menunr), 0, B_BRUSHLOCAL, B_BRUSHDELETE, 0, B_KEEPDATA); + xco= std_libbuttons(block, 0, yco, 0, NULL, B_SIMABRUSHBROWSE, ID_BR, 0, id, NULL, &(G.sima->menunr), 0, B_SIMABRUSHLOCAL, B_SIMABRUSHDELETE, 0, B_KEEPDATA); uiBlockSetCol(block, TH_AUTO); if(brush && !brush->id.lib) { butw= 320-(xco+10); + uiDefButS(block, MENU, B_SIMANOTHING, "Mix %x0|Add %x1|Subtract %x2|Multiply %x3|Lighten %x4|Darken %x5", xco+10,yco,butw,19, &brush->blend, 0, 0, 0, 0, "Blending method for applying brushes"); + + uiDefButBitS(block, TOG|BIT, BRUSH_TORUS, B_SIMABRUSHCHANGE, "Wrap", xco+10,yco-25,butw,19, &brush->flag, 0, 0, 0, 0, "Enables torus wrapping"); + uiBlockBeginAlign(block); - uiDefButBitS(block, TOG|BIT, BRUSH_AIRBRUSH, B_SIMABRUSHCHANGE, "Airbrush", xco+10,yco,butw,19, &brush->flag, 0, 0, 0, 0, "Keep applying paint effect while holding mouse (spray)"); - uiDefButBitS(block, TOG|BIT, IMAGEPAINT_TORUS, B_SIMABRUSHCHANGE, "Wrap", xco+10,yco-20,butw,19, &settings->imapaint.flag, 0, 0, 0, 0, "Enables torus wrapping"); + uiDefButBitS(block, TOG|BIT, BRUSH_AIRBRUSH, B_SIMABRUSHCHANGE, "Airbrush", xco+10,yco-50,butw,19, &brush->flag, 0, 0, 0, 0, "Keep applying paint effect while holding mouse (spray)"); + uiDefButF(block, NUM, B_SIMANOTHING, "Rate ", xco+10,yco-70,butw,19, &brush->rate, 0.01, 1.0, 0, 0, "Number of paints per second for Airbrush"); uiBlockEndAlign(block); - uiDefButS(block, MENU, B_SIMANOTHING, "Mix %x0|Add %x1|Subtract %x2|Multiply %x3|Lighten %x4|Darken %x5", xco+10,yco-45,butw,19, &brush->blend, 0, 0, 0, 0, "Blending method for applying brushes"); - yco -= 25; uiBlockBeginAlign(block); @@ -1040,11 +1047,7 @@ static void image_panel_paint(short cntrl) // IMAGE_HANDLER_PROPERTIES 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"); - - if(brush->flag & BRUSH_AIRBRUSH) - uiDefButF(block, NUMSLI, B_SIMANOTHING, "Flow ", 0,yco-80,200,19, &brush->timing, 1.0, 100.0, 0, 0, "Paint Flow for Air Brush"); - else - uiDefButF(block, NUMSLI, B_SIMANOTHING, "Stepsize ",0,yco-80,200,19, &brush->timing, 1.0, 100.0, 0, 0, "Repeating Paint On %% of Brush diameter"); + 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"); uiBlockEndAlign(block); yco -= 110; diff --git a/source/blender/src/editface.c b/source/blender/src/editface.c index 9c6e244e549..e32971b4070 100644 --- a/source/blender/src/editface.c +++ b/source/blender/src/editface.c @@ -55,13 +55,14 @@ #include "DNA_scene_types.h" #include "DNA_view3d_types.h" -#include "BKE_utildefines.h" +#include "BKE_brush.h" #include "BKE_depsgraph.h" #include "BKE_displist.h" #include "BKE_global.h" #include "BKE_mesh.h" -#include "BKE_texture.h" #include "BKE_object.h" +#include "BKE_texture.h" +#include "BKE_utildefines.h" #include "BSE_view.h" #include "BSE_edit.h" @@ -1465,6 +1466,8 @@ void set_faceselect() /* toggle */ allqueue(REDRAWIMAGE, 0); } +/* Texture Paint */ + void set_texturepaint() /* toggle */ { Object *ob = OBACT; @@ -1488,264 +1491,109 @@ void set_texturepaint() /* toggle */ if(G.f & G_TEXTUREPAINT) G.f &= ~G_TEXTUREPAINT; - else if (me) + else if (me) { G.f |= G_TEXTUREPAINT; + brush_check_exists(&G.scene->toolsettings->imapaint.brush); + } allqueue(REDRAWVIEW3D, 0); } -/** - * Get the view ray through the screen point. - * Uses the OpenGL settings of the active view port. - * The coordinates should be given in viewport coordinates. - * @author Maarten Gribnau - * @param x the x-coordinate of the screen point. - * @param y the y-coordinate of the screen point. - * @param org origin of the view ray. - * @param dir direction of the view ray. - */ -static void get_pick_ray(short *xy, float org[3], float dir[3]) +/* Get the barycentric coordinates of 2d point p in 2d triangle (v1, v2, v3) */ +static void texpaint_barycentric_2d(float *v1, float *v2, float *v3, float *p, float *w) { - double mvmatrix[16]; - double projmatrix[16]; - GLint viewport[4]; - double px, py, pz; - float l; - - /* Get the matrices needed for gluUnProject */ - glGetIntegerv(GL_VIEWPORT, viewport); - glGetDoublev(GL_MODELVIEW_MATRIX, mvmatrix); - glGetDoublev(GL_PROJECTION_MATRIX, projmatrix); - - /* Set up viewport so that gluUnProject will give correct values */ - viewport[0] = 0; - viewport[1] = 0; - /* printf("viewport = (%4d, %4d, %4d, %4d)\n", viewport[0], viewport[1], viewport[2], viewport[3]); */ - /* printf("cursor = (%4d, %4d)\n", x, y); */ - - gluUnProject((GLdouble) xy[0], (GLdouble) xy[1], 0.0, mvmatrix, projmatrix, viewport, &px, &py, &pz); - org[0] = (float)px; org[1] = (float)py; org[2] = (float)pz; - /* printf("world point at z=0.0 is (%f, %f, %f)\n", org[0], org[1], org[2]); */ - gluUnProject((GLdouble) xy[0], (GLdouble) xy[1], 1.0, mvmatrix, projmatrix, viewport, &px, &py, &pz); - /* printf("world point at z=1.0 is (%f, %f, %f)\n", px, py, pz); */ - dir[0] = ((float)px) - org[0]; - dir[1] = ((float)py) - org[1]; - dir[2] = ((float)pz) - org[2]; - l = (float)sqrt(dir[0]*dir[0] + dir[1]*dir[1] + dir[2]*dir[2]); - if (!l) return; - l = 1. / l; - dir[0] *= l; dir[1] *= l; dir[2] *= l; - /* printf("ray org. is (%f, %f, %f)\n", org[0], org[1], org[2]); */ - /* printf("ray dir. is (%f, %f, %f)\n", dir[0], dir[1], dir[2]); */ -} + float b[2], c[2], h[2], div; + Vec2Subf(b, v1, v3); + Vec2Subf(c, v2, v3); + Vec2Subf(h, p, v3); -static int triangle_ray_intersect(float tv0[3], float tv1[3], float tv2[3], float org[3], float dir[3], float uv[2]) -{ - float v1v0[3]; - float v2v0[3]; - float n[3], an[3]; - float t, d, l; - float p[3]; - double u0, v0, u1, v1, u2, v2, uvtemp; - unsigned int iu, iv; - - /* Calculate normal of the plane (cross, normalize) - * Could really use moto here... - */ - v1v0[0] = tv1[0] - tv0[0]; - v1v0[1] = tv1[1] - tv0[1]; - v1v0[2] = tv1[2] - tv0[2]; - v2v0[0] = tv2[0] - tv0[0]; - v2v0[1] = tv2[1] - tv0[1]; - v2v0[2] = tv2[2] - tv0[2]; - n[0] = (v1v0[1] * v2v0[2]) - (v1v0[2] * v2v0[1]); - n[1] = (v1v0[2] * v2v0[0]) - (v1v0[0] * v2v0[2]); - n[2] = (v1v0[0] * v2v0[1]) - (v1v0[1] * v2v0[0]); - l = sqrt(n[0]*n[0] + n[1]*n[1] + n[2]*n[2]); - if (!l) return 0; - l = 1. / l; - n[0] *= l; n[1] *= l; n[2] *= l; - - /* Calculate intersection point */ - t = n[0]*dir[0] + n[1]*dir[1] + n[2]*dir[2]; - if (fabs(t) < 1.0e-6) return 0; - d = -(n[0]*tv0[0] + n[1]*tv0[1] + n[2]*tv0[2]); - t = -(((n[0]*org[0] + n[1]*org[1] + n[2]*org[2]) + d) / t); - if (t < 0) return 0; - p[0] = org[0] + dir[0]*t; - p[1] = org[1] + dir[1]*t; - p[2] = org[2] + dir[2]*t; - /*printf("intersection at (%f, %f, %f)\n", p[0], p[1], p[2]);*/ - - /* Calculate the largest component of the normal */ - an[0] = fabs(n[0]); an[1] = fabs(n[1]); an[2] = fabs(n[2]); - if ((an[0] > an[1]) && (an[0] > an[2])) { - iu = 1; iv = 2; - } - else if ((an[1] > an[0]) && (an[1] > an[2])) { - iu = 2; iv = 0; - } - else { - iu = 0; iv = 1; - } - /* printf("iu, iv = (%d, %d)\n", iu, iv); */ - - /* Calculate (u,v) */ - u0 = p[iu] - tv0[iu]; - v0 = p[iv] - tv0[iv]; - u1 = tv1[iu] - tv0[iu]; - v1 = tv1[iv] - tv0[iv]; - u2 = tv2[iu] - tv0[iu]; - v2 = tv2[iv] - tv0[iv]; - /* printf("u0, v0, u1, v1, u2, v2 = (%f, %f, %f, %f, %f, %f)\n", u0, v0, u1, v1, u2, v2); */ - - /* These calculations should be in double precision. - * On windows we get inpredictable results in single precision - */ - if (u1 == 0) { - uvtemp = u0/u2; - uv[1] = (float)uvtemp; - /* if ((uv[1] >= 0.) && (uv[1] <= 1.)) { */ - uv[0] = (float)((v0 - uvtemp*v2) / v1); - /* } */ + div= b[0]*c[1] - b[1]*c[0]; + + if (div == 0.0) { + w[0]= w[1]= w[2]= 1.0f/3.0f; } else { - uvtemp = (v0*u1 - u0*v1)/(v2*u1-u2*v1); - uv[1] = (float)uvtemp; - /* if ((uv[1] >= 0) && (uv[1] <= 1)) { */ - uv[0] = (float)((u0 - uvtemp*u2) / u1); - /* } */ + div = 1.0/div; + w[0] = (h[0]*c[1] - h[1]*c[0])*div; + w[1] = (b[0]*h[1] - b[1]*h[0])*div; + w[2] = 1.0 - w[0] - w[1]; } - /* printf("uv[0], uv[1] = (%f, %f)\n", uv[0], uv[1]); */ - return ((uv[0] >= 0) && (uv[1] >= 0) && ((uv[0]+uv[1]) <= 1)) ? 2 : 1; } -/** - * Returns the vertex (local) coordinates of a face. - * No bounds checking! - * @author Maarten Gribnau - * @param mesh the mesh with the face. - * @param face the face. - * @param v1 vertex 1 coordinates. - * @param v2 vertex 2 coordinates. - * @param v3 vertex 3 coordinates. - * @param v4 vertex 4 coordinates. - * @return number of vertices of this face - */ -static int face_get_vertex_coordinates(Mesh* mesh, TFace* face, float v1[3], float v2[3], float v3[3], float v4[3]) +/* Get 2d vertex coordinates of tface projected onto screen */ +static void texpaint_project(Object *ob, double *model, double *proj, GLint *view, float *co, float *pco) { - int num_vertices; - MVert *mv; - MFace *mf = (MFace *) (((MFace *)mesh->mface) + (face - (TFace *) mesh->tface)); - - num_vertices = mf->v4 == 0 ? 3 : 4; - mv = mesh->mvert + mf->v1; - v1[0] = mv->co[0]; v1[1] = mv->co[1]; v1[2] = mv->co[2]; - mv = mesh->mvert + mf->v2; - v2[0] = mv->co[0]; v2[1] = mv->co[1]; v2[2] = mv->co[2]; - mv = mesh->mvert + mf->v3; - v3[0] = mv->co[0]; v3[1] = mv->co[1]; v3[2] = mv->co[2]; - if (num_vertices == 4) { - mv = mesh->mvert + mf->v4; - v4[0] = mv->co[0]; v4[1] = mv->co[1]; v4[2] = mv->co[2]; - } + float obco[3]; + double winx, winy, winz; + + VecCopyf(obco, co); + Mat4MulVecfl(ob->obmat, obco); + gluProject(obco[0], obco[1], obco[2], model, proj, view, &winx, &winy, &winz); - return num_vertices; + pco[0]= (float)winx; + pco[1]= (float)winy; } -/** - * Finds texture coordinates from face edge interpolation values. - * @author Maarten Gribnau - * @param face the face. - * @param v1 vertex 1 index. - * @param v2 vertex 2 index. - * @param v3 vertex 3 index. - * @param a interpolation value of edge v2-v1. - * @param b interpolation value of edge v3-v1. - * @param u (u,v) coordinate. - * @param v (u,v) coordinate. - */ -static void face_get_uv(TFace* face, int v1, int v2, int v3, float a, float b, float *uv) +static int texpaint_projected_verts(Object *ob, Mesh *mesh, TFace *tf, float *v1, float *v2, float *v3, float *v4) { - float uv01[2], uv21[2]; - - /* Pin a,b inside [0,1] range */ -#if 0 - a = (float)fmod(a, 1.); - b = (float)fmod(b, 1.); -#else - if (a < 0.f) a = 0.f; - else if (a > 1.f) a = 1.f; - if (b < 0.f) b = 0.f; - else if (b > 1.f) b = 1.f; -#endif - - /* Convert to texture coordinates */ - uv01[0] = face->uv[v2][0] - face->uv[v1][0]; - uv01[1] = face->uv[v2][1] - face->uv[v1][1]; - uv21[0] = face->uv[v3][0] - face->uv[v1][0]; - uv21[1] = face->uv[v3][1] - face->uv[v1][1]; - uv01[0] *= a; - uv01[1] *= a; - uv21[0] *= b; - uv21[1] *= b; - uv[0] = face->uv[v1][0] + (uv01[0] + uv21[0]); - uv[1] = face->uv[v1][1] + (uv01[1] + uv21[1]); + MFace *mf = mesh->mface + (tf - mesh->tface); + double model[16], proj[16]; + GLint view[4]; + + persp(PERSP_VIEW); + + /* get the need opengl matrices */ + glGetIntegerv(GL_VIEWPORT, view); + glGetDoublev(GL_MODELVIEW_MATRIX, model); + glGetDoublev(GL_PROJECTION_MATRIX, proj); + view[0] = view[1] = 0; + + /* project the verts */ + texpaint_project(ob, model, proj, view, (mesh->mvert+mf->v1)->co, v1); + texpaint_project(ob, model, proj, view, (mesh->mvert+mf->v2)->co, v2); + texpaint_project(ob, model, proj, view, (mesh->mvert+mf->v3)->co, v3); + if(mf->v4) + texpaint_project(ob, model, proj, view, (mesh->mvert+mf->v4)->co, v4); + + return (mf->v4? 4: 3); } -/** - * Get the (u,v) coordinates on a face from a point in screen coordinates. - * The coordinates should be given in viewport coordinates. - * @author Maarten Gribnau - * @param object the object with the mesh - * @param mesh the mesh with the face to be picked. - * @param face the face to be picked. - * @param x the x-coordinate to pick at. - * @param y the y-coordinate to pick at. - * @param u the u-coordinate calculated. - * @param v the v-coordinate calculated. - * @return intersection result: - * 0 == no intersection, (u,v) invalid - * 1 == intersection, (u,v) valid - */ -int face_pick_uv(Object* object, Mesh* mesh, TFace* face, short *xy, float *uv) +/* compute uv coordinates of mouse in face */ +void texpaint_pick_uv(Object *ob, Mesh *mesh, TFace *tf, short *xy, float *uv) { - float org[3], dir[3]; - float ab[2]; - float v1[3], v2[3], v3[3], v4[3]; - int result; - int num_verts; - - /* Get a view ray to intersect with the face */ - get_pick_ray(xy, org, dir); - - /* Convert local vertex coordinates to world */ - num_verts = face_get_vertex_coordinates(mesh, face, v1, v2, v3, v4); - /* Convert local vertex coordinates to world */ - Mat4MulVecfl(object->obmat, v1); - Mat4MulVecfl(object->obmat, v2); - Mat4MulVecfl(object->obmat, v3); - if (num_verts > 3) { - Mat4MulVecfl(object->obmat, v4); - } + float v1[2], v2[2], v3[2], v4[2], p[2], w[3]; + int nvert; + + /* compute barycentric coordinates of point in face and interpolate uv's. + it's ok to compute the barycentric coords on the projected positions, + because they are invariant under affine transform */ + nvert= texpaint_projected_verts(ob, mesh, tf, v1, v2, v3, v4); + + p[0]= xy[0]; + p[1]= xy[1]; + + if (nvert == 4) { + texpaint_barycentric_2d(v1, v2, v4, p, w); - /* Get (u,v) values (local face coordinates) of intersection point - * If face is a quad, there are two triangles to check. - */ - result = triangle_ray_intersect(v2, v1, v3, org, dir, ab); - if ( (num_verts == 3) || ((num_verts == 4) && (result > 1)) ) { - /* Face is a triangle or a quad with a hit on the first triangle */ - face_get_uv(face, 1, 0, 2, ab[0], ab[1], uv); - /* printf("triangle 1, texture (u,v)=(%f, %f)\n", *u, *v); */ + if(w[0] < 0.0f) { + /* if w[0] is negative, co is on the other side of the v1-v3 edge, + so we interpolate using the other triangle */ + texpaint_barycentric_2d(v2, v3, v4, p, w); + + uv[0]= tf->uv[1][0]*w[0] + tf->uv[2][0]*w[1] + tf->uv[3][0]*w[2]; + uv[1]= tf->uv[1][1]*w[0] + tf->uv[2][1]*w[1] + tf->uv[3][1]*w[2]; + } + else { + uv[0]= tf->uv[0][0]*w[0] + tf->uv[1][0]*w[1] + tf->uv[3][0]*w[2]; + uv[1]= tf->uv[0][1]*w[0] + tf->uv[1][1]*w[1] + tf->uv[3][1]*w[2]; + } } else { - /* Face is a quad and no intersection with first triangle */ - result = triangle_ray_intersect(v4, v3, v1, org, dir, ab); - face_get_uv(face, 3, 2, 0, ab[0], ab[1], uv); - /* printf("triangle 2, texture (u,v)=(%f, %f)\n", *u, *v); */ + texpaint_barycentric_2d(v1, v2, v3, p, w); + uv[0]= tf->uv[0][0]*w[0] + tf->uv[1][0]*w[1] + tf->uv[2][0]*w[2]; + uv[1]= tf->uv[0][1]*w[0] + tf->uv[1][1]*w[1] + tf->uv[2][1]*w[2]; } - return result > 0; } /* Selects all faces which have the same uv-texture as the active face diff --git a/source/blender/src/imagepaint.c b/source/blender/src/imagepaint.c index 627d3aaf18f..a535cb855ab 100644 --- a/source/blender/src/imagepaint.c +++ b/source/blender/src/imagepaint.c @@ -48,6 +48,7 @@ #ifdef WIN32 #include "BLI_winstuff.h" #endif +#include "BLI_arithb.h" #include "IMB_imbuf_types.h" @@ -83,6 +84,37 @@ #include "blendef.h" #include "mydevice.h" +/* Structs */ + +typedef struct ImagePaintPixmap { + unsigned int width, height, rowbytes, shared; + char *rect; +} ImagePaintPixmap; + +typedef struct ImagePaintBrush { + ImagePaintPixmap *pixmap; + float rgb[3], alpha; + unsigned int inner_radius, outer_radius; + short torus; +} ImagePaintBrush; + +typedef struct ImagePaintState { + float mousepos[2]; /* current mouse position in image pixel coordinates */ + float lastmousepos[2]; /* last mouse position in image pixel coordinates */ + + float accumdistance; /* accumulated distance of brush since last paint op */ + float lastpaintpos[2]; /* position of last paint op */ + + double accumtime; /* accumulated time since last paint op (airbrush) */ + double lasttime; /* time of last update */ + + ImagePaintPixmap *canvas; + ImagePaintPixmap *clonecanvas; + ImagePaintBrush *brush; + ToolSettings *settings; + short firsttouch; +} ImagePaintState; + /* ImagePaintPixmap */ #define IMAPAINT_FLOAT_TO_CHAR(f) ((char)(f*255)) @@ -106,11 +138,6 @@ #define IMAPAINT_RGB_COPY(a, b) { a[0]=b[0]; a[1]=b[1]; a[2]=b[2]; } #define IMAPAINT_RGBA_COPY(a, b) { *((int*)a)=*((int*)b); } -typedef struct ImagePaintPixmap { - unsigned int width, height, rowbytes, shared; - char *rect; -} ImagePaintPixmap; - static ImagePaintPixmap *imapaint_pixmap_new(unsigned int w, unsigned int h, char *rect) { ImagePaintPixmap *pm = MEM_callocN(sizeof(ImagePaintPixmap), "ImagePaintPixmap"); @@ -138,13 +165,6 @@ static void imapaint_pixmap_free(ImagePaintPixmap *pm) /* ImagePaintBrush */ -typedef struct ImagePaintBrush { - ImagePaintPixmap *pixmap; - float rgb[3], alpha; - unsigned int inner_radius, outer_radius; - short torus, blend; -} ImagePaintBrush; - static void imapaint_brush_pixmap_refresh(ImagePaintBrush *brush) { ImagePaintPixmap *pm = brush->pixmap; @@ -242,8 +262,8 @@ static char *imapaint_pixmap_get_rgba_torus(ImagePaintPixmap *pm, unsigned int x static void imapaint_pixmap_clip(ImagePaintPixmap *pm, ImagePaintPixmap *bpm, float *pos, unsigned int *off, unsigned int *boff, unsigned int *dim) { - int x = (int)(pos[0]*pm->width - bpm->width/2); - int y = (int)(pos[1]*pm->height - bpm->height/2); + int x = (int)(pos[0] - bpm->width/2); + int y = (int)(pos[1] - bpm->height/2); dim[0] = bpm->width; dim[1] = bpm->height; @@ -305,8 +325,8 @@ static void imapaint_pixmap_blend_torus(ImagePaintPixmap *pm, ImagePaintPixmap * unsigned int x, y, out_off[2], mx, my; char *out, *in; - out_off[0] = (int)(pos[0]*pm->width - bpm->width/2); - out_off[1] = (int)(pos[1]*pm->height - bpm->height/2); + out_off[0] = (int)(pos[0] - bpm->width/2); + out_off[1] = (int)(pos[1] - bpm->height/2); for (y=0; y < bpm->height; y++) { in = imapaint_pixmap_get_rgba(bpm, 0, y); @@ -348,8 +368,8 @@ static void imapaint_blend_line(ImagePaintPixmap *pm, ImagePaintBrush *brush, fl float numsteps, t, pos[2]; int step, d[2]; - d[0] = (int)((end[0] - start[0])*pm->width); - d[1] = (int)((end[1] - start[1])*pm->height); + d[0] = (int)(end[0] - start[0]); + d[1] = (int)(end[1] - start[1]); numsteps = sqrt(d[0]*d[0] + d[1]*d[1])/(brush->pixmap->width/4.0f); if(numsteps < 1.0) @@ -357,30 +377,29 @@ static void imapaint_blend_line(ImagePaintPixmap *pm, ImagePaintBrush *brush, fl for (step=0; step < numsteps; step++) { t = (step+1)/numsteps; - pos[0] = start[0] + d[0]*t/pm->width; - pos[1] = start[1] + d[1]*t/pm->height; + pos[0] = start[0] + d[0]*t; + pos[1] = start[1] + d[1]*t; if (brush->torus) - imapaint_pixmap_blend_torus(pm, brush->pixmap, pos, brush->blend); + imapaint_pixmap_blend_torus(pm, brush->pixmap, pos, BRUSH_BLEND_MIX); else - imapaint_pixmap_blend(pm, brush->pixmap, pos, brush->blend); + imapaint_pixmap_blend(pm, brush->pixmap, pos, BRUSH_BLEND_MIX); } } -static void imapaint_soften_sharpen(ImagePaintPixmap *pm, ImagePaintBrush *brush, float *pos, short sharpen) +static void imapaint_lift_soften(ImagePaintPixmap *pm, ImagePaintBrush *brush, float *pos, short torus) { ImagePaintPixmap *bpm = brush->pixmap; unsigned int x, y, count, xi, yi, xo, yo; unsigned int out_off[2], in_off[2], dim[2]; - float outrgb[3], finrgb[3]; - short torus= brush->torus; + float outrgb[3]; char *inrgb, *out; if (torus) { dim[0] = bpm->width; dim[1] = bpm->width; - in_off[0] = (int)(pos[0]*pm->width - bpm->width/2); - in_off[1] = (int)(pos[1]*pm->height - bpm->width/2); + in_off[0] = (int)(pos[0] - bpm->width/2); + in_off[1] = (int)(pos[1] - bpm->width/2); out_off[0] = out_off[1] = 0; } else { @@ -402,8 +421,10 @@ static void imapaint_soften_sharpen(ImagePaintPixmap *pm, ImagePaintBrush *brush /* sum and average surrounding pixels */ count = 1; IMAPAINT_CHAR_RGB_TO_FLOAT(outrgb, inrgb); +#if 0 if (sharpen) IMAPAINT_FLOAT_RGB_COPY(finrgb, outrgb); +#endif count += imapaint_pixmap_add_if(pm, xi-1, yi-1, outrgb, torus); count += imapaint_pixmap_add_if(pm, xi-1, yi , outrgb, torus); @@ -420,6 +441,7 @@ static void imapaint_soften_sharpen(ImagePaintPixmap *pm, ImagePaintBrush *brush outrgb[1] /= count; outrgb[2] /= count; +#if 0 if (sharpen) { /* unsharp masking - creates ugly artifacts and is disabled for now, needs some sort of clamping to reduce artifacts */ @@ -431,6 +453,7 @@ static void imapaint_soften_sharpen(ImagePaintPixmap *pm, ImagePaintBrush *brush outrgb[1] = IMAPAINT_FLOAT_CLAMP(outrgb[1]); outrgb[2] = IMAPAINT_FLOAT_CLAMP(outrgb[2]); } +#endif /* write into brush buffer */ xo = out_off[0] + x; @@ -439,11 +462,6 @@ static void imapaint_soften_sharpen(ImagePaintPixmap *pm, ImagePaintBrush *brush IMAPAINT_FLOAT_RGB_TO_CHAR(out, outrgb); } } - - if (torus) - imapaint_pixmap_blend_torus(pm, bpm, pos, brush->blend); - else - imapaint_pixmap_blend(pm, bpm, pos, brush->blend); } static void imapaint_lift_smear(ImagePaintPixmap *pm, ImagePaintBrush *brush, float *pos) @@ -452,8 +470,8 @@ static void imapaint_lift_smear(ImagePaintPixmap *pm, ImagePaintBrush *brush, fl int in_off[2], x, y; char *out, *in; - in_off[0] = (int)(pos[0]*pm->width - bpm->width/2); - in_off[1] = (int)(pos[1]*pm->height - bpm->height/2); + in_off[0] = (int)(pos[0] - bpm->width/2); + in_off[1] = (int)(pos[1] - bpm->height/2); for (y=0; y < bpm->height; y++) { out = imapaint_pixmap_get_rgba(bpm, 0, y); @@ -464,17 +482,6 @@ static void imapaint_lift_smear(ImagePaintPixmap *pm, ImagePaintBrush *brush, fl } } -static void imapaint_smear(ImagePaintPixmap *pm, ImagePaintBrush *brush, float *start, float *end) -{ - float pos[2]; - - pos[0]= 2*start[0] - end[0]; - pos[1]= 2*start[1] - end[1]; - - imapaint_lift_smear(pm, brush, pos); - imapaint_blend_line(pm, brush, start, end); -} - static void imapaint_lift_clone(ImagePaintPixmap *pm, ImagePaintBrush *brush, float *pos) { ImagePaintPixmap *bpm = brush->pixmap; @@ -484,8 +491,8 @@ static void imapaint_lift_clone(ImagePaintPixmap *pm, ImagePaintBrush *brush, fl /* we overwrite alphas for pixels outside clone, so need to reload them */ imapaint_brush_pixmap_refresh(brush); - in_off[0] = (int)(pos[0]*pm->width - bpm->width/2); - in_off[1] = (int)(pos[1]*pm->height - bpm->height/2); + in_off[0] = (int)(pos[0] - bpm->width/2); + in_off[1] = (int)(pos[1] - bpm->height/2); for (y=0; y < bpm->height; y++) { out = imapaint_pixmap_get_rgba(bpm, 0, y); @@ -504,96 +511,66 @@ static void imapaint_lift_clone(ImagePaintPixmap *pm, ImagePaintBrush *brush, fl } } -static void imapaint_clone(ImagePaintPixmap *pm, ImagePaintPixmap *cpm, ImagePaintBrush *brush, float *start, float *off) -{ - float pos[2]; - - pos[0]= start[0] - off[0]; - pos[1]= start[1] - off[1]; - - imapaint_lift_clone(cpm, brush, pos); - imapaint_pixmap_blend(pm, brush->pixmap, start, brush->blend); -} - /* 2D image paint */ -#if 0 -struct ImagePaint Gip = { - {NULL, {0.0f, 0.0f}, 0.5f}, - {{{1.0f, 1.0f, 1.0f, 0.2f}, 25, 0.5f, 100.0f}, /* brush */ - {{1.0f, 1.0f, 1.0f, 0.1f}, 25, 0.1f, 100.0f}, /* airbrush */ - {{0.5f, 0.5f, 0.5f, 1.0f}, 25, 0.5f, 100.0f}, /* soften */ - {{1.0f, 1.0f, 1.0f, 0.1f}, 25, 0.1f, 100.0f}, /* aux1 */ - {{0.0f, 0.0f, 0.0f, 0.1f}, 25, 0.1f, 100.0f}, /* aux2 */ - {{1.0f, 1.0f, 1.0f, 0.5f}, 25, 0.1f, 20.0f}, /* smear */ - {{1.0f, 1.0f, 1.0f, 0.5f}, 25, 0.1f, 20.0f}}, /* clone */ - 0, IMAGEPAINT_BRUSH -}; -#endif - -static ImagePaintBrush *imapaint_init_brush() +static int imapaint_state_init(ImagePaintState *state, ToolSettings *settings) { - ToolSettings *settings= G.scene->toolsettings; Brush *brush= settings->imapaint.brush; if (!brush) - return NULL; + return 0; + + memset(state, 0, sizeof(*state)); + state->firsttouch= 1; + state->lasttime= PIL_check_seconds_timer(); + state->settings= settings; /* initialize paint settings */ - if(brush->flag & BRUSH_AIRBRUSH) - settings->imapaint.flag |= IMAGEPAINT_TIMED; - else - settings->imapaint.flag &= ~IMAGEPAINT_TIMED; + state->settings->imapaint.flag |= IMAGEPAINT_DRAWING; /* create brush */ - return imapaint_brush_new(brush->size, brush->size, brush->rgb, brush->alpha, brush->innerradius); + state->brush= imapaint_brush_new(brush->size, brush->size, brush->rgb, + brush->alpha, brush->innerradius); + + return 1; } -static void imapaint_free_brush(ImagePaintBrush *brush) +static void imapaint_state_free(ImagePaintState *state) { - imapaint_brush_free(brush); + state->settings->imapaint.flag &= ~IMAGEPAINT_DRAWING; + + if(state->brush) imapaint_brush_free(state->brush); + if(state->canvas) imapaint_pixmap_free(state->canvas); + if(state->clonecanvas) imapaint_pixmap_free(state->clonecanvas); } -static ImagePaintPixmap *imapaint_init_canvas(ImagePaintPixmap **clonecanvas) +static int imapaint_canvas_init(ImagePaintState *state) { + Brush *brush= state->settings->imapaint.brush; ImBuf *ibuf= NULL, *cloneibuf= NULL; - ImagePaintPixmap *canvas; - ToolSettings *settings= G.scene->toolsettings; - Brush *brush= settings->imapaint.brush; /* verify that we can paint and create canvas */ if(!G.sima->image || !G.sima->image->ibuf || !G.sima->image->ibuf->rect) - return NULL; + return 0; else if(G.sima->image->packedfile) - return NULL; + return 0; ibuf= G.sima->image->ibuf; - canvas= imapaint_pixmap_new(ibuf->x, ibuf->y, (char*)ibuf->rect); - - if (clonecanvas) { - /* create clone canvas */ - if(brush && (settings->imapaint.tool == PAINT_TOOL_CLONE)) { - int w, h; - if(!brush->clone.image || !brush->clone.image->ibuf) - return 0; - - cloneibuf= brush->clone.image->ibuf; - w = cloneibuf->x; - h = cloneibuf->y; - *clonecanvas= imapaint_pixmap_new(w, h, (char*)cloneibuf->rect); - } - else - *clonecanvas= NULL; - } + state->canvas= imapaint_pixmap_new(ibuf->x, ibuf->y, (char*)ibuf->rect); - return canvas; -} + /* create clone canvas */ + if(brush && (state->settings->imapaint.tool == PAINT_TOOL_CLONE)) { + int w, h; + if(!brush->clone.image || !brush->clone.image->ibuf) + return 0; -static void imapaint_free_canvas(ImagePaintPixmap *canvas, ImagePaintPixmap *clonecanvas) -{ - imapaint_pixmap_free(canvas); - if(clonecanvas) - imapaint_pixmap_free(clonecanvas); + cloneibuf= brush->clone.image->ibuf; + w = cloneibuf->x; + h = cloneibuf->y; + state->clonecanvas= imapaint_pixmap_new(w, h, (char*)cloneibuf->rect); + } + + return 1; } void imapaint_redraw_tool(void) @@ -628,157 +605,189 @@ static void imapaint_compute_uvco(short *mval, float *uv) areamouseco_to_ipoco(G.v2d, mval, &uv[0], &uv[1]); } -static void imapaint_paint_tool(ImagePaintBrush *brush, ImagePaintPixmap *canvas, ImagePaintPixmap *clonecanvas, float *prevuv, float *uv) +static void imapaint_compute_imageco(ImagePaintPixmap *pm, short *mval, float *mousepos) { - ToolSettings *settings= G.scene->toolsettings; - Brush *curbrush= settings->imapaint.brush; - - brush->torus= (settings->imapaint.flag & IMAGEPAINT_TORUS)? 1: 0; - brush->blend= curbrush->blend; - - if(settings->imapaint.tool == PAINT_TOOL_SOFTEN) - imapaint_soften_sharpen(canvas, brush, prevuv, 0); - else if(settings->imapaint.tool == PAINT_TOOL_SMEAR) - imapaint_smear(canvas, brush, prevuv, uv); - else if(settings->imapaint.tool == PAINT_TOOL_CLONE) - imapaint_clone(canvas, clonecanvas, brush, prevuv, curbrush->clone.offset); - else if(curbrush->flag & BRUSH_AIRBRUSH) - imapaint_blend_line(canvas, brush, uv, uv); - else - imapaint_blend_line(canvas, brush, prevuv, uv); + areamouseco_to_ipoco(G.v2d, mval, &mousepos[0], &mousepos[1]); + mousepos[0] *= pm->width; + mousepos[1] *= pm->height; } -void imagepaint_paint(short mousebutton) +static void imapaint_paint_op(ImagePaintState *s, float *lastpos, float *pos) { - ImagePaintBrush *brush; - ImagePaintPixmap *canvas, *clonecanvas=NULL; - short prevmval[2], mval[2]; - double prevtime, curtime; - float prevuv[2], uv[2]; - int paint= 0, moved= 0, firsttouch=1 ; - ToolSettings *settings= G.scene->toolsettings; - Brush *curbrush= settings->imapaint.brush; - - if (!(canvas = imapaint_init_canvas(&clonecanvas))) { - if(G.sima->image && G.sima->image->packedfile) - error("Painting in packed images not supported"); - return; + ImagePaintPixmap *canvas= s->canvas; + ImagePaintPixmap *clonecanvas= s->clonecanvas; + ImagePaintBrush *brush= s->brush; + short tool= s->settings->imapaint.tool; + short torus= s->settings->imapaint.brush->flag & BRUSH_TORUS; + short blend= s->settings->imapaint.brush->blend; + float *offset= s->settings->imapaint.brush->clone.offset; + float liftpos[2]; + + /* lift from canvas */ + if(tool == PAINT_TOOL_SOFTEN) { + imapaint_lift_soften(canvas, brush, pos, torus); } - else if (!(brush = imapaint_init_brush())) { - imapaint_free_canvas(canvas, clonecanvas); - return; + else if(tool == PAINT_TOOL_SMEAR) { + imapaint_lift_smear(canvas, brush, lastpos); } - - getmouseco_areawin(prevmval); - prevtime = PIL_check_seconds_timer(); + else if(tool == PAINT_TOOL_CLONE && clonecanvas) { + liftpos[0]= pos[0] - offset[0]*clonecanvas->width; + liftpos[1]= pos[1] - offset[1]*clonecanvas->height; - settings->imapaint.flag |= IMAGEPAINT_DRAWING; + imapaint_lift_clone(clonecanvas, brush, liftpos); + } - while(get_mbut() & mousebutton) { - getmouseco_areawin(mval); + /* blend into canvas */ + if (torus) + imapaint_pixmap_blend_torus(canvas, brush->pixmap, pos, blend); + else + imapaint_pixmap_blend(canvas, brush->pixmap, pos, blend); +} - if(firsttouch) - moved= paint= 1; - else - moved= paint= (prevmval[0] != mval[0]) || (prevmval[1] != mval[1]); +static void imapaint_state_do(ImagePaintState *s, short *painted) +{ + if (s->firsttouch) { + /* always paint exactly once on first touch */ + if (s->settings->imapaint.tool != PAINT_TOOL_SMEAR) + imapaint_paint_op(s, s->mousepos, s->mousepos); + + s->firsttouch= 0; + s->lastpaintpos[0]= s->mousepos[0]; + s->lastpaintpos[1]= s->mousepos[1]; + if (painted) *painted |= 1; + } + else { + Brush *brush= s->settings->imapaint.brush; + float startdistance, spacing, step, paintpos[2], dmousepos[2]; + int totpaintops= 0; - if(settings->imapaint.flag & IMAGEPAINT_TIMED) { - /* see if need to draw because of timer */ - curtime = PIL_check_seconds_timer(); + /* compute brush spacing adapted to brush size */ + spacing= brush->size*brush->spacing*0.01f; - if(((curtime - prevtime) > (5.0/curbrush->timing)) || firsttouch) { - prevtime= curtime; - paint= 1; - } - else paint= 0; - } - else if(paint && !firsttouch) { - /* check if we moved enough to draw */ - float dmval[2], d, dlimit; + /* setup starting distance, direction vector and accumulated distance */ + startdistance= s->accumdistance; + Vec2Subf(dmousepos, s->mousepos, s->lastmousepos); + s->accumdistance += Normalise2(dmousepos); - dmval[0]= prevmval[0] - mval[0]; - dmval[1]= prevmval[1] - mval[1]; + /* do paint op over unpainted distance */ + while (s->accumdistance >= spacing) { + step= spacing - startdistance; + paintpos[0]= s->lastmousepos[0] + dmousepos[0]*step; + paintpos[1]= s->lastmousepos[1] + dmousepos[1]*step; - d= sqrt(dmval[0]*dmval[0] + dmval[1]*dmval[1]); - dlimit= curbrush->size*G.sima->zoom*curbrush->timing/200.0; + imapaint_paint_op(s, s->lastpaintpos, paintpos); - if (d < dlimit) - paint= 0; - } + s->lastpaintpos[0]= paintpos[0]; + s->lastpaintpos[1]= paintpos[1]; + s->accumdistance -= spacing; + startdistance -= spacing; + totpaintops++; - if(paint) { - /* do the actual painting */ - imapaint_compute_uvco(prevmval, prevuv); - imapaint_compute_uvco(mval, uv); + if (painted) *painted |= 1; + } - imapaint_paint_tool(brush, canvas, clonecanvas, prevuv, uv); + /* do airbrush paint ops, based on the number of paint ops left over + from regular painting */ + if (brush->flag & BRUSH_AIRBRUSH) { + double curtime= PIL_check_seconds_timer(); + double painttime= brush->rate*totpaintops; - prevmval[0]= mval[0]; - prevmval[1]= mval[1]; - } - firsttouch = 0; + s->accumtime += curtime - s->lasttime; + if (s->accumtime <= painttime) + s->accumtime= 0.0; + else + s->accumtime -= painttime; - if(paint) - imapaint_redraw(0, paint); - else if(moved && (settings->imapaint.flag & IMAGEPAINT_DRAW_TOOL)) - imapaint_redraw(0, paint); - } + while (s->accumtime >= brush->rate) { + if (s->settings->imapaint.tool != PAINT_TOOL_SMEAR) + imapaint_paint_op(s, s->mousepos, s->mousepos); + s->accumtime -= brush->rate; - settings->imapaint.flag &= ~IMAGEPAINT_DRAWING; + totpaintops++; + } - imapaint_free_brush(brush); - imapaint_free_canvas(canvas, clonecanvas); - G.sima->image->ibuf->userflags |= IB_BITMAPDIRTY; + s->lasttime= curtime; + } - imapaint_redraw(1, 0); + if ((totpaintops > 0) && painted) *painted |= 1; + } } -void imagepaint_pick(short mousebutton) +void imagepaint_paint(short mousebutton) { - ToolSettings *settings= G.scene->toolsettings; - Brush *brush= settings->imapaint.brush; + ImagePaintState state; + short prevmval[2], mval[2], painted, moved; - if(brush && (settings->imapaint.tool == PAINT_TOOL_CLONE)) { - if(brush->clone.image && brush->clone.image->ibuf) { - short prevmval[2], mval[2]; - float prevuv[2], uv[2]; - - getmouseco_areawin(prevmval); + /* setup data structures */ + if (!(imapaint_state_init(&state, G.scene->toolsettings))) { + return; + } + else if (!imapaint_canvas_init(&state)) { + if(G.sima->image && G.sima->image->packedfile) + error("Painting in packed images not supported"); + imapaint_state_free(&state); + return; + } + + /* initialize coordinates and time */ + getmouseco_areawin(mval); + imapaint_compute_imageco(state.canvas, mval, state.mousepos); - while(get_mbut() & mousebutton) { - getmouseco_areawin(mval); + prevmval[0]= mval[0]; + prevmval[1]= mval[1]; + state.lastmousepos[0]= state.mousepos[0]; + state.lastmousepos[1]= state.mousepos[1]; + state.lasttime= PIL_check_seconds_timer(); - if((prevmval[0] != mval[0]) || (prevmval[1] != mval[1]) ) { - /* mouse moved, so move the clone image */ - imapaint_compute_uvco(prevmval, prevuv); - imapaint_compute_uvco(mval, uv); + /* start by painting once */ + imapaint_state_do(&state, NULL); + imapaint_redraw(0, 1); - brush->clone.offset[0] += uv[0] - prevuv[0]; - brush->clone.offset[1] += uv[1] - prevuv[1]; + /* paint loop */ + while(get_mbut() & mousebutton) { + getmouseco_areawin(mval); + moved= painted= 0; - force_draw(0); + if((mval[0] != prevmval[0]) || (mval[1] != prevmval[1])) { + prevmval[0]= mval[0]; + prevmval[1]= mval[1]; + imapaint_compute_imageco(state.canvas, mval, state.mousepos); + moved= 1; + } + else if (!(state.settings->imapaint.brush->flag & BRUSH_AIRBRUSH)) + continue; - prevmval[0]= mval[0]; - prevmval[1]= mval[1]; - } - } + imapaint_state_do(&state, &painted); + + state.lastmousepos[0]= state.mousepos[0]; + state.lastmousepos[1]= state.mousepos[1]; + + if(painted) { + imapaint_redraw(0, painted); } + else if(moved && (state.settings->imapaint.flag & IMAGEPAINT_DRAW_TOOL)) + imapaint_redraw(0, painted); } - else if(brush) { - extern VPaint Gvp; - sample_vpaint(); - brush->rgb[0]= Gvp.r; - brush->rgb[1]= Gvp.g; - brush->rgb[2]= Gvp.b; - } + /* clean up */ + imapaint_state_free(&state); + + G.sima->image->ibuf->userflags |= IB_BITMAPDIRTY; + imapaint_redraw(1, 0); } /* these will be moved */ int facesel_face_pick(Mesh *me, short *mval, unsigned int *index, short rect); -int face_pick_uv(Object* object, Mesh* mesh, TFace* face, short *xy, float *uv); +void texpaint_pick_uv(Object *ob, Mesh *mesh, TFace *tf, short *xy, float *mousepos); -void texturepaint_paint() +static void texpaint_compute_imageco(ImagePaintPixmap *pm, Object *ob, Mesh *mesh, TFace *tf, short *xy, float *imageco) +{ + texpaint_pick_uv(ob, mesh, tf, xy, imageco); + imageco[0] *= pm->width; + imageco[1] *= pm->height; +} + +void texturepaint_paint(short mousebutton) { Object *ob; Mesh *me; @@ -788,34 +797,22 @@ void texturepaint_paint() Image *img=NULL, *img_old = NULL; ImagePaintBrush *brush; ImagePaintPixmap *canvas = 0; - unsigned int face_index, mousebutton; + unsigned int face_index; char *warn_packed_file = 0; float uv[2], uv_old[2]; extern VPaint Gvp; ImBuf *ibuf= NULL; ob = OBACT; - if (!ob) { - error("No active object"); return; - } - if (!(ob->lay & G.vd->lay)) { - error("The active object is not in this layer"); return; - } + if (!ob || !(ob->lay & G.vd->lay)) return; me = get_mesh(ob); - if (!me) { - error("The active object does not have a mesh obData"); return; - } + if (!me) return; brush = imapaint_brush_new(Gvp.size, Gvp.size, &Gvp.r, Gvp.a, 0.5); - if (!brush) { - error("Can't create brush"); return; - } + if (!brush) return; persp(PERSP_VIEW); - if (U.flag & USER_LMOUSESELECT) mousebutton = R_MOUSE; - else mousebutton = L_MOUSE; - getmouseco_areawin(xy_old); while (get_mbut() & mousebutton) { getmouseco_areawin(xy); @@ -840,7 +837,7 @@ void texturepaint_paint() if (img != img_old) { /* Faces have different textures. Finish drawing in the old face. */ if (face_old && canvas) { - face_pick_uv(ob, me, face_old, xy, uv); + texpaint_compute_imageco(canvas, ob, me, face_old, xy, uv); imapaint_blend_line(canvas, brush, uv_old, uv); img_old->ibuf->userflags |= IB_BITMAPDIRTY; /* Delete old canvas */ @@ -854,8 +851,8 @@ void texturepaint_paint() /* MAART: skipx is not set most of the times. Make a guess. */ canvas = imapaint_pixmap_new(ibuf->x, ibuf->y, (char*)ibuf->rect); if (canvas) { - face_pick_uv(ob, me, face, xy_old, uv_old); - face_pick_uv(ob, me, face, xy, uv); + texpaint_compute_imageco(canvas, ob, me, face, xy_old, uv_old); + texpaint_compute_imageco(canvas, ob, me, face, xy, uv); imapaint_blend_line(canvas, brush, uv_old, uv); ibuf->userflags |= IB_BITMAPDIRTY; } @@ -873,15 +870,15 @@ void texturepaint_paint() if (canvas) { /* Finish drawing in the old face. */ if (face_old) { - face_pick_uv(ob, me, face_old, xy, uv); + texpaint_compute_imageco(canvas, ob, me, face_old, xy, uv); imapaint_blend_line(canvas, brush, uv_old, uv); img_old->ibuf->userflags |= IB_BITMAPDIRTY; } /* Start drawing in the new face. */ if (face) { - face_pick_uv(ob, me, face, xy_old, uv_old); - face_pick_uv(ob, me, face, xy, uv); + texpaint_compute_imageco(canvas, ob, me, face, xy_old, uv_old); + texpaint_compute_imageco(canvas, ob, me, face, xy, uv); imapaint_blend_line(canvas, brush, uv_old, uv); ibuf->userflags |= IB_BITMAPDIRTY; } @@ -892,7 +889,7 @@ void texturepaint_paint() /* Same face, continue drawing */ if (face && canvas) { /* Get the new (u,v) coordinates */ - face_pick_uv(ob, me, face, xy, uv); + texpaint_compute_imageco(canvas, ob, me, face, xy, uv); imapaint_blend_line(canvas, brush, uv_old, uv); ibuf->userflags |= IB_BITMAPDIRTY; } @@ -933,3 +930,38 @@ void texturepaint_paint() allqueue(REDRAWHEADERS, 0); } +void imagepaint_pick(short mousebutton) +{ + ToolSettings *settings= G.scene->toolsettings; + Brush *brush= settings->imapaint.brush; + + if(brush && (settings->imapaint.tool == PAINT_TOOL_CLONE)) { + if(brush->clone.image && brush->clone.image->ibuf) { + short prevmval[2], mval[2]; + float lastmousepos[2], mousepos[2]; + + getmouseco_areawin(prevmval); + + while(get_mbut() & mousebutton) { + getmouseco_areawin(mval); + + if((prevmval[0] != mval[0]) || (prevmval[1] != mval[1]) ) { + /* mouse moved, so move the clone image */ + imapaint_compute_uvco(prevmval, lastmousepos); + imapaint_compute_uvco(mval, mousepos); + + brush->clone.offset[0] += mousepos[0] - lastmousepos[0]; + brush->clone.offset[1] += mousepos[1] - lastmousepos[1]; + + force_draw(0); + + prevmval[0]= mval[0]; + prevmval[1]= mval[1]; + } + } + } + } + else if(brush) + sample_vpaint(); +} + diff --git a/source/blender/src/space.c b/source/blender/src/space.c index 538f664663d..aa3536ec2ba 100644 --- a/source/blender/src/space.c +++ b/source/blender/src/space.c @@ -835,7 +835,7 @@ static void winqreadview3dspace(ScrArea *sa, void *spacedata, BWinEvent *evt) Object *ob= OBACT; /* do not change! */ float *curs; int doredraw= 0, pupval; - unsigned short event= evt->event; + unsigned short event= evt->event, origevent= evt->event; short val= evt->val; char ascii= evt->ascii; @@ -1011,7 +1011,7 @@ static void winqreadview3dspace(ScrArea *sa, void *spacedata, BWinEvent *evt) vertex_paint(); } else if (G.f & G_TEXTUREPAINT) { - texturepaint_paint(); + texturepaint_paint(origevent==LEFTMOUSE? L_MOUSE: R_MOUSE); } break; case MIDDLEMOUSE: diff --git a/source/blender/src/vpaint.c b/source/blender/src/vpaint.c index d1b903d08c6..811486eb35a 100644 --- a/source/blender/src/vpaint.c +++ b/source/blender/src/vpaint.c @@ -50,6 +50,7 @@ #include "DNA_action_types.h" #include "DNA_armature_types.h" +#include "DNA_brush_types.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" #include "DNA_modifier_types.h" @@ -534,14 +535,20 @@ void sample_vpaint() /* frontbuf */ cp = (char *)&col; - Gvp.r= cp[0]; - Gvp.r /= 255.0; - - Gvp.g= cp[1]; - Gvp.g /= 255.0; + if(G.f & (G_VERTEXPAINT|G_WEIGHTPAINT)) { + Gvp.r= cp[0]/255.0f; + Gvp.g= cp[1]/255.0f; + Gvp.b= cp[2]/255.0f; + } + else { + Brush *brush= G.scene->toolsettings->imapaint.brush; - Gvp.b= cp[2]; - Gvp.b /= 255.0; + if(brush) { + brush->rgb[0]= cp[0]/255.0f; + brush->rgb[1]= cp[1]/255.0f; + brush->rgb[2]= cp[2]/255.0f; + } + } allqueue(REDRAWBUTSEDIT, 0); addqueue(curarea->win, REDRAW, 1); /* needed for when panel is open... */ |