2.5:
authorBrecht Van Lommel <brechtvanlommel@pandora.be>
Thu, 19 Feb 2009 23:53:40 +0000 (23:53 +0000)
committerBrecht Van Lommel <brechtvanlommel@pandora.be>
Thu, 19 Feb 2009 23:53:40 +0000 (23:53 +0000)
* Image painting back. 2d paint, 3d paint and projection, undo,
  pressure, repeating paint operations, etc should all work.
  Drawing cursor needs a bit of work, only gets shown when enabling
  texture paint mode now.

* Move sculpt, image paint, and vertex/weight paint into a single
  sculpt_paint module. Doesn't make much difference now, but nice
  to have it together for better integration and consistency in
  the future.

36 files changed:
source/blender/editors/Makefile
source/blender/editors/SConscript
source/blender/editors/include/ED_screen.h
source/blender/editors/include/ED_sculpt.h
source/blender/editors/include/ED_view3d.h
source/blender/editors/mesh/editface.c
source/blender/editors/screen/screen_ops.c
source/blender/editors/sculpt_paint/Makefile [moved from source/blender/editors/sculpt/Makefile with 98% similarity]
source/blender/editors/sculpt_paint/SConscript [moved from source/blender/editors/sculpt/SConscript with 75% similarity]
source/blender/editors/sculpt_paint/paint_image.c [new file with mode: 0644]
source/blender/editors/sculpt_paint/paint_intern.h [new file with mode: 0644]
source/blender/editors/sculpt_paint/paint_ops.c [new file with mode: 0644]
source/blender/editors/sculpt_paint/paint_utils.c [new file with mode: 0644]
source/blender/editors/sculpt_paint/paint_vertex.c [moved from source/blender/editors/space_view3d/vpaint.c with 95% similarity]
source/blender/editors/sculpt_paint/sculpt.c [moved from source/blender/editors/sculpt/sculpt.c with 100% similarity]
source/blender/editors/sculpt_paint/sculpt_intern.h [moved from source/blender/editors/sculpt/sculpt_intern.h with 100% similarity]
source/blender/editors/sculpt_paint/sculpt_stroke.c [moved from source/blender/editors/sculpt/stroke.c with 100% similarity]
source/blender/editors/space_api/spacetypes.c
source/blender/editors/space_image/space_image.c
source/blender/editors/space_view3d/drawmesh.c
source/blender/editors/space_view3d/drawobject.c
source/blender/editors/space_view3d/space_view3d.c
source/blender/editors/space_view3d/view3d_draw.c
source/blender/editors/space_view3d/view3d_header.c
source/blender/editors/space_view3d/view3d_intern.h
source/blender/editors/space_view3d/view3d_ops.c
source/blender/editors/util/ed_util.c
source/blender/editors/util/undo.c
source/blender/editors/uvedit/Makefile
source/blender/editors/uvedit/SConscript
source/blender/makesdna/DNA_brush_types.h
source/blender/makesdna/DNA_scene_types.h
source/blender/makesrna/intern/makesrna.c
source/blender/makesrna/intern/rna_brush.c
source/blender/windowmanager/WM_types.h
source/blender/windowmanager/wm_event_types.h

index 312227ba4e9614be1ab0feba99c590037b3a1279..8a819195fbdb0f3f6ef4d236793cc1d216aa9d16 100644 (file)
@@ -29,6 +29,6 @@
 # Bounces make to subdirectories.
 
 SOURCEDIR = source/blender/editors
-DIRS = armature mesh animation object sculpt datafiles transform screen curve gpencil physics preview uvedit space_outliner space_time space_view3d interface util  space_api space_graph space_image space_node space_buttons space_info space_file space_sound space_action space_nla space_script space_text space_sequencer
+DIRS = armature mesh animation object sculpt_paint datafiles transform screen curve gpencil physics preview uvedit space_outliner space_time space_view3d interface util  space_api space_graph space_image space_node space_buttons space_info space_file space_sound space_action space_nla space_script space_text space_sequencer
 
 include nan_subdirs.mk
index 8346092976e66035a09cddf0c313bdffd7bc39e9..a99d21b19a4fa70e5c9f93502058c82185cd4084 100644 (file)
@@ -31,5 +31,5 @@ SConscript(['datafiles/SConscript',
                        'space_sequencer/SConscript',
                        'transform/SConscript',
                        'screen/SConscript',
-                       'sculpt/SConscript',
+                       'sculpt_paint/SConscript',
                        'uvedit/SConscript'])
index d93f0546524b746cf1f9e1598e8dfd311c444b59..0433bf3f235c1cce8169458a8c574dba6a1d96af 100644 (file)
@@ -99,6 +99,7 @@ void  ED_keymap_screen(struct wmWindowManager *wm);
 int            ED_operator_screenactive(struct bContext *C);
 int            ED_operator_screen_mainwinactive(struct bContext *C);
 int            ED_operator_areaactive(struct bContext *C);
+int            ED_operator_regionactive(struct bContext *C);
 
 int            ED_operator_scene_editable(struct bContext *C);
 
index f42bb9b46a1f1b9b91577a6ac97b8ec38797af84..c6a8881a0c61ebec6332bed207092c2a1422801e 100644 (file)
 struct bContext;
 struct wmWindowManager;
 
+/* sculpt.c */
 void ED_operatortypes_sculpt(void);
-void ED_keymap_sculpt(wmWindowManager *wm);
+void ED_keymap_sculpt(struct wmWindowManager *wm);
+
+/* paint_ops.c */
+void ED_operatortypes_paint(void);
+
+/* paint_image.c */
+void undo_imagepaint_step(int step);
+void undo_imagepaint_clear(void);
 
 #endif
index f26f6b7460db2eea683f0fe009b76751abd7a4b0..37e9c657d27654404f195755a993e77d5cf67247 100644 (file)
@@ -112,6 +112,8 @@ unsigned int view3d_sample_backbuf_rect(struct ViewContext *vc, short mval[2], i
                                                                                void *handle, unsigned int (*indextest)(void *handle, unsigned int index));
 unsigned int view3d_sample_backbuf(struct ViewContext *vc, int x, int y);
 
+int view_autodist(struct Scene *scene, struct ARegion *ar, struct View3D *v3d, short *mval, float mouse_worldloc[3]);
+
 /* select */
 #define MAXPICKBUF      10000
 short view3d_opengl_select(struct ViewContext *vc, unsigned int *buffer, unsigned int bufsize, rcti *input);
