[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