[Bf-blender-cvs] SVN commit: /data/svn/bf-blender [17533] branches/projection-paint/source/ blender/src/imagepaint.c: fix for painting onto faces from a side view in perspective mode, they would have jaggie clipping applied that looked bad.

Campbell Barton ideasman42 at gmail.com
Sat Nov 22 15:54:50 CET 2008


Revision: 17533
          http://projects.blender.org/plugins/scmsvn/viewcvs.php?view=rev&root=bf-blender&revision=17533
Author:   campbellbarton
Date:     2008-11-22 15:54:49 +0100 (Sat, 22 Nov 2008)

Log Message:
-----------
fix for painting onto faces from a side view in perspective mode, they would have jaggie clipping applied that looked bad.

The reason for this was with transforming screenspace points outside the face into UV space and when the bucket bounds was enough outside the face.
For faces viewed side on, this transformation would be applied incorrectly (a bit like trying to apply a projection matrix to a point behind the view), the buckets UV space coords would be incorrect and the wrong pixels would be initialized for that face.

solution is to clip the screenspace face with the bucket before getting the UVs. This should also be a bit faster since the clipped polyline will have a smaller bounding box.

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-22 11:15:07 UTC (rev 17532)
+++ branches/projection-paint/source/blender/src/imagepaint.c	2008-11-22 14:54:49 UTC (rev 17533)
@@ -482,7 +482,7 @@
 }
 
 /* fast projection bucket array lookup, use the safe version for bound checking  */
-static int project_paint_BucketOffset(ProjPaintState *ps, float projCo2D[2])
+static int project_bucket_offset(ProjPaintState *ps, float projCo2D[2])
 {
 	/* If we were not dealing with screenspace 2D coords we could simple do...
 	 * ps->bucketRect[x + (y*ps->buckets_y)] */
@@ -498,9 +498,9 @@
 		(	(	(int)(( (projCo2D[1] - ps->screen_min[1])  / ps->screen_height) * ps->buckets_y)) * ps->buckets_x );
 }
 
