Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCampbell Barton <ideasman42@gmail.com>2008-11-18 03:17:13 +0300
committerCampbell Barton <ideasman42@gmail.com>2008-11-18 03:17:13 +0300
commita54dc97c1363d601c3f4777b09dae72b65a6a44d (patch)
treebbdd6a86389203dc5525af928d315436ebf86faa
parenta10d4192b50d793b08dd340bd1004d374847e8ea (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.h9
-rw-r--r--source/blender/src/buttons_editing.c10
-rw-r--r--source/blender/src/imagepaint.c490
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();