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. @@

_______________________________________________
Bf-blender-cvs mailing list
[email protected]
http://lists.blender.org/mailman/listinfo/bf-blender-cvs

Reply via email to