-static int project_paint_BucketOffsetSafe(ProjPaintState *ps, float projCo2D[2])
+static int project_bucket_offset_safe(ProjPaintState *ps, float projCo2D[2])
 {
-	int bucket_index = project_paint_BucketOffset(ps, projCo2D);
+	int bucket_index = project_bucket_offset(ps, projCo2D);
 	
 	if (bucket_index < 0 || bucket_index >= ps->buckets_x*ps->buckets_y) {	
 		return -1;
@@ -623,7 +623,7 @@
 	float z_depth_best = MAXFLOAT, z_depth;
 	MFace *mf;
 	
-	bucket_index = project_paint_BucketOffsetSafe(ps, pt);
+	bucket_index = project_bucket_offset_safe(ps, pt);
 	if (bucket_index==-1)
 		return -1;
 	
@@ -894,7 +894,7 @@
 
 /* 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)
+static int pixel_bounds_uv(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 */
 	
@@ -918,6 +918,33 @@
 	return (min_px[0] == max_px[0] || min_px[1] == max_px[1]) ? 0 : 1;
 }
 
+static int pixel_bounds_array(float (* uv)[2], int min_px[2], int max_px[2], int ibuf_x, int ibuf_y, int tot)
+{
+	float min_uv[2], max_uv[2]; /* UV bounds */
+	
+	if (tot==0) {
+		return 0;
+	}
+	
+	INIT_MINMAX2(min_uv, max_uv);
+	
+	while (tot--) {
+		DO_MINMAX2((*uv), min_uv, max_uv);
+		uv++;
+	}
+	
+	min_px[0] = (int)(ibuf_x * min_uv[0]);
+	min_px[1] = (int)(ibuf_y * min_uv[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]);*/
+	
+	/* face uses no UV area when quantized to pixels? */
+	return (min_px[0] == max_px[0] || min_px[1] == max_px[1]) ? 0 : 1;
+}
+
 #ifndef PROJ_DEBUG_NOSEAMBLEED
 
 /* This function returns 1 if this face has a seam along the 2 face-vert indicies
@@ -1420,8 +1447,8 @@
 	}
 	
 #ifdef PROJ_DEBUG_PAINT
-	if (ibuf->rect_float)	((float *)projPixel->pixel)[1] = 0;
-	else					((char *)projPixel->pixel)[1] = 0;
+	if (ibuf->rect_float)	projPixel->pixel.f_pt[0] = 0;
+	else					projPixel->pixel.ch_pt[0] = 0;
 #endif
 	projPixel->image_index = image_index;
 	
@@ -1567,8 +1594,116 @@
 	VecAddf(insetCos[2], insetCos[2], cent);
 }
 
-static void rect_to_uvspace(
-		ProjPaintState *ps, float bucket_bounds[4],
+
+static float Vec2Lenf_nosqrt(float *v1, float *v2)
+{
+	float x, y;
+
+	x = v1[0]-v2[0];
+	y = v1[1]-v2[1];
+	return x*x+y*y;
+}
+
+static float Vec2Lenf_nosqrt_other(float *v1, float v2_1, float v2_2)
+{
+	float x, y;
+
+	x = v1[0]-v2_1;
+	y = v1[1]-v2_2;
+	return x*x+y*y;
+}
+
+static int project_bucket_isect_ptv(float bucket_bounds[4], float pt[2])
+{
+	/* first check if we are INSIDE the bucket */
+	if (	bucket_bounds[PROJ_BUCKET_LEFT] <=	pt[0] &&
+			bucket_bounds[PROJ_BUCKET_RIGHT] >=	pt[0] &&
+			bucket_bounds[PROJ_BUCKET_BOTTOM] <=pt[1] &&
+			bucket_bounds[PROJ_BUCKET_TOP] >=	pt[1]	)
+	{
+		return 1;
+	} else {
+		return 0;
+	}
+}
+
+static int project_bucket_isect_pt(float bucket_bounds[4], float x, float y)
+{
+	/* first check if we are INSIDE the bucket */
+	if (	bucket_bounds[PROJ_BUCKET_LEFT] <=	x &&
+			bucket_bounds[PROJ_BUCKET_RIGHT] >=	x &&
+			bucket_bounds[PROJ_BUCKET_BOTTOM] <=y &&
+			bucket_bounds[PROJ_BUCKET_TOP] >=	y	)
+	{
+		return 1;
+	} else {
+		return 0;
+	}
+}
+
+/* note, use a squared value so we can use Vec2Lenf_nosqrt
+ * be sure that you have done a bounds check first or this may fail */
+/* only give bucket_bounds as an arg because we need it elsewhere */
+static int project_bucket_isect_circle(ProjPaintState *ps, int bucket_x, int bucket_y, float cent[2], float radius_squared, float bucket_bounds[4])
+{
+	// printf("%d %d - %f %f %f %f - %f %f \n", bucket_x, bucket_y, bucket_bounds[0], bucket_bounds[1], bucket_bounds[2], bucket_bounds[3], cent[0], cent[1]);
+
+	/* first check if we are INSIDE the bucket */
+	/* if (	bucket_bounds[PROJ_BUCKET_LEFT] <=	cent[0] &&
+			bucket_bounds[PROJ_BUCKET_RIGHT] >=	cent[0] &&
+			bucket_bounds[PROJ_BUCKET_BOTTOM] <=	cent[1] &&
+			bucket_bounds[PROJ_BUCKET_TOP] >=	cent[1]	)
+	{
+		return 1;
+	}*/
+	
+	/* Would normally to a simple intersection test, however we know the bounds of these 2 alredy intersect 
+	 * so we only need to test if the center is inside the vertical or horizontal bounds on either axis,
+	 * this is even less work then an intersection test */
+	if (  (	bucket_bounds[PROJ_BUCKET_LEFT] <=		cent[0] &&
+			bucket_bounds[PROJ_BUCKET_RIGHT] >=		cent[0]) ||
+		  (	bucket_bounds[PROJ_BUCKET_BOTTOM] <=	cent[1] &&
+			bucket_bounds[PROJ_BUCKET_TOP] >=		cent[1]) )
+	{
+		return 1;
+	}
+	 
+	/* out of bounds left */
+	if (cent[0] < bucket_bounds[PROJ_BUCKET_LEFT]) {
+		/* lower left out of radius test */
+		if (cent[1] < bucket_bounds[PROJ_BUCKET_BOTTOM]) {
+			return (Vec2Lenf_nosqrt_other(cent, bucket_bounds[PROJ_BUCKET_LEFT], bucket_bounds[PROJ_BUCKET_BOTTOM]) < radius_squared) ? 1 : 0;
+		} 
+		/* top left test */
+		else if (cent[1] > bucket_bounds[PROJ_BUCKET_TOP]) {
+			return (Vec2Lenf_nosqrt_other(cent, bucket_bounds[PROJ_BUCKET_LEFT], bucket_bounds[PROJ_BUCKET_TOP]) < radius_squared) ? 1 : 0;
+		}
+	}
+	else if (cent[0] > bucket_bounds[PROJ_BUCKET_RIGHT]) {
+		/* lower right out of radius test */
+		if (cent[1] < bucket_bounds[PROJ_BUCKET_BOTTOM]) {
+			return (Vec2Lenf_nosqrt_other(cent, bucket_bounds[PROJ_BUCKET_RIGHT], bucket_bounds[PROJ_BUCKET_BOTTOM]) < radius_squared) ? 1 : 0;
+		} 
+		/* top right test */
+		else if (cent[1] > bucket_bounds[PROJ_BUCKET_TOP]) {
+			return (Vec2Lenf_nosqrt_other(cent, bucket_bounds[PROJ_BUCKET_RIGHT], bucket_bounds[PROJ_BUCKET_TOP]) < radius_squared) ? 1 : 0;
+		}
+	}
+	
+	return 0;
+}
+
+
+
+/* Note for rect_to_uvspace_ortho() and rect_to_uvspace_persp()
+ * in ortho view this function gives good results when bucket_bounds are outside the triangle
+ * however in some cases, perspective view will mess up with faces that have minimal screenspace area (viewed from the side)
+ * 
+ * for this reason its not relyable in this case so we'll use the Simple Barycentric' funcs that only account for points inside the triangle.
+ * however switching back to this for ortho is always an option */
+
+static void rect_to_uvspace_ortho(
+		float bucket_bounds[4],
 		float *v1coSS, float *v2coSS, float *v3coSS,
 		float *uv1co, float *uv2co, float *uv3co,
 		float bucket_bounds_uv[4][2]
@@ -1580,30 +1715,360 @@
 	/* get the UV space bounding box */
 	uv[0] = bucket_bounds[PROJ_BUCKET_RIGHT];
 	uv[1] = bucket_bounds[PROJ_BUCKET_BOTTOM];
-	if (ps->is_ortho)	BarycentricWeights2f(v1coSS, v2coSS, v3coSS, uv, w);
-	else				BarycentricWeightsPersp2f(v1coSS, v2coSS, v3coSS, uv, w);	
+	BarycentricWeightsSimple2f(v1coSS, v2coSS, v3coSS, uv, w);	
 	Vec2Weightf(bucket_bounds_uv[0], uv1co, uv2co, uv3co, w);
 
 	//uv[0] = bucket_bounds[PROJ_BUCKET_RIGHT]; // set above
 	uv[1] = bucket_bounds[PROJ_BUCKET_TOP];
-	if (ps->is_ortho)	BarycentricWeights2f(v1coSS, v2coSS, v3coSS, uv, w);
-	else				BarycentricWeightsPersp2f(v1coSS, v2coSS, v3coSS, uv, w);
+	BarycentricWeightsSimple2f(v1coSS, v2coSS, v3coSS, uv, w);
 	Vec2Weightf(bucket_bounds_uv[1], uv1co, uv2co, uv3co, w);
 
 	uv[0] = bucket_bounds[PROJ_BUCKET_LEFT];
 	//uv[1] = bucket_bounds[PROJ_BUCKET_TOP]; // set above
-	if (ps->is_ortho)	BarycentricWeights2f(v1coSS, v2coSS, v3coSS, uv, w);
-	else				BarycentricWeightsPersp2f(v1coSS, v2coSS, v3coSS, uv, w);
+	BarycentricWeightsSimple2f(v1coSS, v2coSS, v3coSS, uv, w);
 	Vec2Weightf(bucket_bounds_uv[2], uv1co, uv2co, uv3co, w);
 
 	//uv[0] = bucket_bounds[PROJ_BUCKET_LEFT]; // set above
 	uv[1] = bucket_bounds[PROJ_BUCKET_BOTTOM];
-	if (ps->is_ortho)	BarycentricWeights2f(v1coSS, v2coSS, v3coSS, uv, w);
-	else				BarycentricWeightsPersp2f(v1coSS, v2coSS, v3coSS, uv, w);
+	BarycentricWeightsSimple2f(v1coSS, v2coSS, v3coSS, uv, w);
 	Vec2Weightf(bucket_bounds_uv[3], uv1co, uv2co, uv3co, w);
 }
 
