diff options
author | Campbell Barton <ideasman42@gmail.com> | 2008-11-18 03:17:13 +0300 |
---|---|---|
committer | Campbell Barton <ideasman42@gmail.com> | 2008-11-18 03:17:13 +0300 |
commit | a54dc97c1363d601c3f4777b09dae72b65a6a44d (patch) | |
tree | bbdd6a86389203dc5525af928d315436ebf86faa | |
parent | a10d4192b50d793b08dd340bd1004d374847e8ea (diff) |
* Added buttons for accessing options "occlude", "backface cull" and "bleed", note that bleed is 0.0 by default, before it was always 2.0;
* use a faster method of getting a pixels screenspace location.
* check if its possible to break out of pixel to bucket/face intersection early - ~7% overall speedup (ignoring redraw time).
* removed scanline intersection function (added back incase they were needed but it looks like there not)
* speedup for painting with only 1 image (use a loop without context switching checks)
* more commenting + cleanup
-rw-r--r-- | source/blender/makesdna/DNA_scene_types.h | 9 | ||||
-rw-r--r-- | source/blender/src/buttons_editing.c | 10 | ||||
-rw-r--r-- | source/blender/src/imagepaint.c | 490 |
3 files changed, 232 insertions, 277 deletions
diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index c1c164f136f..fc2cb575fde 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -345,7 +345,9 @@ typedef struct TimeMarker { typedef struct ImagePaintSettings { struct Brush *brush; short flag, tool; - int pad3; + + /* for projection painting only - todo - use flags */ + float seam_bleed; } ImagePaintSettings; typedef struct ParticleBrushData { @@ -799,6 +801,11 @@ typedef struct Scene { #define IMAGEPAINT_DRAW_TOOL 2 #define IMAGEPAINT_DRAW_TOOL_DRAWING 4 +/* projection painting only */ +#define IMAGEPAINT_PROJECT_XRAY 8 +#define IMAGEPAINT_PROJECT_BACKFACE 16 +#define IMAGEPAINT_PROJECT_IGNORE_SEAMS 32 + /* toolsettings->uvcalc_flag */ #define UVCALC_FILLHOLES 1 #define UVCALC_NO_ASPECT_CORRECT 2 /* would call this UVCALC_ASPECT_CORRECT, except it should be default with old file */ diff --git a/source/blender/src/buttons_editing.c b/source/blender/src/buttons_editing.c index 1fd1538fb4b..c2a7c8102a0 100644 --- a/source/blender/src/buttons_editing.c +++ b/source/blender/src/buttons_editing.c @@ -6367,8 +6367,16 @@ static void editing_panel_mesh_paint(void) uiDefButBitS(block, TOG|BIT, BRUSH_AIRBRUSH, B_BRUSHCHANGE, "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_NOP, "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; + + /* Projection Painting */ + uiBlockBeginAlign(block); + uiDefButBitS(block, TOGN|BIT, IMAGEPAINT_PROJECT_XRAY, B_NOP, "Occlude", xco+10,yco-70,butw,19, &settings->imapaint.flag, 0, 0, 0, 0, "Only paint onto the faces directly under the brush (slower)"); + uiDefButBitS(block, TOGN|BIT, IMAGEPAINT_PROJECT_BACKFACE, B_NOP, "Backface Cull", xco+10,yco-90,butw,19, &settings->imapaint.flag, 0, 0, 0, 0, "Ignore faces pointing away from the view (faster)"); + uiDefButBitS(block, TOGN|BIT, IMAGEPAINT_PROJECT_IGNORE_SEAMS, B_NOP, "Bleed", xco+10,yco-110,butw/2,19, &settings->imapaint.flag, 0, 0, 0, 0, "Extend paint beyond the faces UVs to reduce seams (in pixels, slower)"); + uiDefButF(block, NUM, B_NOP, "", xco+10 + (butw/2),yco-110,butw/2,19, &settings->imapaint.seam_bleed, 2.0, 8.0, 0, 0, "Extend paint beyond the faces UVs to reduce seams (in pixels, slower)"); + uiBlockEndAlign(block); uiBlockBeginAlign(block); uiDefButF(block, COL, B_VPCOLSLI, "", 0,yco,200,19, brush->rgb, 0, 0, 0, 0, ""); diff --git a/source/blender/src/imagepaint.c b/source/blender/src/imagepaint.c index 09592c97756..272bad09224 100644 --- a/source/blender/src/imagepaint.c +++ b/source/blender/src/imagepaint.c @@ -139,7 +139,8 @@ typedef struct ImagePaintPartialRedraw { int enabled; } ImagePaintPartialRedraw; -/* testing options */ + +/* ProjectionPaint defines */ /* approx the number of buckets to have under the brush, * used with the brush size to set the ps->buckets_x and ps->buckets_y value. @@ -152,12 +153,10 @@ typedef struct ImagePaintPartialRedraw { #define PROJ_BUCKET_RECT_MIN 4 #define PROJ_BUCKET_RECT_MAX 256 - -#define PROJ_BOUNDBOX_DIV 6 /* TODO - test other values, this is a guess, seems ok */ +#define PROJ_BOUNDBOX_DIV 8 #define PROJ_BOUNDBOX_SQUARED (PROJ_BOUNDBOX_DIV * PROJ_BOUNDBOX_DIV) //#define PROJ_DEBUG_PAINT 1 -#define PROJ_DEBUG_NOSCANLINE 1 //#define PROJ_DEBUG_NOSEAMBLEED 1 //#define PROJ_DEBUG_PRINT_THREADS 1 #define PROJ_DEBUG_WINCLIP 1 @@ -185,6 +184,9 @@ typedef struct ImagePaintPartialRedraw { #define PROJ_BUCKET_BOTTOM 2 #define PROJ_BUCKET_TOP 3 +/* This is mainly a convenience struct used so we can keep an array of images we use + * Thir imbufs, etc, in 1 array, When using threads this array is copied for each thread + * because 'partRedrawRect' and 'touch' values would not be thread safe */ typedef struct ProjectPaintImage { Image *ima; ImBuf *ibuf; @@ -193,6 +195,7 @@ typedef struct ProjectPaintImage { int touch; } ProjectPaintImage; +/* Main projection painting struct passed to all projection painting functions */ typedef struct ProjectPaintState { Brush *brush; short tool, blend; @@ -254,13 +257,6 @@ typedef struct ProjectPaintState { int context_bucket_x, context_bucket_y; /* must lock threads while accessing these */ } ProjectPaintState; -#ifndef PROJ_DEBUG_NOSCANLINE -typedef struct ProjectScanline { - int v[3]; /* verts for this scanline, 0,1,2 or 0,2,3 */ - float x_limits[2]; /* UV min|max for this scanline */ -} ProjectScanline; -#endif - typedef struct ProjectPixel { float projCo2D[2]; /* the floating point screen projection of this pixel */ char origColor[4]; @@ -461,7 +457,7 @@ static void undo_imagepaint_push_end() } } - +/* fast projection bucket array lookup, use the safe version for bound checking */ static int project_paint_BucketOffset(ProjectPaintState *ps, float projCo2D[2]) { /* If we were not dealing with screenspace 2D coords we could simple do... @@ -567,8 +563,7 @@ static float tri_depth_2d(float v1[3], float v2[3], float v3[3], float pt[2], fl } -/* return the topmost face in screen coords index or -1 - * bucket_index can be -1 if we dont know it to begin with */ +/* Return the top-most face index that the screen space coord 'pt' touches (or -1) */ static int project_paint_PickFace(ProjectPaintState *ps, float pt[2], float w[3], int *side) { LinkNode *node; float w_tmp[3]; @@ -626,6 +621,8 @@ static int project_paint_PickFace(ProjectPaintState *ps, float pt[2], float w[3] return best_face_index; /* will be -1 or a valid face */ } +/* TODO move to "source/blender/imbuf/intern/imageprocess.c" - also try bilinear weight which is faster */ + /************************************************************************** * INTERPOLATIONS * @@ -714,8 +711,7 @@ static void bicubic_interpolation_px(ImBuf *in, float x, float y, float rgba_fp[ } } -/* bucket_index is optional, since in some cases we know it */ -/* TODO FLOAT */ /* add a function that does this for float buffers */ +/* Set the top-most face color that the screen space coord 'pt' touches (or return 0 if none touch) */ static int project_paint_PickColor(ProjectPaintState *ps, float pt[2], float *rgba_fp, char *rgba, int interp) { float w[3], uv[2]; @@ -806,10 +802,11 @@ static int project_paint_PickColor(ProjectPaintState *ps, float pt[2], float *rg return 1; } -/* return... - * 0 : no occlusion +/* Check if 'pt' is infront of the 3 verts on the Z axis (used for screenspace occlusuion test) + * return... + * 0 : no occlusion * -1 : no occlusion but 2D intersection is true (avoid testing the other half of a quad) - * 1 : occluded */ + * 1 : occluded */ static int project_paint_PointOcclude(float pt[3], float v1[3], float v2[3], float v3[3]) { @@ -837,7 +834,9 @@ static int project_paint_PointOcclude(float pt[3], float v1[3], float v2[3], flo } -/* check, pixelScreenCo must be in screenspace, its Z-Depth only needs to be used for comparison */ +/* Check if a screenspace location is occluded by any other faces + * check, pixelScreenCo must be in screenspace, its Z-Depth only needs to be used for comparison + * and dosn't need to be correct in relation to X and Y coords (this is the case in perspective view) */ static int project_bucket_point_occluded(ProjectPaintState *ps, int bucket_index, int orig_face, float pixelScreenCo[4]) { LinkNode *node = ps->bucketFaces[bucket_index]; @@ -871,23 +870,8 @@ static int project_bucket_point_occluded(ProjectPaintState *ps, int bucket_index } if (isect_ret==1) { -#if 0 - /* Cheap Optimization! - * This function runs for every UV Screen pixel, - * therefor swapping the swapping the faces for this buckets list helps because - * the next ~5 to ~200 runs will can hit the first face each time. */ - if (ps->bucketFaces[bucket_index] != node) { - /* SWAP(void *, ps->bucketFaces[bucket_index]->link, node->link); */ - - /* dont need to use swap since we alredy have face_index */ - node->link = ps->bucketFaces[bucket_index]->link; /* move the value item to the current value */ - ps->bucketFaces[bucket_index]->link = (void *) face_index; - - /*printf("swapping %d %d\n", (int)node->link, face_index);*/ - } /*else { - printf("first hit %d\n", face_index); - }*/ -#endif + /* TODO - we may want to cache the first hit, + * it is not possible to swap the face order in the list anymore */ return 1; } } @@ -946,111 +930,16 @@ static int line_isect_x(float p1[2], float p2[2], float x_level, float *y_isect) } } -#ifndef PROJ_DEBUG_NOSCANLINE -static int project_face_scanline(ProjectScanline *sc, float y_level, float v1[2], float v2[2], float v3[2], float v4[2]) -{ - /* Create a scanlines for the face at this Y level - * triangles will only ever have 1 scanline, quads may have 2 */ - int totscanlines = 0; - short i1=0,i2=0,i3=0; - - if (v4) { /* This is a quad?*/ - int i4=0, i_mid=0; - float xi1, xi2, xi3, xi4, xi_mid; - - i1 = line_isect_y(v1, v2, y_level, &xi1); - if (i1 != ISECT_TRUE_P2) /* rare cases we could be on the line, in these cases we dont want to intersect with the same point twice */ - i2 = line_isect_y(v2, v3, y_level, &xi2); - - if (i1 && i2) { /* both the first 2 edges intersect, this means the second half of the quad wont intersect */ - sc->v[0] = 0; - sc->v[1] = 1; - sc->v[2] = 2; - sc->x_limits[0] = MIN2(xi1, xi2); - sc->x_limits[1] = MAX2(xi1, xi2); - totscanlines = 1; - } else { - if (i2 != ISECT_TRUE_P2) - i3 = line_isect_y(v3, v4, y_level, &xi3); - if (i1 != ISECT_TRUE_P1 && i3 != ISECT_TRUE_P2) - i4 = line_isect_y(v4, v1, y_level, &xi4); - - if (i3 && i4) { /* second 2 edges only intersect, same as above */ - sc->v[0] = 0; - sc->v[1] = 2; - sc->v[2] = 3; - sc->x_limits[0] = MIN2(xi3, xi4); - sc->x_limits[1] = MAX2(xi3, xi4); - totscanlines = 1; - } else { - /* OK - we have a not-so-simple case, both sides of the quad intersect. - * Will need to have 2 scanlines */ - if ((i1||i2) && (i3||i4)) { - i_mid = line_isect_y(v1, v3, y_level, &xi_mid); - /* it would be very rare this would be false, but possible */ - sc->v[0] = 0; - sc->v[1] = 1; - sc->v[2] = 2; - sc->x_limits[0] = MIN2((i1?xi1:xi2), xi_mid); - sc->x_limits[1] = MAX2((i1?xi1:xi2), xi_mid); - - sc++; - sc->v[0] = 0; - sc->v[1] = 2; - sc->v[2] = 3; - sc->x_limits[0] = MIN2((i3?xi3:xi4), xi_mid); - sc->x_limits[1PROJ_DEBUG_NOSEAMBLEED] = MAX2((i3?xi3:xi4), xi_mid); - - totscanlines = 2; - } - } - } - } else { /* triangle */ - int i = 0; - - i1 = line_isect_y(v1, v2, y_level, &sc->x_limits[0]); - if (i1) i++; - - if (i1 != ISECT_TRUE_P2) { - i2 = line_isect_y(v2, v3, y_level, &sc->x_limits[i]); - if (i2) i++; - } - - /* if the triangle intersects then the first 2 lines must */ - if (i!=0) { - if (i!=2) { - /* if we are here then this really should not fail since 2 edges MUST intersect */ - if (i1 != ISECT_TRUE_P1 && i2 != ISECT_TRUE_P2) { - i3 = line_isect_y(v3, v1, y_level, &sc->x_limits[i]); - if (i3) i++; - - } - } - - if (i==2) { - if (sc->x_limits[0] > sc->x_limits[1]) { - SWAP(float, sc->x_limits[0], sc->x_limits[1]); - } - sc->v[0] = 0; - sc->v[1] = 1; - sc->v[2] = 2; - totscanlines = 1; - } - } - } - /* done setting up scanlines */ - return totscanlines; -} -#endif // PROJ_DEBUG_NOSCANLINE - +/* simple func use for comparing UV locations to check if there are seams */ static int cmp_uv(float vec2a[2], float vec2b[2]) { return ((fabs(vec2a[0]-vec2b[0]) < 0.0001) && (fabs(vec2a[1]-vec2b[1]) < 0.0001)) ? 1:0; } -/* return zero if there is no area in the returned rectangle */ -static int uv_image_rect(float uv1[2], float uv2[2], float uv3[2], float uv4[2], int min_px[2], int max_px[2], int x_px, int y_px, int is_quad) +/* set min_px and max_px to the image space bounds of the UV coords + * return zero if there is no area in the returned rectangle */ +static int uv_image_rect(float uv1[2], float uv2[2], float uv3[2], float uv4[2], int min_px[2], int max_px[2], int ibuf_x, int ibuf_y, int is_quad) { float min_uv[2], max_uv[2]; /* UV bounds */ @@ -1062,11 +951,11 @@ static int uv_image_rect(float uv1[2], float uv2[2], float uv3[2], float uv4[2], if (is_quad) DO_MINMAX2(uv4, min_uv, max_uv); - min_px[0] = (int)(x_px * min_uv[0]); - min_px[1] = (int)(y_px * min_uv[1]); + min_px[0] = (int)(ibuf_x * min_uv[0]); + min_px[1] = (int)(ibuf_y * min_uv[1]); - max_px[0] = (int)(x_px * max_uv[0]) +1; - max_px[1] = (int)(y_px * max_uv[1]) +1; + max_px[0] = (int)(ibuf_x * max_uv[0]) +1; + max_px[1] = (int)(ibuf_y * max_uv[1]) +1; /*printf("%d %d %d %d \n", min_px[0], min_px[1], max_px[0], max_px[1]);*/ @@ -1075,7 +964,9 @@ static int uv_image_rect(float uv1[2], float uv2[2], float uv3[2], float uv4[2], } #ifndef PROJ_DEBUG_NOSEAMBLEED -/* TODO - set the seam flag on the other face to avoid double lookups */ + +/* This function returns 1 if this face has a seam along the 2 face-vert indicies + * 'orig_i1_fidx' and 'orig_i2_fidx' */ static int check_seam(ProjectPaintState *ps, int orig_face, int orig_i1_fidx, int orig_i2_fidx, int *other_face, int *orig_fidx) { LinkNode *node; @@ -1111,9 +1002,6 @@ static int check_seam(ProjectPaintState *ps, int orig_face, int orig_i1_fidx, in if (i2_fidx != -1) { /* This IS an adjacent face!, now lets check if the UVs are ok */ - - - tf = ps->dm_mtface + face_index; /* set up the other face */ @@ -1140,12 +1028,13 @@ static int check_seam(ProjectPaintState *ps, int orig_face, int orig_i1_fidx, in return 1; } - +/* TODO - move to arithb.c */ +/* Converts an angle to a length that can be used for maintaining an even margin around UV's */ static float angleToLength(float angle) { float x,y, fac; - // Alredy accounted for + // already accounted for if (angle < 0.000001) return 1.0; @@ -1162,8 +1051,10 @@ static float angleToLength(float angle) return sqrt((x*x)+(y*y)); } -/* takes a faces UV's and assigns outset coords to outset_uv */ -static void uv_image_outset(float (*orig_uv)[2], float (*outset_uv)[2], float scaler, int x_px, int y_px, int is_quad) +/* Calculate outset UV's, this is not the same as simply scaling the UVs, + * since the outset coords are a margin that keep an even distance from the original UV's, + * note that the image aspect is taken into account */ +static void uv_image_outset(float (*orig_uv)[2], float (*outset_uv)[2], float scaler, int ima_x, int ima_y, int is_quad) { float a1,a2,a3,a4=0.0; float puv[4][2]; /* pixelspace uv's */ @@ -1171,18 +1062,18 @@ static void uv_image_outset(float (*orig_uv)[2], float (*outset_uv)[2], float sc float dir1[2], dir2[2], dir3[2], dir4[2]; /* make UV's in pixel space so we can */ - puv[0][0] = orig_uv[0][0] * x_px; - puv[0][1] = orig_uv[0][1] * y_px; + puv[0][0] = orig_uv[0][0] * ima_x; + puv[0][1] = orig_uv[0][1] * ima_y; - puv[1][0] = orig_uv[1][0] * x_px; - puv[1][1] = orig_uv[1][1] * y_px; + puv[1][0] = orig_uv[1][0] * ima_x; + puv[1][1] = orig_uv[1][1] * ima_y; - puv[2][0] = orig_uv[2][0] * x_px; - puv[2][1] = orig_uv[2][1] * y_px; + puv[2][0] = orig_uv[2][0] * ima_x; + puv[2][1] = orig_uv[2][1] * ima_y; if (is_quad) { - puv[3][0] = orig_uv[3][0] * x_px; - puv[3][1] = orig_uv[3][1] * y_px; + puv[3][0] = orig_uv[3][0] * ima_x; + puv[3][1] = orig_uv[3][1] * ima_y; } /* face edge directions */ @@ -1202,22 +1093,16 @@ static void uv_image_outset(float (*orig_uv)[2], float (*outset_uv)[2], float sc } if (is_quad) { - a1 = NormalizedVecAngle2_2D(dir4, dir1); - a2 = NormalizedVecAngle2_2D(dir1, dir2); - a3 = NormalizedVecAngle2_2D(dir2, dir3); - a4 = NormalizedVecAngle2_2D(dir3, dir4); + a1 = angleToLength( NormalizedVecAngle2_2D(dir4, dir1) ); + a2 = angleToLength( NormalizedVecAngle2_2D(dir1, dir2) ); + a3 = angleToLength( NormalizedVecAngle2_2D(dir2, dir3) ); + a4 = angleToLength( NormalizedVecAngle2_2D(dir3, dir4) ); } else { - a1 = NormalizedVecAngle2_2D(dir3, dir1); - a2 = NormalizedVecAngle2_2D(dir1, dir2); - a3 = NormalizedVecAngle2_2D(dir2, dir3); + a1 = angleToLength( NormalizedVecAngle2_2D(dir3, dir1) ); + a2 = angleToLength( NormalizedVecAngle2_2D(dir1, dir2) ); + a3 = angleToLength( NormalizedVecAngle2_2D(dir2, dir3) ); } - a1 = angleToLength(a1); - a2 = angleToLength(a2); - a3 = angleToLength(a3); - if (is_quad) - a4 = angleToLength(a4); - if (is_quad) { Vec2Subf(no1, dir4, dir1); Vec2Subf(no2, dir1, dir2); @@ -1235,17 +1120,17 @@ static void uv_image_outset(float (*orig_uv)[2], float (*outset_uv)[2], float sc Vec2Addf(outset_uv[1], puv[1], no2); Vec2Addf(outset_uv[2], puv[2], no3); Vec2Addf(outset_uv[3], puv[3], no4); - outset_uv[0][0] /= x_px; - outset_uv[0][1] /= y_px; + outset_uv[0][0] /= ima_x; + outset_uv[0][1] /= ima_y; - outset_uv[1][0] /= x_px; - outset_uv[1][1] /= y_px; + outset_uv[1][0] /= ima_x; + outset_uv[1][1] /= ima_y; - outset_uv[2][0] /= x_px; - outset_uv[2][1] /= y_px; + outset_uv[2][0] /= ima_x; + outset_uv[2][1] /= ima_y; - outset_uv[3][0] /= x_px; - outset_uv[3][1] /= y_px; + outset_uv[3][0] /= ima_x; + outset_uv[3][1] /= ima_y; } else { Vec2Subf(no1, dir3, dir1); Vec2Subf(no2, dir1, dir2); @@ -1259,14 +1144,14 @@ static void uv_image_outset(float (*orig_uv)[2], float (*outset_uv)[2], float sc Vec2Addf(outset_uv[0], puv[0], no1); Vec2Addf(outset_uv[1], puv[1], no2); Vec2Addf(outset_uv[2], puv[2], no3); - outset_uv[0][0] /= x_px; - outset_uv[0][1] /= y_px; + outset_uv[0][0] /= ima_x; + outset_uv[0][1] /= ima_y; - outset_uv[1][0] /= x_px; - outset_uv[1][1] /= y_px; + outset_uv[1][0] /= ima_x; + outset_uv[1][1] /= ima_y; - outset_uv[2][0] /= x_px; - outset_uv[2][1] /= y_px; + outset_uv[2][0] /= ima_x; + outset_uv[2][1] /= ima_y; } } @@ -1315,6 +1200,10 @@ static float lambda_cp_line2(float p[2], float l1[2], float l2[2]) return(Inp2f(u,h)/Inp2f(u,u)); } +/* Converts a UV location to a 3D screenspace location + * Takes a 'uv' and 3 UV coords, and sets the values of pixelScreenCo + * + * This is used for finding a pixels location in screenspace for painting */ static void screen_px_from_ortho( ProjectPaintState *ps, float uv[2], float v1co[3], float v2co[3], float v3co[3], /* Screenspace coords */ @@ -1328,32 +1217,40 @@ static void screen_px_from_ortho( pixelScreenCo[2] = v1co[2]*w[0] + v2co[2]*w[1] + v3co[2]*w[2]; } +/* same as screen_px_from_ortho except we need to take into account + * the perspective W coord for each vert */ static void screen_px_from_persp( ProjectPaintState *ps, float uv[2], float v1co[3], float v2co[3], float v3co[3], /* Worldspace coords */ float uv1co[2], float uv2co[2], float uv3co[2], float pixelScreenCo[4]) { - float w[3]; + float w[3], wtot; BarycentricWeightsSimple2f(uv1co,uv2co,uv3co,uv,w); - pixelScreenCo[0] = v1co[0]*w[0] + v2co[0]*w[1] + v3co[0]*w[2]; - pixelScreenCo[1] = v1co[1]*w[0] + v2co[1]*w[1] + v3co[1]*w[2]; - pixelScreenCo[2] = v1co[2]*w[0] + v2co[2]*w[1] + v3co[2]*w[2]; - pixelScreenCo[3] = 1.0; - Mat4MulVec4fl(ps->projectMat, pixelScreenCo); + /* re-weight from the 4th coord of each screen vert */ + w[0] *= v1co[3]; + w[1] *= v2co[3]; + w[2] *= v3co[3]; + + wtot = w[0]+w[1]+w[2]; - // if( pixelScreenCo[3] > 0.001 ) { ??? TODO - /* screen space, not clamped */ - pixelScreenCo[0] = (float)(curarea->winx/2.0)+(curarea->winx/2.0)*pixelScreenCo[0]/pixelScreenCo[3]; - pixelScreenCo[1] = (float)(curarea->winy/2.0)+(curarea->winy/2.0)*pixelScreenCo[1]/pixelScreenCo[3]; - pixelScreenCo[2] = pixelScreenCo[2]/pixelScreenCo[3]; /* Use the depth for bucket point occlusion */ + w[0]/=wtot; + w[1]/=wtot; + w[2]/=wtot; + /* done re-weighting */ + + pixelScreenCo[0] = v1co[0]*w[0] + v2co[0]*w[1] + v3co[0]*w[2]; + pixelScreenCo[1] = v1co[1]*w[0] + v2co[1]*w[1] + v3co[1]*w[2]; + pixelScreenCo[2] = v1co[2]*w[0] + v2co[2]*w[1] + v3co[2]*w[2]; } /* Only run this function once for new ProjectPixelClone's */ -#define pixel_size 4 +#define IMA_CHAR_PX_SIZE 4 +/* run this function when we know a bucket's, face's pixel can be initialized, + * adding to the LinkList 'ps->bucketRect' */ static void project_paint_uvpixel_init(ProjectPaintState *ps, int thread_index, ImBuf *ibuf, short x, short y, int bucket_index, int face_index, int image_index, float pixelScreenCo[4]) { ProjectPixel *projPixel; @@ -1394,13 +1291,14 @@ static void project_paint_uvpixel_init(ProjectPaintState *ps, int thread_index, projPixel = (ProjectPixel *)BLI_memarena_alloc(ps->arena_mt[thread_index], size); if (ibuf->rect_float) { - projPixel->pixel = (void *) ((( float * ) ibuf->rect_float) + (( x + y * ibuf->x ) * pixel_size)); + projPixel->pixel = (void *) ((( float * ) ibuf->rect_float) + (( x + y * ibuf->x ) * IMA_CHAR_PX_SIZE)); /* TODO float support for origColor */ } else { - projPixel->pixel = (void *) ((( char * ) ibuf->rect) + (( x + y * ibuf->x ) * pixel_size)); + projPixel->pixel = (void *) ((( char * ) ibuf->rect) + (( x + y * ibuf->x ) * IMA_CHAR_PX_SIZE)); *((unsigned int *)projPixel->origColor) = *((unsigned int *)projPixel->pixel); } + /* screenspace unclamped, we could keep its z and w values but dont need them at the moment */ VECCOPY2D(projPixel->projCo2D, pixelScreenCo); projPixel->x_px = x; @@ -1430,8 +1328,6 @@ static void project_paint_uvpixel_init(ProjectPaintState *ps, int thread_index, } } - /* screenspace unclamped */ - #ifdef PROJ_DEBUG_PAINT if (ibuf->rect_float) ((float *)projPixel->pixel)[1] = 0; else ((char *)projPixel->pixel)[1] = 0; @@ -1446,6 +1342,7 @@ static void project_paint_uvpixel_init(ProjectPaintState *ps, int thread_index, } } +/* intersect two 2D boundboxes */ static void uvpixel_rect_intersect(int min_target[2], int max_target[2], int min_a[2], int max_a[2], int min_b[2], int max_b[2]) { min_target[0] = MAX2(min_a[0], min_b[0]); @@ -1626,7 +1523,8 @@ static void rect_to_uvspace( } -/* initialize pixels from this face where it intersects with the bucket_index, initialize pixels for removing seams */ +/* One of the most important function for projectiopn painting, since it selects the pixels to be added into each bucket. + * initialize pixels from this face where it intersects with the bucket_index, optionally initialize pixels for removing seams */ static void project_paint_face_init(ProjectPaintState *ps, int thread_index, int bucket_index, int face_index, int image_index, float bucket_bounds[4], ImBuf *ibuf) { /* Projection vars, to get the 3D locations into screen space */ @@ -1657,21 +1555,19 @@ static void project_paint_face_init(ProjectPaintState *ps, int thread_index, int float xhalfpx, yhalfpx; float ibuf_xf = ibuf->x, ibuf_yf = ibuf->y; - int i1,i2,i3; + int has_x_isect = -1, has_isect = -1; /* for early loop exit */ - /* scanlines since quads can have 2 triangles intersecting the same vertical location */ -#ifndef PROJ_DEBUG_NOSCANLINE - ProjectScanline scanlines[2]; - ProjectScanline *sc; - int totscanlines; /* can only be 1 or 2, oh well */ -#endif + + int i1,i2,i3; vCo[0] = ps->dm_mvert[ (*(&mf->v1 )) ].co; vCo[1] = ps->dm_mvert[ (*(&mf->v1 + 1)) ].co; vCo[2] = ps->dm_mvert[ (*(&mf->v1 + 2)) ].co; - + /* Use tf_uv_pxoffset instead of tf->uv so we can offset the UV half a pixel + * this is done so we can avoid offseting all the pixels by 0.5 which causes + * problems when wrapping negative coords */ xhalfpx = 0.5 / ibuf_xf; yhalfpx = 0.5 / ibuf_yf; @@ -1720,11 +1616,13 @@ static void project_paint_face_init(ProjectPaintState *ps, int thread_index, int uvpixel_rect_intersect(min_px, max_px, min_px_bucket, max_px_bucket, min_px_tf, max_px_tf); /* clip face and */ - - + + has_isect = 0; for (y = min_px[1]; y < max_px[1]; y++) { //uv[1] = (((float)y) + 0.5) / (float)ibuf->y; uv[1] = (float)y / ibuf_yf; /* use pixel offset UV coords instead */ + + has_x_isect = 0; for (x = min_px[0]; x < max_px[0]; x++) { //uv[0] = (((float)x) + 0.5) / ibuf->x; uv[0] = (float)x / ibuf_xf; /* use pixel offset UV coords instead */ @@ -1736,12 +1634,22 @@ static void project_paint_face_init(ProjectPaintState *ps, int thread_index, int if (ps->is_ortho) { screen_px_from_ortho(ps, uv, v1coSS,v2coSS,v3coSS, uv1co,uv2co,uv3co, pixelScreenCo); } else { - screen_px_from_persp(ps, uv, vCo[i1],vCo[i2],vCo[i3], uv1co,uv2co,uv3co, pixelScreenCo); + screen_px_from_persp(ps, uv, v1coSS,v2coSS,v3coSS, uv1co,uv2co,uv3co, pixelScreenCo); } project_paint_uvpixel_init(ps, thread_index, ibuf, x, y, bucket_index, face_index, image_index, pixelScreenCo); + + has_x_isect = has_isect = 1; + } else if (has_x_isect) { + /* assuming the face is not a bow-tie - we know we cant intersect again on the X */ + break; } } + + /* no intersection for this entire row, after some intersection above means we can quit now */ + if (has_x_isect==0 && has_isect) { + //break; + } } } } while(i--); @@ -1845,10 +1753,13 @@ static void project_paint_face_init(ProjectPaintState *ps, int thread_index, int if (uv_image_rect(seam_subsection[0], seam_subsection[1], seam_subsection[2], seam_subsection[3], min_px, max_px, ibuf->x, ibuf->y, 1)) { /* bounds between the seam rect and the uvspace bucket pixels */ - + + has_isect = 0; for (y = min_px[1]; y < max_px[1]; y++) { // uv[1] = (((float)y) + 0.5) / (float)ibuf->y; uv[1] = (float)y / ibuf_yf; /* use offset uvs instead */ + + has_x_isect = 0; for (x = min_px[0]; x < max_px[0]; x++) { //uv[0] = (((float)x) + 0.5) / (float)ibuf->x; uv[0] = (float)x / ibuf_xf; /* use offset uvs instead */ @@ -1882,8 +1793,16 @@ static void project_paint_face_init(ProjectPaintState *ps, int thread_index, int } project_paint_uvpixel_init(ps, thread_index, ibuf, x, y, bucket_index, face_index, image_index, pixelScreenCo); + } else if (has_x_isect) { + /* assuming the face is not a bow-tie - we know we cant intersect again on the X */ + break; } } + + /* no intersection for this entire row, after some intersection above means we can quit now */ + if (has_x_isect==0 && has_isect) { + break; + } } } } @@ -1913,6 +1832,7 @@ static void project_paint_rect(ProjectPaintState *ps, float min[2], float max[2] CLAMP(bucket_max[1], 0, ps->buckets_y); } +/* set bucket_bounds to a screen space-aligned floating point bound-box */ static void project_bucket_bounds(ProjectPaintState *ps, int bucket_x, int bucket_y, float bucket_bounds[4]) { bucket_bounds[ PROJ_BUCKET_LEFT ] = ps->screen_min[0]+((bucket_x)*(ps->screen_width / ps->buckets_x)); /* left */ @@ -1922,57 +1842,65 @@ static void project_bucket_bounds(ProjectPaintState *ps, int bucket_x, int bucke bucket_bounds[ PROJ_BUCKET_TOP ] = ps->screen_min[1]+((bucket_y+1)*(ps->screen_height / ps->buckets_y)); /* top */ } -/* have bucket_bounds as an arg so we dont need to give bucket_x/y the rect function need */ +/* Fill this bucket with pixels from the faces that intersect it. + * + * have bucket_bounds as an argument so we don;t need to give bucket_x/y the rect function needs */ static void project_paint_bucket_init(ProjectPaintState *ps, int thread_index, int bucket_index, float bucket_bounds[4]) { LinkNode *node; int face_index, image_index; - ImBuf *ibuf; + ImBuf *ibuf = NULL; MTFace *tf; Image *tpage_last = NULL; - int tpage_index; if ((node = ps->bucketFaces[bucket_index])) { - do { - face_index = (int)node->link; - - /* Image context switching */ - tf = ps->dm_mtface+face_index; - if (tpage_last != tf->tpage) { - tpage_last = tf->tpage; - - image_index = -1; /* sanity check */ - - for (tpage_index=0; tpage_index < ps->image_tot; tpage_index++) { - if (ps->projImages[tpage_index].ima == tpage_last) { - image_index = tpage_index; - break; + + if (ps->image_tot==1) { + /* Simple loop, no context switching */ + ibuf = ps->projImages[0].ibuf; + do { + project_paint_face_init(ps, thread_index, bucket_index, (int)node->link, 0, bucket_bounds, ibuf); + node = node->next; + } while (node); + } else { + + /* More complicated loop, switch between images */ + do { + face_index = (int)node->link; + + /* Image context switching */ + tf = ps->dm_mtface+face_index; + if (tpage_last != tf->tpage) { + tpage_last = tf->tpage; + + image_index = -1; /* sanity check */ + + for (image_index=0; image_index < ps->image_tot; image_index++) { + if (ps->projImages[image_index].ima == tpage_last) { + ibuf = ps->projImages[image_index].ibuf; + break; + } } } + /* context switching done */ - if (image_index==-1) { - printf("Error, should never happen!\n"); - return; - } + project_paint_face_init(ps, thread_index, bucket_index, face_index, image_index, bucket_bounds, ibuf); - ibuf = ps->projImages[image_index].ibuf; - } - project_paint_face_init(ps, thread_index, bucket_index, face_index, image_index, bucket_bounds, ibuf); - - node = node->next; - } while (node); + node = node->next; + } while (node); + } } ps->bucketFlags[bucket_index] |= PROJ_BUCKET_INIT; } -/* We want to know if a bucket and a face overlap in screenspace +/* We want to know if a bucket and a face overlap in screen-space * * Note, if this ever returns false positives its not that bad, since a face in the bounding area will have its pixels * calculated when it might not be needed later, (at the moment at least) - * obviously it shouldnt have bugs though */ + * obviously it shouldn't have bugs though */ static int project_bucket_face_isect(ProjectPaintState *ps, float min[2], float max[2], int bucket_x, int bucket_y, int bucket_index, MFace *mf) { @@ -2036,13 +1964,14 @@ static int project_bucket_face_isect(ProjectPaintState *ps, float min[2], float return 0; } +/* Add faces to the bucket but dont initialize its pixels */ static void project_paint_delayed_face_init(ProjectPaintState *ps, MFace *mf, MTFace *tf, int face_index) { float min[2], max[2]; int bucket_min[2], bucket_max[2]; /* for ps->bucketRect indexing */ int i, a, bucket_x, bucket_y, bucket_index; - int has_x_isect = -1, has_isect = -1; /* for early loop exit */ + int has_x_isect = -1, has_isect = 0; /* for early loop exit */ INIT_MINMAX2(min,max); @@ -2127,12 +2056,12 @@ static void project_paint_begin( ProjectPaintState *ps, short mval[2]) int image_index; /* memory sized to add to arena size */ - int tot_bucketMem=0; - int tot_faceSeamFlagMem=0; + int tot_bucketRectMem=0; + int tot_faceSeamFlagsMem=0; int tot_faceSeamUVMem=0; - int tot_faceListMem=0; - int tot_bucketFlagMem=0; - int tot_bucketVertFacesMem=0; + int tot_bucketFacesMem=0; + int tot_bucketFlagsMem=0; + int tot_vertFacesMem=0; /* ---- end defines ---- */ @@ -2232,48 +2161,48 @@ static void project_paint_begin( ProjectPaintState *ps, short mval[2]) CLAMP(ps->buckets_x, PROJ_BUCKET_RECT_MIN, PROJ_BUCKET_RECT_MAX); CLAMP(ps->buckets_y, PROJ_BUCKET_RECT_MIN, PROJ_BUCKET_RECT_MAX); - tot_bucketMem = sizeof(LinkNode *) * ps->buckets_x * ps->buckets_y; - tot_faceListMem = sizeof(LinkNode *) * ps->buckets_x * ps->buckets_y; + tot_bucketRectMem = sizeof(LinkNode *) * ps->buckets_x * ps->buckets_y; + tot_bucketFacesMem = sizeof(LinkNode *) * ps->buckets_x * ps->buckets_y; - tot_bucketFlagMem = sizeof(char) * ps->buckets_x * ps->buckets_y; + tot_bucketFlagsMem = sizeof(char) * ps->buckets_x * ps->buckets_y; #ifndef PROJ_DEBUG_NOSEAMBLEED if (ps->seam_bleed_px > 0.0) { /* UV Seams for bleeding */ - tot_bucketVertFacesMem = sizeof(LinkNode *) * ps->dm_totvert; - tot_faceSeamFlagMem = sizeof(char) * ps->dm_totface; + tot_vertFacesMem = sizeof(LinkNode *) * ps->dm_totvert; + tot_faceSeamFlagsMem = sizeof(char) * ps->dm_totface; tot_faceSeamUVMem = sizeof(float) * ps->dm_totface * 8; } #endif /* BLI_memarena_new uses calloc */ ps->arena = - BLI_memarena_new( tot_bucketMem + - tot_faceListMem + - tot_faceSeamFlagMem + + BLI_memarena_new( tot_bucketRectMem + + tot_bucketFacesMem + + tot_faceSeamFlagsMem + tot_faceSeamUVMem + - tot_bucketVertFacesMem + (1<<18)); + tot_vertFacesMem + (1<<18)); BLI_memarena_use_calloc(ps->arena); - ps->bucketRect = (LinkNode **)BLI_memarena_alloc( ps->arena, tot_bucketMem); - ps->bucketFaces= (LinkNode **)BLI_memarena_alloc( ps->arena, tot_faceListMem); + ps->bucketRect = (LinkNode **)BLI_memarena_alloc( ps->arena, tot_bucketRectMem); + ps->bucketFaces= (LinkNode **)BLI_memarena_alloc( ps->arena, tot_bucketFacesMem); - ps->bucketFlags= (char *)BLI_memarena_alloc( ps->arena, tot_bucketFlagMem); + ps->bucketFlags= (char *)BLI_memarena_alloc( ps->arena, tot_bucketFlagsMem); #ifndef PROJ_DEBUG_NOSEAMBLEED if (ps->seam_bleed_px > 0.0) { - ps->vertFaces= (LinkNode **)BLI_memarena_alloc( ps->arena, tot_bucketVertFacesMem); - ps->faceSeamFlags = (char *)BLI_memarena_alloc( ps->arena, tot_faceSeamFlagMem); + ps->vertFaces= (LinkNode **)BLI_memarena_alloc( ps->arena, tot_vertFacesMem); + ps->faceSeamFlags = (char *)BLI_memarena_alloc( ps->arena, tot_faceSeamFlagsMem); ps->faceSeamUVs= BLI_memarena_alloc( ps->arena, tot_faceSeamUVMem); } #endif - // calloced - memset(ps->bucketRect, 0, tot_bucketMem); - // calloced - memset(ps->bucketFaces, 0, tot_faceListMem); - // calloced - memset(ps->bucketFlags, 0, tot_bucketFlagMem); + // calloced - memset(ps->bucketRect, 0, tot_bucketRectMem); + // calloced - memset(ps->bucketFaces, 0, tot_bucketFacesMem); + // calloced - memset(ps->bucketFlags, 0, tot_bucketFlagsMem); #ifndef PROJ_DEBUG_NOSEAMBLEED - // calloced - memset(ps->faceSeamFlags,0, tot_faceSeamFlagMem); + // calloced - memset(ps->faceSeamFlags,0, tot_faceSeamFlagsMem); // calloced - if (ps->seam_bleed_px > 0.0) { - // calloced - memset(ps->vertFaces, 0, tot_bucketVertFacesMem); + // calloced - memset(ps->vertFaces, 0, tot_vertFacesMem); /* TODO dosnt need zeroing? */ // calloced - memset(ps->faceSeamUVs, 0, tot_faceSeamUVMem); // calloced - } @@ -2492,7 +2421,8 @@ static void project_paint_end( ProjectPaintState *ps ) /* This is a BIT ODD, but overwrite the undo tiles image info with this pixels original color * because allocating the tiles allong the way slows down painting */ /* TODO float buffer */ - ((unsigned int *)tile->rect)[ (projPixel->x_px - x_round) + (projPixel->y_px - y_round) * IMAPAINT_TILE_SIZE ] = *((unsigned int *)projPixel->origColor); + ((unsigned int *)tile->rect)[ (projPixel->x_px - x_round) + (projPixel->y_px - y_round) * IMAPAINT_TILE_SIZE ] = *((unsigned int *)projPixel->origColor); + } pixel_node = pixel_node->next; @@ -3199,8 +3129,8 @@ static void imapaint_paint_sub_stroke_project( project_paint_bucket_init(ps, thread_index, bucket_index, bucket_bounds); } - /* TODO - we may want to init clone data in a seperate to project_paint_bucket_init - * so we dont go overboard and init too many clone pixels */ + /* TODO - we may want to init clone data in a separate to project_paint_bucket_init + * so we don't go overboard and init too many clone pixels */ if ((node = ps->bucketRect[bucket_index])) { @@ -3704,7 +3634,7 @@ void imagepaint_paint(short mousebutton, short texpaint) } /* TODO - grease pencil stroke is very basic now and only useful for benchmarking, should make this nicer */ - //stroke_gp = 1; + stroke_gp = 0; /* if (G.rt==123) { @@ -3763,16 +3693,25 @@ void imagepaint_paint(short mousebutton, short texpaint) if (project) { /* setup projection painting data */ - ps.do_backfacecull = 1; - ps.do_occlude = 1; + ps.do_backfacecull = (settings->imapaint.flag & IMAGEPAINT_PROJECT_BACKFACE) ? 0 : 1; + ps.do_occlude = (settings->imapaint.flag & IMAGEPAINT_PROJECT_XRAY) ? 0 : 1; #ifndef PROJ_DEBUG_NOSEAMBLEED - ps.seam_bleed_px = 2.0; /* pixel num to bleed */ + if (settings->imapaint.flag & IMAGEPAINT_PROJECT_IGNORE_SEAMS) { + ps.seam_bleed_px = 0.0; + } else { + ps.seam_bleed_px = settings->imapaint.seam_bleed; /* pixel num to bleed */ + if (ps.seam_bleed_px < 2.0) + ps.seam_bleed_px = 2.0; + } + #endif project_paint_begin(&ps, mval); if (stroke_gp) { tot_gp = imapaint_paint_gp_to_stroke(&points_gp); vec_gp = points_gp; + prevmval[0]= (short)vec_gp[0]; + prevmval[1]= (short)vec_gp[1]; } spacing = ((float)ps.brush->size)/100.0 * ps.brush->spacing; @@ -3811,7 +3750,7 @@ void imagepaint_paint(short mousebutton, short texpaint) } init = 0; - + } else { if((mval[0] != prevmval[0]) || (mval[1] != prevmval[1])) { imapaint_paint_stroke(&s, painter, texpaint, prevmval, mval, time, pressure); @@ -3840,7 +3779,8 @@ void imagepaint_paint(short mousebutton, short texpaint) if (points_gp) MEM_freeN(points_gp); - printf("timed test %f\n", (float)(PIL_check_seconds_timer() - benchmark_time)); + if (stroke_gp) + printf("timed test %f\n", (float)(PIL_check_seconds_timer() - benchmark_time)); imapaint_redraw(1, texpaint, s.image); undo_imagepaint_push_end(); |