@@ -119,6 +121,7 @@ short view3d_opengl_select(struct ViewContext *vc, unsigned int *buffer, unsigne
 void view3d_set_viewcontext(struct bContext *C, struct ViewContext *vc);
 void view3d_operator_needs_opengl(const struct bContext *C);
 void view3d_get_view_aligned_coordinate(struct ViewContext *vc, float *fp, short mval[2]);
+void view3d_get_object_project_mat(struct RegionView3D *v3d, struct Object *ob, float pmat[4][4], float vmat[4][4]);;
 
 /* XXX should move to arithb.c */
 int edge_inside_circle(short centx, short centy, short rad, short x1, short y1, short x2, short y2);
@@ -126,6 +129,5 @@ int edge_inside_circle(short centx, short centy, short rad, short x1, short y1,
 /* modes */
 void ED_view3d_exit_paint_modes(struct bContext *C);
 
-
 #endif /* ED_VIEW3D_H */
 
index ab6ec464a9bb0cf66cd62bf5d1e4d9491108656a..a6c5e5beccf051fc0ba5d4cc5736c48487ecb63d 100644 (file)
@@ -144,16 +144,6 @@ MTFace *EM_get_active_mtface(EditMesh *em, EditFace **act_efa, MCol **mcol, int
        return NULL;
 }
 
-static void make_tfaces(Object *ob) 
-{
-       Mesh *me= ob->data;
-       
-       if(!me->mtface) {
-               me->mtface= CustomData_add_layer(&me->fdata, CD_MTFACE, CD_DEFAULT,
-                                                NULL, me->totface);
-       }
-}
-
 void reveal_tface(Scene *scene)
 {
        Mesh *me;
@@ -734,155 +724,4 @@ void face_borderselect(Scene *scene, ARegion *ar)
 #endif
 }
 
-/* Texture Paint */
-
-void set_texturepaint(Scene *scene) /* toggle */
-{
-       Object *ob = OBACT;
-       Mesh *me = 0;
-       
-       if(ob==NULL) return;
-       
-       if (object_data_is_libdata(ob)) {
-// XXX         error_libdata();
-               return;
-       }
-
-       me= get_mesh(ob);
-       
-       if(me)
-               DAG_object_flush_update(scene, ob, OB_RECALC_DATA);
-
-       if(G.f & G_TEXTUREPAINT) {
-               G.f &= ~G_TEXTUREPAINT;
-               GPU_paint_set_mipmap(1);
-       }
-       else if (me) {
-               G.f |= G_TEXTUREPAINT;
-
-               if(me->mtface==NULL)
-                       make_tfaces(ob);
-
-               brush_check_exists(&scene->toolsettings->imapaint.brush);
-               GPU_paint_set_mipmap(0);
-       }
-
-}
-
-static void texpaint_project(Object *ob, float *model, float *proj, float *co, float *pco)
-{
-       VECCOPY(pco, co);
-       pco[3]= 1.0f;
-
-       Mat4MulVecfl(ob->obmat, pco);
-       Mat4MulVecfl((float(*)[4])model, pco);
-       Mat4MulVec4fl((float(*)[4])proj, pco);
-}
 
-static void texpaint_tri_weights(Object *ob, float *v1, float *v2, float *v3, float *co, float *w)
-{
-       float pv1[4], pv2[4], pv3[4], h[3], divw;
-       float model[16], proj[16], wmat[3][3], invwmat[3][3];
-       GLint view[4];
-
-       /* compute barycentric coordinates */
-
-       /* get the needed opengl matrices */
-       glGetIntegerv(GL_VIEWPORT, view);
-       glGetFloatv(GL_MODELVIEW_MATRIX, model);
-       glGetFloatv(GL_PROJECTION_MATRIX, proj);
-       view[0] = view[1] = 0;
-
-       /* project the verts */
-       texpaint_project(ob, model, proj, v1, pv1);
-       texpaint_project(ob, model, proj, v2, pv2);
-       texpaint_project(ob, model, proj, v3, pv3);
-
-       /* do inverse view mapping, see gluProject man page */
-       h[0]= (co[0] - view[0])*2.0f/view[2] - 1;
-       h[1]= (co[1] - view[1])*2.0f/view[3] - 1;
-       h[2]= 1.0f;
-
-       /* solve for (w1,w2,w3)/perspdiv in:
-          h*perspdiv = Project*Model*(w1*v1 + w2*v2 + w3*v3) */
-
-       wmat[0][0]= pv1[0];  wmat[1][0]= pv2[0];  wmat[2][0]= pv3[0];
-       wmat[0][1]= pv1[1];  wmat[1][1]= pv2[1];  wmat[2][1]= pv3[1];
-       wmat[0][2]= pv1[3];  wmat[1][2]= pv2[3];  wmat[2][2]= pv3[3];
-
-       Mat3Inv(invwmat, wmat);
-       Mat3MulVecfl(invwmat, h);
-
-       VECCOPY(w, h);
-
-       /* w is still divided by perspdiv, make it sum to one */
-       divw= w[0] + w[1] + w[2];
-       if(divw != 0.0f)
-               VecMulf(w, 1.0f/divw);
-}
-
-/* compute uv coordinates of mouse in face */
-void texpaint_pick_uv(Scene *scene, Object *ob, Mesh *mesh, unsigned int faceindex, short *xy, float *uv)
-{
-       DerivedMesh *dm = mesh_get_derived_final(scene, ob, CD_MASK_BAREMESH);
-       int *index = dm->getFaceDataArray(dm, CD_ORIGINDEX);
-       MTFace *tface = dm->getFaceDataArray(dm, CD_MTFACE), *tf;
-       int numfaces = dm->getNumFaces(dm), a;
-       float p[2], w[3], absw, minabsw;
-       MFace mf;
-       MVert mv[4];
-
-       minabsw = 1e10;
-       uv[0] = uv[1] = 0.0;
-
-// XXX persp(PERSP_VIEW);
-
-       /* test all faces in the derivedmesh with the original index of the picked face */
-       for (a = 0; a < numfaces; a++) {
-               if (index[a] == faceindex) {
-                       dm->getFace(dm, a, &mf);
-
-                       dm->getVert(dm, mf.v1, &mv[0]);
-                       dm->getVert(dm, mf.v2, &mv[1]);
-                       dm->getVert(dm, mf.v3, &mv[2]);
-                       if (mf.v4)
-                               dm->getVert(dm, mf.v4, &mv[3]);
-
-                       tf= &tface[a];
-
-                       p[0]= xy[0];
-                       p[1]= xy[1];
-
-                       if (mf.v4) {
-                               /* the triangle with the largest absolute values is the one
-                                  with the most negative weights */
-                               texpaint_tri_weights(ob, mv[0].co, mv[1].co, mv[3].co, p, w);
-                               absw= fabs(w[0]) + fabs(w[1]) + fabs(w[2]);
-                               if(absw < minabsw) {
-                                       uv[0]= tf->uv[0][0]*w[0] + tf->uv[1][0]*w[1] + tf->uv[3][0]*w[2];
-                                       uv[1]= tf->uv[0][1]*w[0] + tf->uv[1][1]*w[1] + tf->uv[3][1]*w[2];
-                                       minabsw = absw;
-                               }
-
-                               texpaint_tri_weights(ob, mv[1].co, mv[2].co, mv[3].co, p, w);
-                               absw= fabs(w[0]) + fabs(w[1]) + fabs(w[2]);
-                               if (absw < minabsw) {
-                                       uv[0]= tf->uv[1][0]*w[0] + tf->uv[2][0]*w[1] + tf->uv[3][0]*w[2];
-                                       uv[1]= tf->uv[1][1]*w[0] + tf->uv[2][1]*w[1] + tf->uv[3][1]*w[2];
-                                       minabsw = absw;
-                               }
-                       }
-                       else {
-                               texpaint_tri_weights(ob, mv[0].co, mv[1].co, mv[2].co, p, w);
-                               absw= fabs(w[0]) + fabs(w[1]) + fabs(w[2]);
-                               if (absw < minabsw) {
-                                       uv[0]= tf->uv[0][0]*w[0] + tf->uv[1][0]*w[1] + tf->uv[2][0]*w[2];
-                                       uv[1]= tf->uv[0][1]*w[0] + tf->uv[1][1]*w[1] + tf->uv[2][1]*w[2];
-                                       minabsw = absw;
-                               }
-                       }
-               }
-       }
-
-       dm->release(dm);
-}
index e67e2d6e7a646d3f4a0d565a3c6a52def6decb12..49acfed859c64d333d88f3b91060e93ca555e654 100644 (file)
 
 /* ************** Exported Poll tests ********************** */
 
+int ED_operator_regionactive(bContext *C)
+{
+       if(CTX_wm_window(C)==NULL) return 0;
+       if(CTX_wm_screen(C)==NULL) return 0;
+       if(CTX_wm_region(C)==NULL) return 0;
+       return 1;
+}
+
 int ED_operator_areaactive(bContext *C)
 {
        if(CTX_wm_window(C)==NULL) return 0;
similarity index 98%
rename from source/blender/editors/sculpt/Makefile
rename to source/blender/editors/sculpt_paint/Makefile
index 6b5c8baf27aa676f031885f9235056ca7fb3ffd6..e810f7efbe4e6085686e23edcf64de28d1b9adae 100644 (file)
@@ -28,7 +28,7 @@
 #
 # Makes module object directory and bounces make to subdirectories.
 
-LIBNAME = ed_sculpt
+LIBNAME = ed_sculpt_paint
 DIR = $(OCGDIR)/blender/$(LIBNAME)
 
 include nan_compile.mk
similarity index 75%
rename from source/blender/editors/sculpt/SConscript
rename to source/blender/editors/sculpt_paint/SConscript
index 430563323dec75d1002c5315b39745a42abc6c5c..3e00453e049fad735114e0fa298e152b3bad79a6 100644 (file)
@@ -8,4 +8,4 @@ incs += ' ../../windowmanager #/intern/guardedalloc #/extern/glew/include'
 incs += ' ../../render/extern/include #/intern/guardedalloc #intern/bmfont'
 incs += ' ../../gpu ../../makesrna'
 
-env.BlenderLib ( 'bf_editors_sculpt', sources, Split(incs), [], libtype=['core'], priority=[40] )
\ No newline at end of file
+env.BlenderLib ( 'bf_editors_sculpt_paint', sources, Split(incs), [], libtype=['core'], priority=[40] )
diff --git a/source/blender/editors/sculpt_paint/paint_image.c b/source/blender/editors/sculpt_paint/paint_image.c
new file mode 100644 (file)
index 0000000..ee5aa1e
--- /dev/null
@@ -0,0 +1,5078 @@
+/**
+ * $Id$
+ * imagepaint.c
+ *
+ * Functions to paint images in 2D and 3D.
+ * 
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA       02111-1307, USA.
+ *
+ * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
+ * All rights reserved.
+ *
+ * The Original Code is: some of this file.
+ *
+ * Contributor(s): Jens Ole Wund (bjornmose), Campbell Barton (ideasman42)
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include <float.h>
+#include <string.h>
+#include <stdio.h>
+#include <math.h>
+
+#include "MEM_guardedalloc.h"
+
+#ifdef WIN32
+#include "BLI_winstuff.h"
+#endif
+#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 "BLI_threads.h"
+
+#include "IMB_imbuf.h"
+#include "IMB_imbuf_types.h"
+
+#include "DNA_brush_types.h"
+#include "DNA_image_types.h"
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_node_types.h"
+#include "DNA_object_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_screen_types.h"
+#include "DNA_space_types.h"
+#include "DNA_userdef_types.h"
+#include "DNA_view3d_types.h"
+#include "DNA_windowmanager_types.h"
+
+#include "BKE_context.h"
+#include "BKE_brush.h"
+#include "BKE_global.h"
+#include "BKE_image.h"
+#include "BKE_main.h"
+#include "BKE_mesh.h"
+#include "BKE_node.h"
+#include "BKE_utildefines.h"
+#include "BKE_DerivedMesh.h"
+#include "BKE_report.h"
+#include "BKE_depsgraph.h"
+
+#include "BIF_gl.h"
+#include "BIF_glutil.h"
+
+#include "UI_interface.h"
+#include "UI_view2d.h"
+
+#include "ED_image.h"
+#include "ED_object.h"
+#include "ED_screen.h"
+#include "ED_view3d.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+
+#include "GPU_draw.h"
+
+#include "paint_intern.h"
+
+/* Defines and Structs */
+
+#define IMAPAINT_CHAR_TO_FLOAT(c) ((c)/255.0f)
+
+#define IMAPAINT_FLOAT_RGB_TO_CHAR(c, f) { (c)[0]=FTOCHAR((f)[0]); (c)[1]=FTOCHAR((f)[1]); (c)[2]=FTOCHAR((f)[2]); }
+#define IMAPAINT_FLOAT_RGBA_TO_CHAR(c, f) { (c)[0]=FTOCHAR((f)[0]); (c)[1]=FTOCHAR((f)[1]); (c)[2]=FTOCHAR((f)[2]); (c)[3]=FTOCHAR((f)[3]); }
+
+#define IMAPAINT_CHAR_RGB_TO_FLOAT(f, c) { (f)[0]=IMAPAINT_CHAR_TO_FLOAT((c)[0]); (f)[1]=IMAPAINT_CHAR_TO_FLOAT((c)[1]); (f)[2]=IMAPAINT_CHAR_TO_FLOAT((c)[2]); }
+#define IMAPAINT_CHAR_RGBA_TO_FLOAT(f, c) { (f)[0]=IMAPAINT_CHAR_TO_FLOAT((c)[0]); (f)[1]=IMAPAINT_CHAR_TO_FLOAT((c)[1]); (f)[2]=IMAPAINT_CHAR_TO_FLOAT((c)[2]); (f)[3]=IMAPAINT_CHAR_TO_FLOAT((c)[3]); }
+#define IMAPAINT_FLOAT_RGB_COPY(a, b) VECCOPY(a, b)
+
+#define IMAPAINT_TILE_BITS                     6
+#define IMAPAINT_TILE_SIZE                     (1 << IMAPAINT_TILE_BITS)
+#define IMAPAINT_TILE_NUMBER(size)     (((size)+IMAPAINT_TILE_SIZE-1) >> IMAPAINT_TILE_BITS)
+
+#define MAXUNDONAME    64
+
+static void imapaint_image_update(SpaceImage *sima, Image *image, ImBuf *ibuf, short texpaint);
+
+
+typedef struct ImagePaintState {
+       SpaceImage *sima;
+       View2D *v2d;
+       Scene *scene;
+       bScreen *screen;
+
+       Brush *brush;
+       short tool, blend;
+       Image *image;
+       ImBuf *canvas;
+       ImBuf *clonecanvas;
+       short clonefreefloat;
+       char *warnpackedfile;
+       char *warnmultifile;
+
+       /* texture paint only */
+       Object *ob;
+       Mesh *me;
+       int faceindex;
+       float uv[2];
+} ImagePaintState;
+
+typedef struct ImagePaintPartialRedraw {
+       int x1, y1, x2, y2;
+       int enabled;
+} ImagePaintPartialRedraw;
+
+
+/* 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.
+ * 
+ * When 3 - a brush should have ~9 buckets under it at once
+ * ...this helps for threading while painting as well as
+ * avoiding initializing pixels that wont touch the brush */
+#define PROJ_BUCKET_BRUSH_DIV 4
+
+#define PROJ_BUCKET_RECT_MIN 4
+#define PROJ_BUCKET_RECT_MAX 256
+
+#define PROJ_BOUNDBOX_DIV 8
+#define PROJ_BOUNDBOX_SQUARED  (PROJ_BOUNDBOX_DIV * PROJ_BOUNDBOX_DIV)
+
+//#define PROJ_DEBUG_PAINT 1
+//#define PROJ_DEBUG_NOSEAMBLEED 1
+//#define PROJ_DEBUG_PRINT_CLIP 1
+#define PROJ_DEBUG_WINCLIP 1
+
+/* projectFaceSeamFlags 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_SEAM1        1<<0    /* If this face has a seam on any of its edges */
+#define PROJ_FACE_SEAM2        1<<1
+#define PROJ_FACE_SEAM3        1<<2
+#define PROJ_FACE_SEAM4        1<<3
+
+#define PROJ_FACE_NOSEAM1      1<<4
+#define PROJ_FACE_NOSEAM2      1<<5
+#define PROJ_FACE_NOSEAM3      1<<6
+#define PROJ_FACE_NOSEAM4      1<<7
+
+/* a slightly scaled down face is used to get fake 3D location for edge pixels in the seams
+ * as this number approaches  1.0f the likelihood increases of float precision errors where
+ * it is occluded by an adjacent face */
+#define PROJ_FACE_SCALE_SEAM   0.99f
+
+#define PROJ_BUCKET_NULL               0
+#define PROJ_BUCKET_INIT               1<<0
+// #define PROJ_BUCKET_CLONE_INIT      1<<1
+
+/* used for testing doubles, if a point is on a line etc */
+#define PROJ_GEOM_TOLERANCE 0.0002f
+
+/* vert flags */
+#define PROJ_VERT_CULL 1
+
+#define PI_80_DEG ((M_PI_2 / 9) * 8)
+
+/* 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 ProjPaintImage {
+       Image *ima;
+       ImBuf *ibuf;
+       ImagePaintPartialRedraw *partRedrawRect;
+       struct UndoTile **undoRect; /* only used to build undo tiles after painting */
+       int touch;
+} ProjPaintImage;
+
+/* Main projection painting struct passed to all projection painting functions */
+typedef struct ProjPaintState {
+       View3D *v3d;
+       RegionView3D *rv3d;
+       ARegion *ar;
+       Scene *scene;
+
+       Brush *brush;
+       short tool, blend;
+       Object *ob;
+       /* end similarities with ImagePaintState */
+       
+       DerivedMesh    *dm;
+       int                     dm_totface;
+       int                     dm_totvert;
+       
+       MVert              *dm_mvert;
+       MFace              *dm_mface;
+       MTFace             *dm_mtface;
+       MTFace             *dm_mtface_clone;    /* other UV layer, use for cloning between layers */
+       MTFace             *dm_mtface_mask;
+       
+       /* projection painting only */
+       MemArena *arena_mt[BLENDER_MAX_THREADS];/* for multithreading, the first item is sometimes used for non threaded cases too */
+       LinkNode **bucketRect;                          /* screen sized 2D array, each pixel has a linked list of ProjPixel's */
+       LinkNode **bucketFaces;                         /* bucketRect aligned array linkList of faces overlapping each bucket */
+       unsigned char *bucketFlags;                                     /* store if the bucks have been initialized  */
+#ifndef PROJ_DEBUG_NOSEAMBLEED
+       char *faceSeamFlags;                            /* store info about faces, if they are initialized etc*/
+       float (*faceSeamUVs)[4][2];                     /* expanded UVs for faces to use as seams */
+       LinkNode **vertFaces;                           /* Only needed for when seam_bleed_px is enabled, use to find UV seams */
+       char *vertFlags;                                        /* store options per vert, now only store if the vert is pointing away from the view */
+#endif
+       int buckets_x;                                          /* The size of the bucket grid, the grid span's screenMin/screenMax so you can paint outsize the screen or with 2 brushes at once */
+       int buckets_y;
+       
+       ProjPaintImage *projImages;
+       
+       int image_tot;                          /* size of projectImages array */
+       
+       float (*screenCoords)[4];       /* verts projected into floating point screen space */
+       
+       float screenMin[2];                     /* 2D bounds for mesh verts on the screen's plane (screenspace) */
+       float screenMax[2]; 
+       float screen_width;                     /* Calculated from screenMin & screenMax */
+       float screen_height;
+       
+       /* options for projection painting */
+       int do_layer_clone;
+       int do_layer_mask;
+       int do_layer_mask_inv;
+       
+       short do_occlude;                       /* Use raytraced occlusion? - ortherwise will paint right through to the back*/
+       short do_backfacecull;  /* ignore faces with normals pointing away, skips a lot of raycasts if your normals are correctly flipped */
+       short do_mask_normal;                   /* mask out pixels based on their normals */
+       float normal_angle;                             /* what angle to mask at*/
+       float normal_angle_inner;
+       float normal_angle_range;               /* difference between normal_angle and normal_angle_inner, for easy access */
+       
+       short is_ortho;
+       short is_airbrush;                                      /* only to avoid using (ps.brush->flag & BRUSH_AIRBRUSH) */
+       short is_texbrush;                                      /* only to avoid running  */
+#ifndef PROJ_DEBUG_NOSEAMBLEED
+       float seam_bleed_px;
+#endif
+       /* clone vars */
+       float cloneOffset[2];
+       
+       float projectMat[4][4];         /* Projection matrix, use for getting screen coords */
+       float viewMat[4][4];
+       float viewDir[3];                       /* View vector, use for do_backfacecull and for ray casting with an ortho viewport  */
+       float viewPos[3];                       /* View location in object relative 3D space, so can compare to verts  */
+       float clipsta, clipend;
+       
+       /* threads */
+       int thread_tot;
+       int bucketMin[2];
+       int bucketMax[2];
+       int context_bucket_x, context_bucket_y; /* must lock threads while accessing these */
+} ProjPaintState;
+
+typedef union pixelPointer
+{
+       float *f_pt;                    /* float buffer */
+       unsigned int *uint_pt; /* 2 ways to access a char buffer */
+       unsigned char *ch_pt;
+} PixelPointer;
+
+typedef union pixelStore
+{
+       unsigned char ch[4];
+       unsigned int uint;
+       float f[4];
+} PixelStore;
+
+typedef struct ProjPixel {
+       float projCoSS[2]; /* the floating point screen projection of this pixel */
+       
+       /* Only used when the airbrush is disabled.
+        * Store the max mask value to avoid painting over an area with a lower opacity
+        * with an advantage that we can avoid touching the pixel at all, if the 
+        * new mask value is lower then mask_max */
+       unsigned short mask_max;
+       
+       /* for various reasons we may want to mask out painting onto this pixel */
+       unsigned short mask;
+       
+       short x_px, y_px;
+       
+       PixelStore origColor;
+       PixelStore newColor;
+       PixelPointer pixel;
+       
+       short image_index; /* if anyone wants to paint onto more then 32768 images they can bite me */
+       unsigned char bb_cell_index;
+} ProjPixel;
+
+typedef struct ProjPixelClone {
+       struct ProjPixel __pp;
+       PixelStore clonepx;
+} ProjPixelClone;
+
+/* Finish projection painting structs */
+
+
+typedef struct UndoTile {
+       struct UndoTile *next, *prev;
+       ID id;
+       void *rect;
+       int x, y;
+} UndoTile;
+
+typedef struct UndoElem {
+       struct UndoElem *next, *prev;
+       char name[MAXUNDONAME];
+       unsigned long undosize;
+
+       ImBuf *ibuf;
+       ListBase tiles;
+} UndoElem;
+
+static ListBase undobase = {NULL, NULL};
+static UndoElem *curundo = NULL;
+static ImagePaintPartialRedraw imapaintpartial = {0, 0, 0, 0, 0};
+
+/* UNDO */
+
+/* internal functions */
+
+static void undo_copy_tile(UndoTile *tile, ImBuf *tmpibuf, ImBuf *ibuf, int restore)
+{
+       /* copy or swap contents of tile->rect and region in ibuf->rect */
+       IMB_rectcpy(tmpibuf, ibuf, 0, 0, tile->x*IMAPAINT_TILE_SIZE,
+               tile->y*IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE);
+
+       if(ibuf->rect_float) {
+               SWAP(void*, tmpibuf->rect_float, tile->rect);
+       } else {
+               SWAP(void*, tmpibuf->rect, tile->rect);
+       }
+       
+       if(restore)
+               IMB_rectcpy(ibuf, tmpibuf, tile->x*IMAPAINT_TILE_SIZE,
+                       tile->y*IMAPAINT_TILE_SIZE, 0, 0, IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE);
+}
+
+static UndoTile *undo_init_tile(ID *id, ImBuf *ibuf, ImBuf **tmpibuf, int x_tile, int y_tile)
+{
+       UndoTile *tile;
+       int allocsize;
+       
+       if (*tmpibuf==NULL)
+               *tmpibuf = IMB_allocImBuf(IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE, 32, IB_rectfloat|IB_rect, 0);
+       
+       tile= MEM_callocN(sizeof(UndoTile), "ImaUndoTile");
+       tile->id= *id;
+       tile->x= x_tile;
+       tile->y= y_tile;
+
+       allocsize= IMAPAINT_TILE_SIZE*IMAPAINT_TILE_SIZE*4;
+       allocsize *= (ibuf->rect_float)? sizeof(float): sizeof(char);
+       tile->rect= MEM_mapallocN(allocsize, "ImaUndoRect");
+
+       undo_copy_tile(tile, *tmpibuf, ibuf, 0);
+       curundo->undosize += allocsize;
+
+       BLI_addtail(&curundo->tiles, tile);
+       
+       return tile;
+}
+
+static void undo_restore(UndoElem *undo)
+{
+       Image *ima = NULL;
+       ImBuf *ibuf, *tmpibuf;
+       UndoTile *tile;
+
+       if(!undo)
+               return;
+
+       tmpibuf= IMB_allocImBuf(IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE, 32,
+                               IB_rectfloat|IB_rect, 0);
+       
+       for(tile=undo->tiles.first; tile; tile=tile->next) {
+               /* find image based on name, pointer becomes invalid with global undo */
+               if(ima && strcmp(tile->id.name, ima->id.name)==0);
+               else {
+                       for(ima=G.main->image.first; ima; ima=ima->id.next)
+                               if(strcmp(tile->id.name, ima->id.name)==0)
+                                       break;
+               }
+
+               ibuf= BKE_image_get_ibuf(ima, NULL);
+
+               if (!ima || !ibuf || !(ibuf->rect || ibuf->rect_float))
+                       continue;
+
+               undo_copy_tile(tile, tmpibuf, ibuf, 1);
+
+               GPU_free_image(ima); /* force OpenGL reload */
+               if(ibuf->rect_float)
+                       imb_freerectImBuf(ibuf); /* force recreate of char rect */
+       }
+
+       IMB_freeImBuf(tmpibuf);
+}
+
+static void undo_free(UndoElem *undo)
+{
+       UndoTile *tile;
+
+       for(tile=undo->tiles.first; tile; tile=tile->next)
+               MEM_freeN(tile->rect);
+       BLI_freelistN(&undo->tiles);
+}
+
+static void undo_imagepaint_push_begin(char *name)
+{
+       UndoElem *uel;
+       int nr;
+       
+       /* Undo push is split up in begin and end, the reason is that as painting
+        * happens more tiles are added to the list, and at the very end we know
+        * how much memory the undo used to remove old undo elements */
+
+       /* remove all undos after (also when curundo==NULL) */
+       while(undobase.last != curundo) {
+               uel= undobase.last;
+               undo_free(uel);
+               BLI_freelinkN(&undobase, uel);
+       }
+       
+       /* make new */
+       curundo= uel= MEM_callocN(sizeof(UndoElem), "undo file");
+       BLI_addtail(&undobase, uel);
+
+       /* name can be a dynamic string */
+       strncpy(uel->name, name, MAXUNDONAME-1);
+       
+       /* limit amount to the maximum amount*/
+       nr= 0;
+       uel= undobase.last;
+       while(uel) {
+               nr++;
+               if(nr==U.undosteps) break;
+               uel= uel->prev;
+       }
+       if(uel) {
+               while(undobase.first!=uel) {
+                       UndoElem *first= undobase.first;
+                       undo_free(first);
+                       BLI_freelinkN(&undobase, first);
+               }
+       }
+}
+
+static void undo_imagepaint_push_end()
+{
+       UndoElem *uel;
+       unsigned long totmem, maxmem;
+
+       if(U.undomemory != 0) {
+               /* limit to maximum memory (afterwards, we can't know in advance) */
+               totmem= 0;
+               maxmem= ((unsigned long)U.undomemory)*1024*1024;
+
+               uel= undobase.last;
+               while(uel) {
+                       totmem+= uel->undosize;
+                       if(totmem>maxmem) break;
+                       uel= uel->prev;
+               }
+
+               if(uel) {
+                       while(undobase.first!=uel) {
+                               UndoElem *first= undobase.first;
+                               undo_free(first);
+                               BLI_freelinkN(&undobase, first);
+                       }
+               }
+       }
+}
+
+void undo_imagepaint_step(int step)
+{
+       UndoElem *undo;
+
+       if(step==1) {
+               if(curundo==NULL);
+               else {
+                       if(G.f & G_DEBUG) printf("undo %s\n", curundo->name);
+                       undo_restore(curundo);
+                       curundo= curundo->prev;
+               }
+       }
+       else if(step==-1) {
+               if((curundo!=NULL && curundo->next==NULL) || undobase.first==NULL);
+               else {
+                       undo= (curundo && curundo->next)? curundo->next: undobase.first;
+                       undo_restore(undo);
+                       curundo= undo;
+                       if(G.f & G_DEBUG) printf("redo %s\n", undo->name);
+               }
+       }
+}
+
+void undo_imagepaint_clear(void)
+{
+       UndoElem *uel;
+       
+       uel= undobase.first;
+       while(uel) {
+               undo_free(uel);
+               uel= uel->next;
+       }
+
+       BLI_freelistN(&undobase);
+       curundo= NULL;
+}
+
+/* fast projection bucket array lookup, use the safe version for bound checking  */
+static int project_bucket_offset(const ProjPaintState *ps, const float projCoSS[2])
+{
+       /* If we were not dealing with screenspace 2D coords we could simple do...
+        * ps->bucketRect[x + (y*ps->buckets_y)] */
+       
+       /* please explain?
+        * projCoSS[0] - ps->screenMin[0]       : zero origin
+        * ... / ps->screen_width                               : range from 0.0 to 1.0
+        * ... * ps->buckets_x          : use as a bucket index
+        *
+        * Second multiplication does similar but for vertical offset
+        */
+       return  (       (int)(((projCoSS[0] - ps->screenMin[0]) / ps->screen_width)  * ps->buckets_x)) + 
+               (       (       (int)(((projCoSS[1] - ps->screenMin[1])  / ps->screen_height) * ps->buckets_y)) * ps->buckets_x);
+}
+
+static int project_bucket_offset_safe(const ProjPaintState *ps, const float projCoSS[2])
+{
+       int bucket_index = project_bucket_offset(ps, projCoSS);
+       
+       if (bucket_index < 0 || bucket_index >= ps->buckets_x*ps->buckets_y) {  
+               return -1;
+       }
+       else {
+               return bucket_index;
+       }
+}
+
+#define SIDE_OF_LINE(pa, pb, pp)       ((pa[0]-pp[0])*(pb[1]-pp[1]))-((pb[0]-pp[0])*(pa[1]-pp[1]))
+
+static float AreaSignedF2Dfl(float *v1, float *v2, float *v3)
+{
+   return (float)(0.5f*((v1[0]-v2[0])*(v2[1]-v3[1]) +
+(v1[1]-v2[1])*(v3[0]-v2[0])));
+}
+
+static void BarycentricWeights2f(float pt[2], float v1[2], float v2[2], float v3[2], float w[3])
+{
+   float wtot_inv, wtot;
+
+   w[0] = AreaSignedF2Dfl(v2, v3, pt);
+   w[1] = AreaSignedF2Dfl(v3, v1, pt);
+   w[2] = AreaSignedF2Dfl(v1, v2, pt);
+   wtot = w[0]+w[1]+w[2];
+
+   if (wtot != 0.0f) {
+       wtot_inv = 1.0f/wtot;
+
+       w[0] = w[0]*wtot_inv;
+       w[1] = w[1]*wtot_inv;
+       w[2] = w[2]*wtot_inv;
+   }
+   else /* dummy values for zero area face */
+       w[0] = w[1] = w[2] = 1.0f/3.0f;
+}
+
+/* still use 2D X,Y space but this works for verts transformed by a perspective matrix, using their 4th component as a weight */
+static void BarycentricWeightsPersp2f(float pt[2], float v1[4], float v2[4], float v3[4], float w[3])
+{
+   float wtot_inv, wtot;
+
+   w[0] = AreaSignedF2Dfl(v2, v3, pt) / v1[3];
+   w[1] = AreaSignedF2Dfl(v3, v1, pt) / v2[3];
+   w[2] = AreaSignedF2Dfl(v1, v2, pt) / v3[3];
+   wtot = w[0]+w[1]+w[2];
+
+   if (wtot != 0.0f) {
+       wtot_inv = 1.0f/wtot;
+
+       w[0] = w[0]*wtot_inv;
+       w[1] = w[1]*wtot_inv;
+       w[2] = w[2]*wtot_inv;
+   }
+   else /* dummy values for zero area face */
+       w[0] = w[1] = w[2] = 1.0f/3.0f;
+}
+
+static void VecWeightf(float p[3], const float v1[3], const float v2[3], const float v3[3], const float w[3])
+{
+       p[0] = v1[0]*w[0] + v2[0]*w[1] + v3[0]*w[2];
+       p[1] = v1[1]*w[0] + v2[1]*w[1] + v3[1]*w[2];
+       p[2] = v1[2]*w[0] + v2[2]*w[1] + v3[2]*w[2];
+}
+
+static void Vec2Weightf(float p[2], const float v1[2], const float v2[2], const float v3[2], const float w[3])
+{
+       p[0] = v1[0]*w[0] + v2[0]*w[1] + v3[0]*w[2];
+       p[1] = v1[1]*w[0] + v2[1]*w[1] + v3[1]*w[2];
+}
+
+static float VecZDepthOrtho(float pt[2], float v1[3], float v2[3], float v3[3], float w[3])
+{
+       BarycentricWeights2f(pt, v1, v2, v3, w);
+       return (v1[2]*w[0]) + (v2[2]*w[1]) + (v3[2]*w[2]);
+}
+
+static float VecZDepthPersp(float pt[2], float v1[3], float v2[3], float v3[3], float w[3])
+{
+       BarycentricWeightsPersp2f(pt, v1, v2, v3, w);
+       return (v1[2]*w[0]) + (v2[2]*w[1]) + (v3[2]*w[2]);
+}
+
+
+/* Return the top-most face index that the screen space coord 'pt' touches (or -1) */
+static int project_paint_PickFace(const ProjPaintState *ps, float pt[2], float w[3], int *side)
+{
+       LinkNode *node;
+       float w_tmp[3];
+       float *v1, *v2, *v3, *v4;
+       int bucket_index;
+       int face_index;
+       int best_side = -1;
+       int best_face_index = -1;
+       float z_depth_best = FLT_MAX, z_depth;
+       MFace *mf;
+       
+       bucket_index = project_bucket_offset_safe(ps, pt);
+       if (bucket_index==-1)
+               return -1;
+       
+       
+       
+       /* we could return 0 for 1 face buckets, as long as this function assumes
+        * that the point its testing is only every originated from an existing face */
+       
+       for (node= ps->bucketFaces[bucket_index]; node; node= node->next) {
+               face_index = GET_INT_FROM_POINTER(node->link);
+               mf= ps->dm_mface + face_index;
+               
+               v1= ps->screenCoords[mf->v1];
+               v2= ps->screenCoords[mf->v2];
+               v3= ps->screenCoords[mf->v3];
+               
+               if (IsectPT2Df(pt, v1, v2, v3)) {
+                       if (ps->is_ortho)       z_depth= VecZDepthOrtho(pt, v1, v2, v3, w_tmp);
+                       else                            z_depth= VecZDepthPersp(pt, v1, v2, v3, w_tmp);
+                       
+                       if (z_depth < z_depth_best) {
+                               best_face_index = face_index;
+                               best_side = 0;
+                               z_depth_best = z_depth;
+                               VECCOPY(w, w_tmp);
+                       }
+               }
+               else if (mf->v4) {
+                       v4= ps->screenCoords[mf->v4];
+                       
+                       if (IsectPT2Df(pt, v1, v3, v4)) {
+                               if (ps->is_ortho)       z_depth= VecZDepthOrtho(pt, v1, v3, v4, w_tmp);
+                               else                            z_depth= VecZDepthPersp(pt, v1, v3, v4, w_tmp);
+
+                               if (z_depth < z_depth_best) {
+                                       best_face_index = face_index;
+                                       best_side= 1;
+                                       z_depth_best = z_depth;
+                                       VECCOPY(w, w_tmp);
+                               }
+                       }
+               }
+       }
+       
+       *side = best_side;
+       return best_face_index; /* will be -1 or a valid face */
+}
+
+/* Converts a uv coord into a pixel location wrapping if the uv is outside 0-1 range */
+static void uvco_to_wrapped_pxco(float uv[2], int ibuf_x, int ibuf_y, float *x, float *y)
+{
+       /* use */
+       *x = (float)fmod(uv[0], 1.0f);
+       *y = (float)fmod(uv[1], 1.0f);
+       
+       if (*x < 0.0f) *x += 1.0f;
+       if (*y < 0.0f) *y += 1.0f;
+       
+       *x = *x * ibuf_x - 0.5f;
+       *y = *y * ibuf_y - 0.5f;
+}
+
+/* Set the top-most face color that the screen space coord 'pt' touches (or return 0 if none touch) */
+static int project_paint_PickColor(const ProjPaintState *ps, float pt[2], float *rgba_fp, unsigned char *rgba, const int interp)
+{
+       float w[3], uv[2];
+       int side;
+       int face_index;
+       MTFace *tf;
+       ImBuf *ibuf;
+       int xi, yi;
+       
+       
+       face_index = project_paint_PickFace(ps, pt, w, &side);
+       
+       if (face_index == -1)
+               return 0;
+       
+       tf = ps->dm_mtface + face_index;
+       
+       if (side == 0) {
+               Vec2Weightf(uv, tf->uv[0], tf->uv[1], tf->uv[2], w);
+       }
+       else { /* QUAD */
+               Vec2Weightf(uv, tf->uv[0], tf->uv[2], tf->uv[3], w);
+       }
+       
+       ibuf = BKE_image_get_ibuf((Image *)tf->tpage, NULL); /* TODO - this may be slow, the only way around it is to have an ibuf index per face */
+       
+
+       
+       if (interp) {
+               float x, y;
+               uvco_to_wrapped_pxco(uv, ibuf->x, ibuf->y, &x, &y);
+               
+               if (ibuf->rect_float) {
+                       if (rgba_fp) {
+                               bilinear_interpolation_color(ibuf, NULL, rgba_fp, x, y);
+                       }
+                       else {
+                               float rgba_tmp_f[4];
+                               bilinear_interpolation_color(ibuf, NULL, rgba_tmp_f, x, y);
+                               IMAPAINT_FLOAT_RGBA_TO_CHAR(rgba, rgba_tmp_f);
+                       }
+               }
+               else {
+                       if (rgba) {
+                               bilinear_interpolation_color(ibuf, rgba, NULL, x, y);
+                       }
+                       else {
+                               unsigned char rgba_tmp[4];
+                               bilinear_interpolation_color(ibuf, rgba_tmp, NULL, x, y);
+                               IMAPAINT_CHAR_RGBA_TO_FLOAT(rgba_fp, rgba_tmp);
+                       }
+               }
+       }
+       else {
+               xi = (uv[0]*ibuf->x) + 0.5f;
+               yi = (uv[1]*ibuf->y) + 0.5f;
+               
+               //if (xi<0 || xi>=ibuf->x  ||  yi<0 || yi>=ibuf->y) return 0;
+               
+               /* wrap */
+               xi = ((int)(uv[0]*ibuf->x)) % ibuf->x;
+               if (xi<0) xi += ibuf->x;
+               yi = ((int)(uv[1]*ibuf->y)) % ibuf->y;
+               if (yi<0) yi += ibuf->y;
+               
+               
+               if (rgba) {
+                       if (ibuf->rect_float) {
+                               float *rgba_tmp_fp = ibuf->rect_float + (xi + yi * ibuf->x * 4);
+                               IMAPAINT_FLOAT_RGBA_TO_CHAR(rgba, rgba_tmp_fp);
+                       }
+                       else {
+                               *((unsigned int *)rgba) = *(unsigned int *)(((char *)ibuf->rect) + ((xi + yi * ibuf->x) * 4));
+                       }
+               }
+               
+               if (rgba_fp) {
+                       if (ibuf->rect_float) {
+                               QUATCOPY(rgba_fp, ((float *)ibuf->rect_float + ((xi + yi * ibuf->x) * 4)));
+                       }
+                       else {
+                               char *tmp_ch= ((char *)ibuf->rect) + ((xi + yi * ibuf->x) * 4);
+                               IMAPAINT_CHAR_RGBA_TO_FLOAT(rgba_fp, tmp_ch);
+                       }
+               }
+       }
+       return 1;
+}
+
+/* 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
+    2  : occluded with w[3] weights set (need to know in some cases) */
+
+static int project_paint_occlude_ptv(float pt[3], float v1[3], float v2[3], float v3[3], float w[3], int is_ortho)
+{
+       /* if all are behind us, return false */
+       if(v1[2] > pt[2] && v2[2] > pt[2] && v3[2] > pt[2])
+               return 0;
+               
+       /* do a 2D point in try intersection */
+       if (!IsectPT2Df(pt, v1, v2, v3))
+               return 0; /* we know there is  */
+       
+
+       /* From here on we know there IS an intersection */
+       /* if ALL of the verts are infront of us then we know it intersects ? */
+       if(v1[2] < pt[2] && v2[2] < pt[2] && v3[2] < pt[2]) {
+               return 1;
+       }
+       else {
+               /* we intersect? - find the exact depth at the point of intersection */
+               /* Is this point is occluded by another face? */
+               if (is_ortho) {
+                       if (VecZDepthOrtho(pt, v1, v2, v3, w) < pt[2]) return 2;
+               }
+               else {
+                       if (VecZDepthPersp(pt, v1, v2, v3, w) < pt[2]) return 2;
+               }
+       }
+       return -1;
+}
+
+
+static int project_paint_occlude_ptv_clip(
+               const ProjPaintState *ps, const MFace *mf,
+               float pt[3], float v1[3], float v2[3], float v3[3],
+               const int side )
+{
+       float w[3], wco[3];
+       int ret = project_paint_occlude_ptv(pt, v1, v2, v3, w, ps->is_ortho);
+
+       if (ret <= 0)
+               return ret;
+
+       if (ret==1) { /* weights not calculated */
+               if (ps->is_ortho)       BarycentricWeights2f(pt, v1, v2, v3, w);
+               else                            BarycentricWeightsPersp2f(pt, v1, v2, v3, w);
+       }
+
+       /* Test if we're in the clipped area, */
+       if (side)       VecWeightf(wco, ps->dm_mvert[mf->v1].co, ps->dm_mvert[mf->v3].co, ps->dm_mvert[mf->v4].co, w);
+       else            VecWeightf(wco, ps->dm_mvert[mf->v1].co, ps->dm_mvert[mf->v2].co, ps->dm_mvert[mf->v3].co, w);
+       
+       Mat4MulVecfl(ps->ob->obmat, wco);
+       if(!view3d_test_clipping(ps->rv3d, wco)) {
+               return 1;
+       }
+       
+       return -1;
+}
+
+
+/* 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(const ProjPaintState *ps, LinkNode *bucketFace, const int orig_face, float pixelScreenCo[4])
+{
+       MFace *mf;
+       int face_index;
+       int isect_ret;
+       float w[3]; /* not needed when clipping */
+       
+       /* we could return 0 for 1 face buckets, as long as this function assumes
+        * that the point its testing is only every originated from an existing face */
+
+       for (; bucketFace; bucketFace = bucketFace->next) {
+               face_index = GET_INT_FROM_POINTER(bucketFace->link);
+
+               if (orig_face != face_index) {
+                       mf = ps->dm_mface + face_index;
+                       if(ps->rv3d->rflag & RV3D_CLIPPING)
+                               isect_ret = project_paint_occlude_ptv_clip(ps, mf, pixelScreenCo, ps->screenCoords[mf->v1], ps->screenCoords[mf->v2], ps->screenCoords[mf->v3], 0);
+                       else
+                               isect_ret = project_paint_occlude_ptv(pixelScreenCo, ps->screenCoords[mf->v1], ps->screenCoords[mf->v2], ps->screenCoords[mf->v3], w, ps->is_ortho);
+
+                       /* Note, if isect_ret==-1 then we dont want to test the other side of the quad */
+                       if (isect_ret==0 && mf->v4) {
+                               if(ps->rv3d->rflag & RV3D_CLIPPING)
+                                       isect_ret = project_paint_occlude_ptv_clip(ps, mf, pixelScreenCo, ps->screenCoords[mf->v1], ps->screenCoords[mf->v3], ps->screenCoords[mf->v4], 1);
+                               else
+                                       isect_ret = project_paint_occlude_ptv(pixelScreenCo, ps->screenCoords[mf->v1], ps->screenCoords[mf->v3], ps->screenCoords[mf->v4], w, ps->is_ortho);
+                       }
+                       if (isect_ret==1) {
+                               /* TODO - we may want to cache the first hit,
+                                * it is not possible to swap the face order in the list anymore */
+                               return 1;
+                       }
+               }
+       }
+       return 0;
+}
+
+/* basic line intersection, could move to arithb.c, 2 points with a horiz line
+ * 1 for an intersection, 2 if the first point is aligned, 3 if the second point is aligned */
+#define ISECT_TRUE 1
+#define ISECT_TRUE_P1 2
+#define ISECT_TRUE_P2 3
+static int line_isect_y(const float p1[2], const float p2[2], const float y_level, float *x_isect)
+{
+       float y_diff;
+       
+       if (y_level==p1[1]) { /* are we touching the first point? - no interpolation needed */
+               *x_isect = p1[0];
+               return ISECT_TRUE_P1;
+       }
+       if (y_level==p2[1]) { /* are we touching the second point? - no interpolation needed */
+               *x_isect = p2[0];
+               return ISECT_TRUE_P2;
+       }
+       
+       y_diff= fabs(p1[1]-p2[1]); /* yuck, horizontal line, we cant do much here */
+       
+       if (y_diff < 0.000001f) {
+               *x_isect = (p1[0]+p2[0]) * 0.5f;
+               return ISECT_TRUE;              
+       }
+       
+       if (p1[1] > y_level && p2[1] < y_level) {
+               *x_isect = (p2[0]*(p1[1]-y_level) + p1[0]*(y_level-p2[1])) / y_diff;  /*(p1[1]-p2[1]);*/
+               return ISECT_TRUE;
+       }
+       else if (p1[1] < y_level && p2[1] > y_level) {
+               *x_isect = (p2[0]*(y_level-p1[1]) + p1[0]*(p2[1]-y_level)) / y_diff;  /*(p2[1]-p1[1]);*/
+               return ISECT_TRUE;
+       }
+       else {
+               return 0;
+       }
+}
+
+static int line_isect_x(const float p1[2], const float p2[2], const float x_level, float *y_isect)
+{
+       float x_diff;
+       
+       if (x_level==p1[0]) { /* are we touching the first point? - no interpolation needed */
+               *y_isect = p1[1];
+               return ISECT_TRUE_P1;
+       }
+       if (x_level==p2[0]) { /* are we touching the second point? - no interpolation needed */
+               *y_isect = p2[1];
+               return ISECT_TRUE_P2;
+       }
+       
+       x_diff= fabs(p1[0]-p2[0]); /* yuck, horizontal line, we cant do much here */
+       
+       if (x_diff < 0.000001) { /* yuck, vertical line, we cant do much here */
+               *y_isect = (p1[0]+p2[0]) * 0.5f;
+               return ISECT_TRUE;              
+       }
+       
+       if (p1[0] > x_level && p2[0] < x_level) {
+               *y_isect = (p2[1]*(p1[0]-x_level) + p1[1]*(x_level-p2[0])) / x_diff; /*(p1[0]-p2[0]);*/
+               return ISECT_TRUE;
+       }
+       else if (p1[0] < x_level && p2[0] > x_level) {
+               *y_isect = (p2[1]*(x_level-p1[0]) + p1[1]*(p2[0]-x_level)) / x_diff; /*(p2[0]-p1[0]);*/
+               return ISECT_TRUE;
+       }
+       else {
+               return 0;
+       }
+}
+
+/* simple func use for comparing UV locations to check if there are seams.
+ * Its possible this gives incorrect results, when the UVs for 1 face go into the next 
+ * tile, but do not do this for the adjacent face, it could return a false positive.
+ * This is so unlikely that Id not worry about it. */
+static int cmp_uv(const float vec2a[2], const float vec2b[2])
+{
+       /* if the UV's are not between 0.0 and 1.0 */
+       float xa = (float)fmod(vec2a[0], 1.0f);
+       float ya = (float)fmod(vec2a[1], 1.0f);
+       
+       float xb = (float)fmod(vec2b[0], 1.0f);
+       float yb = (float)fmod(vec2b[1], 1.0f); 
+       
+       if (xa < 0.0f) xa += 1.0f;
+       if (ya < 0.0f) ya += 1.0f;
+       
+       if (xb < 0.0f) xb += 1.0f;
+       if (yb < 0.0f) yb += 1.0f;
+       
+       return ((fabs(xa-xb) < PROJ_GEOM_TOLERANCE) && (fabs(ya-yb) < PROJ_GEOM_TOLERANCE)) ? 1:0;
+}
+
+
+/* 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 pixel_bounds_uv(
+               const float uv1[2], const float uv2[2], const float uv3[2], const float uv4[2],
+               rcti *bounds_px,
+               const int ibuf_x, const int ibuf_y,
+               int is_quad
+) {
+       float min_uv[2], max_uv[2]; /* UV bounds */
+       
+       INIT_MINMAX2(min_uv, max_uv);
+       
+       DO_MINMAX2(uv1, min_uv, max_uv);
+       DO_MINMAX2(uv2, min_uv, max_uv);
+       DO_MINMAX2(uv3, min_uv, max_uv);
+       if (is_quad)
+               DO_MINMAX2(uv4, min_uv, max_uv);
+       
+       bounds_px->xmin = (int)(ibuf_x * min_uv[0]);
+       bounds_px->ymin = (int)(ibuf_y * min_uv[1]);
+       
+       bounds_px->xmax = (int)(ibuf_x * max_uv[0]) +1;
+       bounds_px->ymax = (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 (bounds_px->xmin == bounds_px->xmax || bounds_px->ymin == bounds_px->ymax) ? 0 : 1;
+}
+
+static int pixel_bounds_array(float (* uv)[2], rcti *bounds_px, const int ibuf_x, const 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++;
+       }
+       
+       bounds_px->xmin = (int)(ibuf_x * min_uv[0]);
+       bounds_px->ymin = (int)(ibuf_y * min_uv[1]);
+       
+       bounds_px->xmax = (int)(ibuf_x * max_uv[0]) +1;
+       bounds_px->ymax = (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 (bounds_px->xmin == bounds_px->xmax || bounds_px->ymin == bounds_px->ymax) ? 0 : 1;
+}
+
+#ifndef PROJ_DEBUG_NOSEAMBLEED
+
+/* 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(const ProjPaintState *ps, const int orig_face, const int orig_i1_fidx, const int orig_i2_fidx, int *other_face, int *orig_fidx)
+{
+       LinkNode *node;
+       int face_index;
+       int i1, i2;
+       int i1_fidx = -1, i2_fidx = -1; /* index in face */
+       MFace *mf;
+       MTFace *tf;
+       const MFace *orig_mf = ps->dm_mface + orig_face;  
+       const MTFace *orig_tf = ps->dm_mtface + orig_face;
+       
+       /* vert indicies from face vert order indicies */
+       i1 = (*(&orig_mf->v1 + orig_i1_fidx));
+       i2 = (*(&orig_mf->v1 + orig_i2_fidx));
+       
+       for (node = ps->vertFaces[i1]; node; node = node->next) {
+               face_index = GET_INT_FROM_POINTER(node->link);
+
+               if (face_index != orig_face) {
+                       mf = ps->dm_mface + face_index;
+                       /* could check if the 2 faces images match here,
+                        * but then there wouldn't be a way to return the opposite face's info */
+                       
+                       
+                       /* We need to know the order of the verts in the adjacent face 
+                        * set the i1_fidx and i2_fidx to (0,1,2,3) */
+                       if              (mf->v1==i1)                    i1_fidx = 0;
+                       else if (mf->v2==i1)                    i1_fidx = 1;
+                       else if (mf->v3==i1)                    i1_fidx = 2;
+                       else if (mf->v4 && mf->v4==i1)  i1_fidx = 3;
+                       
+                       if              (mf->v1==i2)                    i2_fidx = 0;
+                       else if (mf->v2==i2)                    i2_fidx = 1;
+                       else if (mf->v3==i2)                    i2_fidx = 2;
+                       else if (mf->v4 && mf->v4==i2)  i2_fidx = 3;
+                       
+                       /* Only need to check if 'i2_fidx' is valid because we know i1_fidx is the same vert on both faces */
+                       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 */
+                               *other_face = face_index;
+                               *orig_fidx = (i1_fidx < i2_fidx) ? i1_fidx : i2_fidx;
+                               
+                               /* first test if they have the same image */
+                               if (    (orig_tf->tpage == tf->tpage) &&
+                                               cmp_uv(orig_tf->uv[orig_i1_fidx], tf->uv[i1_fidx]) &&
+                                               cmp_uv(orig_tf->uv[orig_i2_fidx], tf->uv[i2_fidx]) )
+                               {
+                                       // printf("SEAM (NONE)\n");
+                                       return 0;
+                                       
+                               }
+                               else {
+                                       // printf("SEAM (UV GAP)\n");
+                                       return 1;
+                               }
+                       }
+               }
+       }
+       // printf("SEAM (NO FACE)\n");
+       *other_face = -1;
+       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)
+{
+       // already accounted for
+       if (angle < 0.000001f) {
+               return 1.0f;
+       }
+       else {
+               return fabs(1.0f / cos(angle * (M_PI/180.0f)));
+       }
+}
+
+/* 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], const float scaler, const int ibuf_x, const int ibuf_y, const int is_quad)
+{
+       float a1, a2, a3, a4=0.0f;
+       float puv[4][2]; /* pixelspace uv's */
+       float no1[2], no2[2], no3[2], no4[2]; /* normals */
+       float dir1[2], dir2[2], dir3[2], dir4[2];
+       float ibuf_x_inv = 1.0f / (float)ibuf_x; 
+       float ibuf_y_inv = 1.0f / (float)ibuf_y; 
+       
+       /* make UV's in pixel space so we can */
+       puv[0][0] = orig_uv[0][0] * ibuf_x;
+       puv[0][1] = orig_uv[0][1] * ibuf_y;
+       
+       puv[1][0] = orig_uv[1][0] * ibuf_x;
+       puv[1][1] = orig_uv[1][1] * ibuf_y;
+       
+       puv[2][0] = orig_uv[2][0] * ibuf_x;
+       puv[2][1] = orig_uv[2][1] * ibuf_y;
+       
+       if (is_quad) {
+               puv[3][0] = orig_uv[3][0] * ibuf_x;
+               puv[3][1] = orig_uv[3][1] * ibuf_y;
+       }
+       
+       /* face edge directions */
+       Vec2Subf(dir1, puv[1], puv[0]);
+       Vec2Subf(dir2, puv[2], puv[1]);
+       Normalize2(dir1);
+       Normalize2(dir2);
+       
+       if (is_quad) {
+               Vec2Subf(dir3, puv[3], puv[2]);
+               Vec2Subf(dir4, puv[0], puv[3]);
+               Normalize2(dir3);
+               Normalize2(dir4);
+       }
+       else {
+               Vec2Subf(dir3, puv[0], puv[2]);
+               Normalize2(dir3);
+       }
+       
+       if (is_quad) {
+               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 = angleToLength(NormalizedVecAngle2_2D(dir3, dir1));
+               a2 = angleToLength(NormalizedVecAngle2_2D(dir1, dir2));
+               a3 = angleToLength(NormalizedVecAngle2_2D(dir2, dir3));
+       }
+       
+       if (is_quad) {
+               Vec2Subf(no1, dir4, dir1);
+               Vec2Subf(no2, dir1, dir2);
+               Vec2Subf(no3, dir2, dir3);
+               Vec2Subf(no4, dir3, dir4);
+               Normalize2(no1);
+               Normalize2(no2);
+               Normalize2(no3);
+               Normalize2(no4);
+               Vec2Mulf(no1, a1*scaler);
+               Vec2Mulf(no2, a2*scaler);
+               Vec2Mulf(no3, a3*scaler);
+               Vec2Mulf(no4, a4*scaler);
+               Vec2Addf(outset_uv[0], puv[0], no1);
+               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] *= ibuf_x_inv;
+               outset_uv[0][1] *= ibuf_y_inv;
+               
+               outset_uv[1][0] *= ibuf_x_inv;
+               outset_uv[1][1] *= ibuf_y_inv;
+               
+               outset_uv[2][0] *= ibuf_x_inv;
+               outset_uv[2][1] *= ibuf_y_inv;
+               
+               outset_uv[3][0] *= ibuf_x_inv;
+               outset_uv[3][1] *= ibuf_y_inv;
+       }
+       else {
+               Vec2Subf(no1, dir3, dir1);
+               Vec2Subf(no2, dir1, dir2);
+               Vec2Subf(no3, dir2, dir3);
+               Normalize2(no1);
+               Normalize2(no2);
+               Normalize2(no3);
+               Vec2Mulf(no1, a1*scaler);
+               Vec2Mulf(no2, a2*scaler);
+               Vec2Mulf(no3, a3*scaler);
+               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] *= ibuf_x_inv;
+               outset_uv[0][1] *= ibuf_y_inv;
+               
+               outset_uv[1][0] *= ibuf_x_inv;
+               outset_uv[1][1] *= ibuf_y_inv;
+               
+               outset_uv[2][0] *= ibuf_x_inv;
+               outset_uv[2][1] *= ibuf_y_inv;
+       }
+}
+
+/* 
+ * Be tricky with flags, first 4 bits are PROJ_FACE_SEAM1 to 4, last 4 bits are PROJ_FACE_NOSEAM1 to 4
+ * 1<<i - where i is (0-3) 
+ * 
+ * If we're multithreadng, make sure threads are locked when this is called
+ */
+static void project_face_seams_init(const ProjPaintState *ps, const int face_index, const int is_quad)
+{
+       int other_face, other_fidx; /* vars for the other face, we also set its flag */
+       int fidx1 = is_quad ? 3 : 2;
+       int fidx2 = 0; /* next fidx in the face (0,1,2,3) -> (1,2,3,0) or (0,1,2) -> (1,2,0) for a tri */
+       
+       do {
+               if ((ps->faceSeamFlags[face_index] & (1<<fidx1|16<<fidx1)) == 0) {
+                       if (check_seam(ps, face_index, fidx1, fidx2, &other_face, &other_fidx)) {
+                               ps->faceSeamFlags[face_index] |= 1<<fidx1;
+                               if (other_face != -1)
+                                       ps->faceSeamFlags[other_face] |= 1<<other_fidx;
+                       }
+                       else {
+                               ps->faceSeamFlags[face_index] |= 16<<fidx1;
+                               if (other_face != -1)
+                                       ps->faceSeamFlags[other_face] |= 16<<other_fidx; /* second 4 bits for disabled */
+                       }
+               }
+               
+               fidx2 = fidx1;
+       } while (fidx1--);
+}
+#endif // PROJ_DEBUG_NOSEAMBLEED
+
+
+/* TODO - move to arithb.c */
+
+/* little sister we only need to know lambda */
+static float lambda_cp_line2(const float p[2], const float l1[2], const float l2[2])
+{
+       float h[2], u[2];
+       
+       u[0] = l2[0] - l1[0];
+       u[1] = l2[1] - l1[1];
+
+       h[0] = p[0] - l1[0];
+       h[1] = p[1] - l1[1];
+       
+       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(
+               float uv[2],
+               float v1co[3], float v2co[3], float v3co[3], /* Screenspace coords */
+               float uv1co[2], float uv2co[2], float uv3co[2],
+               float pixelScreenCo[4],
+               float w[3])
+{
+       BarycentricWeights2f(uv, uv1co, uv2co, uv3co, w);
+       VecWeightf(pixelScreenCo, v1co, v2co, v3co, w);
+}
+
+/* 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(
+               float uv[2],
+               float v1co[3], float v2co[3], float v3co[3], /* screenspace coords */
+               float uv1co[2], float uv2co[2], float uv3co[2],
+               float pixelScreenCo[4],
+               float w[3])
+{
+
+       float wtot_inv, wtot;
+       BarycentricWeights2f(uv, uv1co, uv2co, uv3co, w);
+       
+       /* 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 (wtot > 0.0f) {
+               wtot_inv = 1.0f / wtot;
+               w[0] *= wtot_inv;
+               w[1] *= wtot_inv;
+               w[2] *= wtot_inv;
+       }
+       else {
+               w[0] = w[1] = w[2] = 1.0/3.0; /* dummy values for zero area face */
+       }
+       /* done re-weighting */
+       
+       VecWeightf(pixelScreenCo, v1co, v2co, v3co, w);
+}
+
+static void project_face_pixel(const MTFace *tf_other, ImBuf *ibuf_other, const float w[3], int side, unsigned char rgba_ub[4], float rgba_f[4])
+{
+       float *uvCo1, *uvCo2, *uvCo3;
+       float uv_other[2], x, y;
+       
+       uvCo1 =  (float *)tf_other->uv[0];
+       if (side==1) {
+               uvCo2 =  (float *)tf_other->uv[2];
+               uvCo3 =  (float *)tf_other->uv[3];
+       }
+       else {
+               uvCo2 =  (float *)tf_other->uv[1];
+               uvCo3 =  (float *)tf_other->uv[2];
+       }
+       
+       Vec2Weightf(uv_other, uvCo1, uvCo2, uvCo3, w);
+       
+       /* use */
+       uvco_to_wrapped_pxco(uv_other, ibuf_other->x, ibuf_other->y, &x, &y);
+       
+       
+       if (ibuf_other->rect_float) { /* from float to float */
+               bilinear_interpolation_color(ibuf_other, NULL, rgba_f, x, y);
+       }
+       else { /* from char to float */
+               bilinear_interpolation_color(ibuf_other, rgba_ub, NULL, x, y);
+       }
+               
+}
+
+/* run this outside project_paint_uvpixel_init since pixels with mask 0 dont need init */
+float project_paint_uvpixel_mask(
+               const ProjPaintState *ps,
+               const int face_index,
+               const int side,
+               const float w[3])
+{
+       float mask;
+       
+       /* Image Mask */
+       if (ps->do_layer_mask) {
+               /* another UV layers image is masking this one's */
+               ImBuf *ibuf_other;
+               const MTFace *tf_other = ps->dm_mtface_mask + face_index;
+               
+               if (tf_other->tpage && (ibuf_other = BKE_image_get_ibuf((Image *)tf_other->tpage, NULL))) {
+                       /* BKE_image_get_ibuf - TODO - this may be slow */
+                       unsigned char rgba_ub[4];
+                       float rgba_f[4];
+                       
+                       project_face_pixel(tf_other, ibuf_other, w, side, rgba_ub, rgba_f);
+                       
+                       if (ibuf_other->rect_float) { /* from float to float */
+                               mask = ((rgba_f[0]+rgba_f[1]+rgba_f[2])/3.0f) * rgba_f[3];
+                       }
+                       else { /* from char to float */
+                               mask = ((rgba_ub[0]+rgba_ub[1]+rgba_ub[2])/(256*3.0f)) * (rgba_ub[3]/256.0f);
+                       }
+                       
+                       if (!ps->do_layer_mask_inv) /* matching the gimps layer mask black/white rules, white==full opacity */
+                               mask = (1.0f - mask);
+
+                       if (mask == 0.0f) {
+                               return 0.0f;
+                       }
+               }
+               else {
+                       return 0.0f;
+               }
+       } else {
+               mask = 1.0f;
+       }
+       
+       /* calculate mask */
+       if (ps->do_mask_normal) {
+               MFace *mf = ps->dm_mface + face_index;
+               short *no1, *no2, *no3;
+               float no[3], angle;
+               no1 = ps->dm_mvert[mf->v1].no;
+               if (side==1) {
+                       no2 = ps->dm_mvert[mf->v3].no;
+                       no3 = ps->dm_mvert[mf->v4].no;
+               }
+               else {
+                       no2 = ps->dm_mvert[mf->v2].no;
+                       no3 = ps->dm_mvert[mf->v3].no;
+               }
+               
+               no[0] = w[0]*no1[0] + w[1]*no2[0] + w[2]*no3[0];
+               no[1] = w[0]*no1[1] + w[1]*no2[1] + w[2]*no3[1];
+               no[2] = w[0]*no1[2] + w[1]*no2[2] + w[2]*no3[2];
+               Normalize(no);
+               
+               /* now we can use the normal as a mask */
+               if (ps->is_ortho) {
+                       angle = NormalizedVecAngle2((float *)ps->viewDir, no);
+               }
+               else {
+                       /* Annoying but for the perspective view we need to get the pixels location in 3D space :/ */
+                       float viewDirPersp[3];
+                       float *co1, *co2, *co3;
+                       co1 = ps->dm_mvert[mf->v1].co;
+                       if (side==1) {
+                               co2 = ps->dm_mvert[mf->v3].co;
+                               co3 = ps->dm_mvert[mf->v4].co;
+                       }
+                       else {
+                               co2 = ps->dm_mvert[mf->v2].co;
+                               co3 = ps->dm_mvert[mf->v3].co;
+                       }
+
+                       /* Get the direction from the viewPoint to the pixel and normalize */
+                       viewDirPersp[0] = (ps->viewPos[0] - (w[0]*co1[0] + w[1]*co2[0] + w[2]*co3[0]));
+                       viewDirPersp[1] = (ps->viewPos[1] - (w[0]*co1[1] + w[1]*co2[1] + w[2]*co3[1]));
+                       viewDirPersp[2] = (ps->viewPos[2] - (w[0]*co1[2] + w[1]*co2[2] + w[2]*co3[2]));
+                       Normalize(viewDirPersp);
+                       
+                       angle = NormalizedVecAngle2(viewDirPersp, no);
+               }
+               
+               if (angle >= ps->normal_angle) {
+                       return 0.0f; /* outsize the normal limit*/
+               }
+               else if (angle > ps->normal_angle_inner) {
+                       mask *= (ps->normal_angle - angle) / ps->normal_angle_range;
+               } /* otherwise no mask normal is needed, were within the limit */
+       }
+       
+       // This only works when the opacity dosnt change while painting, stylus pressure messes with this
+       // so dont use it.
+       // if (ps->is_airbrush==0) mask *= ps->brush->alpha;
+       
+       return mask;
+}
+
+/* run this function when we know a bucket's, face's pixel can be initialized,
+ * return the ProjPixel which is added to 'ps->bucketRect[bucket_index]' */
+static ProjPixel *project_paint_uvpixel_init(
+               const ProjPaintState *ps,
+               MemArena *arena,
+               const ImBuf *ibuf,
+               short x_px, short y_px,
+               const float mask,
+               const int face_index,
+               const int image_index,
+               const float pixelScreenCo[4],
+               const int side,
+               const float w[3])
+{
+       ProjPixel *projPixel;
+       short size;
+       
+       /* wrap pixel location */
+       x_px = x_px % ibuf->x;
+       if (x_px<0) x_px += ibuf->x;
+       y_px = y_px % ibuf->y;
+       if (y_px<0) y_px += ibuf->y;
+       
+       if (ps->tool==PAINT_TOOL_CLONE) {
+               size = sizeof(ProjPixelClone);
+       }
+       else if (ps->tool==PAINT_TOOL_SMEAR) {
+               size = sizeof(ProjPixelClone);
+       }
+       else {
+               size = sizeof(ProjPixel);
+       }
+       
+       projPixel = (ProjPixel *)BLI_memarena_alloc(arena, size);
+       //memset(projPixel, 0, size);
+       
+       if (ibuf->rect_float) {
+               projPixel->pixel.f_pt = (float *)ibuf->rect_float + ((x_px + y_px * ibuf->x) * 4);
+               projPixel->origColor.f[0] = projPixel->newColor.f[0] = projPixel->pixel.f_pt[0];  
+               projPixel->origColor.f[1] = projPixel->newColor.f[1] = projPixel->pixel.f_pt[1];  
+               projPixel->origColor.f[2] = projPixel->newColor.f[2] = projPixel->pixel.f_pt[2];  
+               projPixel->origColor.f[3] = projPixel->newColor.f[3] = projPixel->pixel.f_pt[3];  
+       }
+       else {
+               projPixel->pixel.ch_pt = ((unsigned char *)ibuf->rect + ((x_px + y_px * ibuf->x) * 4));
+               projPixel->origColor.uint = projPixel->newColor.uint = *projPixel->pixel.uint_pt;
+       }
+       
+       /* screenspace unclamped, we could keep its z and w values but dont need them at the moment */
+       VECCOPY2D(projPixel->projCoSS, pixelScreenCo);
+       
+       projPixel->x_px = x_px;
+       projPixel->y_px = y_px;
+       
+       projPixel->mask = (unsigned short)(mask * 65535);
+       projPixel->mask_max = 0;
+       
+       /* which bounding box cell are we in?, needed for undo */
+       projPixel->bb_cell_index = ((int)(((float)x_px/(float)ibuf->x) * PROJ_BOUNDBOX_DIV)) + ((int)(((float)y_px/(float)ibuf->y) * PROJ_BOUNDBOX_DIV)) * PROJ_BOUNDBOX_DIV ;
+       
+       /* done with view3d_project_float inline */
+       if (ps->tool==PAINT_TOOL_CLONE) {
+               if (ps->dm_mtface_clone) {
+                       ImBuf *ibuf_other;
+                       const MTFace *tf_other = ps->dm_mtface_clone + face_index;
+                       
+                       if (tf_other->tpage && (ibuf_other = BKE_image_get_ibuf((Image *)tf_other->tpage, NULL))) {
+                               /* BKE_image_get_ibuf - TODO - this may be slow */
+                               
+                               if (ibuf->rect_float) {
+                                       if (ibuf_other->rect_float) { /* from float to float */
+                                               project_face_pixel(tf_other, ibuf_other, w, side, NULL, ((ProjPixelClone *)projPixel)->clonepx.f);
+                                       }
+                                       else { /* from char to float */
+                                               unsigned char rgba_ub[4];
+                                               project_face_pixel(tf_other, ibuf_other, w, side, rgba_ub, NULL);
+                                               IMAPAINT_CHAR_RGBA_TO_FLOAT(((ProjPixelClone *)projPixel)->clonepx.f, rgba_ub);
+                                       }
+                               }
+                               else {
+                                       if (ibuf_other->rect_float) { /* float to char */
+                                               float rgba[4];
+                                               project_face_pixel(tf_other, ibuf_other, w, side, NULL, rgba);
+                                               IMAPAINT_FLOAT_RGBA_TO_CHAR(((ProjPixelClone *)projPixel)->clonepx.ch, rgba)
+                                       }
+                                       else { /* char to char */
+                                               project_face_pixel(tf_other, ibuf_other, w, side, ((ProjPixelClone *)projPixel)->clonepx.ch, NULL);
+                                       }
+                               }
+                       }
+                       else {
+                               if (ibuf->rect_float) {
+                                       ((ProjPixelClone *)projPixel)->clonepx.f[3] = 0;
+                               }
+                               else {
+                                       ((ProjPixelClone *)projPixel)->clonepx.ch[3] = 0;
+                               }
+                       }
+                       
+               }
+               else {
+                       float co[2];
+                       Vec2Subf(co, projPixel->projCoSS, (float *)ps->cloneOffset);
+                       
+                       /* no need to initialize the bucket, we're only checking buckets faces and for this
+                        * the faces are alredy initialized in project_paint_delayed_face_init(...) */
+                       if (ibuf->rect_float) {
+                               if (!project_paint_PickColor(ps, co, ((ProjPixelClone *)projPixel)->clonepx.f, NULL, 1)) {
+                                       ((ProjPixelClone *)projPixel)->clonepx.f[3] = 0; /* zero alpha - ignore */
+                               }
+                       }
+                       else {
+                               if (!project_paint_PickColor(ps, co, NULL, ((ProjPixelClone *)projPixel)->clonepx.ch, 1)) {
+                                       ((ProjPixelClone *)projPixel)->clonepx.ch[3] = 0; /* zero alpha - ignore */
+                               }
+                       }
+               }
+       }
+       
+#ifdef PROJ_DEBUG_PAINT
+       if (ibuf->rect_float)   projPixel->pixel.f_pt[0] = 0;
+       else                                    projPixel->pixel.ch_pt[0] = 0;
+#endif
+       projPixel->image_index = image_index;
+       
+       return projPixel;
+}
+
+static int line_clip_rect2f(
+               rctf *rect,
+               const float l1[2], const float l2[2],
+               float l1_clip[2], float l2_clip[2])
+{
+       /* first account for horizontal, then vertical lines */
+       /* horiz */
+       if (fabs(l1[1]-l2[1]) < PROJ_GEOM_TOLERANCE) {
+               /* is the line out of range on its Y axis? */
+               if (l1[1] < rect->ymin || l1[1] > rect->ymax) {
+                       return 0;
+               }
+               /* line is out of range on its X axis */
+               if ((l1[0] < rect->xmin && l2[0] < rect->xmin) || (l1[0] > rect->xmax && l2[0] > rect->xmax)) {
+                       return 0;
+               }
+               
+               
+               if (fabs(l1[0]-l2[0]) < PROJ_GEOM_TOLERANCE) { /* this is a single point  (or close to)*/
+                       if (BLI_in_rctf(rect, l1[0], l1[1])) {
+                               VECCOPY2D(l1_clip, l1);
+                               VECCOPY2D(l2_clip, l2);
+                               return 1;
+                       }
+                       else {
+                               return 0;
+                       }
+               }
+               
+               VECCOPY2D(l1_clip, l1);
+               VECCOPY2D(l2_clip, l2);
+               CLAMP(l1_clip[0], rect->xmin, rect->xmax);
+               CLAMP(l2_clip[0], rect->xmin, rect->xmax);
+               return 1;
+       }
+       else if (fabs(l1[0]-l2[0]) < PROJ_GEOM_TOLERANCE) {
+               /* is the line out of range on its X axis? */
+               if (l1[0] < rect->xmin || l1[0] > rect->xmax) {
+                       return 0;
+               }
+               
+               /* line is out of range on its Y axis */
+               if ((l1[1] < rect->ymin && l2[1] < rect->ymin) || (l1[1] > rect->ymax && l2[1] > rect->ymax)) {
+                       return 0;
+               }
+               
+               if (fabs(l1[1]-l2[1]) < PROJ_GEOM_TOLERANCE) { /* this is a single point  (or close to)*/
+                       if (BLI_in_rctf(rect, l1[0], l1[1])) {
+                               VECCOPY2D(l1_clip, l1);
+                               VECCOPY2D(l2_clip, l2);
+                               return 1;
+                       }
+                       else {
+                               return 0;
+                       }
+               }
+               
+               VECCOPY2D(l1_clip, l1);
+               VECCOPY2D(l2_clip, l2);
+               CLAMP(l1_clip[1], rect->ymin, rect->ymax);
+               CLAMP(l2_clip[1], rect->ymin, rect->ymax);
+               return 1;
+       }
+       else {
+               float isect;
+               short ok1 = 0;
+               short ok2 = 0;
+               
+               /* Done with vertical lines */
+               
+               /* are either of the points inside the rectangle ? */
+               if (BLI_in_rctf(rect, l1[0], l1[1])) {
+                       VECCOPY2D(l1_clip, l1);
+                       ok1 = 1;
+               }
+               
+               if (BLI_in_rctf(rect, l2[0], l2[1])) {
+                       VECCOPY2D(l2_clip, l2);
+                       ok2 = 1;
+               }
+               
+               /* line inside rect */
+               if (ok1 && ok2) return 1;
+               
+               /* top/bottom */
+               if (line_isect_y(l1, l2, rect->ymin, &isect) && (isect >= rect->xmin) && (isect <= rect->xmax)) {
+                       if (l1[1] < l2[1]) { /* line 1 is outside */
+                               l1_clip[0] = isect;
+                               l1_clip[1] = rect->ymin;
+                               ok1 = 1;
+                       }
+                       else {
+                               l2_clip[0] = isect;
+                               l2_clip[1] = rect->ymin;
+                               ok2 = 2;
+                       }
+               }
+               
+               if (ok1 && ok2) return 1;
+               
+               if (line_isect_y(l1, l2, rect->ymax, &isect) && (isect >= rect->xmin) && (isect <= rect->xmax)) {
+                       if (l1[1] > l2[1]) { /* line 1 is outside */
+                               l1_clip[0] = isect;
+                               l1_clip[1] = rect->ymax;
+                               ok1 = 1;
+                       }
+                       else {
+                               l2_clip[0] = isect;
+                               l2_clip[1] = rect->ymax;
+                               ok2 = 2;
+                       }
+               }
+               
+               if (ok1 && ok2) return 1;
+               
+               /* left/right */
+               if (line_isect_x(l1, l2, rect->xmin, &isect) && (isect >= rect->ymin) && (isect <= rect->ymax)) {
+                       if (l1[0] < l2[0]) { /* line 1 is outside */
+                               l1_clip[0] = rect->xmin;
+                               l1_clip[1] = isect;
+                               ok1 = 1;
+                       }
+                       else {
+                               l2_clip[0] = rect->xmin;
+                               l2_clip[1] = isect;
+                               ok2 = 2;
+                       }
+               }
+       
+               if (ok1 && ok2) return 1;
+               
+               if (line_isect_x(l1, l2, rect->xmax, &isect) && (isect >= rect->ymin) && (isect <= rect->ymax)) {
+                       if (l1[0] > l2[0]) { /* line 1 is outside */
+                               l1_clip[0] = rect->xmax;
+                               l1_clip[1] = isect;
+                               ok1 = 1;
+                       }
+                       else {
+                               l2_clip[0] = rect->xmax;
+                               l2_clip[1] = isect;
+                               ok2 = 2;
+                       }
+               }
+               
+               if (ok1 && ok2) {
+                       return 1;
+               }
+               else {
+                       return 0;
+               }
+       }
+}
+
+
+
+/* scale the quad & tri about its center
+ * scaling by PROJ_FACE_SCALE_SEAM (0.99x) is used for getting fake UV pixel coords that are on the
+ * edge of the face but slightly inside it occlusion tests dont return hits on adjacent faces */
+static void scale_quad(float insetCos[4][3], float *origCos[4], const float inset)
+{
+       float cent[3];
+       cent[0] = (origCos[0][0] + origCos[1][0] + origCos[2][0] + origCos[3][0]) / 4.0f;
+       cent[1] = (origCos[0][1] + origCos[1][1] + origCos[2][1] + origCos[3][1]) / 4.0f;
+       cent[2] = (origCos[0][2] + origCos[1][2] + origCos[2][2] + origCos[3][2]) / 4.0f;
+       
+       VecSubf(insetCos[0], origCos[0], cent);
+       VecSubf(insetCos[1], origCos[1], cent);
+       VecSubf(insetCos[2], origCos[2], cent);
+       VecSubf(insetCos[3], origCos[3], cent);
+       
+       VecMulf(insetCos[0], inset);
+       VecMulf(insetCos[1], inset);
+       VecMulf(insetCos[2], inset);
+       VecMulf(insetCos[3], inset);
+       
+       VecAddf(insetCos[0], insetCos[0], cent);
+       VecAddf(insetCos[1], insetCos[1], cent);
+       VecAddf(insetCos[2], insetCos[2], cent);
+       VecAddf(insetCos[3], insetCos[3], cent);
+}
+
+
+static void scale_tri(float insetCos[4][3], float *origCos[4], const float inset)
+{
+       float cent[3];
+       cent[0] = (origCos[0][0] + origCos[1][0] + origCos[2][0]) / 3.0f;
+       cent[1] = (origCos[0][1] + origCos[1][1] + origCos[2][1]) / 3.0f;
+       cent[2] = (origCos[0][2] + origCos[1][2] + origCos[2][2]) / 3.0f;
+       
+       VecSubf(insetCos[0], origCos[0], cent);
+       VecSubf(insetCos[1], origCos[1], cent);
+       VecSubf(insetCos[2], origCos[2], cent);
+       
+       VecMulf(insetCos[0], inset);
+       VecMulf(insetCos[1], inset);
+       VecMulf(insetCos[2], inset);
+       
+       VecAddf(insetCos[0], insetCos[0], cent);
+       VecAddf(insetCos[1], insetCos[1], cent);
+       VecAddf(insetCos[2], insetCos[2], cent);
+}
+
+
+static float Vec2Lenf_nosqrt(const float *v1, const 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(const float *v1, const float v2_1, const float v2_2)
+{
+       float x, y;
+
+       x = v1[0]-v2_1;
+       y = v1[1]-v2_2;
+       return x*x+y*y;
+}
+
+/* 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(const int bucket_x, const int bucket_y, const float cent[2], const float radius_squared, rctf *bucket_bounds)
+{
+        
+       /* 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 (BLI_in_rctf(bucket_bounds, cent[0], cent[1]))
+               return 1;
+        */
+       
+       if((bucket_bounds->xmin <= cent[0] && bucket_bounds->xmax >= cent[0]) || (bucket_bounds->ymin <= cent[1] && bucket_bounds->ymax >= cent[1]) ) {
+          return 1;
+       }
+       
+       /* out of bounds left */
+       if (cent[0] < bucket_bounds->xmin) {
+               /* lower left out of radius test */
+               if (cent[1] < bucket_bounds->ymin) {
+                       return (Vec2Lenf_nosqrt_other(cent, bucket_bounds->xmin, bucket_bounds->ymin) < radius_squared) ? 1 : 0;
+               } 
+               /* top left test */
+               else if (cent[1] > bucket_bounds->ymax) {
+                       return (Vec2Lenf_nosqrt_other(cent, bucket_bounds->xmin, bucket_bounds->ymax) < radius_squared) ? 1 : 0;
+               }
+       }
+       else if (cent[0] > bucket_bounds->xmax) {
+               /* lower right out of radius test */
+               if (cent[1] < bucket_bounds->ymin) {
+                       return (Vec2Lenf_nosqrt_other(cent, bucket_bounds->xmax, bucket_bounds->ymin) < radius_squared) ? 1 : 0;
+               } 
+               /* top right test */
+               else if (cent[1] > bucket_bounds->ymax) {
+                       return (Vec2Lenf_nosqrt_other(cent, bucket_bounds->xmax, bucket_bounds->ymax) < 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(
+               rctf *bucket_bounds,
+               float *v1coSS, float *v2coSS, float *v3coSS,
+               float *uv1co, float *uv2co, float *uv3co,
+               float bucket_bounds_uv[4][2],
+               const int flip)
+{
+       float uv[2];
+       float w[3];
+       
+       /* get the UV space bounding box */
+       uv[0] = bucket_bounds->xmax;
+       uv[1] = bucket_bounds->ymin;
+       BarycentricWeights2f(uv, v1coSS, v2coSS, v3coSS, w);
+       Vec2Weightf(bucket_bounds_uv[flip?3:0], uv1co, uv2co, uv3co, w);
+
+       //uv[0] = bucket_bounds->xmax; // set above
+       uv[1] = bucket_bounds->ymax;
+       BarycentricWeights2f(uv, v1coSS, v2coSS, v3coSS, w);
+       Vec2Weightf(bucket_bounds_uv[flip?2:1], uv1co, uv2co, uv3co, w);
+
+       uv[0] = bucket_bounds->xmin;
+       //uv[1] = bucket_bounds->ymax; // set above
+       BarycentricWeights2f(uv, v1coSS, v2coSS, v3coSS, w);
+       Vec2Weightf(bucket_bounds_uv[flip?1:2], uv1co, uv2co, uv3co, w);
+
+       //uv[0] = bucket_bounds->xmin; // set above
+       uv[1] = bucket_bounds->ymin;
+       BarycentricWeights2f(uv, v1coSS, v2coSS, v3coSS, w);
+       Vec2Weightf(bucket_bounds_uv[flip?0:3], uv1co, uv2co, uv3co, w);
+}
+
+/* same as above but use BarycentricWeightsPersp2f */
+static void rect_to_uvspace_persp(
+               rctf *bucket_bounds,
+               float *v1coSS, float *v2coSS, float *v3coSS,
+               float *uv1co, float *uv2co, float *uv3co,
+               float bucket_bounds_uv[4][2],
+               const int flip
+       )
+{
+       float uv[2];
+       float w[3];
+       
+       /* get the UV space bounding box */
+       uv[0] = bucket_bounds->xmax;
+       uv[1] = bucket_bounds->ymin;
+       BarycentricWeightsPersp2f(uv, v1coSS, v2coSS, v3coSS, w);
+       Vec2Weightf(bucket_bounds_uv[flip?3:0], uv1co, uv2co, uv3co, w);
+
+       //uv[0] = bucket_bounds->xmax; // set above
+       uv[1] = bucket_bounds->ymax;
+       BarycentricWeightsPersp2f(uv, v1coSS, v2coSS, v3coSS, w);
+       Vec2Weightf(bucket_bounds_uv[flip?2:1], uv1co, uv2co, uv3co, w);
+
+       uv[0] = bucket_bounds->xmin;
+       //uv[1] = bucket_bounds->ymax; // set above
+       BarycentricWeightsPersp2f(uv, v1coSS, v2coSS, v3coSS, w);
+       Vec2Weightf(bucket_bounds_uv[flip?1:2], uv1co, uv2co, uv3co, w);
+
+       //uv[0] = bucket_bounds->xmin; // set above
+       uv[1] = bucket_bounds->ymin;
+       BarycentricWeightsPersp2f(uv, v1coSS, v2coSS, v3coSS, w);
+       Vec2Weightf(bucket_bounds_uv[flip?0:3], uv1co, uv2co, uv3co, w);
+}
+
+/* This works as we need it to but we can save a few steps and not use it */
+
+#if 0
+static float angle_2d_clockwise(const float p1[2], const float p2[2], const 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]);
+}
+#endif
+
+#define ISECT_1 (1)
+#define ISECT_2 (1<<1)
+#define ISECT_3 (1<<2)
+#define ISECT_4 (1<<3)
+#define ISECT_ALL3 ((1<<3)-1)
+#define ISECT_ALL4 ((1<<4)-1)
+
+/* limit must be a fraction over 1.0f */
+static int IsectPT2Df_limit(float pt[2], float v1[2], float v2[2], float v3[2], float limit)
+{
+       return ((AreaF2Dfl(pt,v1,v2) + AreaF2Dfl(pt,v2,v3) + AreaF2Dfl(pt,v3,v1)) / (AreaF2Dfl(v1,v2,v3))) < limit;
+}
+
+/* Clip the face by a bucket and set the uv-space bucket_bounds_uv
+ * so we have the clipped UV's to do pixel intersection tests with 
+ * */
+static int float_z_sort_flip(const void *p1, const void *p2) {
+       return (((float *)p1)[2] < ((float *)p2)[2] ? 1:-1);
+}
+
+static int float_z_sort(const void *p1, const void *p2) {
+       return (((float *)p1)[2] < ((float *)p2)[2] ?-1:1);
+}
+
+static void project_bucket_clip_face(
+               const int is_ortho,
+               rctf *bucket_bounds,
+               float *v1coSS, float *v2coSS, float *v3coSS,
+               float *uv1co, float *uv2co, float *uv3co,
+               float bucket_bounds_uv[8][2],
+               int *tot)
+{
+       int inside_bucket_flag = 0;
+       int inside_face_flag = 0;
+       const int flip = ((SIDE_OF_LINE(v1coSS, v2coSS, v3coSS) > 0.0f) != (SIDE_OF_LINE(uv1co, uv2co, uv3co) > 0.0f));
+       
+       float bucket_bounds_ss[4][2];
+       float w[3];
+
+       /* get the UV space bounding box */
+       inside_bucket_flag |= BLI_in_rctf(bucket_bounds, v1coSS[0], v1coSS[1]);
+       inside_bucket_flag |= BLI_in_rctf(bucket_bounds, v2coSS[0], v2coSS[1])          << 1;
+       inside_bucket_flag |= BLI_in_rctf(bucket_bounds, v3coSS[0], v3coSS[1])          << 2;
+       
+       if (inside_bucket_flag == ISECT_ALL3) {
+               /* all screenspace points are inside the bucket bounding box, this means we dont need to clip and can simply return the UVs */
+               if (flip) { /* facing the back? */
+                       VECCOPY2D(bucket_bounds_uv[0], uv3co);
+                       VECCOPY2D(bucket_bounds_uv[1], uv2co);
+                       VECCOPY2D(bucket_bounds_uv[2], uv1co);
+               }
+               else {
+                       VECCOPY2D(bucket_bounds_uv[0], uv1co);
+                       VECCOPY2D(bucket_bounds_uv[1], uv2co);
+                       VECCOPY2D(bucket_bounds_uv[2], uv3co);
+               }
+               
+               *tot = 3; 
+               return;
+       }
+       
+       /* get the UV space bounding box */
+       /* use IsectPT2Df_limit here so we catch points are are touching the tri edge (or a small fraction over) */
+       bucket_bounds_ss[0][0] = bucket_bounds->xmax;
+       bucket_bounds_ss[0][1] = bucket_bounds->ymin;
+       inside_face_flag |= (IsectPT2Df_limit(bucket_bounds_ss[0], v1coSS, v2coSS, v3coSS, 1+PROJ_GEOM_TOLERANCE) ? ISECT_1 : 0);
+       
+       bucket_bounds_ss[1][0] = bucket_bounds->xmax;
+       bucket_bounds_ss[1][1] = bucket_bounds->ymax;
+       inside_face_flag |= (IsectPT2Df_limit(bucket_bounds_ss[1], v1coSS, v2coSS, v3coSS, 1+PROJ_GEOM_TOLERANCE) ? ISECT_2 : 0);
+
+       bucket_bounds_ss[2][0] = bucket_bounds->xmin;
+       bucket_bounds_ss[2][1] = bucket_bounds->ymax;
+       inside_face_flag |= (IsectPT2Df_limit(bucket_bounds_ss[2], v1coSS, v2coSS, v3coSS, 1+PROJ_GEOM_TOLERANCE) ? ISECT_3 : 0);
+
+       bucket_bounds_ss[3][0] = bucket_bounds->xmin;
+       bucket_bounds_ss[3][1] = bucket_bounds->ymin;
+       inside_face_flag |= (IsectPT2Df_limit(bucket_bounds_ss[3], v1coSS, v2coSS, v3coSS, 1+PROJ_GEOM_TOLERANCE) ? ISECT_4 : 0);
+       
+       if (inside_face_flag == ISECT_ALL4) {
+               /* bucket is totally inside the screenspace face, we can safely use weights */
+               
+               if (is_ortho)   rect_to_uvspace_ortho(bucket_bounds, v1coSS, v2coSS, v3coSS, uv1co, uv2co, uv3co, bucket_bounds_uv, flip);
+               else                    rect_to_uvspace_persp(bucket_bounds, v1coSS, v2coSS, v3coSS, uv1co, uv2co, uv3co, bucket_bounds_uv, flip);
+               
+               *tot = 4;
+               return;
+       }
+       else {
+               /* The Complicated Case! 
+                * 
+                * The 2 cases above are where the face is inside the bucket or the bucket is inside the face.
+                * 
+                * we need to make a convex polyline from the intersection between the screenspace face
+                * and the bucket bounds.
+                * 
+                * There are a number of ways this could be done, currently it just collects all intersecting verts,
+                * and line intersections,  then sorts them clockwise, this is a lot easier then evaluating the geometry to
+                * do a correct clipping on both shapes. */
+               
+               
+               /* add a bunch of points, we know must make up the convex hull which is the clipped rect and triangle */
+               
+               
+               
+               /* Maximum possible 6 intersections when using a rectangle and triangle */
+               float isectVCosSS[8][3]; /* The 3rd float is used to store angle for qsort(), NOT as a Z location */
+               float v1_clipSS[2], v2_clipSS[2];
+               
+               /* calc center*/
+               float cent[2] = {0.0f, 0.0f};
+               /*float up[2] = {0.0f, 1.0f};*/
+               int i;
+               short doubles;
+               
+               (*tot) = 0;
+               
+               if (inside_face_flag & ISECT_1) { VECCOPY2D(isectVCosSS[*tot], bucket_bounds_ss[0]); (*tot)++; }
+               if (inside_face_flag & ISECT_2) { VECCOPY2D(isectVCosSS[*tot], bucket_bounds_ss[1]); (*tot)++; }
+               if (inside_face_flag & ISECT_3) { VECCOPY2D(isectVCosSS[*tot], bucket_bounds_ss[2]); (*tot)++; }
+               if (inside_face_flag & ISECT_4) { VECCOPY2D(isectVCosSS[*tot], bucket_bounds_ss[3]); (*tot)++; }
+               
+               if (inside_bucket_flag & ISECT_1) {     VECCOPY2D(isectVCosSS[*tot], v1coSS); (*tot)++; }
+               if (inside_bucket_flag & ISECT_2) {     VECCOPY2D(isectVCosSS[*tot], v2coSS); (*tot)++; }
+               if (inside_bucket_flag & ISECT_3) {     VECCOPY2D(isectVCosSS[*tot], v3coSS); (*tot)++; }
+               
+               if ((inside_bucket_flag & (ISECT_1|ISECT_2)) != (ISECT_1|ISECT_2)) {
+                       if (line_clip_rect2f(bucket_bounds, v1coSS, v2coSS, v1_clipSS, v2_clipSS)) {
+                               if ((inside_bucket_flag & ISECT_1)==0) { VECCOPY2D(isectVCosSS[*tot], v1_clipSS); (*tot)++; }
+                               if ((inside_bucket_flag & ISECT_2)==0) { VECCOPY2D(isectVCosSS[*tot], v2_clipSS); (*tot)++; }
+                       }
+               }
+               
+               if ((inside_bucket_flag & (ISECT_2|ISECT_3)) != (ISECT_2|ISECT_3)) {
+                       if (line_clip_rect2f(bucket_bounds, v2coSS, v3coSS, v1_clipSS, v2_clipSS)) {
+                               if ((inside_bucket_flag & ISECT_2)==0) { VECCOPY2D(isectVCosSS[*tot], v1_clipSS); (*tot)++; }
+                               if ((inside_bucket_flag & ISECT_3)==0) { VECCOPY2D(isectVCosSS[*tot], v2_clipSS); (*tot)++; }
+                       }
+               }       
+               
+               if ((inside_bucket_flag & (ISECT_3|ISECT_1)) != (ISECT_3|ISECT_1)) {
+                       if (line_clip_rect2f(bucket_bounds, v3coSS, v1coSS, v1_clipSS, v2_clipSS)) {
+                               if ((inside_bucket_flag & ISECT_3)==0) { VECCOPY2D(isectVCosSS[*tot], v1_clipSS); (*tot)++; }
+                               if ((inside_bucket_flag & ISECT_1)==0) { VECCOPY2D(isectVCosSS[*tot], v2_clipSS); (*tot)++; }
+                       }
+               }
+               
+               
+               if ((*tot) < 3) { /* no intersections to speak of */
+                       *tot = 0;
+               }
+       
+               /* now we have all points we need, collect their angles and sort them clockwise */
+               
+               for(i=0; i<(*tot); i++) {
+                       cent[0] += isectVCosSS[i][0];
+                       cent[1] += isectVCosSS[i][1];
+               }
+               cent[0] = cent[0] / (float)(*tot);
+               cent[1] = cent[1] / (float)(*tot);
+               
+               
+               
+               /* Collect angles for every point around the center point */
+
+               
+#if 0  /* uses a few more cycles then the above loop */
+               for(i=0; i<(*tot); i++) {
+                       isectVCosSS[i][2] = angle_2d_clockwise(up, cent, isectVCosSS[i]);
+               }
+#endif
+
+               v1_clipSS[0] = cent[0]; /* Abuse this var for the loop below */
+               v1_clipSS[1] = cent[1] + 1.0f;
+               
+               for(i=0; i<(*tot); i++) {
+                       v2_clipSS[0] = isectVCosSS[i][0] - cent[0];
+                       v2_clipSS[1] = isectVCosSS[i][1] - cent[1];
+                       isectVCosSS[i][2] = atan2(v1_clipSS[0]*v2_clipSS[1] - v1_clipSS[1]*v2_clipSS[0], v1_clipSS[0]*v2_clipSS[0]+v1_clipSS[1]*v2_clipSS[1]); 
+               }
+               
+               if (flip)       qsort(isectVCosSS, *tot, sizeof(float)*3, float_z_sort_flip);
+               else            qsort(isectVCosSS, *tot, sizeof(float)*3, float_z_sort);
+               
+               
+               /* remove doubles */
+               /* first/last check */
+               if (fabs(isectVCosSS[0][0]-isectVCosSS[(*tot)-1][0]) < PROJ_GEOM_TOLERANCE &&  fabs(isectVCosSS[0][1]-isectVCosSS[(*tot)-1][1]) < PROJ_GEOM_TOLERANCE) {
+                       (*tot)--;
+               }
+               
+               /* its possible there is only a few left after remove doubles */
+               if ((*tot) < 3) {
+                       // printf("removed too many doubles A\n");
+                       *tot = 0;
+                       return;
+               }
+               
+               doubles = TRUE;
+               while (doubles==TRUE) {
+                       doubles = FALSE;
+                       for(i=1; i<(*tot); i++) {
+                               if (fabs(isectVCosSS[i-1][0]-isectVCosSS[i][0]) < PROJ_GEOM_TOLERANCE &&
+                                       fabs(isectVCosSS[i-1][1]-isectVCosSS[i][1]) < PROJ_GEOM_TOLERANCE)
+                               {
+                                       int j;
+                                       for(j=i+1; j<(*tot); j++) {
+                                               isectVCosSS[j-1][0] = isectVCosSS[j][0]; 
+                                               isectVCosSS[j-1][1] = isectVCosSS[j][1]; 
+                                       }
+                                       doubles = TRUE; /* keep looking for more doubles */
+                                       (*tot)--;
+                               }
+                       }
+               }
+               
+               /* its possible there is only a few left after remove doubles */
+               if ((*tot) < 3) {
+                       // printf("removed too many doubles B\n");
+                       *tot = 0;
+                       return;
+               }
+               
+               
+               if (is_ortho) {
+                       for(i=0; i<(*tot); i++) {
+                               BarycentricWeights2f(isectVCosSS[i], v1coSS, v2coSS, v3coSS, w);
+                               Vec2Weightf(bucket_bounds_uv[i], uv1co, uv2co, uv3co, w);
+                       }
+               }
+               else {
+                       for(i=0; i<(*tot); i++) {
+                               BarycentricWeightsPersp2f(isectVCosSS[i], v1coSS, v2coSS, v3coSS, w);
+                               Vec2Weightf(bucket_bounds_uv[i], uv1co, uv2co, uv3co, w);
+                       }
+               }
+       }
+
+#ifdef PROJ_DEBUG_PRINT_CLIP
+       /* include this at the bottom of the above function to debug the output */
+
+       {
+               /* If there are ever any problems, */
+               float test_uv[4][2];
+               int i;
+               if (is_ortho)   rect_to_uvspace_ortho(bucket_bounds, v1coSS, v2coSS, v3coSS, uv1co, uv2co, uv3co, test_uv, flip);
+               else                            rect_to_uvspace_persp(bucket_bounds, v1coSS, v2coSS, v3coSS, uv1co, uv2co, uv3co, test_uv, flip);
+               printf("(  [(%f,%f), (%f,%f), (%f,%f), (%f,%f)], ", test_uv[0][0], test_uv[0][1],   test_uv[1][0], test_uv[1][1],    test_uv[2][0], test_uv[2][1],    test_uv[3][0], test_uv[3][1]);
+               
+               printf("  [(%f,%f), (%f,%f), (%f,%f)], ", uv1co[0], uv1co[1],   uv2co[0], uv2co[1],    uv3co[0], uv3co[1]);
+               
+               printf("[");
+               for (i=0; i < (*tot); i++) {
+                       printf("(%f, %f),", bucket_bounds_uv[i][0], bucket_bounds_uv[i][1]);
+               }
+               printf("]),\\\n");
+       }
+#endif
+}
+
+       /*
+# This script creates faces in a blender scene from printed data above.
+
+project_ls = [
+...(output from above block)...
+]
+from Blender import Scene, Mesh, Window, sys, Mathutils
+
+import bpy
+
+V = Mathutils.Vector
+
+def main():
+       sce = bpy.data.scenes.active
+       
+       for item in project_ls:
+               bb = item[0]
+               uv = item[1]
+               poly = item[2]
+               
+               me = bpy.data.meshes.new()
+               ob = sce.objects.new(me)
+               
+               me.verts.extend([V(bb[0]).resize3D(), V(bb[1]).resize3D(), V(bb[2]).resize3D(), V(bb[3]).resize3D()])
+               me.faces.extend([(0,1,2,3),])
+               me.verts.extend([V(uv[0]).resize3D(), V(uv[1]).resize3D(), V(uv[2]).resize3D()])
+               me.faces.extend([(4,5,6),])
+               
+               vs = [V(p).resize3D() for p in poly]
+               print len(vs)
+               l = len(me.verts)
+               me.verts.extend(vs)
+               
+               i = l
+               while i < len(me.verts):
+                       ii = i+1
+                       if ii==len(me.verts):
+                               ii = l
+                       me.edges.extend([i, ii])
+                       i+=1
+
+if __name__ == '__main__':
+       main()
+ */    
+
+
+#undef ISECT_1
+#undef ISECT_2
+#undef ISECT_3
+#undef ISECT_4
+#undef ISECT_ALL3
+#undef ISECT_ALL4
+
+       
+/* checks if pt is inside a convex 2D polyline, the polyline must be ordered rotating clockwise
+ * otherwise it would have to test for mixed (SIDE_OF_LINE > 0.0f) cases */
+int IsectPoly2Df(const float pt[2], float uv[][2], const int tot)
+{
+       int i;
+       if (SIDE_OF_LINE(uv[tot-1], uv[0], pt) < 0.0f)
+               return 0;
+       
+       for (i=1; i<tot; i++) {
+               if (SIDE_OF_LINE(uv[i-1], uv[i], pt) < 0.0f)
+                       return 0;
+               
+       }
+       
+       return 1;
+}
+
+/* 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(const ProjPaintState *ps, const int thread_index, const int bucket_index, const int face_index, const int image_index, rctf *bucket_bounds, const ImBuf *ibuf)
+{
+       /* Projection vars, to get the 3D locations into screen space  */
+       MemArena *arena = ps->arena_mt[thread_index];
+       LinkNode **bucketPixelNodes = ps->bucketRect + bucket_index;
+       LinkNode *bucketFaceNodes = ps->bucketFaces[bucket_index];
+       
+       const MFace *mf = ps->dm_mface + face_index;
+       const MTFace *tf = ps->dm_mtface + face_index;
+       
+       /* UV/pixel seeking data */
+       int x; /* Image X-Pixel */
+       int y;/* Image Y-Pixel */
+       float mask;
+       float uv[2]; /* Image floating point UV - same as x, y but from 0.0-1.0 */
+       
+       int side;
+       float *v1coSS, *v2coSS, *v3coSS; /* vert co screen-space, these will be assigned to mf->v1,2,3 or mf->v1,3,4 */
+       
+       float *vCo[4]; /* vertex screenspace coords */
+       
+       float w[3], wco[3];
+       
+       float *uv1co, *uv2co, *uv3co; /* for convenience only, these will be assigned to tf->uv[0],1,2 or tf->uv[0],2,3 */
+       float pixelScreenCo[4];
+       
+       rcti bounds_px; /* ispace bounds */
+       /* vars for getting uvspace bounds */
+       
+       float tf_uv_pxoffset[4][2]; /* bucket bounds in UV space so we can init pixels only for this face,  */
+       float xhalfpx, yhalfpx;
+       const float ibuf_xf = ibuf->x, ibuf_yf = ibuf->y;
+       
+       int has_x_isect = 0, has_isect = 0; /* for early loop exit */
+       
+       int i1, i2, i3;
+       
+       float uv_clip[8][2];
+       int uv_clip_tot;
+       const short is_ortho = ps->is_ortho;
+       
+       vCo[0] = ps->dm_mvert[mf->v1].co;
+       vCo[1] = ps->dm_mvert[mf->v2].co;
+       vCo[2] = ps->dm_mvert[mf->v3].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.5f / ibuf_xf;
+       yhalfpx = 0.5f / ibuf_yf;
+       
+       tf_uv_pxoffset[0][0] = tf->uv[0][0] - xhalfpx;
+       tf_uv_pxoffset[0][1] = tf->uv[0][1] - yhalfpx;
+
+       tf_uv_pxoffset[1][0] = tf->uv[1][0] - xhalfpx;
+       tf_uv_pxoffset[1][1] = tf->uv[1][1] - yhalfpx;
+       
+       tf_uv_pxoffset[2][0] = tf->uv[2][0] - xhalfpx;
+       tf_uv_pxoffset[2][1] = tf->uv[2][1] - yhalfpx;  
+       
+       if (mf->v4) {
+               vCo[3] = ps->dm_mvert[ mf->v4 ].co;
+               
+               tf_uv_pxoffset[3][0] = tf->uv[3][0] - xhalfpx;
+               tf_uv_pxoffset[3][1] = tf->uv[3][1] - yhalfpx;
+               side = 1;
+       }
+       else {
+               side = 0;
+       }
+       
+       do {
+               if (side==1) {
+                       i1=0; i2=2; i3=3;
+               }
+               else {
+                       i1=0; i2=1; i3=2;
+               }
+               
+               uv1co = tf_uv_pxoffset[i1]; // was tf->uv[i1];
+               uv2co = tf_uv_pxoffset[i2]; // was tf->uv[i2];
+               uv3co = tf_uv_pxoffset[i3]; // was tf->uv[i3];
+
+               v1coSS = ps->screenCoords[ (*(&mf->v1 + i1)) ];
+               v2coSS = ps->screenCoords[ (*(&mf->v1 + i2)) ];
+               v3coSS = ps->screenCoords[ (*(&mf->v1 + i3)) ];
+
+
+               /* This funtion gives is a concave polyline in UV space from the clipped quad and tri*/
+               project_bucket_clip_face(
+                               is_ortho, bucket_bounds,
+                               v1coSS, v2coSS, v3coSS,
+                               uv1co, uv2co, uv3co,
+                               uv_clip, &uv_clip_tot
+               );
+               
+               
+               /* sometimes this happens, better just allow for 8 intersectiosn even though there should be max 6 */
+               /*
+               if (uv_clip_tot>6) {
+                       printf("this should never happen! %d\n", uv_clip_tot);
+               }*/
+               
+
+               if (pixel_bounds_array(uv_clip, &bounds_px, ibuf->x, ibuf->y, uv_clip_tot)) {
+                       
+                       /* clip face and */
+                       
+                       has_isect = 0;
+                       for (y = bounds_px.ymin; y < bounds_px.ymax; y++) {
+                               //uv[1] = (((float)y) + 0.5f) / (float)ibuf->y;
+                               uv[1] = (float)y / ibuf_yf; /* use pixel offset UV coords instead */
+                               
+                               has_x_isect = 0;
+                               for (x = bounds_px.xmin; x < bounds_px.xmax; x++) {
+                                       //uv[0] = (((float)x) + 0.5f) / ibuf->x;
+                                       uv[0] = (float)x / ibuf_xf; /* use pixel offset UV coords instead */
+                                       
+                                       if (IsectPoly2Df(uv, uv_clip, uv_clip_tot)) {
+                                               
+                                               has_x_isect = has_isect = 1;
+                                               
+                                               if (is_ortho)   screen_px_from_ortho(uv, v1coSS, v2coSS, v3coSS, uv1co, uv2co, uv3co, pixelScreenCo, w);
+                                               else                    screen_px_from_persp(uv, v1coSS, v2coSS, v3coSS, uv1co, uv2co, uv3co, pixelScreenCo, w);
+                                               
+                                               /* a pitty we need to get the worldspace pixel location here */
+                                               if(ps->rv3d->rflag & RV3D_CLIPPING) {
+                                                       VecWeightf(wco, ps->dm_mvert[ (*(&mf->v1 + i1)) ].co, ps->dm_mvert[ (*(&mf->v1 + i2)) ].co, ps->dm_mvert[ (*(&mf->v1 + i3)) ].co, w);
+                                                       Mat4MulVecfl(ps->ob->obmat, wco);
+                                                       if(view3d_test_clipping(ps->rv3d, wco)) {
+                                                               continue; /* Watch out that no code below this needs to run */
+                                                       }
+                                               }
+                                               
+                                               /* Is this UV visible from the view? - raytrace */
+                                               /* project_paint_PickFace is less complex, use for testing */
+                                               //if (project_paint_PickFace(ps, pixelScreenCo, w, &side) == face_index) {
+                                               if (ps->do_occlude==0 || !project_bucket_point_occluded(ps, bucketFaceNodes, face_index, pixelScreenCo)) {
+                                                       
+                                                       mask = project_paint_uvpixel_mask(ps, face_index, side, w);
+                                                       
+                                                       if (mask > 0.0f) {
+                                                               BLI_linklist_prepend_arena(
+                                                                       bucketPixelNodes,
+                                                                       project_paint_uvpixel_init(ps, arena, ibuf, x, y, mask, face_index, image_index, pixelScreenCo, side, w),
+                                                                       arena
+                                                               );
+                                                       }
+                                               }
+                                               
+                                       }
+//#if 0
+                                       else if (has_x_isect) {
+                                               /* assuming the face is not a bow-tie - we know we cant intersect again on the X */
+                                               break;
+                                       }
+//#endif
+                               }
+                               
+                               
+#if 0                  /* TODO - investigate why this dosnt work sometimes! it should! */
+                               /* no intersection for this entire row, after some intersection above means we can quit now */
+                               if (has_x_isect==0 && has_isect) { 
+                                       break;
+                               }
+#endif
+                       }
+               }
+       } while(side--);
+
+       
+       
+#ifndef PROJ_DEBUG_NOSEAMBLEED
+       if (ps->seam_bleed_px > 0.0f) {
+               int face_seam_flag;
+               
+               if (ps->thread_tot > 1)
+                       BLI_lock_thread(LOCK_CUSTOM1); /* Other threads could be modifying these vars */
+               
+               face_seam_flag = ps->faceSeamFlags[face_index];
+               
+               /* are any of our edges un-initialized? */
+               if ((face_seam_flag & (PROJ_FACE_SEAM1|PROJ_FACE_NOSEAM1))==0 || 
+                       (face_seam_flag & (PROJ_FACE_SEAM2|PROJ_FACE_NOSEAM2))==0 || 
+                       (face_seam_flag & (PROJ_FACE_SEAM3|PROJ_FACE_NOSEAM3))==0 || 
+                       (face_seam_flag & (PROJ_FACE_SEAM4|PROJ_FACE_NOSEAM4))==0
+               ) {
+                       project_face_seams_init(ps, face_index, mf->v4);
+                       face_seam_flag = ps->faceSeamFlags[face_index];
+                       //printf("seams - %d %d %d %d\n", flag&PROJ_FACE_SEAM1, flag&PROJ_FACE_SEAM2, flag&PROJ_FACE_SEAM3, flag&PROJ_FACE_SEAM4);
+               }
+               
+               if ((face_seam_flag & (PROJ_FACE_SEAM1|PROJ_FACE_SEAM2|PROJ_FACE_SEAM3|PROJ_FACE_SEAM4))==0) {
+                       
+                       if (ps->thread_tot > 1)
+                               BLI_unlock_thread(LOCK_CUSTOM1); /* Other threads could be modifying these vars */
+                       
+               }
+               else {
+                       /* we have a seam - deal with it! */
+                       
+                       /* Now create new UV's for the seam face */
+                       float (*outset_uv)[2] = ps->faceSeamUVs[face_index];
+                       float insetCos[4][3]; /* inset face coords.  NOTE!!! ScreenSace for ortho, Worldspace in prespective view */
+
+                       float *uv_seam_quad[4];
+                       float fac;
+                       float *vCoSS[4]; /* vertex screenspace coords */
+                       
+                       float bucket_clip_edges[2][2]; /* store the screenspace coords of the face, clipped by the bucket's screen aligned rectangle */
+                       float edge_verts_inset_clip[2][3];
+                       int fidx1, fidx2; /* face edge pairs - loop throuh these ((0,1), (1,2), (2,3), (3,0)) or ((0,1), (1,2), (2,0)) for a tri */
+                       
+                       float seam_subsection[4][2];
+                       float fac1, fac2, ftot;
+                       
+                       
+                       if (outset_uv[0][0]==FLT_MAX) /* first time initialize */
+                               uv_image_outset(tf_uv_pxoffset, outset_uv, ps->seam_bleed_px, ibuf->x, ibuf->y, mf->v4);
+                       
+                       /* ps->faceSeamUVs cant be modified when threading, now this is done we can unlock */
+                       if (ps->thread_tot > 1)
+                               BLI_unlock_thread(LOCK_CUSTOM1); /* Other threads could be modifying these vars */
+                       
+                       vCoSS[0] = ps->screenCoords[mf->v1];
+                       vCoSS[1] = ps->screenCoords[mf->v2];
+                       vCoSS[2] = ps->screenCoords[mf->v3];
+                       if (mf->v4)
+                               vCoSS[3] = ps->screenCoords[ mf->v4 ];
+                       
+                       /* PROJ_FACE_SCALE_SEAM must be slightly less then 1.0f */
+                       if (is_ortho) {
+                               if (mf->v4)     scale_quad(insetCos, vCoSS, PROJ_FACE_SCALE_SEAM);
+                               else            scale_tri(insetCos, vCoSS, PROJ_FACE_SCALE_SEAM);
+                       }
+                       else {
+                               if (mf->v4)     scale_quad(insetCos, vCo, PROJ_FACE_SCALE_SEAM);
+                               else            scale_tri(insetCos, vCo, PROJ_FACE_SCALE_SEAM);
+                       }
+                       
+                       side = 0; /* for triangles this wont need to change */
+                       
+                       for (fidx1 = 0; fidx1 < (mf->v4 ? 4 : 3); fidx1++) {
+                               if (mf->v4)             fidx2 = (fidx1==3) ? 0 : fidx1+1; /* next fidx in the face (0,1,2,3) -> (1,2,3,0) */
+                               else                    fidx2 = (fidx1==2) ? 0 : fidx1+1; /* next fidx in the face (0,1,2) -> (1,2,0) */
+                               
+                               if (    (face_seam_flag & (1<<fidx1)) && /* 1<<fidx1 -> PROJ_FACE_SEAM# */
+                                               line_clip_rect2f(bucket_bounds, vCoSS[fidx1], vCoSS[fidx2], bucket_clip_edges[0], bucket_clip_edges[1])
+                               ) {
+
+                                       ftot = Vec2Lenf(vCoSS[fidx1], vCoSS[fidx2]); /* screenspace edge length */
+                                       
+                                       if (ftot > 0.0f) { /* avoid div by zero */
+                                               if (mf->v4) {
+                                                       if (fidx1==2 || fidx2==2)       side= 1;
+                                                       else                                            side= 0;
+                                               }
+                                               
+                                               fac1 = Vec2Lenf(vCoSS[fidx1], bucket_clip_edges[0]) / ftot;
+                                               fac2 = Vec2Lenf(vCoSS[fidx1], bucket_clip_edges[1]) / ftot;
+                                               
+                                               uv_seam_quad[0] = tf_uv_pxoffset[fidx1];
+                                               uv_seam_quad[1] = tf_uv_pxoffset[fidx2];
+                                               uv_seam_quad[2] = outset_uv[fidx2];
+                                               uv_seam_quad[3] = outset_uv[fidx1];
+                                               
+                                               Vec2Lerpf(seam_subsection[0], uv_seam_quad[0], uv_seam_quad[1], fac1);
+                                               Vec2Lerpf(seam_subsection[1], uv_seam_quad[0], uv_seam_quad[1], fac2);
+                                               
+                                               Vec2Lerpf(seam_subsection[2], uv_seam_quad[3], uv_seam_quad[2], fac2);
+                                               Vec2Lerpf(seam_subsection[3], uv_seam_quad[3], uv_seam_quad[2], fac1);
+                                               
+                                               /* if the bucket_clip_edges values Z values was kept we could avoid this
+                                                * Inset needs to be added so occlusion tests wont hit adjacent faces */
+                                               VecLerpf(edge_verts_inset_clip[0], insetCos[fidx1], insetCos[fidx2], fac1);
+                                               VecLerpf(edge_verts_inset_clip[1], insetCos[fidx1], insetCos[fidx2], fac2);
+                                               
+
+                                               if (pixel_bounds_uv(seam_subsection[0], seam_subsection[1], seam_subsection[2], seam_subsection[3], &bounds_px, ibuf->x, ibuf->y, 1)) {
+                                                       /* bounds between the seam rect and the uvspace bucket pixels */
+                                                       
+                                                       has_isect = 0;
+                                                       for (y = bounds_px.ymin; y < bounds_px.ymax; y++) {
+                                                               // uv[1] = (((float)y) + 0.5f) / (float)ibuf->y;
+                                                               uv[1] = (float)y / ibuf_yf; /* use offset uvs instead */
+                                                               
+                                                               has_x_isect = 0;
+                                                               for (x = bounds_px.xmin; x < bounds_px.xmax; x++) {
+                                                                       //uv[0] = (((float)x) + 0.5f) / (float)ibuf->x;
+                                                                       uv[0] = (float)x / ibuf_xf; /* use offset uvs instead */
+                                                                       
+                                                                       /* test we're inside uvspace bucket and triangle bounds */
+                                                                       if (IsectPQ2Df(uv, seam_subsection[0], seam_subsection[1], seam_subsection[2], seam_subsection[3])) {
+                                                                               
+                                                                               /* We need to find the closest point along the face edge,
+                                                                                * getting the screen_px_from_*** wont work because our actual location
+                                                                                * is not relevent, since we are outside the face, Use VecLerpf to find
+                                                                                * our location on the side of the face's UV */
+                                                                               /*
+                                                                               if (is_ortho)   screen_px_from_ortho(ps, uv, v1co, v2co, v3co, uv1co, uv2co, uv3co, pixelScreenCo);
+                                                                               else                                    screen_px_from_persp(ps, uv, v1co, v2co, v3co, uv1co, uv2co, uv3co, pixelScreenCo);
+                                                                               */
+                                                                               
+                                                                               /* Since this is a seam we need to work out where on the line this pixel is */
+                                                                               //fac = lambda_cp_line2(uv, uv_seam_quad[0], uv_seam_quad[1]);
+                                                                               
+                                                                               fac = lambda_cp_line2(uv, seam_subsection[0], seam_subsection[1]);
+                                                                               if (fac < 0.0f)         { VECCOPY(pixelScreenCo, edge_verts_inset_clip[0]); }
+                                                                               else if (fac > 1.0f)    { VECCOPY(pixelScreenCo, edge_verts_inset_clip[1]); }
+                                                                               else                            { VecLerpf(pixelScreenCo, edge_verts_inset_clip[0], edge_verts_inset_clip[1], fac); }
+                                                                               
+                                                                               if (!is_ortho) {
+                                                                                       pixelScreenCo[3] = 1.0f;
+                                                                                       Mat4MulVec4fl((float(*)[4])ps->projectMat, pixelScreenCo); /* cast because of const */
+                                                                                       pixelScreenCo[0] = (float)(ps->ar->winx/2.0f)+(ps->ar->winx/2.0f)*pixelScreenCo[0]/pixelScreenCo[3];    
+                                                                                       pixelScreenCo[1] = (float)(ps->ar->winy/2.0f)+(ps->ar->winy/2.0f)*pixelScreenCo[1]/pixelScreenCo[3];
+                                                                                       pixelScreenCo[2] = pixelScreenCo[2]/pixelScreenCo[3]; /* Use the depth for bucket point occlusion */
+                                                                               }
+                                                                               
+                                                                               if (ps->do_occlude==0 || !project_bucket_point_occluded(ps, bucketFaceNodes, face_index, pixelScreenCo)) {
+                                                                                       
+                                                                                       /* Only bother calculating the weights if we intersect */
+                                                                                       if (ps->do_mask_normal || ps->dm_mtface_clone) {
+                                                                                               /* TODO, this is not QUITE correct since UV is not inside the UV's but good enough for seams */
+                                                                                               if (side) {
+                                                                                                       BarycentricWeights2f(uv, tf_uv_pxoffset[0], tf_uv_pxoffset[2], tf_uv_pxoffset[3], w);
+                                                                                               }
+                                                                                               else {
+                                                                                                       BarycentricWeights2f(uv, tf_uv_pxoffset[0], tf_uv_pxoffset[1], tf_uv_pxoffset[2], w);
+                                                                                               }
+                                                                                               
+                                                                                       }
+                                                                                       
+                                                                                       /* a pitty we need to get the worldspace pixel location here */
+                                                                                       if(ps->rv3d->rflag & RV3D_CLIPPING) {
+                                                                                               if (side)       VecWeightf(wco, ps->dm_mvert[mf->v1].co, ps->dm_mvert[mf->v3].co, ps->dm_mvert[mf->v4].co, w);
+                                                                                               else            VecWeightf(wco, ps->dm_mvert[mf->v1].co, ps->dm_mvert[mf->v2].co, ps->dm_mvert[mf->v3].co, w);
+                                                                                               
+                                                                                               Mat4MulVecfl(ps->ob->obmat, wco);
+                                                                                               if(view3d_test_clipping(ps->rv3d, wco)) {
+                                                                                                       continue; /* Watch out that no code below this needs to run */
+                                                                                               }
+                                                                                       }
+                                                                                       
+                                                                                       mask = project_paint_uvpixel_mask(ps, face_index, side, w);
+                                                                                       
+                                                                                       if (mask > 0.0f) {
+                                                                                               BLI_linklist_prepend_arena(
+                                                                                                       bucketPixelNodes,
+                                                                                                       project_paint_uvpixel_init(ps, arena, ibuf, x, y, mask, face_index, image_index, pixelScreenCo, side, w),
+                                                                                                       arena
+                                                                                               );
+                                                                                       }
+                                                                                       
+                                                                               }
+                                                                       }
+                                                                       else if (has_x_isect) {
+                                                                               /* assuming the face is not a bow-tie - we know we cant intersect again on the X */
+                                                                               break;
+                                                                       }
+                                                               }
+                                                               
+#if 0                                                  /* TODO - investigate why this dosnt work sometimes! it should! */
+                                                               /* no intersection for this entire row, after some intersection above means we can quit now */
+                                                               if (has_x_isect==0 && has_isect) { 
+                                                                       break;
+                                                               }
+#endif
+                                                       }
+                                               }
+                                       }
+                               }
+                       }
+               }
+       }
+#endif // PROJ_DEBUG_NOSEAMBLEED
+}
+
+
+/* takes floating point screenspace min/max and returns int min/max to be used as indicies for ps->bucketRect, ps->bucketFlags */
+static void project_paint_bucket_bounds(const ProjPaintState *ps, const float min[2], const float max[2], int bucketMin[2], int bucketMax[2])
+{
+       /* divide by bucketWidth & bucketHeight so the bounds are offset in bucket grid units */
+       bucketMin[0] = (int)(((float)(min[0] - ps->screenMin[0]) / ps->screen_width) * ps->buckets_x) + 0.5f; /* these offsets of 0.5 and 1.5 seem odd but they are correct */
+       bucketMin[1] = (int)(((float)(min[1] - ps->screenMin[1]) / ps->screen_height) * ps->buckets_y) + 0.5f;
+       
+       bucketMax[0] = (int)(((float)(max[0] - ps->screenMin[0]) / ps->screen_width) * ps->buckets_x) + 1.5f;
+       bucketMax[1] = (int)(((float)(max[1] - ps->screenMin[1]) / ps->screen_height) * ps->buckets_y) + 1.5f;  
+       
+       /* incase the rect is outside the mesh 2d bounds */
+       CLAMP(bucketMin[0], 0, ps->buckets_x);
+       CLAMP(bucketMin[1], 0, ps->buckets_y);
+       
+       CLAMP(bucketMax[0], 0, ps->buckets_x);
+       CLAMP(bucketMax[1], 0, ps->buckets_y);
+}
+
+/* set bucket_bounds to a screen space-aligned floating point bound-box */
+static void project_bucket_bounds(const ProjPaintState *ps, const int bucket_x, const int bucket_y, rctf *bucket_bounds)
+{
+       bucket_bounds->xmin =   ps->screenMin[0]+((bucket_x)*(ps->screen_width / ps->buckets_x));               /* left */
+       bucket_bounds->xmax =   ps->screenMin[0]+((bucket_x+1)*(ps->screen_width / ps->buckets_x));     /* right */
+       
+       bucket_bounds->ymin =   ps->screenMin[1]+((bucket_y)*(ps->screen_height / ps->buckets_y));              /* bottom */
+       bucket_bounds->ymax =   ps->screenMin[1]+((bucket_y+1)*(ps->screen_height  / ps->buckets_y));   /* top */
+}
+
+/* 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_bucket_init(const ProjPaintState *ps, const int thread_index, const int bucket_index, rctf *bucket_bounds)
+{
+       LinkNode *node;
+       int face_index, image_index;
+       ImBuf *ibuf = NULL;
+       MTFace *tf;
+       
+       Image *tpage_last = NULL;
+       
+
+       if (ps->image_tot==1) {
+               /* Simple loop, no context switching */
+               ibuf = ps->projImages[0].ibuf;
+               
+               for (node = ps->bucketFaces[bucket_index]; node; node= node->next) { 
+                       project_paint_face_init(ps, thread_index, bucket_index, GET_INT_FROM_POINTER(node->link), 0, bucket_bounds, ibuf);
+               }
+       }
+       else {
+               
+               /* More complicated loop, switch between images */
+               for (node = ps->bucketFaces[bucket_index]; node; node= node->next) {
+                       face_index = GET_INT_FROM_POINTER(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 */
+                       
+                       project_paint_face_init(ps, thread_index, bucket_index, face_index, image_index, bucket_bounds, ibuf);
+                       
+               }
+       }
+       
+       ps->bucketFlags[bucket_index] |= PROJ_BUCKET_INIT;
+}
+
+
+/* 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 shouldn't have bugs though */
+
+static int project_bucket_face_isect(ProjPaintState *ps, float min[2], float max[2], int bucket_x, int bucket_y, int bucket_index, const MFace *mf)
+{
+       /* TODO - replace this with a tricker method that uses sideofline for all screenCoords's edges against the closest bucket corner */
+       rctf bucket_bounds;
+       float p1[2], p2[2], p3[2], p4[2];
+       float *v, *v1,*v2,*v3,*v4;
+       int fidx;
+       
+       project_bucket_bounds(ps, bucket_x, bucket_y, &bucket_bounds);
+       
+       /* Is one of the faces verts in the bucket bounds? */
+       
+       fidx = mf->v4 ? 3:2;
+       do {
+               v = ps->screenCoords[ (*(&mf->v1 + fidx)) ];
+               if (BLI_in_rctf(&bucket_bounds, v[0], v[1])) {
+                       return 1;
+               }
+       } while (fidx--);
+       
+       v1 = ps->screenCoords[mf->v1];
+       v2 = ps->screenCoords[mf->v2];
+       v3 = ps->screenCoords[mf->v3];
+       if (mf->v4) {
+               v4 = ps->screenCoords[mf->v4];
+       }
+       
+       p1[0] = bucket_bounds.xmin; p1[1] = bucket_bounds.ymin;
+       p2[0] = bucket_bounds.xmin;     p2[1] = bucket_bounds.ymax;
+       p3[0] = bucket_bounds.xmax;     p3[1] = bucket_bounds.ymax;
+       p4[0] = bucket_bounds.xmax;     p4[1] = bucket_bounds.ymin;
+               
+       if (mf->v4) {
+               if(     IsectPQ2Df(p1, v1, v2, v3, v4) || IsectPQ2Df(p2, v1, v2, v3, v4) || IsectPQ2Df(p3, v1, v2, v3, v4) || IsectPQ2Df(p4, v1, v2, v3, v4) ||
+                       /* we can avoid testing v3,v1 because another intersection MUST exist if this intersects */
+                       (IsectLL2Df(p1, p2, v1, v2) || IsectLL2Df(p1, p2, v2, v3) || IsectLL2Df(p1, p2, v3, v4)) ||
+                       (IsectLL2Df(p2, p3, v1, v2) || IsectLL2Df(p2, p3, v2, v3) || IsectLL2Df(p2, p3, v3, v4)) ||
+                       (IsectLL2Df(p3, p4, v1, v2) || IsectLL2Df(p3, p4, v2, v3) || IsectLL2Df(p3, p4, v3, v4)) ||
+                       (IsectLL2Df(p4, p1, v1, v2) || IsectLL2Df(p4, p1, v2, v3) || IsectLL2Df(p4, p1, v3, v4))
+               ) {
+                       return 1;
+               }
+       }
+       else {
+               if(     IsectPT2Df(p1, v1, v2, v3) || IsectPT2Df(p2, v1, v2, v3) || IsectPT2Df(p3, v1, v2, v3) || IsectPT2Df(p4, v1, v2, v3) ||
+                       /* we can avoid testing v3,v1 because another intersection MUST exist if this intersects */
+                       (IsectLL2Df(p1, p2, v1, v2) || IsectLL2Df(p1, p2, v2, v3)) ||
+                       (IsectLL2Df(p2, p3, v1, v2) || IsectLL2Df(p2, p3, v2, v3)) ||
+                       (IsectLL2Df(p3, p4, v1, v2) || IsectLL2Df(p3, p4, v2, v3)) ||
+                       (IsectLL2Df(p4, p1, v1, v2) || IsectLL2Df(p4, p1, v2, v3))
+               ) {
+                       return 1;
+               }
+       }
+
+       return 0;
+}
+
+/* Add faces to the bucket but dont initialize its pixels
+ * TODO - when painting occluded, sort the faces on their min-Z and only add faces that faces that are not occluded */
+static void project_paint_delayed_face_init(ProjPaintState *ps, const MFace *mf, const MTFace *tf, const int face_index)
+{
+       float min[2], max[2], *vCoSS;
+       int bucketMin[2], bucketMax[2]; /* for  ps->bucketRect indexing */
+       int fidx, bucket_x, bucket_y, bucket_index;
+       int has_x_isect = -1, has_isect = 0; /* for early loop exit */
+       MemArena *arena = ps->arena_mt[0]; /* just use the first thread arena since threading has not started yet */
+       
+       INIT_MINMAX2(min, max);
+       
+       fidx = mf->v4 ? 3:2;
+       do {
+               vCoSS = ps->screenCoords[ *(&mf->v1 + fidx) ];
+               DO_MINMAX2(vCoSS, min, max);
+       } while (fidx--);
+       
+       project_paint_bucket_bounds(ps, min, max, bucketMin, bucketMax);
+       
+       for (bucket_y = bucketMin[1]; bucket_y < bucketMax[1]; bucket_y++) {
+               has_x_isect = 0;
+               for (bucket_x = bucketMin[0]; bucket_x < bucketMax[0]; bucket_x++) {
+                       
+                       bucket_index = bucket_x + (bucket_y * ps->buckets_x);
+                       
+                       if (project_bucket_face_isect(ps, min, max, bucket_x, bucket_y, bucket_index, mf)) {
+                               BLI_linklist_prepend_arena(
+                                       &ps->bucketFaces[ bucket_index ],
+                                       SET_INT_IN_POINTER(face_index), /* cast to a pointer to shut up the compiler */
+                                       arena
+                               );
+                               
+                               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;
+               }
+       }
+       
+#ifndef PROJ_DEBUG_NOSEAMBLEED
+       if (ps->seam_bleed_px > 0.0f) {
+               if (!mf->v4) {
+                       ps->faceSeamFlags[face_index] |= PROJ_FACE_NOSEAM4; /* so this wont show up as an untagged edge */
+               }
+               **ps->faceSeamUVs[face_index] = FLT_MAX; /* set as uninitialized */
+       }
+#endif
+}
+
+/* run once per stroke before projection painting */
+static void project_paint_begin(ProjPaintState *ps)
+{      
+       /* Viewport vars */
+       float mat[3][3];
+       
+       float no[3];
+       
+       float (*projScreenCo)[4]; /* Note, we could have 4D vectors are only needed for */
+       float projMargin;
+       /* Image Vars - keep track of images we have used */
+       LinkNode *image_LinkList = NULL;
+       LinkNode *node;
+       
+       ProjPaintImage *projIma;
+       Image *tpage_last = NULL;
+       
+       /* Face vars */
+       MFace *mf;
+       MTFace *tf;
+       
+       int a, i; /* generic looping vars */
+       int image_index = -1, face_index;
+       
+       MemArena *arena; /* at the moment this is just ps->arena_mt[0], but use this to show were not multithreading */
+       
+       /* ---- end defines ---- */
+       
+       /* paint onto the derived mesh */
+       ps->dm = mesh_get_derived_final(ps->scene, ps->ob, ps->v3d->customdata_mask);
+       
+       if ( !CustomData_has_layer( &ps->dm->faceData, CD_MTFACE) ) {
+               ps->dm = NULL;
+               return; 
+       }
+       ps->dm_mvert = ps->dm->getVertArray(ps->dm);
+       ps->dm_mface = ps->dm->getFaceArray(ps->dm);
+       ps->dm_mtface= ps->dm->getFaceDataArray(ps->dm, CD_MTFACE);
+       
+       ps->dm_totvert = ps->dm->getNumVerts(ps->dm);
+       ps->dm_totface = ps->dm->getNumFaces(ps->dm);
+       
+       /* use clone mtface? */
+       
+       
+       /* Note, use the original mesh for getting the clone and mask layer index
+        * this avoids re-generating the derived mesh just to get the new index */
+       if (ps->do_layer_clone) {
+               //int layer_num = CustomData_get_clone_layer(&ps->dm->faceData, CD_MTFACE);
+               int layer_num = CustomData_get_clone_layer(&((Mesh *)ps->ob->data)->fdata, CD_MTFACE);
+               if (layer_num != -1)
+                       ps->dm_mtface_clone = CustomData_get_layer_n(&ps->dm->faceData, CD_MTFACE, layer_num);
+               
+               if (ps->dm_mtface_clone==NULL || ps->dm_mtface_clone==ps->dm_mtface) {
+                       ps->do_layer_clone = 0;
+                       ps->dm_mtface_clone= NULL;
+               }
+       }
+       
+       if (ps->do_layer_mask) {
+               //int layer_num = CustomData_get_mask_layer(&ps->dm->faceData, CD_MTFACE);
+               int layer_num = CustomData_get_mask_layer(&((Mesh *)ps->ob->data)->fdata, CD_MTFACE);
+               if (layer_num != -1)
+                       ps->dm_mtface_mask = CustomData_get_layer_n(&ps->dm->faceData, CD_MTFACE, layer_num);
+               
+               if (ps->dm_mtface_mask==NULL || ps->dm_mtface_mask==ps->dm_mtface) {
+                       ps->do_layer_mask = 0;
+                       ps->dm_mtface_mask = NULL;
+               }
+       }
+       
+
+       
+       ps->viewDir[0] = 0.0f;
+       ps->viewDir[1] = 0.0f;
+       ps->viewDir[2] = 1.0f;
+       
+       view3d_get_object_project_mat(ps->rv3d, ps->ob, ps->projectMat, ps->viewMat);
+       
+       /* viewDir - object relative */
+       Mat4Invert(ps->ob->imat, ps->ob->obmat);
+       Mat3CpyMat4(mat, ps->rv3d->viewinv);
+       Mat3MulVecfl(mat, ps->viewDir);
+       Mat3CpyMat4(mat, ps->ob->imat);
+       Mat3MulVecfl(mat, ps->viewDir);
+       Normalize(ps->viewDir);
+       
+       /* viewPos - object relative */
+       VECCOPY(ps->viewPos, ps->rv3d->viewinv[3]);
+       Mat3CpyMat4(mat, ps->ob->imat);
+       Mat3MulVecfl(mat, ps->viewPos);
+       VecAddf(ps->viewPos, ps->viewPos, ps->ob->imat[3]);
+       
+       {       /* only use these for running 'get_view3d_viewplane' */
+               rctf viewplane;
+               
+               ps->is_ortho = get_view3d_viewplane(ps->v3d, ps->rv3d, ps->ar->winx, ps->ar->winy, &viewplane, &ps->clipsta, &ps->clipend, NULL);
+               
+               //printf("%f %f\n", ps->clipsta, ps->clipend);
+               if (ps->is_ortho) { /* only needed for ortho */
+                       float fac = 2.0f / (ps->clipend - ps->clipsta);  
+                       ps->clipsta *= fac;
+                       ps->clipend *= fac;
+               }
+               else {
+                       /* TODO - can we even adjust for clip start/end? */
+               }
+               
+       }
+       
+       ps->is_airbrush = (ps->brush->flag & BRUSH_AIRBRUSH) ? 1 : 0;
+       
+       ps->is_texbrush = (ps->brush->mtex[ps->brush->texact] && ps->brush->mtex[ps->brush->texact]->tex) ? 1 : 0;
+
+       
+       /* calculate vert screen coords
+        * run this early so we can calculate the x/y resolution of our bucket rect */
+       INIT_MINMAX2(ps->screenMin, ps->screenMax);
+       
+       ps->screenCoords = MEM_mallocN(sizeof(float) * ps->dm_totvert * 4, "ProjectPaint ScreenVerts");
+       projScreenCo = ps->screenCoords;
+       
+       if (ps->is_ortho) {
+               for(a=0; a < ps->dm_totvert; a++, projScreenCo++) {
+                       VECCOPY((*projScreenCo), ps->dm_mvert[a].co);
+                       Mat4MulVecfl(ps->projectMat, (*projScreenCo));
+                       
+                       /* screen space, not clamped */
+                       (*projScreenCo)[0] = (float)(ps->ar->winx/2.0f)+(ps->ar->winx/2.0f)*(*projScreenCo)[0];
+                       (*projScreenCo)[1] = (float)(ps->ar->winy/2.0f)+(ps->ar->winy/2.0f)*(*projScreenCo)[1];
+                       DO_MINMAX2((*projScreenCo), ps->screenMin, ps->screenMax);
+               }
+       }
+       else {
+               for(a=0; a < ps->dm_totvert; a++, projScreenCo++) {
+                       VECCOPY((*projScreenCo), ps->dm_mvert[a].co);
+                       (*projScreenCo)[3] = 1.0f;
+
+                       Mat4MulVec4fl(ps->projectMat, (*projScreenCo));
+
+                       
+                       if ((*projScreenCo)[3] > ps->clipsta) {
+                               /* screen space, not clamped */
+                               (*projScreenCo)[0] = (float)(ps->ar->winx/2.0f)+(ps->ar->winx/2.0f)*(*projScreenCo)[0]/(*projScreenCo)[3];
+                               (*projScreenCo)[1] = (float)(ps->ar->winy/2.0f)+(ps->ar->winy/2.0f)*(*projScreenCo)[1]/(*projScreenCo)[3];
+                               (*projScreenCo)[2] = (*projScreenCo)[2]/(*projScreenCo)[3]; /* Use the depth for bucket point occlusion */
+                               DO_MINMAX2((*projScreenCo), ps->screenMin, ps->screenMax);
+                       }
+                       else {
+                               /* TODO - deal with cases where 1 side of a face goes behind the view ?
+                                * 
+                                * After some research this is actually very tricky, only option is to
+                                * clip the derived mesh before painting, which is a Pain */
+                               (*projScreenCo)[0] = FLT_MAX;
+                       }
+               }
+       }
+       
+       /* If this border is not added we get artifacts for faces that
+        * have a parallel edge and at the bounds of the the 2D projected verts eg
+        * - a single screen aligned quad */
+       projMargin = (ps->screenMax[0] - ps->screenMin[0]) * 0.000001f;
+       ps->screenMax[0] += projMargin;
+       ps->screenMin[0] -= projMargin;
+       projMargin = (ps->screenMax[1] - ps->screenMin[1]) * 0.000001f;
+       ps->screenMax[1] += projMargin;
+       ps->screenMin[1] -= projMargin;
+       
+#ifdef PROJ_DEBUG_WINCLIP
+       CLAMP(ps->screenMin[0], -ps->brush->size, ps->ar->winx + ps->brush->size);
+       CLAMP(ps->screenMax[0], -ps->brush->size, ps->ar->winx + ps->brush->size);
+
+       CLAMP(ps->screenMin[1], -ps->brush->size, ps->ar->winy + ps->brush->size);
+       CLAMP(ps->screenMax[1], -ps->brush->size, ps->ar->winy + ps->brush->size);
+#endif
+       
+       /* only for convenience */
+       ps->screen_width  = ps->screenMax[0] - ps->screenMin[0];
+       ps->screen_height = ps->screenMax[1] - ps->screenMin[1];
+       
+       ps->buckets_x = (int)(ps->screen_width / (((float)ps->brush->size) / PROJ_BUCKET_BRUSH_DIV));
+       ps->buckets_y = (int)(ps->screen_height / (((float)ps->brush->size) / PROJ_BUCKET_BRUSH_DIV));
+       
+       printf("\tscreenspace bucket division x:%d y:%d\n", ps->buckets_x, ps->buckets_y);
+       
+       /* really high values could cause problems since it has to allocate a few
+        * (ps->buckets_x*ps->buckets_y) sized arrays  */
+       CLAMP(ps->buckets_x, PROJ_BUCKET_RECT_MIN, PROJ_BUCKET_RECT_MAX);
+       CLAMP(ps->buckets_y, PROJ_BUCKET_RECT_MIN, PROJ_BUCKET_RECT_MAX);
+       
+       ps->bucketRect = (LinkNode **)MEM_callocN(sizeof(LinkNode *) * ps->buckets_x * ps->buckets_y, "paint-bucketRect");
+       ps->bucketFaces= (LinkNode **)MEM_callocN(sizeof(LinkNode *) * ps->buckets_x * ps->buckets_y, "paint-bucketFaces");
+       
+       ps->bucketFlags= (unsigned char *)MEM_callocN(sizeof(char) * ps->buckets_x * ps->buckets_y, "paint-bucketFaces");
+#ifndef PROJ_DEBUG_NOSEAMBLEED
+       if (ps->seam_bleed_px > 0.0f) {
+               ps->vertFaces= (LinkNode **)MEM_callocN(sizeof(LinkNode *) * ps->dm_totvert, "paint-vertFaces");
+               ps->faceSeamFlags = (char *)MEM_callocN(sizeof(char) * ps->dm_totface, "paint-faceSeamFlags");
+               ps->faceSeamUVs= MEM_mallocN(sizeof(float) * ps->dm_totface * 8, "paint-faceSeamUVs");
+       }
+#endif
+       
+       /* Thread stuff
+        * 
+        * very small brushes run a lot slower multithreaded since the advantage with
+        * threads is being able to fill in multiple buckets at once.
+        * Only use threads for bigger brushes. */
+       
+       if (ps->scene->r.mode & R_FIXED_THREADS) {
+               ps->thread_tot = ps->scene->r.threads;
+       }
+       else {
+               ps->thread_tot = BLI_system_thread_count();
+       }
+       for (a=0; a<ps->thread_tot; a++) {
+               ps->arena_mt[a] = BLI_memarena_new(1<<16);
+       }
+       
+       arena = ps->arena_mt[0]; 
+       
+       if (ps->do_backfacecull && ps->do_mask_normal) {
+               MVert *v = ps->dm_mvert;
+               float viewDirPersp[3];
+               
+               ps->vertFlags = MEM_callocN(sizeof(char) * ps->dm_totvert, "paint-vertFlags");
+               
+               for(a=0; a < ps->dm_totvert; a++, v++) {
+                       no[0] = (float)(v->no[0] / 32767.0f);
+                       no[1] = (float)(v->no[1] / 32767.0f);
+                       no[2] = (float)(v->no[2] / 32767.0f);
+                       
+                       if (ps->is_ortho) {
+                               if (NormalizedVecAngle2(ps->viewDir, no) >= ps->normal_angle) { /* 1 vert of this face is towards us */
+                                       ps->vertFlags[a] |= PROJ_VERT_CULL;
+                               }
+                       }
+                       else {
+                               VecSubf(viewDirPersp, ps->viewPos, v->co);
+                               Normalize(viewDirPersp);
+                               if (NormalizedVecAngle2(viewDirPersp, no) >= ps->normal_angle) { /* 1 vert of this face is towards us */
+                                       ps->vertFlags[a] |= PROJ_VERT_CULL;
+                               }
+                       }
+               }
+       }
+       
+
+       for(face_index = 0, tf = ps->dm_mtface, mf = ps->dm_mface; face_index < ps->dm_totface; mf++, tf++, face_index++) {
+               
+#ifndef PROJ_DEBUG_NOSEAMBLEED
+               /* add face user if we have bleed enabled, set the UV seam flags later */
+               /* annoying but we need to add all faces even ones we never use elsewhere */
+               if (ps->seam_bleed_px > 0.0f) {
+                       BLI_linklist_prepend_arena(&ps->vertFaces[mf->v1], SET_INT_IN_POINTER(face_index), arena);
+                       BLI_linklist_prepend_arena(&ps->vertFaces[mf->v2], SET_INT_IN_POINTER(face_index), arena);
+                       BLI_linklist_prepend_arena(&ps->vertFaces[mf->v3], SET_INT_IN_POINTER(face_index), arena);
+                       if (mf->v4) {
+                               BLI_linklist_prepend_arena(&ps->vertFaces[ mf->v4 ], SET_INT_IN_POINTER(face_index), arena);
+                       }
+               }
+#endif
+               
+               if (tf->tpage && ((G.f & G_FACESELECT)==0 || mf->flag & ME_FACE_SEL)) {
+                       
+                       float *v1coSS, *v2coSS, *v3coSS, *v4coSS;
+                       
+                       v1coSS = ps->screenCoords[mf->v1]; 
+                       v2coSS = ps->screenCoords[mf->v2]; 
+                       v3coSS = ps->screenCoords[mf->v3];
+                       if (mf->v4) {
+                               v4coSS = ps->screenCoords[mf->v4]; 
+                       }
+                       
+                       
+                       if (!ps->is_ortho) {
+                               if (    v1coSS[0]==FLT_MAX ||
+                                               v2coSS[0]==FLT_MAX ||
+                                               v3coSS[0]==FLT_MAX ||
+                                               (mf->v4 && v4coSS[0]==FLT_MAX)
+                               ) {
+                                       continue;
+                               }
+                       }
+                       
+#ifdef PROJ_DEBUG_WINCLIP
+                       /* ignore faces outside the view */
+                       if (
+                                  (v1coSS[0] < ps->screenMin[0] &&
+                                       v2coSS[0] < ps->screenMin[0] &&
+                                       v3coSS[0] < ps->screenMin[0] &&
+                                       (mf->v4 && v4coSS[0] < ps->screenMin[0])) ||
+                                       
+                                  (v1coSS[0] > ps->screenMax[0] &&
+                                       v2coSS[0] > ps->screenMax[0] &&
+                                       v3coSS[0] > ps->screenMax[0] &&
+                                       (mf->v4 && v4coSS[0] > ps->screenMax[0])) ||
+                                       
+                                  (v1coSS[1] < ps->screenMin[1] &&
+                                       v2coSS[1] < ps->screenMin[1] &&
+                                       v3coSS[1] < ps->screenMin[1] &&
+                                       (mf->v4 && v4coSS[1] < ps->screenMin[1])) ||
+                                       
+                                  (v1coSS[1] > ps->screenMax[1] &&
+                                       v2coSS[1] > ps->screenMax[1] &&
+                                       v3coSS[1] > ps->screenMax[1] &&
+                                       (mf->v4 && v4coSS[1] > ps->screenMax[1]))
+                       ) {
+                               continue;
+                       }
+                       
+#endif //PROJ_DEBUG_WINCLIP
+       
+                       
+                       if (ps->do_backfacecull) {
+                               if (ps->do_mask_normal) {
+                                       /* Since we are interpolating the normals of faces, we want to make 
+                                        * sure all the verts are pointing away from the view,
+                                        * not just the face */
+                                       if (    (ps->vertFlags[mf->v1] & PROJ_VERT_CULL) &&
+                                                       (ps->vertFlags[mf->v2] & PROJ_VERT_CULL) &&
+                                                       (ps->vertFlags[mf->v3] & PROJ_VERT_CULL) &&
+                                                       (mf->v4==0 || ps->vertFlags[mf->v4] & PROJ_VERT_CULL)
+                                                       
+                                       ) {
+                                               continue;
+                                       }
+                               }
+                               else {
+                                       if (SIDE_OF_LINE(v1coSS, v2coSS, v3coSS) < 0.0f) {
+                                               continue;
+                                       }
+                                       
+                               }
+                       }
+                       
+                       if (tpage_last != tf->tpage) {
+                               
+                               image_index = BLI_linklist_index(image_LinkList, tf->tpage);
+                               
+                               if (image_index==-1 && BKE_image_get_ibuf((Image *)tf->tpage, NULL)) { /* MemArena dosnt have an append func */
+                                       BLI_linklist_append(&image_LinkList, tf->tpage);
+                                       image_index = ps->image_tot;
+                                       ps->image_tot++;
+                               }
+                               
+                               tpage_last = tf->tpage;
+                       }
+                       
+                       if (image_index != -1) {
+                               /* Initialize the faces screen pixels */
+                               /* Add this to a list to initialize later */
+                               project_paint_delayed_face_init(ps, mf, tf, face_index);
+                       }
+               }
+       }
+       
+       /* build an array of images we use*/
+       projIma = ps->projImages = (ProjPaintImage *)BLI_memarena_alloc(arena, sizeof(ProjPaintImage) * ps->image_tot);
+       
+       for (node= image_LinkList, i=0; node; node= node->next, i++, projIma++) {
+               projIma->ima = node->link;
+               // calloced - projIma->touch = 0;
+               projIma->ibuf = BKE_image_get_ibuf(projIma->ima, NULL);
+               projIma->partRedrawRect =  BLI_memarena_alloc(arena, sizeof(ImagePaintPartialRedraw) * PROJ_BOUNDBOX_SQUARED);
+               // calloced - memset(projIma->partRedrawRect, 0, sizeof(ImagePaintPartialRedraw) * PROJ_BOUNDBOX_SQUARED);
+       }
+       
+       /* we have built the array, discard the linked list */
+       BLI_linklist_free(image_LinkList, NULL);
+}
+
+static void project_paint_begin_clone(ProjPaintState *ps, int mouse[2])
+{
+       /* setup clone offset */
+       if (ps->tool == PAINT_TOOL_CLONE) {
+               float projCo[4];
+               float *curs= give_cursor(ps->scene, ps->v3d);
+               VECCOPY(projCo, curs);
+               Mat4MulVecfl(ps->ob->imat, projCo);
+               
+               projCo[3] = 1.0f;
+               Mat4MulVec4fl(ps->projectMat, projCo);
+               ps->cloneOffset[0] = mouse[0] - ((float)(ps->ar->winx/2.0f)+(ps->ar->winx/2.0f)*projCo[0]/projCo[3]);
+               ps->cloneOffset[1] = mouse[1] - ((float)(ps->ar->winy/2.0f)+(ps->ar->winy/2.0f)*projCo[1]/projCo[3]);
+       }       
+}      
+
+static void project_paint_end(ProjPaintState *ps)
+{
+       int a;
+       
+       /* build undo data from original pixel colors */
+       if(U.uiflag & USER_GLOBALUNDO) {
+               ProjPixel *projPixel;
+               ImBuf *tmpibuf = NULL, *tmpibuf_float = NULL;
+               LinkNode *pixel_node;
+               UndoTile *tile;
+               MemArena *arena = ps->arena_mt[0]; /* threaded arena re-used for non threaded case */
+                               
+               int bucket_tot = (ps->buckets_x * ps->buckets_y); /* we could get an X/Y but easier to loop through all possible buckets */
+               int bucket_index; 
+               int tile_index;
+               int x_round, y_round;
+               int x_tile, y_tile;
+               int is_float = -1;
+               
+               /* context */
+               ProjPaintImage *last_projIma;
+               int last_image_index = -1;
+               int last_tile_width;
+               
+               for(a=0, last_projIma=ps->projImages; a < ps->image_tot; a++, last_projIma++) {
+                       int size = sizeof(UndoTile **) * IMAPAINT_TILE_NUMBER(last_projIma->ibuf->x) * IMAPAINT_TILE_NUMBER(last_projIma->ibuf->y);
+                       last_projIma->undoRect = (UndoTile **) BLI_memarena_alloc(arena, size);
+                       memset(last_projIma->undoRect, 0, size);
+               }
+               
+               for (bucket_index = 0; bucket_index < bucket_tot; bucket_index++) {
+                       /* loop through all pixels */
+                       for(pixel_node= ps->bucketRect[bucket_index]; pixel_node; pixel_node= pixel_node->next) {
+                       
+                               /* ok we have a pixel, was it modified? */
+                               projPixel = (ProjPixel *)pixel_node->link;
+                               
+                               if (last_image_index != projPixel->image_index) {
+                                       /* set the context */
+                                       last_image_index =      projPixel->image_index;
+                                       last_projIma =          ps->projImages + last_image_index;
+                                       last_tile_width =       IMAPAINT_TILE_NUMBER(last_projIma->ibuf->x);
+                                       is_float =                      last_projIma->ibuf->rect_float ? 1 : 0;
+                               }
+                               
+                               
+                               if (    (is_float == 0 && projPixel->origColor.uint != *projPixel->pixel.uint_pt) || 
+                                                               
+                                               (is_float == 1 && 
+                                               (       projPixel->origColor.f[0] != projPixel->pixel.f_pt[0] || 
+                                                       projPixel->origColor.f[1] != projPixel->pixel.f_pt[1] ||
+                                                       projPixel->origColor.f[2] != projPixel->pixel.f_pt[2] ||
+                                                       projPixel->origColor.f[3] != projPixel->pixel.f_pt[3] ))
+                               ) {
+                                       
+                                       x_tile =  projPixel->x_px >> IMAPAINT_TILE_BITS;
+                                       y_tile =  projPixel->y_px >> IMAPAINT_TILE_BITS;
+                                       
+                                       x_round = x_tile * IMAPAINT_TILE_SIZE;
+                                       y_round = y_tile * IMAPAINT_TILE_SIZE;
+                                       
+                                       tile_index = x_tile + y_tile * last_tile_width;
+                                       
+                                       if (last_projIma->undoRect[tile_index]==NULL) {
+                                               /* add the undo tile from the modified image, then write the original colors back into it */
+                                               tile = last_projIma->undoRect[tile_index] = undo_init_tile(&last_projIma->ima->id, last_projIma->ibuf, is_float ? (&tmpibuf_float):(&tmpibuf) , x_tile, y_tile);
+                                       }
+                                       else {
+                                               tile = last_projIma->undoRect[tile_index];
+                                       }
+                                       
+                                       /* 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 */
+                                       
+                                       if (is_float) {
+                                               float *rgba_fp = (float *)tile->rect + (((projPixel->x_px - x_round) + (projPixel->y_px - y_round) * IMAPAINT_TILE_SIZE)) * 4;
+                                               QUATCOPY(rgba_fp, projPixel->origColor.f);
+                                       }
+                                       else {
+                                               ((unsigned int *)tile->rect)[ (projPixel->x_px - x_round) + (projPixel->y_px - y_round) * IMAPAINT_TILE_SIZE ] = projPixel->origColor.uint;
+                                       }
+                               }
+                       }
+               }
+               
+               if (tmpibuf)            IMB_freeImBuf(tmpibuf);
+               if (tmpibuf_float)      IMB_freeImBuf(tmpibuf_float);
+       }
+       /* done calculating undo data */
+       
+       MEM_freeN(ps->screenCoords);
+       MEM_freeN(ps->bucketRect);
+       MEM_freeN(ps->bucketFaces);
+       MEM_freeN(ps->bucketFlags);
+       
+       if (ps->seam_bleed_px > 0.0f) {
+               MEM_freeN(ps->vertFaces);
+               MEM_freeN(ps->faceSeamFlags);
+               MEM_freeN(ps->faceSeamUVs);
+       }
+       
+       if (ps->vertFlags) MEM_freeN(ps->vertFlags);
+       
+       
+       for (a=0; a<ps->thread_tot; a++) {
+               BLI_memarena_free(ps->arena_mt[a]);
+       }
+       
+       ps->dm->release(ps->dm);
+}
+
+/* 1= an undo, -1 is a redo. */
+static void partial_redraw_array_init(ImagePaintPartialRedraw *pr)
+{
+       int tot = PROJ_BOUNDBOX_SQUARED;
+       while (tot--) {
+               pr->x1 = 10000000;
+               pr->y1 = 10000000;
+               
+               pr->x2 = -1;
+               pr->y2 = -1;
+               
+               pr->enabled = 1;
+               
+               pr++;
+       }
+}
+
+
+static int partial_redraw_array_merge(ImagePaintPartialRedraw *pr, ImagePaintPartialRedraw *pr_other, int tot)
+{
+       int touch;
+       while (tot--) {
+               pr->x1 = MIN2(pr->x1, pr_other->x1);
+               pr->y1 = MIN2(pr->y1, pr_other->y1);
+               
+               pr->x2 = MAX2(pr->x2, pr_other->x2);
+               pr->y2 = MAX2(pr->y2, pr_other->y2);
+               
+               if (pr->x2 != -1)
+                       touch = 1;
+               
+               pr++; pr_other++;
+       }
+       
+       return touch;
+}
+
+/* Loop over all images on this mesh and update any we have touched */
+static int project_image_refresh_tagged(ProjPaintState *ps)
+{
+       ImagePaintPartialRedraw *pr;
+       ProjPaintImage *projIma;
+       int a,i;
+       int redraw = 0;
+       
+       
+       for (a=0, projIma=ps->projImages; a < ps->image_tot; a++, projIma++) {
+               if (projIma->touch) {
+                       /* look over each bound cell */
+                       for (i=0; i<PROJ_BOUNDBOX_SQUARED; i++) {
+                               pr = &(projIma->partRedrawRect[i]);
+                               if (pr->x2 != -1) { /* TODO - use 'enabled' ? */
+                                       imapaintpartial = *pr;
+                                       imapaint_image_update(NULL, projIma->ima, projIma->ibuf, 1); /*last 1 is for texpaint*/
+                                       redraw = 1;
+                               }
+                       }
+                       
+                       projIma->touch = 0; /* clear for reuse */
+               }
+       }
+       
+       return redraw;
+}
+
+/* run this per painting onto each mouse location */
+static int project_bucket_iter_init(ProjPaintState *ps, const float mval_f[2])
+{
+       float min_brush[2], max_brush[2];
+       float size_half = ((float)ps->brush->size) * 0.5f;
+       
+       /* so we dont have a bucket bounds that is way too small to paint into */
+       // if (size_half < 1.0f) size_half = 1.0f; // this dosnt work yet :/
+       
+       min_brush[0] = mval_f[0] - size_half;
+       min_brush[1] = mval_f[1] - size_half;
+       
+       max_brush[0] = mval_f[0] + size_half;
+       max_brush[1] = mval_f[1] + size_half;
+       
+       /* offset to make this a valid bucket index */
+       project_paint_bucket_bounds(ps, min_brush, max_brush, ps->bucketMin, ps->bucketMax);
+       
+       /* mouse outside the model areas? */
+       if (ps->bucketMin[0]==ps->bucketMax[0] || ps->bucketMin[1]==ps->bucketMax[1]) {
+               return 0;
+       }
+       
+       ps->context_bucket_x = ps->bucketMin[0];
+       ps->context_bucket_y = ps->bucketMin[1];
+       return 1;
+}
+
+static int project_bucket_iter_next(ProjPaintState *ps, int *bucket_index, rctf *bucket_bounds, const float mval[2])
+{
+       if (ps->thread_tot > 1)
+               BLI_lock_thread(LOCK_CUSTOM1);
+       
+       //printf("%d %d \n", ps->context_bucket_x, ps->context_bucket_y);
+       
+       for ( ; ps->context_bucket_y < ps->bucketMax[1]; ps->context_bucket_y++) {
+               for ( ; ps->context_bucket_x < ps->bucketMax[0]; ps->context_bucket_x++) {
+                       
+                       /* use bucket_bounds for project_bucket_isect_circle and project_bucket_init*/
+                       project_bucket_bounds(ps, ps->context_bucket_x, ps->context_bucket_y, bucket_bounds);
+                       
+                       if (project_bucket_isect_circle(ps->context_bucket_x, ps->context_bucket_y, mval, ps->brush->size * ps->brush->size, bucket_bounds)) {
+                               *bucket_index = ps->context_bucket_x + (ps->context_bucket_y * ps->buckets_x);
+                               ps->context_bucket_x++;
+                               
+                               if (ps->thread_tot > 1)
+                                       BLI_unlock_thread(LOCK_CUSTOM1);
+                               
+                               return 1;
+                       }
+               }
+               ps->context_bucket_x = ps->bucketMin[0];
+       }
+       
+       if (ps->thread_tot > 1)
+               BLI_unlock_thread(LOCK_CUSTOM1);
+       return 0;
+}
+
+/* Each thread gets one of these, also used as an argument to pass to project_paint_op */
+typedef struct ProjectHandle {
+       /* args */
+       ProjPaintState *ps;
+       float prevmval[2];
+       float mval[2];
+       
+       /* annoying but we need to have image bounds per thread, then merge into ps->projectPartialRedraws */
+       ProjPaintImage *projImages;     /* array of partial redraws */
+       
+       /* thread settings */
+       int thread_index;
+} ProjectHandle;
+
+static void blend_color_mix(unsigned char *cp, const unsigned char *cp1, const unsigned char *cp2, const int fac)
+{
+       /* this and other blending modes previously used >>8 instead of /255. both
+          are not equivalent (>>8 is /256), and the former results in rounding
+          errors that can turn colors black fast after repeated blending */
+       const int mfac= 255-fac;
+
+       cp[0]= (mfac*cp1[0]+fac*cp2[0])/255;
+       cp[1]= (mfac*cp1[1]+fac*cp2[1])/255;
+       cp[2]= (mfac*cp1[2]+fac*cp2[2])/255;
+}
+
+static void blend_color_mix_float(float *cp, const float *cp1, const float *cp2, const float fac)
+{
+       const float mfac= 1.0-fac;
+       cp[0]= mfac*cp1[0] + fac*cp2[0];
+       cp[1]= mfac*cp1[1] + fac*cp2[1];
+       cp[2]= mfac*cp1[2] + fac*cp2[2];
+}
+
+static void do_projectpaint_clone(ProjPaintState *ps, ProjPixel *projPixel, float *rgba, float alpha, float mask)
+{
+       if (ps->is_airbrush==0 && mask < 1.0f) {
+               projPixel->newColor.uint = IMB_blend_color(projPixel->newColor.uint, ((ProjPixelClone*)projPixel)->clonepx.uint, (int)(alpha*255), ps->blend);
+               blend_color_mix(projPixel->pixel.ch_pt,  projPixel->origColor.ch, projPixel->newColor.ch, (int)(mask*255));
+       }
+       else {
+               *projPixel->pixel.uint_pt = IMB_blend_color(*projPixel->pixel.uint_pt, ((ProjPixelClone*)projPixel)->clonepx.uint, (int)(alpha*mask*255), ps->blend);
+       }
+}
+
+static void do_projectpaint_clone_f(ProjPaintState *ps, ProjPixel *projPixel, float *rgba, float alpha, float mask)
+{
+       if (ps->is_airbrush==0 && mask < 1.0f) {
+               IMB_blend_color_float(projPixel->newColor.f, projPixel->newColor.f, ((ProjPixelClone *)projPixel)->clonepx.f, alpha, ps->blend);
+               blend_color_mix_float(projPixel->pixel.f_pt,  projPixel->origColor.f, projPixel->newColor.f, mask);
+       }
+       else {
+               IMB_blend_color_float(projPixel->pixel.f_pt, projPixel->pixel.f_pt, ((ProjPixelClone *)projPixel)->clonepx.f, alpha*mask, ps->blend);
+       }
+}
+
+/* do_projectpaint_smear*
+ * 
+ * note, mask is used to modify the alpha here, this is not correct since it allows
+ * accumulation of color greater then 'projPixel->mask' however in the case of smear its not 
+ * really that important to be correct as it is with clone and painting 
+ */
+static void do_projectpaint_smear(ProjPaintState *ps, ProjPixel *projPixel, float *rgba, float alpha, float mask, MemArena *smearArena, LinkNode **smearPixels, float co[2])
+{
+       unsigned char rgba_ub[4];
+       
+       if (project_paint_PickColor(ps, co, NULL, rgba_ub, 1)==0)
+               return; 
+       
+       ((ProjPixelClone *)projPixel)->clonepx.uint = IMB_blend_color(*projPixel->pixel.uint_pt, *((unsigned int *)rgba_ub), (int)(alpha*mask*255), ps->blend);
+       BLI_linklist_prepend_arena(smearPixels, (void *)projPixel, smearArena);
+} 
+
+static void do_projectpaint_smear_f(ProjPaintState *ps, ProjPixel *projPixel, float *rgba, float alpha, float mask, MemArena *smearArena, LinkNode **smearPixels_f, float co[2])
+{
+       unsigned char rgba_ub[4];
+       unsigned char rgba_smear[4];
+       
+       if (project_paint_PickColor(ps, co, NULL, rgba_ub, 1)==0)
+               return;
+       
+       IMAPAINT_FLOAT_RGBA_TO_CHAR(rgba_smear, projPixel->pixel.f_pt);
+       ((ProjPixelClone *)projPixel)->clonepx.uint = IMB_blend_color(*((unsigned int *)rgba_smear), *((unsigned int *)rgba_ub), (int)(alpha*mask*255), ps->blend);
+       BLI_linklist_prepend_arena(smearPixels_f, (void *)projPixel, smearArena);
+}
+
+static void do_projectpaint_draw(ProjPaintState *ps, ProjPixel *projPixel, float *rgba, float alpha, float mask)
+{
+       unsigned char rgba_ub[4];
+       
+       if (ps->is_texbrush) {
+               rgba_ub[0] = FTOCHAR(rgba[0] * ps->brush->rgb[0]);
+               rgba_ub[1] = FTOCHAR(rgba[1] * ps->brush->rgb[1]);
+               rgba_ub[2] = FTOCHAR(rgba[2] * ps->brush->rgb[2]);
+               rgba_ub[3] = FTOCHAR(rgba[3]);
+       }
+       else {
+               IMAPAINT_FLOAT_RGB_TO_CHAR(rgba_ub, ps->brush->rgb);
+               rgba_ub[3] = 255;
+       }
+       
+       if (ps->is_airbrush==0 && mask < 1.0f) {
+               projPixel->newColor.uint = IMB_blend_color(projPixel->newColor.uint, *((unsigned int *)rgba_ub), (int)(alpha*255), ps->blend);
+               blend_color_mix(projPixel->pixel.ch_pt,  projPixel->origColor.ch, projPixel->newColor.ch, (int)(mask*255));
+       }
+       else {
+               *projPixel->pixel.uint_pt = IMB_blend_color(*projPixel->pixel.uint_pt, *((unsigned int *)rgba_ub), (int)(alpha*mask*255), ps->blend);
+       }
+}
+
+static void do_projectpaint_draw_f(ProjPaintState *ps, ProjPixel *projPixel, float *rgba, float alpha, float mask) {
+       if (ps->is_texbrush) {
+               rgba[0] *= ps->brush->rgb[0];
+               rgba[1] *= ps->brush->rgb[1];
+               rgba[2] *= ps->brush->rgb[2];
+       }
+       else {
+               VECCOPY(rgba, ps->brush->rgb);
+       }
+       
+       if (ps->is_airbrush==0 && mask < 1.0f) {
+               IMB_blend_color_float(projPixel->newColor.f, projPixel->newColor.f, rgba, alpha, ps->blend);
+               blend_color_mix_float(projPixel->pixel.f_pt,  projPixel->origColor.f, projPixel->newColor.f, mask);
+       }
+       else {
+               IMB_blend_color_float(projPixel->pixel.f_pt, projPixel->pixel.f_pt, rgba, alpha*mask, ps->blend);
+       }
+}
+
+
+
+/* run this for single and multithreaded painting */
+static void *do_projectpaint_thread(void *ph_v)
+{
+       /* First unpack args from the struct */
+       ProjPaintState *ps =                    ((ProjectHandle *)ph_v)->ps;
+       ProjPaintImage *projImages =    ((ProjectHandle *)ph_v)->projImages;
+       const float *lastpos =                  ((ProjectHandle *)ph_v)->prevmval;
+       const float *pos =                              ((ProjectHandle *)ph_v)->mval;
+       const int thread_index =                ((ProjectHandle *)ph_v)->thread_index;
+       /* Done with args from ProjectHandle */
+
+       LinkNode *node;
+       ProjPixel *projPixel;
+       
+       int last_index = -1;
+       ProjPaintImage *last_projIma;
+       ImagePaintPartialRedraw *last_partial_redraw_cell;
+       
+       float rgba[4], alpha, dist_nosqrt;
+       
+       float brush_size_sqared;
+       float falloff;
+       int bucket_index;
+       int is_floatbuf = 0;
+       const short tool =  ps->tool;
+       rctf bucket_bounds;
+       
+       /* for smear only */
+       float pos_ofs[2];
+       float co[2];
+       float mask = 1.0f; /* airbrush wont use mask */
+       unsigned short mask_short;
+       
+       LinkNode *smearPixels = NULL;
+       LinkNode *smearPixels_f = NULL;
+       MemArena *smearArena = NULL; /* mem arena for this brush projection only */
+       
+       
+       if (tool==PAINT_TOOL_SMEAR) {
+               pos_ofs[0] = pos[0] - lastpos[0];
+               pos_ofs[1] = pos[1] - lastpos[1];
+               
+               smearArena = BLI_memarena_new(1<<16);
+       }
+       
+       /* avoid a square root with every dist comparison */
+       brush_size_sqared = ps->brush->size * ps->brush->size; 
+       
+       /* printf("brush bounds %d %d %d %d\n", bucketMin[0], bucketMin[1], bucketMax[0], bucketMax[1]); */
+       
+       while (project_bucket_iter_next(ps, &bucket_index, &bucket_bounds, pos)) {                              
+               
+               /* Check this bucket and its faces are initialized */
+               if (ps->bucketFlags[bucket_index] == PROJ_BUCKET_NULL) {
+                       /* No pixels initialized */
+                       project_bucket_init(ps, thread_index, bucket_index, &bucket_bounds);
+               }
+
+               for (node = ps->bucketRect[bucket_index]; node; node = node->next) {
+                       
+                       projPixel = (ProjPixel *)node->link;
+                       
+                       /*dist = Vec2Lenf(projPixel->projCoSS, pos);*/ /* correct but uses a sqrt */
+                       dist_nosqrt = Vec2Lenf_nosqrt(projPixel->projCoSS, pos);
+                       
+                       /*if (dist < s->brush->size) {*/ /* correct but uses a sqrt */
+                       if (dist_nosqrt < brush_size_sqared) {
+                               falloff = brush_sample_falloff_noalpha(ps->brush, sqrt(dist_nosqrt));
+                               if (falloff > 0.0f) {
+                                       if (ps->is_texbrush) {
+                                               brush_sample_tex(ps->brush, projPixel->projCoSS, rgba);
+                                               alpha = rgba[3];
+                                       } else {
+                                               alpha = 1.0f;
+                                       }
+                                       
+                                       if (ps->is_airbrush) {
+                                               /* for an aurbrush there is no real mask, so just multiply the alpha by it */
+                                               alpha *= falloff * ps->brush->alpha;
+                                               mask = ((float)projPixel->mask)/65535.0f;
+                                       }
+                                       else {
+                                               /* This brush dosnt accumulate so add some curve to the brushes falloff */
+                                               falloff = 1.0f - falloff;
+                                               falloff = 1.0f - (falloff * falloff);
+                                               
+                                               mask_short = projPixel->mask * (ps->brush->alpha * falloff);
+                                               if (mask_short > projPixel->mask_max) {
+                                                       mask = ((float)mask_short)/65535.0f;
+                                                       projPixel->mask_max = mask_short;
+                                               }
+                                               else {
+                                                       /*mask = ((float)projPixel->mask_max)/65535.0f;*/
+                                                       
+                                                       /* Go onto the next pixel */
+                                                       continue;
+                                               }
+                                       }
+                                       
+                                       if (alpha > 0.0f) {
+                                               
+                                               if (last_index != projPixel->image_index) {
+                                                       last_index = projPixel->image_index;
+                                                       last_projIma = projImages + last_index;
+                                                       
+                                                       last_projIma->touch = 1;
+                                                       is_floatbuf = last_projIma->ibuf->rect_float ? 1 : 0;
+                                               }
+                                               
+                                               last_partial_redraw_cell = last_projIma->partRedrawRect + projPixel->bb_cell_index;
+                                               last_partial_redraw_cell->x1 = MIN2(last_partial_redraw_cell->x1, projPixel->x_px);
+                                               last_partial_redraw_cell->y1 = MIN2(last_partial_redraw_cell->y1, projPixel->y_px);
+                                               
+                                               last_partial_redraw_cell->x2 = MAX2(last_partial_redraw_cell->x2, projPixel->x_px+1);
+                                               last_partial_redraw_cell->y2 = MAX2(last_partial_redraw_cell->y2, projPixel->y_px+1);
+                                               
+                                               
+                                               switch(tool) {
+                                               case PAINT_TOOL_CLONE:
+                                                       if (is_floatbuf) {
+                                                               if (((ProjPixelClone *)projPixel)->clonepx.f[3]) {
+                                                                       do_projectpaint_clone_f(ps, projPixel, rgba, alpha, mask);
+                                                               }
+                                                       }
+                                                       else {
+                                                               if (((ProjPixelClone*)projPixel)->clonepx.ch[3]) { 
+                                                                       do_projectpaint_clone(ps, projPixel, rgba, alpha, mask);
+                                                               }
+                                                       }
+                                                       break;
+                                               case PAINT_TOOL_SMEAR:
+                                                       Vec2Subf(co, projPixel->projCoSS, pos_ofs);
+                                                       
+                                                       if (is_floatbuf)        do_projectpaint_smear_f(ps, projPixel, rgba, alpha, mask, smearArena, &smearPixels_f, co);
+                                                       else                            do_projectpaint_smear(ps, projPixel, rgba, alpha, mask, smearArena, &smearPixels, co);
+                                                       break;
+                                               default:
+                                                       if (is_floatbuf)        do_projectpaint_draw_f(ps, projPixel, rgba, alpha, mask);
+                                                       else                            do_projectpaint_draw(ps, projPixel, rgba, alpha, mask);
+                                                       break;
+                                               }
+                                       }
+                                       /* done painting */
+                               }
+                       }
+               }
+       }
+
+       
+       if (tool==PAINT_TOOL_SMEAR) {
+               
+               for (node= smearPixels; node; node= node->next) { /* this wont run for a float image */
+                       projPixel = node->link;
+                       *projPixel->pixel.uint_pt = ((ProjPixelClone *)projPixel)->clonepx.uint;
+               }
+               
+               for (node= smearPixels_f; node; node= node->next) { /* this wont run for a float image */
+                       projPixel = node->link;
+                       IMAPAINT_CHAR_RGBA_TO_FLOAT(projPixel->pixel.f_pt,  ((ProjPixelClone *)projPixel)->clonepx.ch);
+                       node = node->next;
+               }
+               
+               BLI_memarena_free(smearArena);
+       }
+       
+       return NULL;
+}
+
+static int project_paint_op(void *state, ImBuf *ibufb, float *lastpos, float *pos)
+{
+       /* First unpack args from the struct */
+       ProjPaintState *ps = (ProjPaintState *)state;
+       int touch_any = 0;      
+       
+       ProjectHandle handles[BLENDER_MAX_THREADS];
+       ListBase threads;
+       int a,i;
+       
+       if (!project_bucket_iter_init(ps, pos)) {
+               return 0;
+       }
+       
+       if (ps->thread_tot > 1)
+               BLI_init_threads(&threads, do_projectpaint_thread, ps->thread_tot);
+       
+       /* get the threads running */
+       for(a=0; a < ps->thread_tot; a++) {
+               
+               /* set defaults in handles */
+               //memset(&handles[a], 0, sizeof(BakeShade));
+               
+               handles[a].ps = ps;
+               VECCOPY2D(handles[a].mval, pos);
+               VECCOPY2D(handles[a].prevmval, lastpos);
+               
+               /* thread spesific */
+               handles[a].thread_index = a;
+               
+               handles[a].projImages = (ProjPaintImage *)BLI_memarena_alloc(ps->arena_mt[a], ps->image_tot * sizeof(ProjPaintImage));
+               
+               memcpy(handles[a].projImages, ps->projImages, ps->image_tot * sizeof(ProjPaintImage));
+               
+               /* image bounds */
+               for (i=0; i< ps->image_tot; i++) {
+                       handles[a].projImages[i].partRedrawRect = (ImagePaintPartialRedraw *)BLI_memarena_alloc(ps->arena_mt[a], sizeof(ImagePaintPartialRedraw) * PROJ_BOUNDBOX_SQUARED);
+                       memcpy(handles[a].projImages[i].partRedrawRect, ps->projImages[i].partRedrawRect, sizeof(ImagePaintPartialRedraw) * PROJ_BOUNDBOX_SQUARED);                     
+               }
+
+               if (ps->thread_tot > 1)
+                       BLI_insert_thread(&threads, &handles[a]);
+       }
+       
+       if (ps->thread_tot > 1) /* wait for everything to be done */
+               BLI_end_threads(&threads);
+       else
+               do_projectpaint_thread(&handles[0]);
+               
+       
+       /* move threaded bounds back into ps->projectPartialRedraws */
+       for(i=0; i < ps->image_tot; i++) {
+               int touch = 0;
+               for(a=0; a < ps->thread_tot; a++) {
+                       touch |= partial_redraw_array_merge(ps->projImages[i].partRedrawRect, handles[a].projImages[i].partRedrawRect, PROJ_BOUNDBOX_SQUARED);
+               }
+               
+               if (touch) {
+                       ps->projImages[i].touch = 1;
+                       touch_any = 1;
+               }
+       }
+       
+       return touch_any;
+}
+
+
+static int project_paint_sub_stroke(ProjPaintState *ps, BrushPainter *painter, int *prevmval_i, int *mval_i, double time, float pressure)
+{
+       
+       /* Use mouse coords as floats for projection painting */
+       float pos[2];
+       
+       pos[0] = mval_i[0];
+       pos[1] = mval_i[1];
+       
+       // we may want to use this later 
+       // brush_painter_require_imbuf(painter, ((ibuf->rect_float)? 1: 0), 0, 0);
+       
+       if (brush_painter_paint(painter, project_paint_op, pos, time, pressure, ps)) {
+               return 1;
+       }
+       else return 0;
+}
+
+
+static int project_paint_stroke(ProjPaintState *ps, BrushPainter *painter, int *prevmval_i, int *mval_i, double time, float pressure)
+{
+       int a, redraw;
+       
+       for (a=0; a < ps->image_tot; a++)
+               partial_redraw_array_init(ps->projImages[a].partRedrawRect);
+       
+       redraw= project_paint_sub_stroke(ps, painter, prevmval_i, mval_i, time, pressure);
+       
+       if(project_image_refresh_tagged(ps))
+               return redraw;
+       
+       return 0;
+}
+
+/* Imagepaint Partial Redraw & Dirty Region */
+
+static void imapaint_clear_partial_redraw()
+{
+       memset(&imapaintpartial, 0, sizeof(imapaintpartial));
+}
+
+static void imapaint_dirty_region(Image *ima, ImBuf *ibuf, int x, int y, int w, int h)
+{
+       ImBuf *tmpibuf = NULL;
+       UndoTile *tile;
+       int srcx= 0, srcy= 0, origx;
+
+       IMB_rectclip(ibuf, NULL, &x, &y, &srcx, &srcy, &w, &h);
+
+       if (w == 0 || h == 0)
+               return;
+       
+       if (!imapaintpartial.enabled) {
+               imapaintpartial.x1 = x;
+               imapaintpartial.y1 = y;
+               imapaintpartial.x2 = x+w;
+               imapaintpartial.y2 = y+h;
+               imapaintpartial.enabled = 1;
+       }
+       else {
+               imapaintpartial.x1 = MIN2(imapaintpartial.x1, x);
+               imapaintpartial.y1 = MIN2(imapaintpartial.y1, y);
+               imapaintpartial.x2 = MAX2(imapaintpartial.x2, x+w);
+               imapaintpartial.y2 = MAX2(imapaintpartial.y2, y+h);
+       }
+
+       w = ((x + w - 1) >> IMAPAINT_TILE_BITS);
+       h = ((y + h - 1) >> IMAPAINT_TILE_BITS);
+       origx = (x >> IMAPAINT_TILE_BITS);
+       y = (y >> IMAPAINT_TILE_BITS);
+       
+       for (; y <= h; y++) {
+               for (x=origx; x <= w; x++) {
+                       for(tile=curundo->tiles.first; tile; tile=tile->next)
+                               if(tile->x == x && tile->y == y && strcmp(tile->id.name, ima->id.name)==0)
+                                       break;
+
+                       if(!tile) {
+                               undo_init_tile(&ima->id, ibuf, &tmpibuf, x, y);
+                       }
+               }
+       }
+
+       ibuf->userflags |= IB_BITMAPDIRTY;
+       
+       if (tmpibuf)
+               IMB_freeImBuf(tmpibuf);
+}
+
+static void imapaint_image_update(SpaceImage *sima, Image *image, ImBuf *ibuf, short texpaint)
+{
+       if(ibuf->rect_float)
+               /* TODO - should just update a portion from imapaintpartial! */
+               imb_freerectImBuf(ibuf); /* force recreate of char rect */
+       if(ibuf->mipmap[0])
+               imb_freemipmapImBuf(ibuf);
+
+       /* todo: should set_tpage create ->rect? */
+       if(texpaint || (sima && sima->lock)) {
+               int w = imapaintpartial.x2 - imapaintpartial.x1;
+               int h = imapaintpartial.y2 - imapaintpartial.y1;
+               GPU_paint_update_image(image, imapaintpartial.x1, imapaintpartial.y1, w, h);
+       }
+}
+
+/* Image Paint Operations */
+
+static void imapaint_ibuf_get_set_rgb(ImBuf *ibuf, int x, int y, short torus, short set, float *rgb)
+{
+       if (torus) {
+               x %= ibuf->x;
+               if (x < 0) x += ibuf->x;
+               y %= ibuf->y;
+               if (y < 0) y += ibuf->y;
+       }
+
+       if (ibuf->rect_float) {
+               float *rrgbf = ibuf->rect_float + (ibuf->x*y + x)*4;
+
+               if (set) {
+                       IMAPAINT_FLOAT_RGB_COPY(rrgbf, rgb);
+               } else {
+                       IMAPAINT_FLOAT_RGB_COPY(rgb, rrgbf);
+               }
+       }
+       else {
+               char *rrgb = (char*)ibuf->rect + (ibuf->x*y + x)*4;
+
+               if (set) {
+                       IMAPAINT_FLOAT_RGB_TO_CHAR(rrgb, rgb)
+               } else {
+                       IMAPAINT_CHAR_RGB_TO_FLOAT(rgb, rrgb)
+               }
+       }
+}
+
+static int imapaint_ibuf_add_if(ImBuf *ibuf, unsigned int x, unsigned int y, float *outrgb, short torus)
+{
+       float inrgb[3];
+
+       if ((x >= ibuf->x) || (y >= ibuf->y)) {
+               if (torus) imapaint_ibuf_get_set_rgb(ibuf, x, y, 1, 0, inrgb);
+               else return 0;
+       }
+       else imapaint_ibuf_get_set_rgb(ibuf, x, y, 0, 0, inrgb);
+
+       outrgb[0] += inrgb[0];
+       outrgb[1] += inrgb[1];
+       outrgb[2] += inrgb[2];
+
+       return 1;
+}
+
+static void imapaint_lift_soften(ImBuf *ibuf, ImBuf *ibufb, int *pos, short torus)
+{
+       int x, y, count, xi, yi, xo, yo;
+       int out_off[2], in_off[2], dim[2];
+       float outrgb[3];
+
+       dim[0] = ibufb->x;
+       dim[1] = ibufb->y;
+       in_off[0] = pos[0];
+       in_off[1] = pos[1];
+       out_off[0] = out_off[1] = 0;
+
+       if (!torus) {
+               IMB_rectclip(ibuf, ibufb, &in_off[0], &in_off[1], &out_off[0],
+                       &out_off[1], &dim[0], &dim[1]);
+
+               if ((dim[0] == 0) || (dim[1] == 0))
+                       return;
+       }
+
+       for (y=0; y < dim[1]; y++) {
+               for (x=0; x < dim[0]; x++) {
+                       /* get input pixel */
+                       xi = in_off[0] + x;
+                       yi = in_off[1] + y;
+
+                       count = 1;
+                       imapaint_ibuf_get_set_rgb(ibuf, xi, yi, torus, 0, outrgb);
+
+                       count += imapaint_ibuf_add_if(ibuf, xi-1, yi-1, outrgb, torus);
+                       count += imapaint_ibuf_add_if(ibuf, xi-1, yi  , outrgb, torus);
+                       count += imapaint_ibuf_add_if(ibuf, xi-1, yi+1, outrgb, torus);
+
+                       count += imapaint_ibuf_add_if(ibuf, xi  , yi-1, outrgb, torus);
+                       count += imapaint_ibuf_add_if(ibuf, xi  , yi+1, outrgb, torus);
+
+                       count += imapaint_ibuf_add_if(ibuf, xi+1, yi-1, outrgb, torus);
+                       count += imapaint_ibuf_add_if(ibuf, xi+1, yi  , outrgb, torus);
+                       count += imapaint_ibuf_add_if(ibuf, xi+1, yi+1, outrgb, torus);
+
+                       outrgb[0] /= count;
+                       outrgb[1] /= count;
+                       outrgb[2] /= count;
+
+                       /* write into brush buffer */
+                       xo = out_off[0] + x;
+                       yo = out_off[1] + y;
+                       imapaint_ibuf_get_set_rgb(ibufb, xo, yo, 0, 1, outrgb);
+               }
+       }
+}
+
+static void imapaint_lift_smear(ImBuf *ibuf, ImBuf *ibufb, int *pos)
+{
+       IMB_rectblend_torus(ibufb, ibuf, 0, 0, pos[0], pos[1],
+               ibufb->x, ibufb->y, IMB_BLEND_COPY_RGB);
+}
+
+static ImBuf *imapaint_lift_clone(ImBuf *ibuf, ImBuf *ibufb, int *pos)
+{
+       /* note: allocImbuf returns zero'd memory, so regions outside image will
+          have zero alpha, and hence not be blended onto the image */
+       int w=ibufb->x, h=ibufb->y, destx=0, desty=0, srcx=pos[0], srcy=pos[1];
+       ImBuf *clonebuf= IMB_allocImBuf(w, h, ibufb->depth, ibufb->flags, 0);
+
+       IMB_rectclip(clonebuf, ibuf, &destx, &desty, &srcx, &srcy, &w, &h);
+       IMB_rectblend(clonebuf, ibuf, destx, desty, srcx, srcy, w, h,
+               IMB_BLEND_COPY_RGB);
+       IMB_rectblend(clonebuf, ibufb, destx, desty, destx, desty, w, h,
+               IMB_BLEND_COPY_ALPHA);
+
+       return clonebuf;
+}
+
+static void imapaint_convert_brushco(ImBuf *ibufb, float *pos, int *ipos)
+{
+       ipos[0]= (int)(pos[0] - ibufb->x/2);
+       ipos[1]= (int)(pos[1] - ibufb->y/2);
+}
+
+/* dosnt run for projection painting
+ * only the old style painting in the 3d view */
+static int imapaint_paint_op(void *state, ImBuf *ibufb, float *lastpos, float *pos)
+{
+       ImagePaintState *s= ((ImagePaintState*)state);
+       ImBuf *clonebuf= NULL;
+       short torus= s->brush->flag & BRUSH_TORUS;
+       short blend= s->blend;
+       float *offset= s->brush->clone.offset;
+       float liftpos[2];
+       int bpos[2], blastpos[2], bliftpos[2];
+
+       imapaint_convert_brushco(ibufb, pos, bpos);
+
+       /* lift from canvas */
+       if(s->tool == PAINT_TOOL_SOFTEN) {
+               imapaint_lift_soften(s->canvas, ibufb, bpos, torus);
+       }
+       else if(s->tool == PAINT_TOOL_SMEAR) {
+               if (lastpos[0]==pos[0] && lastpos[1]==pos[1])
+                       return 0;
+
+               imapaint_convert_brushco(ibufb, lastpos, blastpos);
+               imapaint_lift_smear(s->canvas, ibufb, blastpos);
+       }
+       else if(s->tool == PAINT_TOOL_CLONE && s->clonecanvas) {
+               liftpos[0]= pos[0] - offset[0]*s->canvas->x;
+               liftpos[1]= pos[1] - offset[1]*s->canvas->y;
+
+               imapaint_convert_brushco(ibufb, liftpos, bliftpos);
+               clonebuf= imapaint_lift_clone(s->clonecanvas, ibufb, bliftpos);
+       }
+
+       imapaint_dirty_region(s->image, s->canvas, bpos[0], bpos[1], ibufb->x, ibufb->y);
+
+       /* blend into canvas */
+       if(torus)
+               IMB_rectblend_torus(s->canvas, (clonebuf)? clonebuf: ibufb,
+                       bpos[0], bpos[1], 0, 0, ibufb->x, ibufb->y, blend);
+       else
+               IMB_rectblend(s->canvas, (clonebuf)? clonebuf: ibufb,
+                       bpos[0], bpos[1], 0, 0, ibufb->x, ibufb->y, blend);
+                       
+       if(clonebuf) IMB_freeImBuf(clonebuf);
+
+       return 1;
+}
+
+/* 3D TexturePaint */
+
+static int texpaint_break_stroke(float *prevuv, float *fwuv, float *bkuv, float *uv)
+{
+       float d1[2], d2[2];
+       float mismatch = Vec2Lenf(fwuv, uv);
+       float len1 = Vec2Lenf(prevuv, fwuv);
+       float len2 = Vec2Lenf(bkuv, uv);
+
+       Vec2Subf(d1, fwuv, prevuv);
+       Vec2Subf(d2, uv, bkuv);
+
+       return ((Inp2f(d1, d2) < 0.0f) || (mismatch > MAX2(len1, len2)*2));
+}
+
+/* ImagePaint Common */
+
+static int imapaint_canvas_set(ImagePaintState *s, Image *ima)
+{
+       ImBuf *ibuf= BKE_image_get_ibuf(ima, s->sima? &s->sima->iuser: NULL);
+       
+       /* verify that we can paint and set canvas */
+       if(ima->packedfile && ima->rr) {
+               s->warnpackedfile = ima->id.name + 2;
+               return 0;
+       }       
+       else if(ibuf && ibuf->channels!=4) {
+               s->warnmultifile = ima->id.name + 2;
+               return 0;
+       }
+       else if(!ima || !ibuf || !(ibuf->rect || ibuf->rect_float))
+               return 0;
+
+       s->image= ima;
+       s->canvas= ibuf;
+
+       /* set clone canvas */
+       if(s->tool == PAINT_TOOL_CLONE) {
+               ima= s->brush->clone.image;
+               ibuf= BKE_image_get_ibuf(ima, s->sima? &s->sima->iuser: NULL);
+               
+               if(!ima || !ibuf || !(ibuf->rect || ibuf->rect_float))
+                       return 0;
+
+               s->clonecanvas= ibuf;
+
+               if(s->canvas->rect_float && !s->clonecanvas->rect_float) {
+                       /* temporarily add float rect for cloning */
+                       IMB_float_from_rect(s->clonecanvas);
+                       s->clonefreefloat= 1;
+               }
+               else if(!s->canvas->rect_float && !s->clonecanvas->rect)
+                       IMB_rect_from_float(s->clonecanvas);
+       }
+
+       return 1;
+}
+
+static void imapaint_canvas_free(ImagePaintState *s)
+{
+       if (s->clonefreefloat)
+               imb_freerectfloatImBuf(s->clonecanvas);
+}
+
+static int imapaint_paint_sub_stroke(ImagePaintState *s, BrushPainter *painter, Image *image, short texpaint, float *uv, double time, int update, float pressure)
+{
+       ImBuf *ibuf= BKE_image_get_ibuf(image, s->sima? &s->sima->iuser: NULL);
+       float pos[2];
+
+       if(!ibuf)
+               return 0;
+
+       pos[0] = uv[0]*ibuf->x;
+       pos[1] = uv[1]*ibuf->y;
+
+       brush_painter_require_imbuf(painter, ((ibuf->rect_float)? 1: 0), 0, 0);
+
+       if (brush_painter_paint(painter, imapaint_paint_op, pos, time, pressure, s)) {
+               if (update)
+                       imapaint_image_update(s->sima, image, ibuf, texpaint);
+               return 1;
+       }
+       else return 0;
+}
+
+static int imapaint_paint_stroke(ViewContext *vc, ImagePaintState *s, BrushPainter *painter, short texpaint, int *prevmval, int *mval, double time, float pressure)
+{
+       Image *newimage = NULL;
+       float fwuv[2], bkuv[2], newuv[2];
+       unsigned int newfaceindex;
+       int breakstroke = 0, redraw = 0;
+
+       if (texpaint) {
+               /* pick new face and image */
+               if (    imapaint_pick_face(vc, s->me, mval, &newfaceindex) &&
+                               ((G.f & G_FACESELECT)==0 || (s->me->mface+newfaceindex)->flag & ME_FACE_SEL)
+               ) {
+                       ImBuf *ibuf;
+                       
+                       newimage = (Image*)((s->me->mtface+newfaceindex)->tpage);
+                       ibuf= BKE_image_get_ibuf(newimage, s->sima? &s->sima->iuser: NULL);
+
+                       if(ibuf && ibuf->rect)
+                               imapaint_pick_uv(s->scene, s->ob, s->me, newfaceindex, mval, newuv);
+                       else {
+                               newimage = NULL;
+                               newuv[0] = newuv[1] = 0.0f;
+                       }
+               }
+               else
+                       newuv[0] = newuv[1] = 0.0f;
+
+               /* see if stroke is broken, and if so finish painting in old position */
+               if (s->image) {
+                       imapaint_pick_uv(s->scene, s->ob, s->me, s->faceindex, mval, fwuv);
+                       imapaint_pick_uv(s->scene, s->ob, s->me, newfaceindex, prevmval, bkuv);
+
+                       if (newimage == s->image)
+                               breakstroke= texpaint_break_stroke(s->uv, fwuv, bkuv, newuv);
+                       else
+                               breakstroke= 1;
+               }
+               else
+                       fwuv[0]= fwuv[1]= 0.0f;
+
+               if (breakstroke) {
+                       imapaint_pick_uv(s->scene, s->ob, s->me, s->faceindex, mval, fwuv);
+                       redraw |= imapaint_paint_sub_stroke(s, painter, s->image, texpaint,
+                               fwuv, time, 1, pressure);
+                       imapaint_clear_partial_redraw();
+                       brush_painter_break_stroke(painter);
+               }
+
+               /* set new canvas */
+               if (newimage && (newimage != s->image))
+                       if (!imapaint_canvas_set(s, newimage))
+                               newimage = NULL;
+
+               /* paint in new image */
+               if (newimage) {
+                       if (breakstroke)
+                               redraw|= imapaint_paint_sub_stroke(s, painter, newimage,
+                                       texpaint, bkuv, time, 0, pressure);
+                       redraw|= imapaint_paint_sub_stroke(s, painter, newimage, texpaint,
+                               newuv, time, 1, pressure);
+               }
+
+               /* update state */
+               s->image = newimage;
+               s->faceindex = newfaceindex;
+               s->uv[0] = newuv[0];
+               s->uv[1] = newuv[1];
+       }
+       else {
+               UI_view2d_region_to_view(s->v2d, mval[0], mval[1], &newuv[0], &newuv[1]);
+               redraw |= imapaint_paint_sub_stroke(s, painter, s->image, texpaint, newuv,
+                       time, 1, pressure);
+       }
+
+       if (redraw)
+               imapaint_clear_partial_redraw();
+
+       return redraw;
+}
+
+/************************ image paint poll ************************/
+
+static Brush *image_paint_brush(bContext *C)
+{
+       Scene *scene= CTX_data_scene(C);
+       ToolSettings *settings= scene->toolsettings;
+
+       return settings->imapaint.brush;
+}
+
+static int image_paint_poll(bContext *C)
+{
+       if(!image_paint_brush(C))
+               return 0;
+
+       if((G.f & G_TEXTUREPAINT) && CTX_wm_region_view3d(C)) {
+               return 1;
+       }
+       else {
+               ScrArea *sa= CTX_wm_area(C);
+               SpaceImage *sima= (SpaceImage*)CTX_wm_space_data(C);
+
+               if(sa && sa->spacetype==SPACE_IMAGE) {
+                       ARegion *ar= CTX_wm_region(C);
+
+                       if((sima->flag & SI_DRAWTOOL) && ar->regiontype==RGN_TYPE_WINDOW)
+                               return 1;
+               }
+       }
+
+       return 0;
+}
+
+static int image_paint_3d_poll(bContext *C)
+{
+       if(CTX_wm_region_view3d(C))
+               return image_paint_poll(C);
+       
+       return 0;
+}
+
+static int image_paint_2d_clone_poll(bContext *C)
+{
+       Scene *scene= CTX_data_scene(C);
+       ToolSettings *settings= scene->toolsettings;
+       Brush *brush= image_paint_brush(C);
+
+       if(!CTX_wm_region_view3d(C) && image_paint_poll(C))
+               if(brush && (settings->imapaint.tool == PAINT_TOOL_CLONE))
+                       if(brush->clone.image)
+                               return 1;
+       
+       return 0;
+}
+
+/************************ paint operator ************************/
+
+typedef enum PaintMode {
+       PAINT_MODE_2D,
+       PAINT_MODE_3D,
+       PAINT_MODE_3D_PROJECT
+} PaintMode;
+
+typedef struct PaintOperation {
+       PaintMode mode;
+
+       BrushPainter *painter;
+       ImagePaintState s;
+       ProjPaintState ps;
+
+       int first;
+       int prevmouse[2];
+       double starttime;
+
+       ViewContext vc;
+       wmTimer *timer;
+} PaintOperation;
+
+static void paint_redraw(bContext *C, ImagePaintState *s, int final)
+{
+       if(final) {
+               if(s->image)
+                       GPU_free_image(s->image);
+
+               WM_event_add_notifier(C, NC_IMAGE|NA_EDITED, s->image);
+
+               // XXX node update
+#if 0
+               if(!s->sima && s->image) {
+                       /* after paint, tag Image or RenderResult nodes changed */
+                       if(s->scene->nodetree) {
+                               imagepaint_composite_tags(s->scene->nodetree, image, &s->sima->iuser);
+                       }
+                       /* signal composite (hurmf, need an allqueue?) */
+                       if(s->sima->lock) {
+                               ScrArea *sa;
+                               for(sa=s->screen->areabase.first; sa; sa= sa->next) {
+                                       if(sa->spacetype==SPACE_NODE) {
+                                               if(((SpaceNode *)sa->spacedata.first)->treetype==NTREE_COMPOSIT) {
+                                                       addqueue(sa->win, UI_BUT_EVENT, B_NODE_TREE_EXEC);
+                                                       break;
+                                               }
+                                       }
+                               }
+                       }
+               }               
+#endif
+       }
+       else {
+               if(!s->sima || !s->sima->lock)
+                       ED_region_tag_redraw(CTX_wm_region(C));
+               else
+                       WM_event_add_notifier(C, NC_IMAGE|NA_EDITED, s->image);
+       }
+}
+
+static int paint_init(bContext *C, wmOperator *op)
+{
+       Scene *scene= CTX_data_scene(C);
+       ToolSettings *settings= scene->toolsettings;
+       PaintOperation *pop;
+
+       pop= MEM_callocN(sizeof(PaintOperation), "PaintOperation");
+       pop->first= 1;
+       op->customdata= pop;
+       
+       /* initialize from context */
+       if(CTX_wm_region_view3d(C)) {
+               pop->ps.v3d= CTX_wm_view3d(C);
+               pop->ps.rv3d= CTX_wm_region_view3d(C);
+               pop->mode= PAINT_MODE_3D;
+
+               if(!(settings->imapaint.flag & IMAGEPAINT_PROJECT_DISABLE))
+                       pop->mode= PAINT_MODE_3D_PROJECT;
+               else
+                       view3d_set_viewcontext(C, &pop->vc);
+       }
+       else {
+               pop->s.sima= (SpaceImage*)CTX_wm_space_data(C);
+               pop->s.v2d= &CTX_wm_region(C)->v2d;
+       }
+
+       pop->s.scene= scene;
+       pop->ps.scene= scene;
+       pop->s.screen= CTX_wm_screen(C);
+       pop->ps.ar= CTX_wm_region(C);
+
+       /* intialize brush */
+       pop->s.brush = settings->imapaint.brush;
+       pop->s.tool = settings->imapaint.tool;
+       if(pop->mode == PAINT_MODE_3D && (pop->s.tool == PAINT_TOOL_CLONE))
+               pop->s.tool = PAINT_TOOL_DRAW;
+       pop->s.blend = pop->s.brush->blend;
+       
+       if(pop->mode == PAINT_MODE_3D_PROJECT) {
+               pop->ps.brush = pop->s.brush;
+               pop->ps.tool = pop->s.tool;
+               pop->ps.blend = pop->s.blend;
+       }
+
+       if(pop->mode != PAINT_MODE_2D) {
+               pop->ps.ob = pop->s.ob = OBACT;
+               if (!pop->s.ob || !(pop->s.ob->lay & pop->ps.v3d->lay)) return 0;
+               pop->s.me = get_mesh(pop->s.ob);
+               if (!pop->s.me) return 0;
+       }
+       else {
+               pop->s.image = pop->s.sima->image;
+
+               if(!imapaint_canvas_set(&pop->s, pop->s.sima->image)) {
+                       if(pop->s.warnmultifile)
+                               BKE_report(op->reports, RPT_WARNING, "Image requires 4 color channels to paint");
+                       if(pop->s.warnpackedfile)
+                               BKE_report(op->reports, RPT_WARNING, "Packed MultiLayer files cannot be painted");
+
+                       return 0;
+               }
+       }
+       
+       /* note, if we have no UVs on the derived mesh, then we must return here */
+       if(pop->mode == PAINT_MODE_3D_PROJECT) {
+               /* setup projection painting data */
+               pop->ps.do_backfacecull = (settings->imapaint.flag & IMAGEPAINT_PROJECT_BACKFACE) ? 0 : 1;
+               pop->ps.do_occlude = (settings->imapaint.flag & IMAGEPAINT_PROJECT_XRAY) ? 0 : 1;
+               pop->ps.do_mask_normal = (settings->imapaint.flag & IMAGEPAINT_PROJECT_FLAT) ? 0 : 1;;
+               
+               if (pop->ps.tool == PAINT_TOOL_CLONE)
+                       pop->ps.do_layer_clone = (settings->imapaint.flag & IMAGEPAINT_PROJECT_LAYER_CLONE);
+               
+               pop->ps.do_layer_mask = (settings->imapaint.flag & IMAGEPAINT_PROJECT_LAYER_MASK) ? 1 : 0;
+               pop->ps.do_layer_mask_inv = (settings->imapaint.flag & IMAGEPAINT_PROJECT_LAYER_MASK_INV) ? 1 : 0;
+               
+               
+#ifndef PROJ_DEBUG_NOSEAMBLEED
+               pop->ps.seam_bleed_px = settings->imapaint.seam_bleed; /* pixel num to bleed */
+#endif
+
+               if(pop->ps.do_mask_normal) {
+                       pop->ps.normal_angle_inner = settings->imapaint.normal_angle;
+                       pop->ps.normal_angle = (pop->ps.normal_angle_inner + 90.0f) * 0.5f;
+               }
+               else {
+                       pop->ps.normal_angle_inner= pop->ps.normal_angle= settings->imapaint.normal_angle;
+               }
+
+               pop->ps.normal_angle_inner *=   M_PI_2 / 90;
+               pop->ps.normal_angle *=                 M_PI_2 / 90;
+               pop->ps.normal_angle_range = pop->ps.normal_angle - pop->ps.normal_angle_inner;
+               
+               if(pop->ps.normal_angle_range <= 0.0f)
+                       pop->ps.do_mask_normal = 0; /* no need to do blending */
+
+               project_paint_begin(&pop->ps);
+               
+               if(pop->ps.dm==NULL)
+                       return 0;
+       }
+       
+       settings->imapaint.flag |= IMAGEPAINT_DRAWING;
+       undo_imagepaint_push_begin("Image Paint");
+
+       /* create painter */
+       pop->painter= brush_painter_new(pop->s.brush);
+
+       return 1;
+}
+
+static void paint_apply(bContext *C, wmOperator *op, PointerRNA *itemptr)
+{
+       PaintOperation *pop= op->customdata;
+       float time;
+       float pressure;
+       int mouse[2], redraw;
+
+       RNA_int_get_array(itemptr, "mouse", mouse);
+       time= RNA_float_get(itemptr, "time");
+       pressure= RNA_float_get(itemptr, "pressure");
+
+       if(pop->first)
+               project_paint_begin_clone(&pop->ps, mouse);
+
+       if(pop->mode == PAINT_MODE_3D)
+               view3d_operator_needs_opengl(C);
+
+       if(pop->mode == PAINT_MODE_3D_PROJECT) {
+               redraw= project_paint_stroke(&pop->ps, pop->painter, pop->prevmouse, mouse, time, pressure);
+               pop->prevmouse[0]= mouse[0];
+               pop->prevmouse[1]= mouse[1];
+
+       }
+       else { 
+               redraw= imapaint_paint_stroke(&pop->vc, &pop->s, pop->painter, pop->mode == PAINT_MODE_3D, pop->prevmouse, mouse, time, pressure);
+               pop->prevmouse[0]= mouse[0];
+               pop->prevmouse[1]= mouse[1];
+       }
+
+       if(redraw)
+               paint_redraw(C, &pop->s, 0);
+
+       pop->first= 0;
+}
+
+static void paint_exit(bContext *C, wmOperator *op)
+{
+       Scene *scene= CTX_data_scene(C);
+       ToolSettings *settings= scene->toolsettings;
+       PaintOperation *pop= op->customdata;
+
+       if(pop->timer)
+               WM_event_remove_window_timer(CTX_wm_window(C), pop->timer);
+
+       settings->imapaint.flag &= ~IMAGEPAINT_DRAWING;
+       imapaint_canvas_free(&pop->s);
+       brush_painter_free(pop->painter);
+
+       if(pop->mode == PAINT_MODE_3D_PROJECT)
+               project_paint_end(&pop->ps);
+       
+       paint_redraw(C, &pop->s, 1);
+       undo_imagepaint_push_end();
+       
+       if(pop->s.warnmultifile)
+               BKE_reportf(op->reports, RPT_WARNING, "Image requires 4 color channels to paint: %s", pop->s.warnmultifile);
+       if(pop->s.warnpackedfile)
+               BKE_reportf(op->reports, RPT_WARNING, "Packed MultiLayer files cannot be painted: %s", pop->s.warnpackedfile);
+
+       MEM_freeN(pop);
+}
+
+static int paint_exec(bContext *C, wmOperator *op)
+{
+       if(!paint_init(C, op)) {
+               MEM_freeN(op->customdata);
+               return OPERATOR_CANCELLED;
+       }
+
+       RNA_BEGIN(op->ptr, itemptr, "stroke") {
+               paint_apply(C, op, &itemptr);
+       }
+       RNA_END;
+
+       paint_exit(C, op);
+
+       return OPERATOR_FINISHED;
+}
+
+static void paint_apply_event(bContext *C, wmOperator *op, wmEvent *event)
+{
+       ARegion *ar= CTX_wm_region(C);
+       PaintOperation *pop= op->customdata;
+       wmTabletData *wmtab;
+       PointerRNA itemptr;
+       float pressure;
+       double time;
+       int tablet, mouse[2];
+
+       // XXX +1 matches brush location better but
+       // still not exact, find out why and fix ..
+       mouse[0]= event->x - ar->winrct.xmin + 1;
+       mouse[1]= event->y - ar->winrct.ymin + 1;
+
+       time= PIL_check_seconds_timer();
+
+       tablet= 0;
+       pressure= 0;
+       pop->s.blend= pop->s.brush->blend;
+
+       if(event->custom == EVT_DATA_TABLET) {
+               wmtab= event->customdata;
+
+               tablet= (wmtab->Active != EVT_TABLET_NONE);
+               pressure= wmtab->Pressure;
+               if(wmtab->Active == EVT_TABLET_ERASER)
+                       pop->s.blend= BRUSH_BLEND_ERASE_ALPHA;
+       }
+       else
+               pressure= 1.0f;
+
+       if(pop->first) {
+               pop->prevmouse[0]= mouse[0];
+               pop->prevmouse[1]= mouse[1];
+               pop->starttime= time;
+
+               /* special exception here for too high pressure values on first touch in
+                  windows for some tablets, then we just skip first touch ..  */
+               if ((pop->s.brush->flag & (BRUSH_ALPHA_PRESSURE|BRUSH_SIZE_PRESSURE|
+                       BRUSH_SPACING_PRESSURE|BRUSH_RAD_PRESSURE)) && tablet && (pressure >= 0.99f))
+                       return;
+       }
+
+       /* fill in stroke */
+       RNA_collection_add(op->ptr, "stroke", &itemptr);
+
+       RNA_int_set_array(&itemptr, "mouse", mouse);
+       RNA_float_set(&itemptr, "time", (float)(time - pop->starttime));
+       RNA_float_set(&itemptr, "pressure", pressure);
+
+       /* apply */
+       paint_apply(C, op, &itemptr);
+}
+
+static int paint_invoke(bContext *C, wmOperator *op, wmEvent *event)
+{
+       PaintOperation *pop;
+
+       if(!paint_init(C, op)) {
+               MEM_freeN(op->customdata);
+               return OPERATOR_CANCELLED;
+       }
+       
+       paint_apply_event(C, op, event);
+
+       pop= op->customdata;
+       WM_event_add_modal_handler(C, &CTX_wm_window(C)->handlers, op);
+
+       if(pop->s.brush->flag & BRUSH_AIRBRUSH)
+               pop->timer= WM_event_add_window_timer(CTX_wm_window(C), TIMER, 0.01f);
+
+       return OPERATOR_RUNNING_MODAL;
+}
+
+static int paint_modal(bContext *C, wmOperator *op, wmEvent *event)
+{
+       PaintOperation *pop= op->customdata;
+
+       switch(event->type) {
+               case LEFTMOUSE:
+               case MIDDLEMOUSE:
+               case RIGHTMOUSE: // XXX hardcoded
+                       paint_exit(C, op);
+                       return OPERATOR_FINISHED;
+               case MOUSEMOVE:
+                       paint_apply_event(C, op, event);
+                       break;
+               case TIMER:
+                       if(event->customdata == pop->timer)
+                               paint_apply_event(C, op, event);
+                       break;
+       }
+
+       return OPERATOR_RUNNING_MODAL;
+}
+
+static int paint_cancel(bContext *C, wmOperator *op)
+{
+       paint_exit(C, op);
+
+       return OPERATOR_CANCELLED;
+}
+
+void PAINT_OT_image_paint(wmOperatorType *ot)
+{
+       /* identifiers */
+       ot->name= "Image Paint";
+       ot->idname= "PAINT_OT_image_paint";
+       
+       /* api callbacks */
+       ot->exec= paint_exec;
+       ot->invoke= paint_invoke;
+       ot->modal= paint_modal;
+       ot->cancel= paint_cancel;
+       ot->poll= image_paint_poll;
+
+       /* flags */
+       ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+
+       /* properties */
+       RNA_def_collection_runtime(ot->srna, "stroke", &RNA_OperatorStrokeElement, "Stroke", "");
+}
+
+/************************ grab clone operator ************************/
+
+typedef struct GrabClone {
+       float startoffset[2];
+       int startx, starty;
+} GrabClone;
+
+static void grab_clone_apply(bContext *C, wmOperator *op)
+{
+       Brush *brush= image_paint_brush(C);
+       float delta[2];
+
+       RNA_float_get_array(op->ptr, "delta", delta);
+       brush->clone.offset[0] += delta[0];
+       brush->clone.offset[1] += delta[1];
+
+       ED_region_tag_redraw(CTX_wm_region(C));
+}
+
+static int grab_clone_exec(bContext *C, wmOperator *op)
+{
+       grab_clone_apply(C, op);
+
+       return OPERATOR_FINISHED;
+}
+
+static int grab_clone_invoke(bContext *C, wmOperator *op, wmEvent *event)
+{
+       Brush *brush= image_paint_brush(C);
+       GrabClone *cmv;
+
+       cmv= MEM_callocN(sizeof(GrabClone), "GrabClone");
+       cmv->startoffset[0]= brush->clone.offset[0];
+       cmv->startoffset[1]= brush->clone.offset[1];
+       cmv->startx= event->x;
+       cmv->starty= event->y;
+       op->customdata= cmv;
+
+       WM_event_add_modal_handler(C, &CTX_wm_window(C)->handlers, op);
+
+       return OPERATOR_RUNNING_MODAL;
+}
+
+static int grab_clone_modal(bContext *C, wmOperator *op, wmEvent *event)
+{
+       Brush *brush= image_paint_brush(C);
+       ARegion *ar= CTX_wm_region(C);
+       GrabClone *cmv= op->customdata;
+       float startfx, startfy, fx, fy, delta[2];
+       int xmin= ar->winrct.xmin, ymin= ar->winrct.ymin;
+
+       switch(event->type) {
+               case LEFTMOUSE:
+               case MIDDLEMOUSE:
+               case RIGHTMOUSE: // XXX hardcoded
+                       MEM_freeN(op->customdata);
+                       return OPERATOR_FINISHED;
+               case MOUSEMOVE:
+                       /* mouse moved, so move the clone image */
+                       UI_view2d_region_to_view(&ar->v2d, cmv->startx - xmin, cmv->starty - ymin, &startfx, &startfy);
+                       UI_view2d_region_to_view(&ar->v2d, event->x - xmin, event->y - ymin, &fx, &fy);
+
+                       delta[0]= fx - startfx;
+                       delta[1]= fy - startfy;
+                       RNA_float_set_array(op->ptr, "delta", delta);
+
+                       brush->clone.offset[0]= cmv->startoffset[0];
+                       brush->clone.offset[1]= cmv->startoffset[1];
+
+                       grab_clone_apply(C, op);
+                       break;
+       }
+
+       return OPERATOR_RUNNING_MODAL;
+}
+
+static int grab_clone_cancel(bContext *C, wmOperator *op)
+{
+       MEM_freeN(op->customdata);
+       return OPERATOR_CANCELLED;
+}
+
+void PAINT_OT_grab_clone(wmOperatorType *ot)
+{
+       /* identifiers */
+       ot->name= "Grab Clone";
+       ot->idname= "PAINT_OT_grab_clone";
+       
+       /* api callbacks */
+       ot->exec= grab_clone_exec;
+       ot->invoke= grab_clone_invoke;
+       ot->modal= grab_clone_modal;
+       ot->cancel= grab_clone_cancel;
+       ot->poll= image_paint_2d_clone_poll;
+
+       /* flags */
+       ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+
+       /* properties */
+       RNA_def_float_vector(ot->srna, "delta", 2, NULL, -FLT_MAX, FLT_MAX, "Delta", "Delta offset of clone image in 0.0..1.0 coordinates.", -1.0f, 1.0f);
+}
+
+/******************** sample color operator ********************/
+
+static int sample_color_exec(bContext *C, wmOperator *op)
+{
+       Scene *scene= CTX_data_scene(C);
+       Brush *brush= image_paint_brush(C);
+       ARegion *ar= CTX_wm_region(C);
+       int location[2];
+
+       RNA_int_get_array(op->ptr, "location", location);
+       paint_sample_color(scene, ar, location[0], location[1]);
+
+       WM_event_add_notifier(C, NC_BRUSH|NA_EDITED, brush);
+       
+       return OPERATOR_FINISHED;
+}
+
+static int sample_color_invoke(bContext *C, wmOperator *op, wmEvent *event)
+{
+       ARegion *ar= CTX_wm_region(C);
+       int location[2];
+
+       location[0]= event->x - ar->winrct.xmin;
+       location[1]= event->y - ar->winrct.ymin;
+       RNA_int_set_array(op->ptr, "location", location);
+
+       return sample_color_exec(C, op);
+}
+
+void PAINT_OT_sample_color(wmOperatorType *ot)
+{
+       /* identifiers */
+       ot->name= "Sample Color";
+       ot->idname= "PAINT_OT_sample_color";
+       
+       /* api callbacks */
+       ot->exec= sample_color_exec;
+       ot->invoke= sample_color_invoke;
+       ot->poll= image_paint_poll;
+
+       /* flags */
+       ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+
+       /* properties */
+       RNA_def_int_vector(ot->srna, "location", 2, NULL, 0, INT_MAX, "Location", "Cursor location in region coordinates.", 0, 16384);
+}
+
+/******************** set clone cursor operator ********************/
+
+static int set_clone_cursor_exec(bContext *C, wmOperator *op)
+{
+       Scene *scene= CTX_data_scene(C);
+       View3D *v3d= CTX_wm_view3d(C);
+       float *cursor= give_cursor(scene, v3d);
+
+       RNA_float_get_array(op->ptr, "location", cursor);
+       
+       ED_area_tag_redraw(CTX_wm_area(C));
+       
+       return OPERATOR_FINISHED;
+}
+
+static int set_clone_cursor_invoke(bContext *C, wmOperator *op, wmEvent *event)
+{
+       Scene *scene= CTX_data_scene(C);
+       View3D *v3d= CTX_wm_view3d(C);
+       ARegion *ar= CTX_wm_region(C);
+       float location[3];
+       short mval[2];
+
+       mval[0]= event->x - ar->winrct.xmin;
+       mval[1]= event->y - ar->winrct.ymin;
+
+       view3d_operator_needs_opengl(C);
+
+       if(!view_autodist(scene, ar, v3d, mval, location))
+               return OPERATOR_CANCELLED;
+
+       RNA_float_set_array(op->ptr, "location", location);
+
+       return set_clone_cursor_exec(C, op);
+}
+
+void PAINT_OT_set_clone_cursor(wmOperatorType *ot)
+{
+       /* identifiers */
+       ot->name= "Set Clone Cursor";
+       ot->idname= "PAINT_OT_set_clone_cursor";
+       
+       /* api callbacks */
+       ot->exec= set_clone_cursor_exec;
+       ot->invoke= set_clone_cursor_invoke;
+       ot->poll= image_paint_3d_poll;
+
+       /* flags */
+       ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+
+       /* properties */
+       RNA_def_float_vector(ot->srna, "location", 3, NULL, -FLT_MAX, FLT_MAX, "Location", "Cursor location in world space coordinates.", -10000.0f, 10000.0f);
+}
+
+/************************ cursor drawing *******************************/
+
+static void brush_drawcursor(bContext *C, int x, int y, void *customdata)
+{
+       Brush *brush= image_paint_brush(C);
+       RegionView3D *rv3d= CTX_wm_region_view3d(C);
+
+       if(brush) {
+               glPushMatrix();
+
+               glTranslatef((float)x, (float)y, 0.0f);
+
+               if(!rv3d) {
+                       SpaceImage *sima= (SpaceImage*)CTX_wm_space_data(C);
+                       ARegion *ar= CTX_wm_region(C);
+                       float zoomx, zoomy;
+
+                       ED_space_image_zoom(sima, ar, &zoomx, &zoomy);
+                       glScalef(zoomx, zoomy, 1.0f);
+               }
+               
+               glColor4ub(255, 255, 255, 128);
+               glEnable( GL_LINE_SMOOTH );
+               glEnable(GL_BLEND);
+               glutil_draw_lined_arc(0.0, M_PI*2.0, brush->size*0.5f, 40);
+               glDisable(GL_BLEND);
+               glDisable( GL_LINE_SMOOTH );
+               
+               glPopMatrix();
+       }
+}
+
+static void toggle_paint_cursor(bContext *C, int enable)
+{
+       ToolSettings *settings= CTX_data_scene(C)->toolsettings;
+
+       if(settings->imapaint.paintcursor && !enable) {
+               WM_paint_cursor_end(CTX_wm_manager(C), settings->imapaint.paintcursor);
+               settings->imapaint.paintcursor = NULL;
+       }
+       else if(enable)
+               settings->imapaint.paintcursor= WM_paint_cursor_activate(CTX_wm_manager(C), image_paint_poll, brush_drawcursor, NULL);
+}
+
+/******************** texture paint toggle operator ********************/
+
+static int texture_paint_toggle_poll(bContext *C)
+{
+       if(CTX_data_edit_object(C))
+               return 0;
+       if(CTX_data_active_object(C)==NULL)
+               return 0;
+
+       return 1;
+}
+
+static int texture_paint_toggle_exec(bContext *C, wmOperator *op)
+{
+       Scene *scene= CTX_data_scene(C);
+       Object *ob= CTX_data_active_object(C);
+       Mesh *me= 0;
+       
+       if(ob==NULL)
+               return OPERATOR_CANCELLED;
+       
+       if (object_data_is_libdata(ob)) {
+               BKE_report(op->reports, RPT_ERROR, "Can't edit external libdata.");
+               return OPERATOR_CANCELLED;
+       }
+
+       me= get_mesh(ob);
+
+       if(!(G.f & G_TEXTUREPAINT) && !me) {
+               BKE_report(op->reports, RPT_ERROR, "Can only enter texture paint mode for mesh objects.");
+               return OPERATOR_CANCELLED;
+       }
+
+       if(G.f & G_TEXTUREPAINT) {
+               G.f &= ~G_TEXTUREPAINT;
+               GPU_paint_set_mipmap(1);
+               toggle_paint_cursor(C, 0);
+       }
+       else {
+               G.f |= G_TEXTUREPAINT;
+
+               if(me->mtface==NULL)
+                       me->mtface= CustomData_add_layer(&me->fdata, CD_MTFACE, CD_DEFAULT,
+                                                        NULL, me->totface);
+
+               brush_check_exists(&scene->toolsettings->imapaint.brush);
+               GPU_paint_set_mipmap(0);
+               toggle_paint_cursor(C, 1);
+       }
+
+       DAG_object_flush_update(scene, ob, OB_RECALC_DATA);
+       WM_event_add_notifier(C, NC_SCENE|ND_MODE, scene);
+
+       return OPERATOR_FINISHED;
+}
+
+void PAINT_OT_texture_paint_toggle(wmOperatorType *ot)
+{
+       /* identifiers */
+       ot->name= "Texture Paint Toggle";
+       ot->idname= "PAINT_OT_texture_paint_toggle";
+       
+       /* api callbacks */
+       ot->exec= texture_paint_toggle_exec;
+       ot->poll= texture_paint_toggle_poll;
+
+       /* flags */
+       ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
diff --git a/source/blender/editors/sculpt_paint/paint_intern.h b/source/blender/editors/sculpt_paint/paint_intern.h
new file mode 100644 (file)
index 0000000..a5ac326
--- /dev/null
@@ -0,0 +1,62 @@
+/**
+ * $Id:
+ *
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version. 
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ * The Original Code is Copyright (C) 2008 Blender Foundation.
+ * All rights reserved.
+ *
+ * 
+ * Contributor(s): Blender Foundation
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#ifndef ED_PAINT_INTERN_H
+#define ED_PAINT_INTERN_H
+
+struct Scene;
+struct Object;
+struct Mesh;
+struct ViewContext;
+struct wmOperatorType;
+struct ARegion;
+
+/* paint_vertex.c */
+void PAINT_OT_weight_paint_toggle(struct wmOperatorType *ot);
+void PAINT_OT_weight_paint_radial_control(struct wmOperatorType *ot);
+void PAINT_OT_weight_paint(struct wmOperatorType *ot);
+
+void PAINT_OT_vertex_paint_radial_control(struct wmOperatorType *ot);
+void PAINT_OT_vertex_paint_toggle(struct wmOperatorType *ot);
+void PAINT_OT_vertex_paint(struct wmOperatorType *ot);
+
+/* paint_image.c */
+void PAINT_OT_image_paint(struct wmOperatorType *ot);
+void PAINT_OT_grab_clone(struct wmOperatorType *ot);
+void PAINT_OT_sample_color(struct wmOperatorType *ot);
+void PAINT_OT_set_clone_cursor(struct wmOperatorType *ot);
+void PAINT_OT_texture_paint_toggle(struct wmOperatorType *ot);
+
+/* paint_utils.c */
+int imapaint_pick_face(struct ViewContext *vc, struct Mesh *me, int *mval, unsigned int *index);
+void imapaint_pick_uv(struct Scene *scene, struct Object *ob, struct Mesh *mesh, unsigned int faceindex, int *xy, float *uv);
+
+void paint_sample_color(struct Scene *scene, struct ARegion *ar, int x, int y);
+
+#endif /* ED_PAINT_INTERN_H */
+
diff --git a/source/blender/editors/sculpt_paint/paint_ops.c b/source/blender/editors/sculpt_paint/paint_ops.c
new file mode 100644 (file)
index 0000000..0918e8c
--- /dev/null
@@ -0,0 +1,30 @@
+
+#include "ED_sculpt.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "paint_intern.h"
+
+/**************************** registration **********************************/
+
+void ED_operatortypes_paint(void)
+{
+       /* image */
+       WM_operatortype_append(PAINT_OT_texture_paint_toggle);
+       WM_operatortype_append(PAINT_OT_image_paint);
+       WM_operatortype_append(PAINT_OT_sample_color);
+       WM_operatortype_append(PAINT_OT_grab_clone);
+       WM_operatortype_append(PAINT_OT_set_clone_cursor);
+
+       /* weight */
+       WM_operatortype_append(PAINT_OT_weight_paint_toggle);
+       WM_operatortype_append(PAINT_OT_weight_paint_radial_control);
+       WM_operatortype_append(PAINT_OT_weight_paint);
+
+       /* vertex */
+       WM_operatortype_append(PAINT_OT_vertex_paint_radial_control);
+       WM_operatortype_append(PAINT_OT_vertex_paint_toggle);
+       WM_operatortype_append(PAINT_OT_vertex_paint);
+}
+
diff --git a/source/blender/editors/sculpt_paint/paint_utils.c b/source/blender/editors/sculpt_paint/paint_utils.c
new file mode 100644 (file)
index 0000000..e0ac3c9
--- /dev/null
@@ -0,0 +1,191 @@
+
+#include <math.h>
+#include <stdlib.h>
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_object_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_screen_types.h"
+#include "DNA_view3d_types.h"
+
+#include "BLI_arithb.h"
+
+#include "BKE_DerivedMesh.h"
+#include "BKE_global.h"
+#include "BKE_utildefines.h"
+
+#include "BIF_gl.h"
+
+#include "ED_view3d.h"
+
+#include "paint_intern.h"
+
+/* 3D Paint */
+
+static void imapaint_project(Object *ob, float *model, float *proj, float *co, float *pco)
+{
+       VECCOPY(pco, co);
+       pco[3]= 1.0f;
+
+       Mat4MulVecfl(ob->obmat, pco);
+       Mat4MulVecfl((float(*)[4])model, pco);
+       Mat4MulVec4fl((float(*)[4])proj, pco);
+}
+
+static void imapaint_tri_weights(Object *ob, float *v1, float *v2, float *v3, float *co, float *w)
+{
+       float pv1[4], pv2[4], pv3[4], h[3], divw;
+       float model[16], proj[16], wmat[3][3], invwmat[3][3];
+       GLint view[4];
+
+       /* compute barycentric coordinates */
+
+       /* get the needed opengl matrices */
+       glGetIntegerv(GL_VIEWPORT, view);
+       glGetFloatv(GL_MODELVIEW_MATRIX, model);
+       glGetFloatv(GL_PROJECTION_MATRIX, proj);
+       view[0] = view[1] = 0;
+
+       /* project the verts */
+       imapaint_project(ob, model, proj, v1, pv1);
+       imapaint_project(ob, model, proj, v2, pv2);
+       imapaint_project(ob, model, proj, v3, pv3);
+
+       /* do inverse view mapping, see gluProject man page */
+       h[0]= (co[0] - view[0])*2.0f/view[2] - 1;
+       h[1]= (co[1] - view[1])*2.0f/view[3] - 1;
+       h[2]= 1.0f;
+
+       /* solve for(w1,w2,w3)/perspdiv in:
+          h*perspdiv = Project*Model*(w1*v1 + w2*v2 + w3*v3) */
+
+       wmat[0][0]= pv1[0];  wmat[1][0]= pv2[0];  wmat[2][0]= pv3[0];
+       wmat[0][1]= pv1[1];  wmat[1][1]= pv2[1];  wmat[2][1]= pv3[1];
+       wmat[0][2]= pv1[3];  wmat[1][2]= pv2[3];  wmat[2][2]= pv3[3];
+
+       Mat3Inv(invwmat, wmat);
+       Mat3MulVecfl(invwmat, h);
+
+       VECCOPY(w, h);
+
+       /* w is still divided by perspdiv, make it sum to one */
+       divw= w[0] + w[1] + w[2];
+       if(divw != 0.0f)
+               VecMulf(w, 1.0f/divw);
+}
+
+/* compute uv coordinates of mouse in face */
+void imapaint_pick_uv(Scene *scene, Object *ob, Mesh *mesh, unsigned int faceindex, int *xy, float *uv)
+{
+       DerivedMesh *dm = mesh_get_derived_final(scene, ob, CD_MASK_BAREMESH);
+       int *index = dm->getFaceDataArray(dm, CD_ORIGINDEX);
+       MTFace *tface = dm->getFaceDataArray(dm, CD_MTFACE), *tf;
+       int numfaces = dm->getNumFaces(dm), a;
+       float p[2], w[3], absw, minabsw;
+       MFace mf;
+       MVert mv[4];
+
+       minabsw = 1e10;
+       uv[0] = uv[1] = 0.0;
+
+       /* test all faces in the derivedmesh with the original index of the picked face */
+       for(a = 0; a < numfaces; a++) {
+               if(index[a] == faceindex) {
+                       dm->getFace(dm, a, &mf);
+
+                       dm->getVert(dm, mf.v1, &mv[0]);
+                       dm->getVert(dm, mf.v2, &mv[1]);
+                       dm->getVert(dm, mf.v3, &mv[2]);
+                       if(mf.v4)
+                               dm->getVert(dm, mf.v4, &mv[3]);
+
+                       tf= &tface[a];
+
+                       p[0]= xy[0];
+                       p[1]= xy[1];
+
+                       if(mf.v4) {
+                               /* the triangle with the largest absolute values is the one
+                                  with the most negative weights */
+                               imapaint_tri_weights(ob, mv[0].co, mv[1].co, mv[3].co, p, w);
+                               absw= fabs(w[0]) + fabs(w[1]) + fabs(w[2]);
+                               if(absw < minabsw) {
+                                       uv[0]= tf->uv[0][0]*w[0] + tf->uv[1][0]*w[1] + tf->uv[3][0]*w[2];
+                                       uv[1]= tf->uv[0][1]*w[0] + tf->uv[1][1]*w[1] + tf->uv[3][1]*w[2];
+                                       minabsw = absw;
+                               }
+
+                               imapaint_tri_weights(ob, mv[1].co, mv[2].co, mv[3].co, p, w);
+                               absw= fabs(w[0]) + fabs(w[1]) + fabs(w[2]);
+                               if(absw < minabsw) {
+                                       uv[0]= tf->uv[1][0]*w[0] + tf->uv[2][0]*w[1] + tf->uv[3][0]*w[2];
+                                       uv[1]= tf->uv[1][1]*w[0] + tf->uv[2][1]*w[1] + tf->uv[3][1]*w[2];
+                                       minabsw = absw;
+                               }
+                       }
+                       else {
+                               imapaint_tri_weights(ob, mv[0].co, mv[1].co, mv[2].co, p, w);
+                               absw= fabs(w[0]) + fabs(w[1]) + fabs(w[2]);
+                               if(absw < minabsw) {
+                                       uv[0]= tf->uv[0][0]*w[0] + tf->uv[1][0]*w[1] + tf->uv[2][0]*w[2];
+                                       uv[1]= tf->uv[0][1]*w[0] + tf->uv[1][1]*w[1] + tf->uv[2][1]*w[2];
+                                       minabsw = absw;
+                               }
+                       }
+               }
+       }
+
+       dm->release(dm);
+}
+
+///* returns 0 if not found, otherwise 1 */
+int imapaint_pick_face(ViewContext *vc, Mesh *me, int *mval, unsigned int *index)
+{
+       if(!me || me->totface==0)
+               return 0;
+
+       /* sample only on the exact position */
+       *index = view3d_sample_backbuf(vc, mval[0], mval[1]);
+
+       if((*index)<=0 || (*index)>(unsigned int)me->totface)
+               return 0;
+
+       (*index)--;
+       
+       return 1;
+}
+
+/* used for both 3d view and image window */
+void paint_sample_color(Scene *scene, ARegion *ar, int x, int y)       /* frontbuf */
+{
+       VPaint *vp= scene->toolsettings->vpaint;
+       unsigned int col;
+       char *cp;
+
+       CLAMP(x, 0, ar->winx);
+       CLAMP(y, 0, ar->winy);
+       
+       glReadBuffer(GL_FRONT);
+       glReadPixels(x+ar->winrct.xmin, y+ar->winrct.ymin, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &col);
+       glReadBuffer(GL_BACK);
+
+       cp = (char *)&col;
+       
+       if(G.f & (G_VERTEXPAINT|G_WEIGHTPAINT)) {
+               vp->r= cp[0]/255.0f;
+               vp->g= cp[1]/255.0f;
+               vp->b= cp[2]/255.0f;
+       }
+       else {
+               Brush *brush= scene->toolsettings->imapaint.brush;
+
+               if(brush) {
+                       brush->rgb[0]= cp[0]/255.0f;
+                       brush->rgb[1]= cp[1]/255.0f;
+                       brush->rgb[2]= cp[2]/255.0f;
+
+               }
+       }
+}
+
similarity index 95%
rename from source/blender/editors/space_view3d/vpaint.c
rename to source/blender/editors/sculpt_paint/paint_vertex.c
index 1136518d4244c870c6ed00a5c012c11d4ca70ff9..1623c7feef1619147a732b0d819e05d1964f89d5 100644 (file)
@@ -90,8 +90,6 @@
 #include "ED_util.h"
 #include "ED_view3d.h"
 
-#include "view3d_intern.h"
-
        /* vp->mode */
 #define VP_MIX 0
 #define VP_ADD 1
@@ -581,45 +579,6 @@ void vpaint_dogamma(Scene *scene)
        }
 }
 
