[Bf-blender-cvs] SVN commit: /data/svn/bf-blender [17240] branches/projection-paint/source/ blender/src/imagepaint.c: basic projection painting - should work without crashing and not be too slow .

Campbell Barton ideasman42 at gmail.com
Fri Oct 31 06:51:16 CET 2008


Revision: 17240
          http://projects.blender.org/plugins/scmsvn/viewcvs.php?view=rev&root=bf-blender&revision=17240
Author:   campbellbarton
Date:     2008-10-31 06:51:16 +0100 (Fri, 31 Oct 2008)

Log Message:
-----------
basic projection painting - should work without crashing and not be too slow.

does not use any opengl for calculations (2.5 friendly!) and free from screen pixel artifacts. raytracing is used for occlusion.
Details
* Uses a 2D screenspace bucket grid that store intersecting faces and a list of UV pixels to optimize lookups between the brush rectangle UV pixels
* Buckets and faces are initialized when a brush first touches them.
* on initializing all the faces used have their UV pixels are converted into screenspace and copied into the buckets for painting (if the pixel is not occluded by a ray cast).

still a lot to do - blend modes, float buffer, image wrapping, image texture filtering.

Add feature requests here
http://wiki.blender.org/index.php/Wahooney_re/Paint_Branch_Proposals

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-10-31 05:41:45 UTC (rev 17239)
+++ branches/projection-paint/source/blender/src/imagepaint.c	2008-10-31 05:51:16 UTC (rev 17240)
@@ -23,9 +23,9 @@
  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
  * All rights reserved.
  *
- * The Original Code is: all of this file.
+ * The Original Code is: some of this file.
  *
- * Contributor(s): Jens Ole Wund (bjornmose)
+ * Contributor(s): Jens Ole Wund (bjornmose), Campbell Barton (ideasman42)
  *
  * ***** END GPL LICENSE BLOCK *****
  */
@@ -46,6 +46,8 @@
 #include "BLI_arithb.h"
 #include "BLI_blenlib.h"
 #include "BLI_dynstr.h"
+#include "BLI_linklist.h"
+#include "BLI_memarena.h"
 #include "PIL_time.h"
 
 #include "IMB_imbuf.h"
@@ -85,6 +87,8 @@
 #include "BDR_imagepaint.h"
 #include "BDR_vpaint.h"
 
+#include "RE_raytrace.h" /* For projection painting occlusion */
+
 #include "GPU_draw.h"
 
 #include "GHOST_Types.h"
@@ -116,6 +120,7 @@
 	ImBuf *canvas;
 	ImBuf *clonecanvas;
 	short clonefreefloat;
+	short project;			/* is projection texture painting enabled */
 	char *warnpackedfile;
 	char *warnmultifile;
 
@@ -126,6 +131,73 @@
 	float uv[2];
 } ImagePaintState;
 
+
+/* testing options */
+#define PROJ_BUCKET_DIV 128 /* TODO - test other values, this is a guess, seems ok */
+
+#define PROJ_LAZY_INIT 1
+// #define PROJ_PAINT_DEBUG 1
+
+/* projectFaceFlags options */
+#define PROJ_FACE_IGNORE	1<<0	/* When the face is hidden, backfacing or occluded */
+#define PROJ_FACE_INIT	1<<1	/* When we have initialized the faces data */
+#define PROJ_FACE_OWNED	1<<2	/* When the face is in 1 bucket */
+
+#define PROJ_BUCKET_NULL	0
+#define PROJ_BUCKET_INIT	1
+
+/* only for readability */
+#define PROJ_BUCKET_LEFT		0
+#define PROJ_BUCKET_RIGHT	1
+#define PROJ_BUCKET_BOTTOM	2
+#define PROJ_BUCKET_TOP		3
+
+typedef struct ProjectPaintState {
+	/* projection painting only */
+	MemArena *projectArena;		/* use for alocating many pixel structs and link-lists */
+	LinkNode **projectBuckets;	/* screen sized 2D array, each pixel has a linked list of ImagePaintProjectPixel's */
+#ifdef PROJ_LAZY_INIT
+	LinkNode **projectFaces;	/* projectBuckets alligned array linkList of faces overlapping each bucket */
+	char *projectBucketFlags;	/* store if the bucks have been initialized  */
+	char *projectFaceFlags;		/* store info about faces, if they are initialized etc*/
+#endif
+	int bucketsX;				/* The size of the bucket grid, the grid span's viewMin2D/viewMax2D so you can paint outsize the screen or with 2 brushes at once */
+	int bucketsY;
+	
+	Image **projectImages;		/* array of images we are painting onto while, use so we can tag for updates */
+	int projectImageTotal;		/* size of projectImages array */
+	int image_index;			/* current image, use for context switching */
+	
+	float (*projectVertCos2D)[2];	/* verts projected into floating point screen space */
+	
+	RayTree *projectRayTree;	/* ray tracing acceleration structure */
+	Isect isec;					/* re-use ray intersection var */
+	
+	/* options for projection painting */
+	short projectOcclude;		/* Use raytraced occlusion? - ortherwise will paint right through to the back*/
+	short projectBackfaceCull;	/* ignore faces with normals pointing away, skips a lot of raycasts if your normals are correctly flipped */
+	
+	float projectMat[4][4];		/* Projection matrix, use for getting screen coords */
+	float viewMat[4][4];
+	float viewPoint[3];			/* Use only when in perspective mode with projectOcclude, the point we are viewing from, cast rays to this */
+	float viewDir[3];			/* View vector, use for projectBackfaceCull and for ray casting with an ortho viewport  */
+	
+	float viewMin2D[2];			/* 2D bounds for mesh verts on the screen's plane (screenspace) */
+	float viewMax2D[2]; 
+	float viewWidth;			/* Calculated from viewMin2D & viewMax2D */
+	float viewHeight;			
+	
+} ProjectPaintState;
+
+typedef struct ImagePaintProjectPixel {
+	float projCo2D[2]; /* the floating point screen projection of this pixel */
+	char *pixel;
+	int image_index;
+} ImagePaintProjectPixel;
+
+/* Finish projection painting structs */
+
+
 typedef struct UndoTile {
 	struct UndoTile *next, *prev;
 	ID id;
@@ -281,6 +353,606 @@
 	}
 }
 