+/* same as above but use BarycentricWeightsPersp2f */
+static void rect_to_uvspace_persp(
+		float bucket_bounds[4],
+		float *v1coSS, float *v2coSS, float *v3coSS,
+		float *uv1co, float *uv2co, float *uv3co,
+		float bucket_bounds_uv[4][2]
+	)
+{
+	float uv[2];
+	float w[3];
+	
+	/* get the UV space bounding box */
+	uv[0] = bucket_bounds[PROJ_BUCKET_RIGHT];
+	uv[1] = bucket_bounds[PROJ_BUCKET_BOTTOM];
+	BarycentricWeightsSimplePersp2f(v1coSS, v2coSS, v3coSS, uv, w);	
+	Vec2Weightf(bucket_bounds_uv[0], uv1co, uv2co, uv3co, w);
 
+	//uv[0] = bucket_bounds[PROJ_BUCKET_RIGHT]; // set above
+	uv[1] = bucket_bounds[PROJ_BUCKET_TOP];
+	BarycentricWeightsSimplePersp2f(v1coSS, v2coSS, v3coSS, uv, w);
+	Vec2Weightf(bucket_bounds_uv[1], uv1co, uv2co, uv3co, w);
+
+	uv[0] = bucket_bounds[PROJ_BUCKET_LEFT];
+	//uv[1] = bucket_bounds[PROJ_BUCKET_TOP]; // set above
+	BarycentricWeightsSimplePersp2f(v1coSS, v2coSS, v3coSS, uv, w);
+	Vec2Weightf(bucket_bounds_uv[2], uv1co, uv2co, uv3co, w);
+
+	//uv[0] = bucket_bounds[PROJ_BUCKET_LEFT]; // set above
+	uv[1] = bucket_bounds[PROJ_BUCKET_BOTTOM];
+	BarycentricWeightsSimplePersp2f(v1coSS, v2coSS, v3coSS, uv, w);
+	Vec2Weightf(bucket_bounds_uv[3], uv1co, uv2co, uv3co, w);
+}
+
+/* This works as we need it to but we can save a few steps and not use it */
+
+static float angle_2d_clockwise(float p1[2], float p2[2], float p3[2])
+{
+	float v1[2], v2[2];
+	
+	v1[0] = p1[0]-p2[0];	v1[1] = p1[1]-p2[1];
+	v2[0] = p3[0]-p2[0];	v2[1] = p3[1]-p2[1];
+	
+	return -atan2(v1[0]*v2[1] - v1[1]*v2[0], v1[0]*v2[0]+v1[1]*v2[1]);
+}
+
+

@@ Diff output truncated at 10240 characters. @@




More information about the Bf-blender-cvs mailing list