-/* used for both 3d view and image window */
-void sample_vpaint(Scene *scene, ARegion *ar)  /* frontbuf */
-{
-       VPaint *vp= scene->toolsettings->vpaint;
-       unsigned int col;
-       int x, y;
-       short mval[2];
-       char *cp;
-       
-//     getmouseco_areawin(mval);
-       x= mval[0]; y= mval[1];
-       
-       if(x<0 || y<0) return;
-       if(x>=ar->winx || y>=ar->winy) return;
-       
-       glReadBuffer(GL_FRONT);
-       glReadPixels(x, y, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &col);
-       glReadBuffer(GL_BACK);
-
-       cp = (char *)&col;
-       
-       if(G.f & (G_VERTEXPAINT|G_WEIGHTPAINT)) {
-               vp->r= cp[0]/255.0f;
-               vp->g= cp[1]/255.0f;
-               vp->b= cp[2]/255.0f;
-       }
-       else {
-               Brush *brush= scene->toolsettings->imapaint.brush;
-
-               if(brush) {
-                       brush->rgb[0]= cp[0]/255.0f;
-                       brush->rgb[1]= cp[1]/255.0f;
-                       brush->rgb[2]= cp[2]/255.0f;
-
-               }
-       }
-
-}
-
 static unsigned int mcol_blend(unsigned int col1, unsigned int col2, int fac)
 {
        char *cp1, *cp2, *cp;
@@ -1201,12 +1160,12 @@ static int paint_poll_test(bContext *C)
        return 1;
 }
 
