Revision: 17334 http://projects.blender.org/plugins/scmsvn/viewcvs.php?view=rev&root=bf-blender&revision=17334 Author: campbellbarton Date: 2008-11-05 15:45:54 +0100 (Wed, 05 Nov 2008)
Log Message: ----------- projection painting clone tool - gives a similar work flow to cloning in the gimp, Ctrl+Click to set the cursor source, then paint from this location. todo... * pixel interpolation. * clone option can currently only be set from the image paint panel. * only initialize clone pixels under the mouse. * overlap between source/target while painting could cause problems. need to look into this. also fixed some cashes in painting normally. Modified Paths: -------------- branches/projection-paint/source/blender/src/imagepaint.c Modified: branches/projection-paint/source/blender/src/imagepaint.c =================================================================== --- branches/projection-paint/source/blender/src/imagepaint.c 2008-11-05 13:22:10 UTC (rev 17333) +++ branches/projection-paint/source/blender/src/imagepaint.c 2008-11-05 14:45:54 UTC (rev 17334) @@ -145,8 +145,9 @@ #define PROJ_FACE_SEAM4 1<<5 -#define PROJ_BUCKET_NULL 0 -#define PROJ_BUCKET_INIT 1 +#define PROJ_BUCKET_NULL 0 +#define PROJ_BUCKET_INIT 1<<0 +// #define PROJ_BUCKET_CLONE_INIT 1<<1 /* only for readability */ #define PROJ_BUCKET_LEFT 0 @@ -217,8 +218,10 @@ } ProjectPixel; typedef struct ProjectPixelClone { - struct ProjectPixel; - void *source; + struct ProjectPixel __pp; + char backbuf[4]; /* TODO - float buffer? */ + char clonebuf[4]; + //void *source; /* pointer to source pixels */ } ProjectPixelClone; /* Finish projection painting structs */ @@ -396,15 +399,144 @@ ( ( (int)(( (projCo2D[1] - ps->viewMin2D[1]) / ps->viewHeight) * ps->bucketsY)) * ps->bucketsX ); } +static int project_paint_BucketOffsetSafe(ProjectPaintState *ps, float *projCo2D) +{ + int bucket_index = project_paint_BucketOffset(ps, projCo2D); + + if (bucket_index < 0 || bucket_index >= ps->bucketsX*ps->bucketsY) { + return -1; + } else { + return bucket_index; + } +} + +/* assume they intersect */ +static void BarryCentricWeights2f(float v1[2], float v2[2], float v3[2], float pt[2], float w[3]) { + float wtot; + w[0] = AreaF2Dfl(v2, v3, pt); + w[1] = AreaF2Dfl(v3, v1, pt); + w[2] = AreaF2Dfl(v1, v2, pt); + wtot = w[0]+w[1]+w[2]; + w[0]/=wtot; + w[1]/=wtot; + w[2]/=wtot; +} + +static float tri_depth_2d(float v1[3], float v2[3], float v3[3], float pt[2], float w[3]) +{ + BarryCentricWeights2f(v1,v2,v3,pt,w); + return (v1[2]*w[0]) + (v2[2]*w[1]) + (v3[2]*w[2]); +} + + +/* return the topmost face in screen coords index or -1 + * bucket_index can be -1 if we dont know it to begin with */ +static int screenco_pickface(ProjectPaintState *ps, float pt[2], float w[3], int *side) { + LinkNode *node; + float w_tmp[3]; + float *v1, *v2, *v3, *v4; + int bucket_index; + int face_index; + int best_side = -1; + int best_face_index = -1; + float z_depth_best = MAXFLOAT, z_depth; + MFace *mf; + + bucket_index = project_paint_BucketOffsetSafe(ps, pt); + if (bucket_index==-1) + return -1; + + node = ps->projectFaces[bucket_index]; + + /* we could return 0 for 1 face buckets, as long as this function assumes + * that the point its testing is only every originated from an existing face */ + + while (node) { + face_index = (int)node->link; + mf = ps->dm_mface + face_index; + + v1 = ps->projectVertScreenCos[mf->v1]; + v2 = ps->projectVertScreenCos[mf->v2]; + v3 = ps->projectVertScreenCos[mf->v3]; + + if ( IsectPT2Df(pt, v1, v2, v3) ) { + z_depth = tri_depth_2d(v1,v2,v3,pt,w_tmp); + if (z_depth < z_depth_best) { + best_face_index = face_index; + best_side = 0; + z_depth_best = z_depth; + VECCOPY(w, w_tmp); + } + } else if (mf->v4) { + v4 = ps->projectVertScreenCos[mf->v4]; + + if ( IsectPT2Df(pt, v1, v3, v4) ) { + z_depth = tri_depth_2d(v1,v3,v4,pt,w_tmp); + if (z_depth < z_depth_best) { + best_face_index = face_index; + best_side = 1; + z_depth_best = z_depth; + VECCOPY(w, w_tmp); + } + } + } + + node = node->next; + } + + *side = best_side; + return best_face_index; /* will be -1 or a valid face */ +} + +/* bucket_index is optional, since in some cases we know it */ +static int screenco_pickcol(ProjectPaintState *ps, int bucket_index, float pt[2], char rgba[4]) +{ + float w[3], uv[2]; + int side; + int face_index; + MTFace *tf; + ImBuf *ibuf; + int x,y; + char *pixel; + + face_index = screenco_pickface(ps,pt,w, &side); + + if (face_index == -1) + return 0; + + tf = ps->dm_mtface + face_index; + + if (side==0) { + 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]; + } else { /* QUAD */ + uv[0] = tf->uv[0][0]*w[0] + tf->uv[2][0]*w[1] + tf->uv[3][0]*w[2]; + uv[1] = tf->uv[0][1]*w[0] + tf->uv[2][1]*w[1] + tf->uv[3][1]*w[2]; + } + + ibuf = BKE_image_get_ibuf((Image *)tf->tpage, NULL); /* TODO - this may be slow */ + + x = uv[0]*ibuf->x; + y = uv[1]*ibuf->y; + + if (x<0 || x>=ibuf->x || y<0 || y>=ibuf->y) return 0; + + pixel = (( char * ) ibuf->rect) + (( x + y * ibuf->x ) * 4); + + rgba[0] = pixel[0]; + rgba[1] = pixel[1]; + rgba[2] = pixel[2]; + rgba[3] = pixel[3]; + return 1; +} + /* return... * 0 : no occlusion * -1 : no occlusion but 2D intersection is true (avoid testing the other half of a quad) * 1 : occluded */ -static int screenco_tri_pt_occlude(float *pt, float *v1, float *v2, float *v3) +static int screenco_tri_pt_occlude(float pt[3], float v1[3], float v2[3], float v3[3]) { - float w1, w2, w3, wtot; /* weights for converting the pixel into 3d worldspace coords */ - /* if all are behind us, return false */ if(v1[2] > pt[2] && v2[2] > pt[2] && v3[2] > pt[2]) return 0; @@ -419,13 +551,9 @@ if( v1[2] < pt[2] && v2[2] < pt[2] && v3[2] < pt[2]) { return 1; } else { + float w[3]; /* we intersect? - find the exact depth at the point of intersection */ - w1 = AreaF2Dfl(v2, v3, pt); - w2 = AreaF2Dfl(v3, v1, pt); - w3 = AreaF2Dfl(v1, v2, pt); - wtot = w1 + w2 + w3; - - if ((v1[2]*w1/wtot) + (v2[2]*w2/wtot) + (v3[2]*w3/wtot) < pt[2]) { + if (tri_depth_2d(v1,v2,v3,pt,w) < pt[2]) { return 1; /* This point is occluded by another face */ } } @@ -497,7 +625,7 @@ #define ISECT_TRUE 1 #define ISECT_TRUE_P1 2 #define ISECT_TRUE_P2 3 -static int project_scanline_isect(float *p1, float *p2, float y_level, float *x_isect) +static int project_scanline_isect(float p1[2], float p2[2], float y_level, float *x_isect) { if (y_level==p1[1]) { *x_isect = p1[0]; @@ -519,7 +647,7 @@ } } -static int project_face_scanline(ProjectScanline *sc, float y_level, float *v1, float *v2, float *v3, float *v4) +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 */ @@ -614,7 +742,7 @@ return totscanlines; } -static int cmp_uv(float *vec2a, float *vec2b) +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; } @@ -712,7 +840,7 @@ } /* return zero if there is no area in the returned rectangle */ -static int uv_image_rect(float *uv1, float *uv2, float *uv3, float *uv4, int *min_px, int *max_px, int x_px, int y_px, int is_quad) +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) { float min_uv[2], max_uv[2]; /* UV bounds */ int i; @@ -862,41 +990,29 @@ } static screen_px_from_ortho( - ProjectPaintState *ps, float *uv, - float *v1co, float *v2co, float *v3co, /* Screenspace coords */ - float *uv1co, float *uv2co, float *uv3co, - float *pixelScreenCo ) + ProjectPaintState *ps, float uv[2], + float v1co[3], float v2co[3], float v3co[3], /* Screenspace coords */ + float uv1co[2], float uv2co[2], float uv3co[2], + float pixelScreenCo[4] ) { - float w1, w2, w3, wtot; /* weights for converting the pixel into 3d screenspace coords */ - w1 = AreaF2Dfl(uv2co, uv3co, uv); - w2 = AreaF2Dfl(uv3co, uv1co, uv); - w3 = AreaF2Dfl(uv1co, uv2co, uv); - - wtot = w1 + w2 + w3; - w1 /= wtot; w2 /= wtot; w3 /= wtot; - - pixelScreenCo[0] = v1co[0]*w1 + v2co[0]*w2 + v3co[0]*w3; - pixelScreenCo[1] = v1co[1]*w1 + v2co[1]*w2 + v3co[1]*w3; - pixelScreenCo[2] = v1co[2]*w1 + v2co[2]*w2 + v3co[2]*w3; + float w[3]; + BarryCentricWeights2f(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]; } static screen_px_from_persp( - ProjectPaintState *ps, float *uv, - float *v1co, float *v2co, float *v3co, /* Worldspace coords */ - float *uv1co, float *uv2co, float *uv3co, - float *pixelScreenCo ) + 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 w1, w2, w3, wtot; /* weights for converting the pixel into 3d screenspace coords */ - w1 = AreaF2Dfl(uv2co, uv3co, uv); - w2 = AreaF2Dfl(uv3co, uv1co, uv); - w3 = AreaF2Dfl(uv1co, uv2co, uv); - - wtot = w1 + w2 + w3; - w1 /= wtot; w2 /= wtot; w3 /= wtot; - - pixelScreenCo[0] = v1co[0]*w1 + v2co[0]*w2 + v3co[0]*w3; - pixelScreenCo[1] = v1co[1]*w1 + v2co[1]*w2 + v3co[1]*w3; - pixelScreenCo[2] = v1co[2]*w1 + v2co[2]*w2 + v3co[2]*w3; + float w[3]; + BarryCentricWeights2f(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); @@ -908,10 +1024,13 @@ pixelScreenCo[2] = pixelScreenCo[2]/pixelScreenCo[3]; /* Use the depth for bucket point occlusion */ } -/* can provide own own coords, use for seams when we want to bleed our from the original location */ +static void project_paint_bucket_init(ProjectPaintState *ps, int bucket_index); + +/* Only run this function once for new ProjectPixelClone's */ #define pixel_size 4 -static void project_paint_uvpixel_init(ProjectPaintState *ps, ImBuf *ibuf, float *uv, int x, int y, int face_index, float *pixelScreenCo) + +static void project_paint_uvpixel_init(ProjectPaintState *ps, ImBuf *ibuf, float uv[2], int x, int y, int face_index, float pixelScreenCo[4]) { int bucket_index; @@ -919,10 +1038,10 @@ ProjectPixel *projPixel; - bucket_index = project_paint_BucketOffset(ps, pixelScreenCo); + bucket_index = project_paint_BucketOffsetSafe(ps, pixelScreenCo); /* even though it should be clamped, in some cases it can still run over */ - if (bucket_index < 0 || bucket_index >= ps->bucketsX * ps->bucketsY) + if (bucket_index==-1) return; /* Use viewMin2D to make (0,0) the bottom left of the bounds @@ -931,13 +1050,39 @@ /* Is this UV visible from the view? - raytrace */ @@ Diff output truncated at 10240 characters. @@ _______________________________________________ Bf-blender-cvs mailing list Bf-blender-cvs@blender.org http://lists.blender.org/mailman/listinfo/bf-blender-cvs