+
+static MVert * mvert_static = NULL;
+static void project_paint_begin_coords_func(RayFace *face, float **v1, float **v2, float **v3, float **v4)
+{
+	MFace *mface= (MFace*)face;
+
+	*v1= mvert_static[mface->v1].co;
+	*v2= mvert_static[mface->v2].co;
+	*v3= mvert_static[mface->v3].co;
+	*v4= (mface->v4)? mvert_static[mface->v4].co: NULL;
+}
+
+static int project_paint_begin_check_func(Isect *is, int ob, RayFace *face)
+{
+	return 1;
+}
+static int project_paint_BucketOffset(ProjectPaintState *ps, float *projCo2D)
+{
+	
+	/* If we were not dealing with screenspace 2D coords we could simple do...
+	 * ps->projectBuckets[x + (y*ps->bucketsY)] */
+	
+	/* please explain?
+	 * projCo2D[0] - ps->viewMin2D[0]	: zero origin
+	 * ... / ps->viewWidth				: range from 0.0 to 1.0
+	 * ... * ps->bucketsX		: use as a bucket index
+	 *
+	 * Second multiplication does similar but for vertical offset
+	 */
+	return	(	(int)(( (projCo2D[0] - ps->viewMin2D[0]) / ps->viewWidth)  * ps->bucketsX)) + 
+		(	(	(int)(( (projCo2D[1] - ps->viewMin2D[1])  / ps->viewHeight) * ps->bucketsY)) * ps->bucketsX );
+}
+
+static void project_paint_face_init(ImagePaintState *s, ProjectPaintState *ps, MFace *mf, MTFace *tf, ImBuf *ibuf)
+{
+	/* Projection vars, to get the 3D locations into screen space  */
+	ImagePaintProjectPixel *projPixel;
+	
+	float pxWorldCo[3];
+	float pxProjCo[4];
+	
+	/* UV/pixel seeking data */
+	int x; /* Image X-Pixel */
+	int y;/* Image Y-Pixel */
+	float uv[2]; /* Image floating point UV - same as x,y but from 0.0-1.0 */
+	int pixel_size = 4;	/* each pixel is 4 x 8-bits packed in unsigned int */
+	float xmin, ymin, xmax, ymax; /* UV bounds */
+	int xmini, ymini, xmaxi, ymaxi; /* UV Bounds converted to int's for pixel */
+	float w1, w2, w3, wtot; /* weights for converting the pixel into 3d worldspace coords */
+	float *v1co, *v2co, *v3co, *v4co; /* for convenience only */
+	
+	int i;
+	
+	/* clamp to 0-1 for now */
+	xmin = ymin = 1.0f;
+	xmax = ymax = 0.0f;
+	
+	i = mf->v4 ? 3:2;
+	do {
+		xmin = MIN2(xmin, tf->uv[i][0]);
+		ymin = MIN2(ymin, tf->uv[i][1]);
+		
+		xmax = MAX2(xmax, tf->uv[i][0]);
+		ymax = MAX2(ymax, tf->uv[i][1]);
+	} while (i--);
+	
+	xmini = (int)(ibuf->x * xmin);
+	ymini = (int)(ibuf->y * ymin);
+	
+	xmaxi = (int)(ibuf->x * xmax) +1;
+	ymaxi = (int)(ibuf->y * ymax) +1;
+	
+	/*printf("%d %d %d %d \n", xmini, ymini, xmaxi, ymaxi);*/
+	
+	if (xmini < 0) xmini = 0;
+	if (ymini < 0) ymini = 0;
+	
+	if (xmaxi > ibuf->x) xmaxi = ibuf->x;
+	if (ymaxi > ibuf->y) ymaxi = ibuf->y;
+	
+	/* face uses no UV area when quanticed to pixels? */
+	if (xmini == xmaxi || ymini == ymaxi)
+		return;
+
+	v1co = s->me->mvert[mf->v1].co;
+	v2co = s->me->mvert[mf->v2].co;
+	v3co = s->me->mvert[mf->v3].co;
+	if (mf->v4)
+		v4co = s->me->mvert[mf->v4].co;
+	
+	for (y = ymini; y < ymaxi; y++) {
+		uv[1] = (((float)y)+0.5) / (float)ibuf->y;
+		for (x = xmini; x < xmaxi; x++) {
+			uv[0] = (((float)x)+0.5) / (float)ibuf->x;
+			
+			wtot = -1.0;
+			if ( IsectPT2Df( uv, tf->uv[0], tf->uv[1], tf->uv[2] )) {
+				
+				w1 = AreaF2Dfl(tf->uv[1], tf->uv[2], uv);
+				w2 = AreaF2Dfl(tf->uv[2], tf->uv[0], uv);
+				w3 = AreaF2Dfl(tf->uv[0], tf->uv[1], uv);
+				wtot = w1 + w2 + w3;
+				
+				w1 /= wtot; w2 /= wtot; w3 /= wtot;
+				
+				i=2;
+				do {
+					pxWorldCo[i] = v1co[i]*w1 + v2co[i]*w2 + v3co[i]*w3;
+				} while (i--);
+					
+				
+			} else if ( mf->v4 && IsectPT2Df( uv, tf->uv[0], tf->uv[2], tf->uv[3] ) ) {
+				
+				w1 = AreaF2Dfl(tf->uv[2], tf->uv[3], uv);
+				w2 = AreaF2Dfl(tf->uv[3], tf->uv[0], uv);
+				w3 = AreaF2Dfl(tf->uv[0], tf->uv[2], uv);
+				wtot = w1 + w2 + w3;
+				
+				w1 /= wtot; w2 /= wtot; w3 /= wtot;
+				
+				i=2;
+				do {
+					pxWorldCo[i] = v1co[i]*w1 + v3co[i]*w2 + v4co[i]*w3;
+				} while (i--);
+			}
+			
+			/* view3d_project_float(curarea, vec, projCo2D, s->projectMat);
+			if (projCo2D[0]==IS_CLIPPED)
+				continue;*/
+			if (wtot != -1.0) {
+				
+				/* Inline, a bit faster */
+				VECCOPY(pxProjCo, pxWorldCo);
+				pxProjCo[3] = 1.0;
+				
+				Mat4MulVec4fl(ps->projectMat, pxProjCo);
+				
+				if( pxProjCo[3] > 0.001 ) {
+					/* Use viewMin2D to make (0,0) the bottom left of the bounds 
+					 * Then this can be used to index the bucket array */
+					
+					if (ps->projectOcclude) {
+						VECCOPY(ps->isec.start, pxWorldCo);
+						
+						if (G.vd->persp==V3D_ORTHO) {
+							VecAddf(ps->isec.end, pxWorldCo, ps->viewDir);
+						} else { /* value dosnt change but it is modified by RE_ray_tree_intersect() - keep this line */
+							VECCOPY(ps->isec.end, ps->viewPoint);
+						}
+
+						ps->isec.faceorig = mf;
+					}
+					
+					/* Is this UV visible from the view? - raytrace */
+					if (ps->projectOcclude==0 || !RE_ray_tree_intersect(ps->projectRayTree, &ps->isec)) {
+						
+						/* done with view3d_project_float inline */
+						projPixel = (ImagePaintProjectPixel *)BLI_memarena_alloc( ps->projectArena, sizeof(ImagePaintProjectPixel) );
+						
+						/* screenspace unclamped */
+						projPixel->projCo2D[0] = (float)(curarea->winx/2.0)+(curarea->winx/2.0)*pxProjCo[0]/pxProjCo[3];
+						projPixel->projCo2D[1] = (float)(curarea->winy/2.0)+(curarea->winy/2.0)*pxProjCo[1]/pxProjCo[3];
+						
+						projPixel->pixel = (( char * ) ibuf->rect) + (( x + y * ibuf->x ) * pixel_size);
+#ifdef PROJ_PAINT_DEBUG
+						projPixel->pixel[1] = 0;
+#endif
+						projPixel->image_index = ps->image_index;
+						
+						BLI_linklist_prepend_arena(
+							&ps->projectBuckets[ project_paint_BucketOffset(ps, projPixel->projCo2D) ],
+							projPixel,
+							ps->projectArena
+						);
+					}
+				}
+			}
+		}
+	}
+}
+
+
+/* takes floating point screenspace min/max and returns int min/max to be used as indicies for ps->projectBuckets, ps->projectBucketFlags */
+static void project_paint_rect(ProjectPaintState *ps, float min[2], float max[2], int bucket_min[2], int bucket_max[2])
+{
+	/* divide by bucketWidth & bucketHeight so the bounds are offset in bucket grid units */
+	bucket_min[0] = (int)(((float)(min[0] - ps->viewMin2D[0]) / ps->viewWidth) * ps->bucketsX) + 0.5;

@@ Diff output truncated at 10240 characters. @@




More information about the Bf-blender-cvs mailing list