-void VIEW3D_OT_wpaint_toggle(wmOperatorType *ot)
+void PAINT_OT_weight_paint_toggle(wmOperatorType *ot)
 {
        
        /* identifiers */
        ot->name= "Weight Paint Mode";
-       ot->idname= "VIEW3D_OT_wpaint_toggle";
+       ot->idname= "PAINT_OT_weight_paint_toggle";
        
        /* api callbacks */
        ot->exec= set_wpaint;
@@ -1222,7 +1181,7 @@ void VIEW3D_OT_wpaint_toggle(wmOperatorType *ot)
 void paint_radial_control_invoke(wmOperator *op, VPaint *vp)
 {
        int mode = RNA_int_get(op->ptr, "mode");
-       float original_value= 1.0f;
+       float original_value;
 
        if(mode == WM_RADIALCONTROL_SIZE)
                original_value = vp->size;
@@ -1291,12 +1250,12 @@ static int wpaint_radial_control_exec(bContext *C, wmOperator *op)
        return ret;
 }
 
-void VIEW3D_OT_wpaint_radial_control(wmOperatorType *ot)
+void PAINT_OT_weight_paint_radial_control(wmOperatorType *ot)
 {
        WM_OT_radial_control_partial(ot);
 
        ot->name= "Weight Paint Radial Control";
-       ot->idname= "VIEW3D_OT_wpaint_radial_control";
+       ot->idname= "PAINT_OT_weight_paint_radial_control";
 
        ot->invoke= wpaint_radial_control_invoke;
        ot->modal= wpaint_radial_control_modal;
@@ -1307,12 +1266,12 @@ void VIEW3D_OT_wpaint_radial_control(wmOperatorType *ot)
        ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
 }
 
-void VIEW3D_OT_vpaint_radial_control(wmOperatorType *ot)
+void PAINT_OT_vertex_paint_radial_control(wmOperatorType *ot)
 {
        WM_OT_radial_control_partial(ot);
 
        ot->name= "Vertex Paint Radial Control";
-       ot->idname= "VIEW3D_OT_vpaint_radial_control";
+       ot->idname= "PAINT_OT_vertex_paint_radial_control";
 
        ot->invoke= vpaint_radial_control_invoke;
        ot->modal= vpaint_radial_control_modal;
@@ -1391,6 +1350,7 @@ static int wpaint_modal(bContext *C, wmOperator *op, wmEvent *event)
                        float paintweight= wp->weight;
                        int *indexar= wpd->indexar;
                        int totindex, index, alpha, totw;
+                       short mval[2];
                        
                        view3d_operator_needs_opengl(C);
                        
@@ -1401,12 +1361,15 @@ static int wpaint_modal(bContext *C, wmOperator *op, wmEvent *event)
                        
                        MTC_Mat4SwapMat4(wpd->vc.rv3d->persmat, mat);
                        
+                       mval[0]= event->x - vc->ar->winrct.xmin;
+                       mval[1]= event->y - vc->ar->winrct.ymin;
+                       
                        /* which faces are involved */
                        if(wp->flag & VP_AREA) {
-                               totindex= sample_backbuf_area(vc, indexar, me->totface, event->mval[0], event->mval[1], wp->size);
+                               totindex= sample_backbuf_area(vc, indexar, me->totface, mval[0], mval[1], wp->size);
                        }
                        else {
-                               indexar[0]= view3d_sample_backbuf(vc, event->mval[0], event->mval[1]);
+                               indexar[0]= view3d_sample_backbuf(vc, mval[0], mval[1]);
                                if(indexar[0]) totindex= 1;
                                else totindex= 0;
                        }
@@ -1481,7 +1444,7 @@ static int wpaint_modal(bContext *C, wmOperator *op, wmEvent *event)
                                        MFace *mface= me->mface + (indexar[index]-1);
                                        
                                        if((me->dvert+mface->v1)->flag) {
-                                               alpha= calc_vp_alpha_dl(wp, vc, wpd->wpimat, wpd->vertexcosnos+6*mface->v1, event->mval);
+                                               alpha= calc_vp_alpha_dl(wp, vc, wpd->wpimat, wpd->vertexcosnos+6*mface->v1, mval);
                                                if(alpha) {
                                                        do_weight_paint_vertex(wp, ob, mface->v1, alpha, paintweight, wpd->vgroup_mirror);
                                                }
@@ -1489,7 +1452,7 @@ static int wpaint_modal(bContext *C, wmOperator *op, wmEvent *event)
                                        }
                                        
                                        if((me->dvert+mface->v2)->flag) {
-                                               alpha= calc_vp_alpha_dl(wp, vc, wpd->wpimat, wpd->vertexcosnos+6*mface->v2, event->mval);
+                                               alpha= calc_vp_alpha_dl(wp, vc, wpd->wpimat, wpd->vertexcosnos+6*mface->v2, mval);
                                                if(alpha) {
                                                        do_weight_paint_vertex(wp, ob, mface->v2, alpha, paintweight, wpd->vgroup_mirror);
                                                }
@@ -1497,7 +1460,7 @@ static int wpaint_modal(bContext *C, wmOperator *op, wmEvent *event)
                                        }
                                        
                                        if((me->dvert+mface->v3)->flag) {
-                                               alpha= calc_vp_alpha_dl(wp, vc, wpd->wpimat, wpd->vertexcosnos+6*mface->v3, event->mval);
+                                               alpha= calc_vp_alpha_dl(wp, vc, wpd->wpimat, wpd->vertexcosnos+6*mface->v3, mval);
                                                if(alpha) {
                                                        do_weight_paint_vertex(wp, ob, mface->v3, alpha, paintweight, wpd->vgroup_mirror);
                                                }
@@ -1506,7 +1469,7 @@ static int wpaint_modal(bContext *C, wmOperator *op, wmEvent *event)
                                        
                                        if((me->dvert+mface->v4)->flag) {
                                                if(mface->v4) {
-                                                       alpha= calc_vp_alpha_dl(wp, vc, wpd->wpimat, wpd->vertexcosnos+6*mface->v4, event->mval);
+                                                       alpha= calc_vp_alpha_dl(wp, vc, wpd->wpimat, wpd->vertexcosnos+6*mface->v4, mval);
                                                        if(alpha) {
                                                                do_weight_paint_vertex(wp, ob, mface->v4, alpha, paintweight, wpd->vgroup_mirror);
                                                        }
@@ -1628,12 +1591,12 @@ static int wpaint_invoke(bContext *C, wmOperator *op, wmEvent *event)
        return OPERATOR_RUNNING_MODAL;
 }
 
-void VIEW3D_OT_wpaint(wmOperatorType *ot)
+void PAINT_OT_weight_paint(wmOperatorType *ot)
 {
        
        /* identifiers */
        ot->name= "Weight Paint";
-       ot->idname= "VIEW3D_OT_wpaint";
+       ot->idname= "PAINT_OT_weight_paint";
        
        /* api callbacks */
        ot->invoke= wpaint_invoke;
@@ -1646,7 +1609,6 @@ void VIEW3D_OT_wpaint(wmOperatorType *ot)
        
 }
 
-
 /* ************ set / clear vertex paint mode ********** */
 
 
@@ -1704,12 +1666,12 @@ static int set_vpaint(bContext *C, wmOperator *op)              /* toggle */
        return OPERATOR_FINISHED;
 }
 
-void VIEW3D_OT_vpaint_toggle(wmOperatorType *ot)
+void PAINT_OT_vertex_paint_toggle(wmOperatorType *ot)
 {
        
        /* identifiers */
        ot->name= "Vertex Paint Mode";
-       ot->idname= "VIEW3D_OT_vpaint_toggle";
+       ot->idname= "PAINT_OT_vertex_paint_toggle";
        
        /* api callbacks */
        ot->exec= set_vpaint;
@@ -1789,6 +1751,7 @@ static int vpaint_modal(bContext *C, wmOperator *op, wmEvent *event)
                        float mat[4][4];
                        int *indexar= vpd->indexar;
                        int totindex, index;
+                       short mval[2];
                        
                        view3d_operator_needs_opengl(C);
                        
@@ -1797,12 +1760,15 @@ static int vpaint_modal(bContext *C, wmOperator *op, wmEvent *event)
                        wmGetSingleMatrix(mat);
                        wmLoadMatrix(vc->rv3d->viewmat);
                        
+                       mval[0]= event->x - vc->ar->winrct.xmin;
+                       mval[1]= event->y - vc->ar->winrct.ymin;
+                               
                        /* which faces are involved */
                        if(vp->flag & VP_AREA) {
-                               totindex= sample_backbuf_area(vc, indexar, me->totface, event->mval[0], event->mval[1], vp->size);
+                               totindex= sample_backbuf_area(vc, indexar, me->totface, mval[0], mval[1], vp->size);
                        }
                        else {
-                               indexar[0]= view3d_sample_backbuf(vc, event->mval[0], event->mval[1]);
+                               indexar[0]= view3d_sample_backbuf(vc, mval[0], mval[1]);
                                if(indexar[0]) totindex= 1;
                                else totindex= 0;
                        }
@@ -1851,17 +1817,17 @@ static int vpaint_modal(bContext *C, wmOperator *op, wmEvent *event)
                                                
                                        }
                                        
-                                       alpha= calc_vp_alpha_dl(vp, vc, vpd->vpimat, vpd->vertexcosnos+6*mface->v1, event->mval);
+                                       alpha= calc_vp_alpha_dl(vp, vc, vpd->vpimat, vpd->vertexcosnos+6*mface->v1, mval);
                                        if(alpha) vpaint_blend(vp, mcol, mcolorig, vpd->paintcol, alpha);
                                        
-                                       alpha= calc_vp_alpha_dl(vp, vc, vpd->vpimat, vpd->vertexcosnos+6*mface->v2, event->mval);
+                                       alpha= calc_vp_alpha_dl(vp, vc, vpd->vpimat, vpd->vertexcosnos+6*mface->v2, mval);
                                        if(alpha) vpaint_blend(vp, mcol+1, mcolorig+1, vpd->paintcol, alpha);
                                        
-                                       alpha= calc_vp_alpha_dl(vp, vc, vpd->vpimat, vpd->vertexcosnos+6*mface->v3, event->mval);
+                                       alpha= calc_vp_alpha_dl(vp, vc, vpd->vpimat, vpd->vertexcosnos+6*mface->v3, mval);
                                        if(alpha) vpaint_blend(vp, mcol+2, mcolorig+2, vpd->paintcol, alpha);
                                        
                                        if(mface->v4) {
-                                               alpha= calc_vp_alpha_dl(vp, vc, vpd->vpimat, vpd->vertexcosnos+6*mface->v4, event->mval);
+                                               alpha= calc_vp_alpha_dl(vp, vc, vpd->vpimat, vpd->vertexcosnos+6*mface->v4, mval);
                                                if(alpha) vpaint_blend(vp, mcol+3, mcolorig+3, vpd->paintcol, alpha);
                                        }
                                }
@@ -1922,12 +1888,12 @@ static int vpaint_invoke(bContext *C, wmOperator *op, wmEvent *event)
        return OPERATOR_RUNNING_MODAL;
 }
 
-void VIEW3D_OT_vpaint(wmOperatorType *ot)
+void PAINT_OT_vertex_paint(wmOperatorType *ot)
 {
        
        /* identifiers */
        ot->name= "Vertex Paint";
-       ot->idname= "VIEW3D_OT_vpaint";
+       ot->idname= "PAINT_OT_vertex_paint";
        
        /* api callbacks */
        ot->invoke= vpaint_invoke;
@@ -1940,4 +1906,3 @@ void VIEW3D_OT_vpaint(wmOperatorType *ot)
        
 }
 
-
index 86d29840be646e313c72355d007e10cfa677900d..cae8afb2c44a7b5e133e204e72790b5ccd4bec71 100644 (file)
@@ -82,6 +82,7 @@ void ED_spacetypes_init(void)
        ED_operatortypes_mesh();
        ED_operatortypes_sculpt();
        ED_operatortypes_uvedit();
+       ED_operatortypes_paint();
        ED_operatortypes_curve();
        ED_operatortypes_armature();
        ED_marker_operatortypes();
index b088f5920edae3095e7aa460fb7d05c2b6de4bf7..912c2f8642335d1349f158441be988b394def22e 100644 (file)
@@ -189,6 +189,10 @@ void image_keymap(struct wmWindowManager *wm)
        WM_keymap_add_item(keymap, "IMAGE_OT_reload", RKEY, KM_PRESS, KM_ALT, 0);
        WM_keymap_add_item(keymap, "IMAGE_OT_save", SKEY, KM_PRESS, KM_ALT, 0);
 
+       WM_keymap_add_item(keymap, "PAINT_OT_image_paint", ACTIONMOUSE, KM_PRESS, 0, 0);
+       WM_keymap_add_item(keymap, "PAINT_OT_grab_clone", SELECTMOUSE, KM_PRESS, 0, 0);
+       WM_keymap_add_item(keymap, "PAINT_OT_sample_color", SELECTMOUSE, KM_PRESS, 0, 0);
+
        WM_keymap_add_item(keymap, "IMAGE_OT_sample", ACTIONMOUSE, KM_PRESS, 0, 0);
        RNA_enum_set(WM_keymap_add_item(keymap, "IMAGE_OT_set_curves_point", ACTIONMOUSE, KM_PRESS, KM_CTRL, 0)->ptr, "point", 0);
        RNA_enum_set(WM_keymap_add_item(keymap, "IMAGE_OT_set_curves_point", ACTIONMOUSE, KM_PRESS, KM_SHIFT, 0)->ptr, "point", 1);
@@ -353,6 +357,10 @@ static void image_main_area_init(wmWindowManager *wm, ARegion *ar)
        
        // image space manages own v2d
        // UI_view2d_region_reinit(&ar->v2d, V2D_COMMONVIEW_STANDARD, ar->winx, ar->winy);
+
+       /* image paint polls for mode */
+       keymap= WM_keymap_listbase(wm, "ImagePaint", SPACE_IMAGE, 0);
+       WM_event_add_keymap_handler_bb(&ar->handlers, keymap, &ar->v2d.mask, &ar->winrct);
        
        /* own keymap */
        keymap= WM_keymap_listbase(wm, "Image", SPACE_IMAGE, 0);
index 92c8b8f0b69edeb1d9c5e857c1c725eb6bf20e4d..5eb7caf50f10d0528f313e47a0c646dab72afe75 100644 (file)
@@ -478,7 +478,7 @@ void draw_mesh_text(Scene *scene, Object *ob, int glsl)
                return;
 
        /* don't draw when editing */
-       if(me->edit_mesh)
+       if(ob == scene->obedit)
                return;
        else if(ob==OBACT)
                if(FACESEL_PAINT_TEST)
@@ -558,7 +558,7 @@ void draw_mesh_textured(Scene *scene, View3D *v3d, RegionView3D *rv3d, Object *o
        /* draw the textured mesh */
        draw_textured_begin(scene, v3d, rv3d, ob);
 
-       if(me->edit_mesh) {
+       if(ob == scene->obedit) {
                dm->drawMappedFacesTex(dm, draw_em_tf_mapped__set_draw, me->edit_mesh);
        } else if(faceselect) {
                if(G.f & G_WEIGHTPAINT)
@@ -566,8 +566,9 @@ void draw_mesh_textured(Scene *scene, View3D *v3d, RegionView3D *rv3d, Object *o
                else
                        dm->drawMappedFacesTex(dm, draw_tface_mapped__set_draw, me);
        }
-       else
+       else {
                dm->drawFacesTex(dm, draw_tface__set_draw);
+       }
 
        /* draw game engine text hack */
        if(get_ob_property(ob, "Text")) 
@@ -576,7 +577,7 @@ void draw_mesh_textured(Scene *scene, View3D *v3d, RegionView3D *rv3d, Object *o
        draw_textured_end();
        
        /* draw edges and selected faces over textured mesh */
-       if(!me->edit_mesh && faceselect)
+       if(!(ob == scene->obedit) && faceselect)
                draw_tfaces3D(rv3d, ob, me, dm);
 
        /* reset from negative scale correction */
index 85edf6cf5fb45197dfa220e68f1b82411a9a5f16..7df8f3a75db4b296432fdd6e376da4b0f83413ab 100644 (file)
@@ -5360,9 +5360,10 @@ void draw_object_backbufsel(Scene *scene, View3D *v3d, RegionView3D *rv3d, Objec
        switch( ob->type) {
        case OB_MESH:
        {
-               Mesh *me= ob->data;
-               EditMesh *em= me->edit_mesh;
-               if(em) {
+               if(ob == scene->obedit) {
+                       Mesh *me= ob->data;
+                       EditMesh *em= me->edit_mesh;
+
                        DerivedMesh *dm = editmesh_get_derived_cage(scene, ob, em, CD_MASK_BAREMESH);
 
                        EM_init_index_arrays(em, 1, 1, 1);
@@ -5415,7 +5416,7 @@ static void draw_object_mesh_instance(Scene *scene, View3D *v3d, RegionView3D *r
        DerivedMesh *dm=NULL, *edm=NULL;
        int glsl;
        
-       if(me->edit_mesh)
+       if(ob == scene->obedit)
                edm= editmesh_get_derived_base(ob, me->edit_mesh);
        else 
                dm = mesh_get_derived_final(scene, ob, CD_MASK_BAREMESH);
index 36ab867f2ae5cd76d06419fcaf0bc0c408e2f803..d3935b496582229fa364dbf6e28f876f50816842 100644 (file)
@@ -272,6 +272,9 @@ static void view3d_main_area_init(wmWindowManager *wm, ARegion *ar)
        
        /* modal ops keymaps */
        view3d_modal_keymaps(wm, ar, rv3d->lastmode);
+       /* operator poll checks for modes */
+       keymap= WM_keymap_listbase(wm, "ImagePaint", 0, 0);
+       WM_event_add_keymap_handler(&ar->handlers, keymap);
 }
 
 /* type callback, not region itself */
@@ -368,6 +371,11 @@ static void view3d_main_area_listener(ARegion *ar, wmNotifier *wmn)
                                        ED_region_tag_redraw(ar);
                                        break;
                        }
+               case NC_IMAGE:  
+                       /* this could be more fine grained checks if we had
+                        * more context than just the region */
+                       ED_region_tag_redraw(ar);
+                       break;
        }
 }
 
index ba774677420f1f6a2e38d79b2202849ccc3a3596..706ce8c9d400e8c7dce16af4ab02e5dc265c192b 100644 (file)
@@ -1081,7 +1081,7 @@ void backdrawview3d(Scene *scene, ARegion *ar, View3D *v3d)
 #endif
 
        if(G.f & G_VERTEXPAINT || G.f & G_WEIGHTPAINT);
-       else if((G.f & G_TEXTUREPAINT) && scene->toolsettings && (scene->toolsettings->imapaint.flag & IMAGEPAINT_PROJECT_DISABLE)==0);
+       else if((G.f & G_TEXTUREPAINT) && scene->toolsettings && (scene->toolsettings->imapaint.flag & IMAGEPAINT_PROJECT_DISABLE));
        else if(scene->obedit && v3d->drawtype>OB_WIRE && (v3d->flag & V3D_ZBUF_SELECT));
        else {
                v3d->flag &= ~V3D_NEEDBACKBUFDRAW;
index f0b8587ba5641f71700fd6439328a5b44da1ccc4..f30d2f2231fed1dd9c4291e052fee2a0c1627f87 100644 (file)
@@ -137,10 +137,12 @@ static int retopo_mesh_paint_check() {return 0;}
 /* well... in this file a lot of view mode manipulation happens, so let's have it defined here */
 void ED_view3d_exit_paint_modes(bContext *C)
 {
+       if(G.f & G_TEXTUREPAINT)
+               WM_operator_name_call(C, "PAINT_OT_texture_paint_toggle", WM_OP_EXEC_REGION_WIN, NULL);
        if(G.f & G_VERTEXPAINT)
-               WM_operator_name_call(C, "VIEW3D_OT_vpaint_toggle", WM_OP_EXEC_REGION_WIN, NULL);
+               WM_operator_name_call(C, "PAINT_OT_vertex_paint_toggle", WM_OP_EXEC_REGION_WIN, NULL);
        else if(G.f & G_WEIGHTPAINT)
-               WM_operator_name_call(C, "VIEW3D_OT_wpaint_toggle", WM_OP_EXEC_REGION_WIN, NULL);
+               WM_operator_name_call(C, "PAINT_OT_weight_paint_toggle", WM_OP_EXEC_REGION_WIN, NULL);
 
        if(G.f & G_SCULPTMODE)
                WM_operator_name_call(C, "SCULPT_OT_sculptmode_toggle", WM_OP_EXEC_REGION_WIN, NULL);
@@ -5019,7 +5021,7 @@ static void do_view3d_header_buttons(bContext *C, void *arg, int event)
                                ED_view3d_exit_paint_modes(C);
                                if(obedit) ED_object_exit_editmode(C, EM_FREEDATA|EM_FREEUNDO|EM_WAITCURSOR);   /* exit editmode and undo */
                                
-                               WM_operator_name_call(C, "VIEW3D_OT_vpaint_toggle", WM_OP_EXEC_REGION_WIN, NULL);
+                               WM_operator_name_call(C, "PAINT_OT_vertex_paint_toggle", WM_OP_EXEC_REGION_WIN, NULL);
                        }
                } 
                else if (v3d->modeselect == V3D_TEXTUREPAINTMODE_SEL) {
@@ -5027,8 +5029,8 @@ static void do_view3d_header_buttons(bContext *C, void *arg, int event)
                                v3d->flag &= ~V3D_MODE;
                                ED_view3d_exit_paint_modes(C);
                                if(obedit) ED_object_exit_editmode(C, EM_FREEDATA|EM_FREEUNDO|EM_WAITCURSOR);   /* exit editmode and undo */
-                                       
-// XXX                         set_texturepaint();
+
+                               WM_operator_name_call(C, "PAINT_OT_texture_paint_toggle", WM_OP_EXEC_REGION_WIN, NULL);
                        }
                } 
                else if (v3d->modeselect == V3D_WEIGHTPAINTMODE_SEL) {
@@ -5038,7 +5040,7 @@ static void do_view3d_header_buttons(bContext *C, void *arg, int event)
                                if(obedit) 
                                        ED_object_exit_editmode(C, EM_FREEDATA|EM_FREEUNDO|EM_WAITCURSOR);      /* exit editmode and undo */
                                
-                               WM_operator_name_call(C, "VIEW3D_OT_wpaint_toggle", WM_OP_EXEC_REGION_WIN, NULL);
+                               WM_operator_name_call(C, "PAINT_OT_weight_paint_toggle", WM_OP_EXEC_REGION_WIN, NULL);
                        }
                } 
                else if (v3d->modeselect == V3D_POSEMODE_SEL) {
index bf393a966c9c7e4e8c082af808c08a78c5815fc5..123f8674033a60e6dbf46168e1534448bd11d65d 100644 (file)
@@ -112,14 +112,6 @@ void VIEW3D_OT_circle_select(struct wmOperatorType *ot);
 void VIEW3D_OT_borderselect(struct wmOperatorType *ot);
 void VIEW3D_OT_lasso_select(struct wmOperatorType *ot);
 
-/* vpaint.c */
-void VIEW3D_OT_vpaint_radial_control(struct wmOperatorType *ot);
-void VIEW3D_OT_wpaint_radial_control(struct wmOperatorType *ot);
-void VIEW3D_OT_vpaint_toggle(struct wmOperatorType *ot);
-void VIEW3D_OT_vpaint(struct wmOperatorType *ot);
-void VIEW3D_OT_wpaint_toggle(struct wmOperatorType *ot);
-void VIEW3D_OT_wpaint(struct wmOperatorType *ot);
-
 /* view3d_view.c */
 void VIEW3D_OT_smoothview(struct wmOperatorType *ot);
 void VIEW3D_OT_setcameratoview(struct wmOperatorType *ot);
@@ -130,6 +122,8 @@ int boundbox_clip(RegionView3D *rv3d, float obmat[][4], struct BoundBox *bb);
 void view3d_project_short_clip(struct ARegion *ar, float *vec, short *adr, float projmat[4][4], float wmat[4][4]);
 void view3d_project_short_noclip(struct ARegion *ar, float *vec, short *adr, float mat[4][4]);
 
+void view3d_project_float(struct ARegion *a, float *vec, float *adr, float mat[4][4]);
+
 void centerview(struct ARegion *ar, View3D *v3d);
 
 void smooth_view(struct bContext *C, Object *, Object *, float *ofs, float *quat, float *dist, float *lens);
index 645dadcd27578adeed7c1c41932ab6c2fe3166a9..f18b3c64cf8fd8a48072b63f93a00f17d1afe21d 100644 (file)
@@ -83,12 +83,6 @@ void view3d_operatortypes(void)
        WM_operatortype_append(VIEW3D_OT_lasso_select);
        WM_operatortype_append(VIEW3D_OT_setcameratoview);
        WM_operatortype_append(VIEW3D_OT_drawtype);
-       WM_operatortype_append(VIEW3D_OT_vpaint_radial_control);
-       WM_operatortype_append(VIEW3D_OT_wpaint_radial_control);
-       WM_operatortype_append(VIEW3D_OT_vpaint_toggle);
-       WM_operatortype_append(VIEW3D_OT_wpaint_toggle);
-       WM_operatortype_append(VIEW3D_OT_vpaint);
-       WM_operatortype_append(VIEW3D_OT_wpaint);
        WM_operatortype_append(VIEW3D_OT_editmesh_face_toolbox);
        WM_operatortype_append(VIEW3D_OT_properties);
        WM_operatortype_append(VIEW3D_OT_localview);
@@ -109,8 +103,8 @@ void view3d_keymap(wmWindowManager *wm)
        ListBase *keymap= WM_keymap_listbase(wm, "View3D Generic", SPACE_VIEW3D, 0);
        wmKeymapItem *km;
        
-       WM_keymap_add_item(keymap, "VIEW3D_OT_vpaint_toggle", VKEY, KM_PRESS, 0, 0);
-       WM_keymap_add_item(keymap, "VIEW3D_OT_wpaint_toggle", TABKEY, KM_PRESS, KM_CTRL, 0);
+       WM_keymap_add_item(keymap, "PAINT_OT_vertex_paint_toggle", VKEY, KM_PRESS, 0, 0);
+       WM_keymap_add_item(keymap, "PAINT_OT_weight_paint_toggle", TABKEY, KM_PRESS, KM_CTRL, 0);
        
        WM_keymap_add_item(keymap, "VIEW3D_OT_properties", NKEY, KM_PRESS, 0, 0);
        
@@ -118,8 +112,12 @@ void view3d_keymap(wmWindowManager *wm)
        keymap= WM_keymap_listbase(wm, "View3D", SPACE_VIEW3D, 0);
        
        /* paint poll checks mode */
-       WM_keymap_verify_item(keymap, "VIEW3D_OT_vpaint", LEFTMOUSE, KM_PRESS, 0, 0);
-       WM_keymap_verify_item(keymap, "VIEW3D_OT_wpaint", LEFTMOUSE, KM_PRESS, 0, 0);
+       WM_keymap_verify_item(keymap, "PAINT_OT_vertex_paint", LEFTMOUSE, KM_PRESS, 0, 0);
+       WM_keymap_verify_item(keymap, "PAINT_OT_weight_paint", LEFTMOUSE, KM_PRESS, 0, 0);
+
+       WM_keymap_add_item(keymap, "PAINT_OT_image_paint", LEFTMOUSE, KM_PRESS, 0, 0);
+       WM_keymap_add_item(keymap, "PAINT_OT_sample_color", RIGHTMOUSE, KM_PRESS, 0, 0);
+       WM_keymap_add_item(keymap, "PAINT_OT_set_clone_cursor", LEFTMOUSE, KM_PRESS, KM_CTRL, 0);
 
        WM_keymap_add_item(keymap, "SCULPT_OT_brush_stroke", LEFTMOUSE, KM_PRESS, 0, 0);
        WM_keymap_add_item(keymap, "SCULPT_OT_brush_stroke", LEFTMOUSE, KM_PRESS, KM_SHIFT, 0);
@@ -196,10 +194,10 @@ void view3d_keymap(wmWindowManager *wm)
        RNA_enum_set(WM_keymap_add_item(keymap, "SCULPT_OT_radial_control", FKEY, KM_PRESS, KM_SHIFT, 0)->ptr, "mode", WM_RADIALCONTROL_STRENGTH);
        RNA_enum_set(WM_keymap_add_item(keymap, "SCULPT_OT_radial_control", FKEY, KM_PRESS, KM_CTRL, 0)->ptr, "mode", WM_RADIALCONTROL_ANGLE);
 
-       RNA_enum_set(WM_keymap_add_item(keymap, "VIEW3D_OT_vpaint_radial_control", FKEY, KM_PRESS, 0, 0)->ptr, "mode", WM_RADIALCONTROL_SIZE);
-       RNA_enum_set(WM_keymap_add_item(keymap, "VIEW3D_OT_wpaint_radial_control", FKEY, KM_PRESS, 0, 0)->ptr, "mode", WM_RADIALCONTROL_SIZE);
-       RNA_enum_set(WM_keymap_add_item(keymap, "VIEW3D_OT_vpaint_radial_control", FKEY, KM_PRESS, KM_SHIFT, 0)->ptr, "mode", WM_RADIALCONTROL_STRENGTH);
-       RNA_enum_set(WM_keymap_add_item(keymap, "VIEW3D_OT_wpaint_radial_control", FKEY, KM_PRESS, KM_SHIFT, 0)->ptr, "mode", WM_RADIALCONTROL_STRENGTH);
+       RNA_enum_set(WM_keymap_add_item(keymap, "PAINT_OT_vertex_paint_radial_control", FKEY, KM_PRESS, 0, 0)->ptr, "mode", WM_RADIALCONTROL_SIZE);
+       RNA_enum_set(WM_keymap_add_item(keymap, "PAINT_OT_weight_paint_radial_control", FKEY, KM_PRESS, 0, 0)->ptr, "mode", WM_RADIALCONTROL_SIZE);
+       RNA_enum_set(WM_keymap_add_item(keymap, "PAINT_OT_vertex_paint_radial_control", FKEY, KM_PRESS, KM_SHIFT, 0)->ptr, "mode", WM_RADIALCONTROL_STRENGTH);
+       RNA_enum_set(WM_keymap_add_item(keymap, "PAINT_OT_weight_paint_radial_control", FKEY, KM_PRESS, KM_SHIFT, 0)->ptr, "mode", WM_RADIALCONTROL_STRENGTH);
 
        /* TODO - this is just while we have no way to load a text datablock */
        RNA_string_set(WM_keymap_add_item(keymap, "SCRIPT_OT_run_pyfile", PKEY, KM_PRESS, 0, 0)->ptr, "filename", "test.py");
index ae75c9d967b5f64073883ee9c3d25dc395cda6e3..9dca9350754e8b47507dbe60621161b412d12f03 100644 (file)
@@ -45,6 +45,7 @@
 
 #include "ED_armature.h"
 #include "ED_mesh.h"
+#include "ED_sculpt.h"
 #include "ED_util.h"
 
 #include "UI_text.h"
@@ -57,6 +58,7 @@ void ED_editors_exit(bContext *C)
        
        /* frees all editmode undos */
        undo_editmode_clear();
+       undo_imagepaint_clear();
        
        /* global in meshtools... */
        mesh_octree_table(ob, NULL, NULL, 'e');
index 15dba3fc951c6871a63afd512dc90533249179af..0584644c0275fb98ee5b1df5de054b7413cdd85e 100644 (file)
@@ -60,6 +60,7 @@
 #include "ED_mesh.h"
 #include "ED_object.h"
 #include "ED_screen.h"
+#include "ED_sculpt.h"
 #include "ED_util.h"
 
 #include "WM_api.h"
@@ -72,7 +73,6 @@
 
 /* ********* XXX **************** */
 static void undo_push_mball() {}
-static void undo_imagepaint_step() {}
 static void sound_initialize_sounds() {}
 /* ********* XXX **************** */
 
index bd19bfdacb29d53deef1daa65ed5bfe2cac62854..b8a8f0bc8af459f174b90d2243df098056b7fe9a 100644 (file)
@@ -45,6 +45,7 @@ CPPFLAGS += -I../../blenlib
 CPPFLAGS += -I../../makesdna
 CPPFLAGS += -I../../makesrna
 CPPFLAGS += -I../../imbuf
+CPPFLAGS += -I../../gpu
 CPPFLAGS += -I$(NAN_GUARDEDALLOC)/include
 CPPFLAGS += -I$(NAN_OPENNL)/include
 
index 29989a5b4398108e4f5c470aa75ddc8dace1515b..b472b89d23dfe390a831fbe7c01d83294aea9590 100644 (file)
@@ -5,6 +5,6 @@ sources = env.Glob('*.c')
 
 incs = '../include ../../blenlib ../../blenkernel ../../makesdna ../../imbuf'
 incs += ' ../../windowmanager #/intern/guardedalloc #/extern/glew/include'
-incs += ' ../../makesrna #/intern/opennl/extern'
+incs += ' ../../makesrna #/intern/opennl/extern ../../gpu'
 
 env.BlenderLib ( 'bf_editors_uvedit', sources, Split(incs), [], libtype=['core'], priority=[45] )
index 359573a141c4e623727cf8a175bee8cd939ba970..88520a12e758b66a7003b21664758a827fdb62dd 100644 (file)
@@ -51,7 +51,7 @@ typedef struct Brush {
 
        struct BrushClone clone;
 
-       struct CurveMapping *curve;             /* falloff curve */
+       struct CurveMapping *curve;     /* falloff curve */
        struct MTex *mtex[18];          /* MAX_MTEX */
        
        short flag, blend;                      /* general purpose flag, blend mode */
@@ -63,7 +63,7 @@ typedef struct Brush {
        float rgb[3];                           /* color */
        float alpha;                            /* opacity */
 
-       float rot;                              /* rotation in radians */
+       float rot;                                      /* rotation in radians */
 
        short texact;                           /* active texture */
        char sculpt_tool;                       /* active tool */
index e48300917d585ba1cce5f60de6df4584d225dda9..ce3d627863ea940412e33aa098abe52cb591286d 100644 (file)
@@