diff options
author | Campbell Barton <ideasman42@gmail.com> | 2019-04-17 07:17:24 +0300 |
---|---|---|
committer | Campbell Barton <ideasman42@gmail.com> | 2019-04-17 07:21:24 +0300 |
commit | e12c08e8d170b7ca40f204a5b0423c23a9fbc2c1 (patch) | |
tree | 8cf3453d12edb177a218ef8009357518ec6cab6a /source/blender/editors/sculpt_paint/paint_image_proj.c | |
parent | b3dabc200a4b0399ec6b81f2ff2730d07b44fcaa (diff) |
ClangFormat: apply to source, most of intern
Apply clang format as proposed in T53211.
For details on usage and instructions for migrating branches
without conflicts, see:
https://wiki.blender.org/wiki/Tools/ClangFormat
Diffstat (limited to 'source/blender/editors/sculpt_paint/paint_image_proj.c')
-rw-r--r-- | source/blender/editors/sculpt_paint/paint_image_proj.c | 10715 |
1 files changed, 5467 insertions, 5248 deletions
diff --git a/source/blender/editors/sculpt_paint/paint_image_proj.c b/source/blender/editors/sculpt_paint/paint_image_proj.c index 45ae3e9dc11..7a99f819913 100644 --- a/source/blender/editors/sculpt_paint/paint_image_proj.c +++ b/source/blender/editors/sculpt_paint/paint_image_proj.c @@ -45,7 +45,6 @@ #include "BLT_translation.h" - #include "IMB_imbuf.h" #include "IMB_imbuf_types.h" @@ -114,7 +113,7 @@ static void partial_redraw_array_init(ImagePaintPartialRedraw *pr); /* unit_float_to_uchar_clamp as inline function */ BLI_INLINE unsigned char f_to_char(const float val) { - return unit_float_to_uchar_clamp(val); + return unit_float_to_uchar_clamp(val); } /* ProjectionPaint defines */ @@ -131,58 +130,56 @@ BLI_INLINE unsigned char f_to_char(const float val) #define PROJ_BUCKET_RECT_MAX 256 #define PROJ_BOUNDBOX_DIV 8 -#define PROJ_BOUNDBOX_SQUARED (PROJ_BOUNDBOX_DIV * PROJ_BOUNDBOX_DIV) +#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 - #ifndef PROJ_DEBUG_NOSEAMBLEED /* 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_IGNORE (1<<0) /* When the face is hidden, backfacing or occluded */ +//#define PROJ_FACE_INIT (1<<1) /* When we have initialized the faces data */ /* If this face has a seam on any of its edges. */ -#define PROJ_FACE_SEAM0 (1 << 0) -#define PROJ_FACE_SEAM1 (1 << 1) -#define PROJ_FACE_SEAM2 (1 << 2) +# define PROJ_FACE_SEAM0 (1 << 0) +# define PROJ_FACE_SEAM1 (1 << 1) +# define PROJ_FACE_SEAM2 (1 << 2) -#define PROJ_FACE_NOSEAM0 (1 << 4) -#define PROJ_FACE_NOSEAM1 (1 << 5) -#define PROJ_FACE_NOSEAM2 (1 << 6) +# define PROJ_FACE_NOSEAM0 (1 << 4) +# define PROJ_FACE_NOSEAM1 (1 << 5) +# define PROJ_FACE_NOSEAM2 (1 << 6) /* If the seam is completely initialized, including adjecent seams. */ -#define PROJ_FACE_SEAM_INIT0 (1 << 8) -#define PROJ_FACE_SEAM_INIT1 (1 << 9) -#define PROJ_FACE_SEAM_INIT2 (1 << 10) +# define PROJ_FACE_SEAM_INIT0 (1 << 8) +# define PROJ_FACE_SEAM_INIT1 (1 << 9) +# define PROJ_FACE_SEAM_INIT2 (1 << 10) -#define PROJ_FACE_DEGENERATE (1 << 12) +# define PROJ_FACE_DEGENERATE (1 << 12) /* face winding */ -#define PROJ_FACE_WINDING_INIT 1 -#define PROJ_FACE_WINDING_CW 2 +# define PROJ_FACE_WINDING_INIT 1 +# define PROJ_FACE_WINDING_CW 2 /* 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 -#endif /* PROJ_DEBUG_NOSEAMBLEED */ - +# define PROJ_FACE_SCALE_SEAM 0.99f +#endif /* PROJ_DEBUG_NOSEAMBLEED */ -#define PROJ_SRC_VIEW 1 -#define PROJ_SRC_IMAGE_CAM 2 +#define PROJ_SRC_VIEW 1 +#define PROJ_SRC_IMAGE_CAM 2 #define PROJ_SRC_IMAGE_VIEW 3 -#define PROJ_SRC_VIEW_FILL 4 +#define PROJ_SRC_VIEW_FILL 4 #define PROJ_VIEW_DATA_ID "view_data" /* viewmat + winmat + clip_start + clip_end + is_ortho */ #define PROJ_VIEW_DATA_SIZE (4 * 4 + 4 * 4 + 3) -#define PROJ_BUCKET_NULL 0 -#define PROJ_BUCKET_INIT (1 << 0) -// #define PROJ_BUCKET_CLONE_INIT (1<<1) +#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.00075f @@ -198,605 +195,595 @@ BLI_INLINE unsigned char f_to_char(const float val) * their 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; - /** Only used to build undo tiles during painting. */ - volatile void **undoRect; - /** The mask accumulation must happen on canvas, not on space screen bucket. - * Here we store the mask rectangle. */ - unsigned short **maskRect; - /** Store flag to enforce validation of undo rectangle. */ - bool **valid; - bool touch; + Image *ima; + ImBuf *ibuf; + ImagePaintPartialRedraw *partRedrawRect; + /** Only used to build undo tiles during painting. */ + volatile void **undoRect; + /** The mask accumulation must happen on canvas, not on space screen bucket. + * Here we store the mask rectangle. */ + unsigned short **maskRect; + /** Store flag to enforce validation of undo rectangle. */ + bool **valid; + bool touch; } ProjPaintImage; /** * Handle for stroke (operator customdata) */ typedef struct ProjStrokeHandle { - /* Support for painting from multiple views at once, - * currently used to implement symmetry painting, - * we can assume at least the first is set while painting. */ - struct ProjPaintState *ps_views[8]; - int ps_views_tot; - int symmetry_flags; + /* Support for painting from multiple views at once, + * currently used to implement symmetry painting, + * we can assume at least the first is set while painting. */ + struct ProjPaintState *ps_views[8]; + int ps_views_tot; + int symmetry_flags; - int orig_brush_size; + int orig_brush_size; - bool need_redraw; + bool need_redraw; - /* trick to bypass regular paint and allow clone picking */ - bool is_clone_cursor_pick; + /* trick to bypass regular paint and allow clone picking */ + bool is_clone_cursor_pick; - /* In ProjPaintState, only here for convenience */ - Scene *scene; - Brush *brush; + /* In ProjPaintState, only here for convenience */ + Scene *scene; + Brush *brush; } ProjStrokeHandle; typedef struct LoopSeamData { - float seam_uvs[2][2]; - float seam_puvs[2][2]; - float corner_dist_sq[2]; + float seam_uvs[2][2]; + float seam_puvs[2][2]; + float corner_dist_sq[2]; } LoopSeamData; /* Main projection painting struct passed to all projection painting functions */ typedef struct ProjPaintState { - View3D *v3d; - RegionView3D *rv3d; - ARegion *ar; - Depsgraph *depsgraph; - Scene *scene; - /* PROJ_SRC_**** */ - int source; - - /* the paint color. It can change depending of inverted mode or not */ - float paint_color[3]; - float paint_color_linear[3]; - float dither; - - Brush *brush; - short tool, blend, mode; - - float brush_size; - Object *ob; - /* for symmetry, we need to store modified object matrix */ - float obmat[4][4]; - float obmat_imat[4][4]; - /* end similarities with ImagePaintState */ - - Image *stencil_ima; - Image *canvas_ima; - Image *clone_ima; - float stencil_value; - - /* projection painting only */ - /** for multithreading, the first item is sometimes used for non threaded cases too. */ - MemArena *arena_mt[BLENDER_MAX_THREADS]; - /** screen sized 2D array, each pixel has a linked list of ProjPixel's */ - LinkNode **bucketRect; - /** bucketRect aligned array linkList of faces overlapping each bucket. */ - LinkNode **bucketFaces; - /** store if the bucks have been initialized. */ - unsigned char *bucketFlags; - - /** store options per vert, now only store if the vert is pointing away from the view. */ - char *vertFlags; - /** 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_x; - int buckets_y; - - /** result of project_paint_pixel_sizeof(), constant per stroke. */ - int pixel_sizeof; - - /** size of projectImages array. */ - int image_tot; - - /** verts projected into floating point screen space. */ - float (*screenCoords)[4]; - /** 2D bounds for mesh verts on the screen's plane (screenspace). */ - float screenMin[2]; - float screenMax[2]; - /** Calculated from screenMin & screenMax. */ - float screen_width; - float screen_height; - /** from the carea or from the projection render. */ - int winx, winy; - - /* options for projection painting */ - bool do_layer_clone; - bool do_layer_stencil; - bool do_layer_stencil_inv; - bool do_stencil_brush; - bool do_material_slots; - - /** Use raytraced occlusion? - ortherwise will paint right through to the back. */ - bool do_occlude; - /** ignore faces with normals pointing away, - * skips a lot of raycasts if your normals are correctly flipped. */ - bool do_backfacecull; - /** mask out pixels based on their normals. */ - bool do_mask_normal; - /** mask out pixels based on cavity. */ - bool do_mask_cavity; - /** what angle to mask at. */ - float normal_angle; - /** cos(normal_angle), faster to compare. */ - float normal_angle__cos; - float normal_angle_inner; - float normal_angle_inner__cos; - /** difference between normal_angle and normal_angle_inner, for easy access. */ - float normal_angle_range; - - /** quick access to (me->editflag & ME_EDIT_PAINT_FACE_SEL) */ - bool do_face_sel; - bool is_ortho; - /** the object is negative scaled. */ - bool is_flip_object; - /** use masking during painting. Some operations such as airbrush may disable. */ - bool do_masking; - /** only to avoid running. */ - bool is_texbrush; - /** mask brush is applied before masking. */ - bool is_maskbrush; + View3D *v3d; + RegionView3D *rv3d; + ARegion *ar; + Depsgraph *depsgraph; + Scene *scene; + /* PROJ_SRC_**** */ + int source; + + /* the paint color. It can change depending of inverted mode or not */ + float paint_color[3]; + float paint_color_linear[3]; + float dither; + + Brush *brush; + short tool, blend, mode; + + float brush_size; + Object *ob; + /* for symmetry, we need to store modified object matrix */ + float obmat[4][4]; + float obmat_imat[4][4]; + /* end similarities with ImagePaintState */ + + Image *stencil_ima; + Image *canvas_ima; + Image *clone_ima; + float stencil_value; + + /* projection painting only */ + /** for multithreading, the first item is sometimes used for non threaded cases too. */ + MemArena *arena_mt[BLENDER_MAX_THREADS]; + /** screen sized 2D array, each pixel has a linked list of ProjPixel's */ + LinkNode **bucketRect; + /** bucketRect aligned array linkList of faces overlapping each bucket. */ + LinkNode **bucketFaces; + /** store if the bucks have been initialized. */ + unsigned char *bucketFlags; + + /** store options per vert, now only store if the vert is pointing away from the view. */ + char *vertFlags; + /** 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_x; + int buckets_y; + + /** result of project_paint_pixel_sizeof(), constant per stroke. */ + int pixel_sizeof; + + /** size of projectImages array. */ + int image_tot; + + /** verts projected into floating point screen space. */ + float (*screenCoords)[4]; + /** 2D bounds for mesh verts on the screen's plane (screenspace). */ + float screenMin[2]; + float screenMax[2]; + /** Calculated from screenMin & screenMax. */ + float screen_width; + float screen_height; + /** from the carea or from the projection render. */ + int winx, winy; + + /* options for projection painting */ + bool do_layer_clone; + bool do_layer_stencil; + bool do_layer_stencil_inv; + bool do_stencil_brush; + bool do_material_slots; + + /** Use raytraced occlusion? - ortherwise will paint right through to the back. */ + bool do_occlude; + /** ignore faces with normals pointing away, + * skips a lot of raycasts if your normals are correctly flipped. */ + bool do_backfacecull; + /** mask out pixels based on their normals. */ + bool do_mask_normal; + /** mask out pixels based on cavity. */ + bool do_mask_cavity; + /** what angle to mask at. */ + float normal_angle; + /** cos(normal_angle), faster to compare. */ + float normal_angle__cos; + float normal_angle_inner; + float normal_angle_inner__cos; + /** difference between normal_angle and normal_angle_inner, for easy access. */ + float normal_angle_range; + + /** quick access to (me->editflag & ME_EDIT_PAINT_FACE_SEL) */ + bool do_face_sel; + bool is_ortho; + /** the object is negative scaled. */ + bool is_flip_object; + /** use masking during painting. Some operations such as airbrush may disable. */ + bool do_masking; + /** only to avoid running. */ + bool is_texbrush; + /** mask brush is applied before masking. */ + bool is_maskbrush; #ifndef PROJ_DEBUG_NOSEAMBLEED - float seam_bleed_px; - float seam_bleed_px_sq; + float seam_bleed_px; + float seam_bleed_px_sq; #endif - /* clone vars */ - float cloneOffset[2]; - - /** Projection matrix, use for getting screen coords. */ - float projectMat[4][4]; - /** inverse of projectMat. */ - float projectMatInv[4][4]; - /** View vector, use for do_backfacecull and for ray casting with an ortho viewport. */ - float viewDir[3]; - /** View location in object relative 3D space, so can compare to verts. */ - float viewPos[3]; - float clip_start, clip_end; - - /* reproject vars */ - Image *reproject_image; - ImBuf *reproject_ibuf; - bool reproject_ibuf_free_float; - bool reproject_ibuf_free_uchar; - - /* threads */ - int thread_tot; - int bucketMin[2]; - int bucketMax[2]; - /** must lock threads while accessing these. */ - int context_bucket_x, context_bucket_y; - - struct CurveMapping *cavity_curve; - BlurKernel *blurkernel; - - - - /* -------------------------------------------------------------------- */ - /* Vars shared between multiple views (keep last) */ - /** - * This data is owned by ``ProjStrokeHandle.ps_views[0]``, - * all other views re-use the data. - */ + /* clone vars */ + float cloneOffset[2]; + + /** Projection matrix, use for getting screen coords. */ + float projectMat[4][4]; + /** inverse of projectMat. */ + float projectMatInv[4][4]; + /** View vector, use for do_backfacecull and for ray casting with an ortho viewport. */ + float viewDir[3]; + /** View location in object relative 3D space, so can compare to verts. */ + float viewPos[3]; + float clip_start, clip_end; + + /* reproject vars */ + Image *reproject_image; + ImBuf *reproject_ibuf; + bool reproject_ibuf_free_float; + bool reproject_ibuf_free_uchar; + + /* threads */ + int thread_tot; + int bucketMin[2]; + int bucketMax[2]; + /** must lock threads while accessing these. */ + int context_bucket_x, context_bucket_y; + + struct CurveMapping *cavity_curve; + BlurKernel *blurkernel; + + /* -------------------------------------------------------------------- */ + /* Vars shared between multiple views (keep last) */ + /** + * This data is owned by ``ProjStrokeHandle.ps_views[0]``, + * all other views re-use the data. + */ #define PROJ_PAINT_STATE_SHARED_MEMCPY(ps_dst, ps_src) \ - MEMCPY_STRUCT_AFTER(ps_dst, ps_src, is_shared_user) + MEMCPY_STRUCT_AFTER(ps_dst, ps_src, is_shared_user) -#define PROJ_PAINT_STATE_SHARED_CLEAR(ps) \ - MEMSET_STRUCT_AFTER(ps, 0, is_shared_user) +#define PROJ_PAINT_STATE_SHARED_CLEAR(ps) MEMSET_STRUCT_AFTER(ps, 0, is_shared_user) - bool is_shared_user; + bool is_shared_user; - ProjPaintImage *projImages; - /** cavity amount for vertices. */ - float *cavities; + ProjPaintImage *projImages; + /** cavity amount for vertices. */ + float *cavities; #ifndef PROJ_DEBUG_NOSEAMBLEED - /** store info about faces, if they are initialized etc*/ - ushort *faceSeamFlags; - /** save the winding of the face in uv space, - * helps as an extra validation step for seam detection. */ - char *faceWindingFlags; - /** expanded UVs for faces to use as seams. */ - LoopSeamData (*loopSeamData); - /** Only needed for when seam_bleed_px is enabled, use to find UV seams. */ - LinkNode **vertFaces; - /** Seams per vert, to find adjacent seams. */ - ListBase *vertSeams; + /** store info about faces, if they are initialized etc*/ + ushort *faceSeamFlags; + /** save the winding of the face in uv space, + * helps as an extra validation step for seam detection. */ + char *faceWindingFlags; + /** expanded UVs for faces to use as seams. */ + LoopSeamData(*loopSeamData); + /** Only needed for when seam_bleed_px is enabled, use to find UV seams. */ + LinkNode **vertFaces; + /** Seams per vert, to find adjacent seams. */ + ListBase *vertSeams; #endif - SpinLock *tile_lock; - - Mesh *me_eval; - bool me_eval_free; - int totlooptri_eval; - int totloop_eval; - int totpoly_eval; - int totedge_eval; - int totvert_eval; - - const MVert *mvert_eval; - const MEdge *medge_eval; - const MPoly *mpoly_eval; - const MLoop *mloop_eval; - const MLoopTri *mlooptri_eval; - - const MLoopUV *mloopuv_stencil_eval; - - /** - * \note These UV layers are aligned to \a mpoly_eval - * but each pointer references the start of the layer, - * so a loop indirection is needed as well. - */ - const MLoopUV **poly_to_loop_uv; - /** other UV map, use for cloning between layers. */ - const MLoopUV **poly_to_loop_uv_clone; - - /* Actual material for each index, either from object or Mesh datablock... */ - Material **mat_array; - - bool use_colormanagement; + SpinLock *tile_lock; + + Mesh *me_eval; + bool me_eval_free; + int totlooptri_eval; + int totloop_eval; + int totpoly_eval; + int totedge_eval; + int totvert_eval; + + const MVert *mvert_eval; + const MEdge *medge_eval; + const MPoly *mpoly_eval; + const MLoop *mloop_eval; + const MLoopTri *mlooptri_eval; + + const MLoopUV *mloopuv_stencil_eval; + + /** + * \note These UV layers are aligned to \a mpoly_eval + * but each pointer references the start of the layer, + * so a loop indirection is needed as well. + */ + const MLoopUV **poly_to_loop_uv; + /** other UV map, use for cloning between layers. */ + const MLoopUV **poly_to_loop_uv_clone; + + /* Actual material for each index, either from object or Mesh datablock... */ + Material **mat_array; + + bool use_colormanagement; } ProjPaintState; typedef union pixelPointer { - /** float buffer. */ - float *f_pt; - /** 2 ways to access a char buffer. */ - unsigned int *uint_pt; - unsigned char *ch_pt; + /** float buffer. */ + float *f_pt; + /** 2 ways to access a char buffer. */ + unsigned int *uint_pt; + unsigned char *ch_pt; } PixelPointer; typedef union pixelStore { - unsigned char ch[4]; - unsigned int uint; - float f[4]; + unsigned char ch[4]; + unsigned int uint; + float f[4]; } PixelStore; typedef struct ProjPixel { - /** the floating point screen projection of this pixel. */ - float projCoSS[2]; - float worldCoSS[3]; + /** the floating point screen projection of this pixel. */ + float projCoSS[2]; + float worldCoSS[3]; - short x_px, y_px; + short x_px, y_px; - /** if anyone wants to paint onto more than 65535 images they can bite me. */ - unsigned short image_index; - unsigned char bb_cell_index; + /** if anyone wants to paint onto more than 65535 images they can bite me. */ + unsigned short image_index; + unsigned char bb_cell_index; - /* for various reasons we may want to mask out painting onto this pixel */ - unsigned short mask; + /* for various reasons we may want to mask out painting onto this pixel */ + unsigned short mask; - /* 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_accum */ - unsigned short *mask_accum; + /* 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_accum */ + unsigned short *mask_accum; - /* horrible hack, store tile valid flag pointer here to re-validate tiles - * used for anchored and drag-dot strokes */ - bool *valid; + /* horrible hack, store tile valid flag pointer here to re-validate tiles + * used for anchored and drag-dot strokes */ + bool *valid; - PixelPointer origColor; - PixelStore newColor; - PixelPointer pixel; + PixelPointer origColor; + PixelStore newColor; + PixelPointer pixel; } ProjPixel; typedef struct ProjPixelClone { - struct ProjPixel __pp; - PixelStore clonepx; + struct ProjPixel __pp; + PixelStore clonepx; } ProjPixelClone; /* undo tile pushing */ typedef struct { - SpinLock *lock; - bool masked; - unsigned short tile_width; - ImBuf **tmpibuf; - ProjPaintImage *pjima; + SpinLock *lock; + bool masked; + unsigned short tile_width; + ImBuf **tmpibuf; + ProjPaintImage *pjima; } TileInfo; typedef struct VertSeam { - struct VertSeam *next, *prev; - int tri; - uint loop; - float angle; - bool normal_cw; - float uv[2]; + struct VertSeam *next, *prev; + int tri; + uint loop; + float angle; + bool normal_cw; + float uv[2]; } VertSeam; - /* -------------------------------------------------------------------- */ /** \name MLoopTri accessor functions. * \{ */ BLI_INLINE const MPoly *ps_tri_index_to_mpoly(const ProjPaintState *ps, int tri_index) { - return &ps->mpoly_eval[ps->mlooptri_eval[tri_index].poly]; + return &ps->mpoly_eval[ps->mlooptri_eval[tri_index].poly]; } #define PS_LOOPTRI_AS_VERT_INDEX_3(ps, lt) \ - ps->mloop_eval[lt->tri[0]].v, \ - ps->mloop_eval[lt->tri[1]].v, \ - ps->mloop_eval[lt->tri[2]].v, + ps->mloop_eval[lt->tri[0]].v, ps->mloop_eval[lt->tri[1]].v, ps->mloop_eval[lt->tri[2]].v, #define PS_LOOPTRI_AS_UV_3(uvlayer, lt) \ - uvlayer[lt->poly][lt->tri[0]].uv, \ - uvlayer[lt->poly][lt->tri[1]].uv, \ - uvlayer[lt->poly][lt->tri[2]].uv, + uvlayer[lt->poly][lt->tri[0]].uv, uvlayer[lt->poly][lt->tri[1]].uv, \ + uvlayer[lt->poly][lt->tri[2]].uv, -#define PS_LOOPTRI_ASSIGN_UV_3(uv_tri, uvlayer, lt) { \ - (uv_tri)[0] = uvlayer[lt->poly][lt->tri[0]].uv; \ - (uv_tri)[1] = uvlayer[lt->poly][lt->tri[1]].uv; \ - (uv_tri)[2] = uvlayer[lt->poly][lt->tri[2]].uv; \ -} ((void)0) +#define PS_LOOPTRI_ASSIGN_UV_3(uv_tri, uvlayer, lt) \ + { \ + (uv_tri)[0] = uvlayer[lt->poly][lt->tri[0]].uv; \ + (uv_tri)[1] = uvlayer[lt->poly][lt->tri[1]].uv; \ + (uv_tri)[2] = uvlayer[lt->poly][lt->tri[2]].uv; \ + } \ + ((void)0) /** \} */ - - /* Finish projection painting structs */ static TexPaintSlot *project_paint_face_paint_slot(const ProjPaintState *ps, int tri_index) { - const MPoly *mp = ps_tri_index_to_mpoly(ps, tri_index); - Material *ma = ps->mat_array[mp->mat_nr]; - return ma ? ma->texpaintslot + ma->paint_active_slot : NULL; + const MPoly *mp = ps_tri_index_to_mpoly(ps, tri_index); + Material *ma = ps->mat_array[mp->mat_nr]; + return ma ? ma->texpaintslot + ma->paint_active_slot : NULL; } static Image *project_paint_face_paint_image(const ProjPaintState *ps, int tri_index) { - if (ps->do_stencil_brush) { - return ps->stencil_ima; - } - else { - const MPoly *mp = ps_tri_index_to_mpoly(ps, tri_index); - Material *ma = ps->mat_array[mp->mat_nr]; - TexPaintSlot *slot = ma ? ma->texpaintslot + ma->paint_active_slot : NULL; - return slot ? slot->ima : ps->canvas_ima; - } + if (ps->do_stencil_brush) { + return ps->stencil_ima; + } + else { + const MPoly *mp = ps_tri_index_to_mpoly(ps, tri_index); + Material *ma = ps->mat_array[mp->mat_nr]; + TexPaintSlot *slot = ma ? ma->texpaintslot + ma->paint_active_slot : NULL; + return slot ? slot->ima : ps->canvas_ima; + } } static TexPaintSlot *project_paint_face_clone_slot(const ProjPaintState *ps, int tri_index) { - const MPoly *mp = ps_tri_index_to_mpoly(ps, tri_index); - Material *ma = ps->mat_array[mp->mat_nr]; - return ma ? ma->texpaintslot + ma->paint_clone_slot : NULL; + const MPoly *mp = ps_tri_index_to_mpoly(ps, tri_index); + Material *ma = ps->mat_array[mp->mat_nr]; + return ma ? ma->texpaintslot + ma->paint_clone_slot : NULL; } static Image *project_paint_face_clone_image(const ProjPaintState *ps, int tri_index) { - const MPoly *mp = ps_tri_index_to_mpoly(ps, tri_index); - Material *ma = ps->mat_array[mp->mat_nr]; - TexPaintSlot *slot = ma ? ma->texpaintslot + ma->paint_clone_slot : NULL; - return slot ? slot->ima : ps->clone_ima; + const MPoly *mp = ps_tri_index_to_mpoly(ps, tri_index); + Material *ma = ps->mat_array[mp->mat_nr]; + TexPaintSlot *slot = ma ? ma->texpaintslot + ma->paint_clone_slot : NULL; + return slot ? slot->ima : ps->clone_ima; } /* 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); + /* 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; - } + 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; + } } static float VecZDepthOrtho( - const float pt[2], - const float v1[3], const float v2[3], const float v3[3], - float w[3]) + const float pt[2], const float v1[3], const float v2[3], const float v3[3], float w[3]) { - barycentric_weights_v2(v1, v2, v3, pt, w); - return (v1[2] * w[0]) + (v2[2] * w[1]) + (v3[2] * w[2]); + barycentric_weights_v2(v1, v2, v3, pt, w); + return (v1[2] * w[0]) + (v2[2] * w[1]) + (v3[2] * w[2]); } static float VecZDepthPersp( - const float pt[2], - const float v1[4], const float v2[4], const float v3[4], - float w[3]) + const float pt[2], const float v1[4], const float v2[4], const float v3[4], float w[3]) { - float wtot_inv, wtot; - float w_tmp[3]; - - barycentric_weights_v2_persp(v1, v2, v3, pt, w); - /* for the depth we need the weights to match what - * barycentric_weights_v2 would return, in this case its easiest just to - * undo the 4th axis division and make it unit-sum - * - * don't call barycentric_weights_v2() because our callers expect 'w' - * to be weighted from the perspective */ - w_tmp[0] = w[0] * v1[3]; - w_tmp[1] = w[1] * v2[3]; - w_tmp[2] = w[2] * v3[3]; - - wtot = w_tmp[0] + w_tmp[1] + w_tmp[2]; - - if (wtot != 0.0f) { - wtot_inv = 1.0f / wtot; - - w_tmp[0] = w_tmp[0] * wtot_inv; - w_tmp[1] = w_tmp[1] * wtot_inv; - w_tmp[2] = w_tmp[2] * wtot_inv; - } - else /* dummy values for zero area face */ - w_tmp[0] = w_tmp[1] = w_tmp[2] = 1.0f / 3.0f; - /* done mimicking barycentric_weights_v2() */ - - return (v1[2] * w_tmp[0]) + (v2[2] * w_tmp[1]) + (v3[2] * w_tmp[2]); + float wtot_inv, wtot; + float w_tmp[3]; + + barycentric_weights_v2_persp(v1, v2, v3, pt, w); + /* for the depth we need the weights to match what + * barycentric_weights_v2 would return, in this case its easiest just to + * undo the 4th axis division and make it unit-sum + * + * don't call barycentric_weights_v2() because our callers expect 'w' + * to be weighted from the perspective */ + w_tmp[0] = w[0] * v1[3]; + w_tmp[1] = w[1] * v2[3]; + w_tmp[2] = w[2] * v3[3]; + + wtot = w_tmp[0] + w_tmp[1] + w_tmp[2]; + + if (wtot != 0.0f) { + wtot_inv = 1.0f / wtot; + + w_tmp[0] = w_tmp[0] * wtot_inv; + w_tmp[1] = w_tmp[1] * wtot_inv; + w_tmp[2] = w_tmp[2] * wtot_inv; + } + else /* dummy values for zero area face */ + w_tmp[0] = w_tmp[1] = w_tmp[2] = 1.0f / 3.0f; + /* done mimicking barycentric_weights_v2() */ + + return (v1[2] * w_tmp[0]) + (v2[2] * w_tmp[1]) + (v3[2] * w_tmp[2]); } - /* Return the top-most face index that the screen space coord 'pt' touches (or -1) */ -static int project_paint_PickFace( - const ProjPaintState *ps, const float pt[2], - float w[3]) +static int project_paint_PickFace(const ProjPaintState *ps, const float pt[2], float w[3]) { - LinkNode *node; - float w_tmp[3]; - int bucket_index; - int best_tri_index = -1; - float z_depth_best = FLT_MAX, z_depth; - - 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) { - const int tri_index = POINTER_AS_INT(node->link); - const MLoopTri *lt = &ps->mlooptri_eval[tri_index]; - const float *vtri_ss[3] = { - ps->screenCoords[ps->mloop_eval[lt->tri[0]].v], - ps->screenCoords[ps->mloop_eval[lt->tri[1]].v], - ps->screenCoords[ps->mloop_eval[lt->tri[2]].v], - }; - - - if (isect_point_tri_v2(pt, UNPACK3(vtri_ss))) { - if (ps->is_ortho) { - z_depth = VecZDepthOrtho(pt, UNPACK3(vtri_ss), w_tmp); - } - else { - z_depth = VecZDepthPersp(pt, UNPACK3(vtri_ss), w_tmp); - } - - if (z_depth < z_depth_best) { - best_tri_index = tri_index; - z_depth_best = z_depth; - copy_v3_v3(w, w_tmp); - } - } - } - - /** will be -1 or a valid face. */ - return best_tri_index; + LinkNode *node; + float w_tmp[3]; + int bucket_index; + int best_tri_index = -1; + float z_depth_best = FLT_MAX, z_depth; + + 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) { + const int tri_index = POINTER_AS_INT(node->link); + const MLoopTri *lt = &ps->mlooptri_eval[tri_index]; + const float *vtri_ss[3] = { + ps->screenCoords[ps->mloop_eval[lt->tri[0]].v], + ps->screenCoords[ps->mloop_eval[lt->tri[1]].v], + ps->screenCoords[ps->mloop_eval[lt->tri[2]].v], + }; + + if (isect_point_tri_v2(pt, UNPACK3(vtri_ss))) { + if (ps->is_ortho) { + z_depth = VecZDepthOrtho(pt, UNPACK3(vtri_ss), w_tmp); + } + else { + z_depth = VecZDepthPersp(pt, UNPACK3(vtri_ss), w_tmp); + } + + if (z_depth < z_depth_best) { + best_tri_index = tri_index; + z_depth_best = z_depth; + copy_v3_v3(w, w_tmp); + } + } + } + + /** will be -1 or a valid face. */ + return best_tri_index; } /* Converts a uv coord into a pixel location wrapping if the uv is outside 0-1 range */ static void uvco_to_wrapped_pxco(const float uv[2], int ibuf_x, int ibuf_y, float *x, float *y) { - /* use */ - *x = fmodf(uv[0], 1.0f); - *y = fmodf(uv[1], 1.0f); + /* use */ + *x = fmodf(uv[0], 1.0f); + *y = fmodf(uv[1], 1.0f); - if (*x < 0.0f) *x += 1.0f; - if (*y < 0.0f) *y += 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; + *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 bool project_paint_PickColor( - const ProjPaintState *ps, const float pt[2], - float *rgba_fp, unsigned char *rgba, const bool interp) +static bool project_paint_PickColor(const ProjPaintState *ps, + const float pt[2], + float *rgba_fp, + unsigned char *rgba, + const bool interp) { - const MLoopTri *lt; - const float *lt_tri_uv[3]; - float w[3], uv[2]; - int tri_index; - Image *ima; - ImBuf *ibuf; - int xi, yi; - - tri_index = project_paint_PickFace(ps, pt, w); - - if (tri_index == -1) - return 0; - - lt = &ps->mlooptri_eval[tri_index]; - PS_LOOPTRI_ASSIGN_UV_3(lt_tri_uv, ps->poly_to_loop_uv, lt); - - interp_v2_v2v2v2(uv, UNPACK3(lt_tri_uv), w); - - ima = project_paint_face_paint_image(ps, tri_index); - /** we must have got the imbuf before getting here. */ - ibuf = BKE_image_get_first_ibuf(ima); - if (!ibuf) return 0; - - 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_wrap(ibuf, NULL, rgba_fp, x, y); - } - else { - float rgba_tmp_f[4]; - bilinear_interpolation_color_wrap(ibuf, NULL, rgba_tmp_f, x, y); - premul_float_to_straight_uchar(rgba, rgba_tmp_f); - } - } - else { - if (rgba) { - bilinear_interpolation_color_wrap(ibuf, rgba, NULL, x, y); - } - else { - unsigned char rgba_tmp[4]; - bilinear_interpolation_color_wrap(ibuf, rgba_tmp, NULL, x, y); - straight_uchar_to_premul_float(rgba_fp, rgba_tmp); - } - } - } - else { - //xi = (int)((uv[0]*ibuf->x) + 0.5f); - //yi = (int)((uv[1]*ibuf->y) + 0.5f); - //if (xi < 0 || xi >= ibuf->x || yi < 0 || yi >= ibuf->y) return 0; - - /* wrap */ - xi = mod_i((int)(uv[0] * ibuf->x), ibuf->x); - yi = mod_i((int)(uv[1] * ibuf->y), ibuf->y); - - if (rgba) { - if (ibuf->rect_float) { - const float *rgba_tmp_fp = ibuf->rect_float + (xi + yi * ibuf->x * 4); - premul_float_to_straight_uchar(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) { - copy_v4_v4(rgba_fp, (ibuf->rect_float + ((xi + yi * ibuf->x) * 4))); - } - else { - unsigned char *tmp_ch = ((unsigned char *)ibuf->rect) + ((xi + yi * ibuf->x) * 4); - straight_uchar_to_premul_float(rgba_fp, tmp_ch); - } - } - } - BKE_image_release_ibuf(ima, ibuf, NULL); - return 1; + const MLoopTri *lt; + const float *lt_tri_uv[3]; + float w[3], uv[2]; + int tri_index; + Image *ima; + ImBuf *ibuf; + int xi, yi; + + tri_index = project_paint_PickFace(ps, pt, w); + + if (tri_index == -1) + return 0; + + lt = &ps->mlooptri_eval[tri_index]; + PS_LOOPTRI_ASSIGN_UV_3(lt_tri_uv, ps->poly_to_loop_uv, lt); + + interp_v2_v2v2v2(uv, UNPACK3(lt_tri_uv), w); + + ima = project_paint_face_paint_image(ps, tri_index); + /** we must have got the imbuf before getting here. */ + ibuf = BKE_image_get_first_ibuf(ima); + if (!ibuf) + return 0; + + 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_wrap(ibuf, NULL, rgba_fp, x, y); + } + else { + float rgba_tmp_f[4]; + bilinear_interpolation_color_wrap(ibuf, NULL, rgba_tmp_f, x, y); + premul_float_to_straight_uchar(rgba, rgba_tmp_f); + } + } + else { + if (rgba) { + bilinear_interpolation_color_wrap(ibuf, rgba, NULL, x, y); + } + else { + unsigned char rgba_tmp[4]; + bilinear_interpolation_color_wrap(ibuf, rgba_tmp, NULL, x, y); + straight_uchar_to_premul_float(rgba_fp, rgba_tmp); + } + } + } + else { + //xi = (int)((uv[0]*ibuf->x) + 0.5f); + //yi = (int)((uv[1]*ibuf->y) + 0.5f); + //if (xi < 0 || xi >= ibuf->x || yi < 0 || yi >= ibuf->y) return 0; + + /* wrap */ + xi = mod_i((int)(uv[0] * ibuf->x), ibuf->x); + yi = mod_i((int)(uv[1] * ibuf->y), ibuf->y); + + if (rgba) { + if (ibuf->rect_float) { + const float *rgba_tmp_fp = ibuf->rect_float + (xi + yi * ibuf->x * 4); + premul_float_to_straight_uchar(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) { + copy_v4_v4(rgba_fp, (ibuf->rect_float + ((xi + yi * ibuf->x) * 4))); + } + else { + unsigned char *tmp_ch = ((unsigned char *)ibuf->rect) + ((xi + yi * ibuf->x) * 4); + straight_uchar_to_premul_float(rgba_fp, tmp_ch); + } + } + } + BKE_image_release_ibuf(ima, ibuf, NULL); + return 1; } /** @@ -807,121 +794,125 @@ static bool project_paint_PickColor( * - `1`: occluded * - `2`: occluded with `w[3]` weights set (need to know in some cases) */ -static int project_paint_occlude_ptv( - const float pt[3], - const float v1[4], const float v2[4], const float v3[4], - float w[3], const bool is_ortho) +static int project_paint_occlude_ptv(const float pt[3], + const float v1[4], + const float v2[4], + const float v3[4], + float w[3], + const bool 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 (!isect_point_tri_v2(pt, v1, v2, v3)) - return 0; - - - /* 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; + /* 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 (!isect_point_tri_v2(pt, v1, v2, v3)) + return 0; + + /* 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 float pt[3], - const float v1[4], const float v2[4], const float v3[4], - const float v1_3d[3], const float v2_3d[3], const float v3_3d[3], - float w[3], const bool is_ortho, RegionView3D *rv3d) +static int project_paint_occlude_ptv_clip(const float pt[3], + const float v1[4], + const float v2[4], + const float v3[4], + const float v1_3d[3], + const float v2_3d[3], + const float v3_3d[3], + float w[3], + const bool is_ortho, + RegionView3D *rv3d) { - float wco[3]; - int ret = project_paint_occlude_ptv(pt, v1, v2, v3, w, is_ortho); + float wco[3]; + int ret = project_paint_occlude_ptv(pt, v1, v2, v3, w, is_ortho); - if (ret <= 0) - return ret; + if (ret <= 0) + return ret; - if (ret == 1) { /* weights not calculated */ - if (is_ortho) { - barycentric_weights_v2(v1, v2, v3, pt, w); - } - else { - barycentric_weights_v2_persp(v1, v2, v3, pt, w); - } - } + if (ret == 1) { /* weights not calculated */ + if (is_ortho) { + barycentric_weights_v2(v1, v2, v3, pt, w); + } + else { + barycentric_weights_v2_persp(v1, v2, v3, pt, w); + } + } - /* Test if we're in the clipped area, */ - interp_v3_v3v3v3(wco, v1_3d, v2_3d, v3_3d, w); + /* Test if we're in the clipped area, */ + interp_v3_v3v3v3(wco, v1_3d, v2_3d, v3_3d, w); - if (!ED_view3d_clipping_test(rv3d, wco, true)) { - return 1; - } + if (!ED_view3d_clipping_test(rv3d, wco, true)) { + return 1; + } - 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 doesn't need to be correct in relation to X and Y coords * (this is the case in perspective view) */ -static bool project_bucket_point_occluded( - const ProjPaintState *ps, LinkNode *bucketFace, - const int orig_face, const float pixelScreenCo[4]) +static bool project_bucket_point_occluded(const ProjPaintState *ps, + LinkNode *bucketFace, + const int orig_face, + const float pixelScreenCo[4]) { - int isect_ret; - const bool do_clip = ps->rv3d ? (ps->rv3d->rflag & RV3D_CLIPPING) != 0 : 0; - - /* 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) { - const int tri_index = POINTER_AS_INT(bucketFace->link); - - if (orig_face != tri_index) { - const MLoopTri *lt = &ps->mlooptri_eval[tri_index]; - const float *vtri_ss[3] = { - ps->screenCoords[ps->mloop_eval[lt->tri[0]].v], - ps->screenCoords[ps->mloop_eval[lt->tri[1]].v], - ps->screenCoords[ps->mloop_eval[lt->tri[2]].v], - }; - float w[3]; - - if (do_clip) { - const float *vtri_co[3] = { - ps->mvert_eval[ps->mloop_eval[lt->tri[0]].v].co, - ps->mvert_eval[ps->mloop_eval[lt->tri[1]].v].co, - ps->mvert_eval[ps->mloop_eval[lt->tri[2]].v].co, - }; - isect_ret = project_paint_occlude_ptv_clip( - pixelScreenCo, UNPACK3(vtri_ss), UNPACK3(vtri_co), - w, ps->is_ortho, ps->rv3d); - } - else { - isect_ret = project_paint_occlude_ptv( - pixelScreenCo, UNPACK3(vtri_ss), - 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 true; - } - } - } - return false; + int isect_ret; + const bool do_clip = ps->rv3d ? (ps->rv3d->rflag & RV3D_CLIPPING) != 0 : 0; + + /* 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) { + const int tri_index = POINTER_AS_INT(bucketFace->link); + + if (orig_face != tri_index) { + const MLoopTri *lt = &ps->mlooptri_eval[tri_index]; + const float *vtri_ss[3] = { + ps->screenCoords[ps->mloop_eval[lt->tri[0]].v], + ps->screenCoords[ps->mloop_eval[lt->tri[1]].v], + ps->screenCoords[ps->mloop_eval[lt->tri[2]].v], + }; + float w[3]; + + if (do_clip) { + const float *vtri_co[3] = { + ps->mvert_eval[ps->mloop_eval[lt->tri[0]].v].co, + ps->mvert_eval[ps->mloop_eval[lt->tri[1]].v].co, + ps->mvert_eval[ps->mloop_eval[lt->tri[2]].v].co, + }; + isect_ret = project_paint_occlude_ptv_clip( + pixelScreenCo, UNPACK3(vtri_ss), UNPACK3(vtri_co), w, ps->is_ortho, ps->rv3d); + } + else { + isect_ret = project_paint_occlude_ptv(pixelScreenCo, UNPACK3(vtri_ss), 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 true; + } + } + } + return false; } /* Basic line intersection, could move to math_geom.c, 2 points with a horizontal line @@ -931,77 +922,77 @@ static bool project_bucket_point_occluded( #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; - - /* are we touching the first point? - no interpolation needed */ - if (y_level == p1[1]) { - *x_isect = p1[0]; - return ISECT_TRUE_P1; - } - /* are we touching the second point? - no interpolation needed */ - if (y_level == p2[1]) { - *x_isect = p2[0]; - return ISECT_TRUE_P2; - } - - /** yuck, horizontal line, we cant do much here. */ - y_diff = fabsf(p1[1] - p2[1]); - - 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) { - /* (p1[1] - p2[1]); */ - *x_isect = (p2[0] * (p1[1] - y_level) + p1[0] * (y_level - p2[1])) / y_diff; - return ISECT_TRUE; - } - else if (p1[1] < y_level && p2[1] > y_level) { - /* (p2[1] - p1[1]); */ - *x_isect = (p2[0] * (y_level - p1[1]) + p1[0] * (p2[1] - y_level)) / y_diff; - return ISECT_TRUE; - } - else { - return 0; - } + float y_diff; + + /* are we touching the first point? - no interpolation needed */ + if (y_level == p1[1]) { + *x_isect = p1[0]; + return ISECT_TRUE_P1; + } + /* are we touching the second point? - no interpolation needed */ + if (y_level == p2[1]) { + *x_isect = p2[0]; + return ISECT_TRUE_P2; + } + + /** yuck, horizontal line, we cant do much here. */ + y_diff = fabsf(p1[1] - p2[1]); + + 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) { + /* (p1[1] - p2[1]); */ + *x_isect = (p2[0] * (p1[1] - y_level) + p1[0] * (y_level - p2[1])) / y_diff; + return ISECT_TRUE; + } + else if (p1[1] < y_level && p2[1] > y_level) { + /* (p2[1] - p1[1]); */ + *x_isect = (p2[0] * (y_level - p1[1]) + p1[0] * (p2[1] - y_level)) / y_diff; + 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; - } - - /* yuck, horizontal line, we cant do much here */ - x_diff = fabsf(p1[0] - p2[0]); - - /* yuck, vertical line, we cant do much here */ - if (x_diff < 0.000001f) { - *y_isect = (p1[0] + p2[0]) * 0.5f; - return ISECT_TRUE; - } - - if (p1[0] > x_level && p2[0] < x_level) { - /* (p1[0] - p2[0]); */ - *y_isect = (p2[1] * (p1[0] - x_level) + p1[1] * (x_level - p2[0])) / x_diff; - return ISECT_TRUE; - } - else if (p1[0] < x_level && p2[0] > x_level) { - /* (p2[0] - p1[0]); */ - *y_isect = (p2[1] * (x_level - p1[0]) + p1[1] * (p2[0] - x_level)) / x_diff; - return ISECT_TRUE; - } - else { - return 0; - } + 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; + } + + /* yuck, horizontal line, we cant do much here */ + x_diff = fabsf(p1[0] - p2[0]); + + /* yuck, vertical line, we cant do much here */ + if (x_diff < 0.000001f) { + *y_isect = (p1[0] + p2[0]) * 0.5f; + return ISECT_TRUE; + } + + if (p1[0] > x_level && p2[0] < x_level) { + /* (p1[0] - p2[0]); */ + *y_isect = (p2[1] * (p1[0] - x_level) + p1[1] * (x_level - p2[0])) / x_diff; + return ISECT_TRUE; + } + else if (p1[0] < x_level && p2[0] > x_level) { + /* (p2[0] - p1[0]); */ + *y_isect = (p2[1] * (x_level - p1[0]) + p1[1] * (p2[0] - x_level)) / x_diff; + return ISECT_TRUE; + } + else { + return 0; + } } /* simple func use for comparing UV locations to check if there are seams. @@ -1011,389 +1002,394 @@ static int line_isect_x(const float p1[2], const float p2[2], const float x_leve #ifndef PROJ_DEBUG_NOSEAMBLEED static bool cmp_uv(const float vec2a[2], const float vec2b[2]) { - /* if the UV's are not between 0.0 and 1.0 */ - float xa = fmodf(vec2a[0], 1.0f); - float ya = fmodf(vec2a[1], 1.0f); + /* if the UV's are not between 0.0 and 1.0 */ + float xa = fmodf(vec2a[0], 1.0f); + float ya = fmodf(vec2a[1], 1.0f); - float xb = fmodf(vec2b[0], 1.0f); - float yb = fmodf(vec2b[1], 1.0f); + float xb = fmodf(vec2b[0], 1.0f); + float yb = fmodf(vec2b[1], 1.0f); - if (xa < 0.0f) xa += 1.0f; - if (ya < 0.0f) ya += 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; + if (xb < 0.0f) + xb += 1.0f; + if (yb < 0.0f) + yb += 1.0f; - return ((fabsf(xa - xb) < PROJ_GEOM_TOLERANCE) && (fabsf(ya - yb) < PROJ_GEOM_TOLERANCE)) ? 1 : 0; + return ((fabsf(xa - xb) < PROJ_GEOM_TOLERANCE) && (fabsf(ya - yb) < PROJ_GEOM_TOLERANCE)) ? 1 : + 0; } #endif /* 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 */ #ifndef PROJ_DEBUG_NOSEAMBLEED -static bool pixel_bounds_uv( - const float uv_quad[4][2], - rcti *bounds_px, - const int ibuf_x, const int ibuf_y - ) +static bool pixel_bounds_uv(const float uv_quad[4][2], + rcti *bounds_px, + const int ibuf_x, + const int ibuf_y) { - /* UV bounds */ - float min_uv[2], max_uv[2]; + /* UV bounds */ + float min_uv[2], max_uv[2]; - INIT_MINMAX2(min_uv, max_uv); + INIT_MINMAX2(min_uv, max_uv); - minmax_v2v2_v2(min_uv, max_uv, uv_quad[0]); - minmax_v2v2_v2(min_uv, max_uv, uv_quad[1]); - minmax_v2v2_v2(min_uv, max_uv, uv_quad[2]); - minmax_v2v2_v2(min_uv, max_uv, uv_quad[3]); + minmax_v2v2_v2(min_uv, max_uv, uv_quad[0]); + minmax_v2v2_v2(min_uv, max_uv, uv_quad[1]); + minmax_v2v2_v2(min_uv, max_uv, uv_quad[2]); + minmax_v2v2_v2(min_uv, max_uv, uv_quad[3]); - bounds_px->xmin = (int)(ibuf_x * min_uv[0]); - bounds_px->ymin = (int)(ibuf_y * min_uv[1]); + 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; + 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]);*/ + /*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; + /* face uses no UV area when quantized to pixels? */ + return (bounds_px->xmin == bounds_px->xmax || bounds_px->ymin == bounds_px->ymax) ? 0 : 1; } #endif -static bool pixel_bounds_array(float (*uv)[2], rcti *bounds_px, const int ibuf_x, const int ibuf_y, int tot) +static bool pixel_bounds_array( + float (*uv)[2], rcti *bounds_px, const int ibuf_x, const int ibuf_y, int tot) { - /* UV bounds */ - float min_uv[2], max_uv[2]; + /* UV bounds */ + float min_uv[2], max_uv[2]; - if (tot == 0) { - return 0; - } + if (tot == 0) { + return 0; + } - INIT_MINMAX2(min_uv, max_uv); + INIT_MINMAX2(min_uv, max_uv); - while (tot--) { - minmax_v2v2_v2(min_uv, max_uv, (*uv)); - uv++; - } + while (tot--) { + minmax_v2v2_v2(min_uv, max_uv, (*uv)); + uv++; + } - bounds_px->xmin = (int)(ibuf_x * min_uv[0]); - bounds_px->ymin = (int)(ibuf_y * min_uv[1]); + 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; + 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]);*/ + /*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; + /* 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 static void project_face_winding_init(const ProjPaintState *ps, const int tri_index) { - /* detect the winding of faces in uv space */ - const MLoopTri *lt = &ps->mlooptri_eval[tri_index]; - const float *lt_tri_uv[3] = { PS_LOOPTRI_AS_UV_3(ps->poly_to_loop_uv, lt) }; - float winding = cross_tri_v2(lt_tri_uv[0], lt_tri_uv[1], lt_tri_uv[2]); + /* detect the winding of faces in uv space */ + const MLoopTri *lt = &ps->mlooptri_eval[tri_index]; + const float *lt_tri_uv[3] = {PS_LOOPTRI_AS_UV_3(ps->poly_to_loop_uv, lt)}; + float winding = cross_tri_v2(lt_tri_uv[0], lt_tri_uv[1], lt_tri_uv[2]); - if (winding > 0) - ps->faceWindingFlags[tri_index] |= PROJ_FACE_WINDING_CW; + if (winding > 0) + ps->faceWindingFlags[tri_index] |= PROJ_FACE_WINDING_CW; - ps->faceWindingFlags[tri_index] |= PROJ_FACE_WINDING_INIT; + ps->faceWindingFlags[tri_index] |= PROJ_FACE_WINDING_INIT; } /* This function returns 1 if this face has a seam along the 2 face-vert indices * 'orig_i1_fidx' and 'orig_i2_fidx' */ -static bool 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) +static bool 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) { - const MLoopTri *orig_lt = &ps->mlooptri_eval[orig_face]; - const float *orig_lt_tri_uv[3] = { PS_LOOPTRI_AS_UV_3(ps->poly_to_loop_uv, orig_lt) }; - /* vert indices from face vert order indices */ - const unsigned int i1 = ps->mloop_eval[orig_lt->tri[orig_i1_fidx]].v; - const unsigned int i2 = ps->mloop_eval[orig_lt->tri[orig_i2_fidx]].v; - LinkNode *node; - /* index in face */ - int i1_fidx = -1, i2_fidx = -1; - - for (node = ps->vertFaces[i1]; node; node = node->next) { - const int tri_index = POINTER_AS_INT(node->link); - - if (tri_index != orig_face) { - const MLoopTri *lt = &ps->mlooptri_eval[tri_index]; - const int lt_vtri[3] = { PS_LOOPTRI_AS_VERT_INDEX_3(ps, lt) }; - /* 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) */ - i1_fidx = BKE_MESH_TESSTRI_VINDEX_ORDER(lt_vtri, i1); - i2_fidx = BKE_MESH_TESSTRI_VINDEX_ORDER(lt_vtri, i2); - - /* 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) { - const float *lt_tri_uv[3] = { PS_LOOPTRI_AS_UV_3(ps->poly_to_loop_uv, lt) }; - Image *tpage = project_paint_face_paint_image(ps, tri_index); - Image *orig_tpage = project_paint_face_paint_image(ps, orig_face); - - BLI_assert(i1_fidx != -1); - - /* This IS an adjacent face!, now lets check if the UVs are ok */ - - /* set up the other face */ - *other_face = tri_index; - - /* we check if difference is 1 here, else we might have a case of edge 2-0 for a tri */ - *orig_fidx = (i1_fidx < i2_fidx && (i2_fidx - i1_fidx == 1)) ? i1_fidx : i2_fidx; - - /* initialize face winding if needed */ - if ((ps->faceWindingFlags[tri_index] & PROJ_FACE_WINDING_INIT) == 0) - project_face_winding_init(ps, tri_index); - - /* first test if they have the same image */ - if ((orig_tpage == tpage) && - cmp_uv(orig_lt_tri_uv[orig_i1_fidx], lt_tri_uv[i1_fidx]) && - cmp_uv(orig_lt_tri_uv[orig_i2_fidx], lt_tri_uv[i2_fidx])) - { - /* if faces don't have the same winding in uv space, - * they are on the same side so edge is boundary */ - if ((ps->faceWindingFlags[tri_index] & PROJ_FACE_WINDING_CW) != - (ps->faceWindingFlags[orig_face] & PROJ_FACE_WINDING_CW)) - { - return 1; - } - - // printf("SEAM (NONE)\n"); - return 0; - - } - else { - // printf("SEAM (UV GAP)\n"); - return 1; - } - } - } - } - // printf("SEAM (NO FACE)\n"); - *other_face = -1; - return 1; + const MLoopTri *orig_lt = &ps->mlooptri_eval[orig_face]; + const float *orig_lt_tri_uv[3] = {PS_LOOPTRI_AS_UV_3(ps->poly_to_loop_uv, orig_lt)}; + /* vert indices from face vert order indices */ + const unsigned int i1 = ps->mloop_eval[orig_lt->tri[orig_i1_fidx]].v; + const unsigned int i2 = ps->mloop_eval[orig_lt->tri[orig_i2_fidx]].v; + LinkNode *node; + /* index in face */ + int i1_fidx = -1, i2_fidx = -1; + + for (node = ps->vertFaces[i1]; node; node = node->next) { + const int tri_index = POINTER_AS_INT(node->link); + + if (tri_index != orig_face) { + const MLoopTri *lt = &ps->mlooptri_eval[tri_index]; + const int lt_vtri[3] = {PS_LOOPTRI_AS_VERT_INDEX_3(ps, lt)}; + /* 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) */ + i1_fidx = BKE_MESH_TESSTRI_VINDEX_ORDER(lt_vtri, i1); + i2_fidx = BKE_MESH_TESSTRI_VINDEX_ORDER(lt_vtri, i2); + + /* 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) { + const float *lt_tri_uv[3] = {PS_LOOPTRI_AS_UV_3(ps->poly_to_loop_uv, lt)}; + Image *tpage = project_paint_face_paint_image(ps, tri_index); + Image *orig_tpage = project_paint_face_paint_image(ps, orig_face); + + BLI_assert(i1_fidx != -1); + + /* This IS an adjacent face!, now lets check if the UVs are ok */ + + /* set up the other face */ + *other_face = tri_index; + + /* we check if difference is 1 here, else we might have a case of edge 2-0 for a tri */ + *orig_fidx = (i1_fidx < i2_fidx && (i2_fidx - i1_fidx == 1)) ? i1_fidx : i2_fidx; + + /* initialize face winding if needed */ + if ((ps->faceWindingFlags[tri_index] & PROJ_FACE_WINDING_INIT) == 0) + project_face_winding_init(ps, tri_index); + + /* first test if they have the same image */ + if ((orig_tpage == tpage) && cmp_uv(orig_lt_tri_uv[orig_i1_fidx], lt_tri_uv[i1_fidx]) && + cmp_uv(orig_lt_tri_uv[orig_i2_fidx], lt_tri_uv[i2_fidx])) { + /* if faces don't have the same winding in uv space, + * they are on the same side so edge is boundary */ + if ((ps->faceWindingFlags[tri_index] & PROJ_FACE_WINDING_CW) != + (ps->faceWindingFlags[orig_face] & PROJ_FACE_WINDING_CW)) { + return 1; + } + + // printf("SEAM (NONE)\n"); + return 0; + } + else { + // printf("SEAM (UV GAP)\n"); + return 1; + } + } + } + } + // printf("SEAM (NO FACE)\n"); + *other_face = -1; + return 1; } -static VertSeam *find_adjacent_seam(const ProjPaintState *ps, uint loop_index, uint vert_index, VertSeam **r_seam) +static VertSeam *find_adjacent_seam(const ProjPaintState *ps, + uint loop_index, + uint vert_index, + VertSeam **r_seam) { - ListBase *vert_seams = &ps->vertSeams[vert_index]; - VertSeam *seam = vert_seams->first; - VertSeam *adjacent = NULL; - - while (seam->loop != loop_index) { - seam = seam->next; - } - - if (r_seam) { - *r_seam = seam; - } - - /* Circulate through the (sorted) vert seam array, in the direction of the seam normal, - * until we find the first opposing seam, matching in UV space. */ - if (seam->normal_cw) { - LISTBASE_CIRCULAR_BACKWARD_BEGIN(vert_seams, adjacent, seam) - { - if ((adjacent->normal_cw != seam->normal_cw) && - cmp_uv(adjacent->uv, seam->uv)) - { - break; - } - } - LISTBASE_CIRCULAR_BACKWARD_END(vert_seams, adjacent, seam); - } - else { - LISTBASE_CIRCULAR_FORWARD_BEGIN(vert_seams, adjacent, seam) - { - if ((adjacent->normal_cw != seam->normal_cw) && - cmp_uv(adjacent->uv, seam->uv)) - { - break; - } - } - LISTBASE_CIRCULAR_FORWARD_END(vert_seams, adjacent, seam); - } - - BLI_assert(adjacent); - - return adjacent; + ListBase *vert_seams = &ps->vertSeams[vert_index]; + VertSeam *seam = vert_seams->first; + VertSeam *adjacent = NULL; + + while (seam->loop != loop_index) { + seam = seam->next; + } + + if (r_seam) { + *r_seam = seam; + } + + /* Circulate through the (sorted) vert seam array, in the direction of the seam normal, + * until we find the first opposing seam, matching in UV space. */ + if (seam->normal_cw) { + LISTBASE_CIRCULAR_BACKWARD_BEGIN (vert_seams, adjacent, seam) { + if ((adjacent->normal_cw != seam->normal_cw) && cmp_uv(adjacent->uv, seam->uv)) { + break; + } + } + LISTBASE_CIRCULAR_BACKWARD_END(vert_seams, adjacent, seam); + } + else { + LISTBASE_CIRCULAR_FORWARD_BEGIN (vert_seams, adjacent, seam) { + if ((adjacent->normal_cw != seam->normal_cw) && cmp_uv(adjacent->uv, seam->uv)) { + break; + } + } + LISTBASE_CIRCULAR_FORWARD_END(vert_seams, adjacent, seam); + } + + BLI_assert(adjacent); + + return adjacent; } /* Computes the normal of two seams at their intersection, * and returns the angle between the seam and its normal. */ static float compute_seam_normal(VertSeam *seam, VertSeam *adj, float r_no[2]) { - const float PI_2 = M_PI * 2.0f; - float angle[2]; - float angle_rel, angle_no; + const float PI_2 = M_PI * 2.0f; + float angle[2]; + float angle_rel, angle_no; - if (seam->normal_cw) { - angle[0] = adj->angle; - angle[1] = seam->angle; - } - else { - angle[0] = seam->angle; - angle[1] = adj->angle; - } + if (seam->normal_cw) { + angle[0] = adj->angle; + angle[1] = seam->angle; + } + else { + angle[0] = seam->angle; + angle[1] = adj->angle; + } - angle_rel = angle[1] - angle[0]; + angle_rel = angle[1] - angle[0]; - if (angle_rel < 0.0f) { - angle_rel += PI_2; - } + if (angle_rel < 0.0f) { + angle_rel += PI_2; + } - angle_rel *= 0.5f; + angle_rel *= 0.5f; - angle_no = angle_rel + angle[0]; + angle_no = angle_rel + angle[0]; - if (angle_no > M_PI) { - angle_no -= PI_2; - } + if (angle_no > M_PI) { + angle_no -= PI_2; + } - r_no[0] = cosf(angle_no); - r_no[1] = sinf(angle_no); + r_no[0] = cosf(angle_no); + r_no[1] = sinf(angle_no); - return angle_rel; + return angle_rel; } /* 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( - const ProjPaintState *ps, float (*orig_uv)[2], float (*puv)[2], - uint tri_index, const int ibuf_x, const int ibuf_y) +static void uv_image_outset(const ProjPaintState *ps, + float (*orig_uv)[2], + float (*puv)[2], + uint tri_index, + const int ibuf_x, + const int ibuf_y) { - int fidx[2]; - uint loop_index; - uint vert[2]; - const MLoopTri *ltri = &ps->mlooptri_eval[tri_index]; + int fidx[2]; + uint loop_index; + uint vert[2]; + const MLoopTri *ltri = &ps->mlooptri_eval[tri_index]; - float ibuf_inv[2]; + float ibuf_inv[2]; - ibuf_inv[0] = 1.0f / (float)ibuf_x; - ibuf_inv[1] = 1.0f / (float)ibuf_y; + ibuf_inv[0] = 1.0f / (float)ibuf_x; + ibuf_inv[1] = 1.0f / (float)ibuf_y; - for (fidx[0] = 0; fidx[0] < 3; fidx[0]++) { - LoopSeamData *seam_data; - float (*seam_uvs)[2]; - float ang[2]; + for (fidx[0] = 0; fidx[0] < 3; fidx[0]++) { + LoopSeamData *seam_data; + float(*seam_uvs)[2]; + float ang[2]; - if ((ps->faceSeamFlags[tri_index] & (PROJ_FACE_SEAM0 << fidx[0])) == 0) { - continue; - } + if ((ps->faceSeamFlags[tri_index] & (PROJ_FACE_SEAM0 << fidx[0])) == 0) { + continue; + } - loop_index = ltri->tri[fidx[0]]; + loop_index = ltri->tri[fidx[0]]; - seam_data = &ps->loopSeamData[loop_index]; - seam_uvs = seam_data->seam_uvs; + seam_data = &ps->loopSeamData[loop_index]; + seam_uvs = seam_data->seam_uvs; - if (seam_uvs[0][0] != FLT_MAX) { - continue; - } + if (seam_uvs[0][0] != FLT_MAX) { + continue; + } - fidx[1] = (fidx[0] == 2) ? 0 : fidx[0] + 1; + fidx[1] = (fidx[0] == 2) ? 0 : fidx[0] + 1; - vert[0] = ps->mloop_eval[loop_index].v; - vert[1] = ps->mloop_eval[ltri->tri[fidx[1]]].v; + vert[0] = ps->mloop_eval[loop_index].v; + vert[1] = ps->mloop_eval[ltri->tri[fidx[1]]].v; - for (uint i = 0; i < 2; i++) { - VertSeam *seam; - VertSeam *adj = find_adjacent_seam(ps, loop_index, vert[i], &seam); - float no[2]; - float len_fact; - float tri_ang; + for (uint i = 0; i < 2; i++) { + VertSeam *seam; + VertSeam *adj = find_adjacent_seam(ps, loop_index, vert[i], &seam); + float no[2]; + float len_fact; + float tri_ang; - ang[i] = compute_seam_normal(seam, adj, no); - tri_ang = ang[i] - M_PI_2; + ang[i] = compute_seam_normal(seam, adj, no); + tri_ang = ang[i] - M_PI_2; - if (tri_ang > 0.0f) { - const float dist = ps->seam_bleed_px * tanf(tri_ang); - seam_data->corner_dist_sq[i] = SQUARE(dist); - } - else { - seam_data->corner_dist_sq[i] = 0.0f; - } + if (tri_ang > 0.0f) { + const float dist = ps->seam_bleed_px * tanf(tri_ang); + seam_data->corner_dist_sq[i] = SQUARE(dist); + } + else { + seam_data->corner_dist_sq[i] = 0.0f; + } - len_fact = cosf(tri_ang); - len_fact = UNLIKELY(len_fact < FLT_EPSILON) ? FLT_MAX : (1.0f / len_fact); + len_fact = cosf(tri_ang); + len_fact = UNLIKELY(len_fact < FLT_EPSILON) ? FLT_MAX : (1.0f / len_fact); - /* Clamp the length factor, see: T62236. */ - len_fact = MIN2(len_fact, 10.0f); + /* Clamp the length factor, see: T62236. */ + len_fact = MIN2(len_fact, 10.0f); - mul_v2_fl(no, ps->seam_bleed_px * len_fact); + mul_v2_fl(no, ps->seam_bleed_px * len_fact); - add_v2_v2v2(seam_data->seam_puvs[i], puv[fidx[i]], no); + add_v2_v2v2(seam_data->seam_puvs[i], puv[fidx[i]], no); - mul_v2_v2v2(seam_uvs[i], seam_data->seam_puvs[i], ibuf_inv); - } + mul_v2_v2v2(seam_uvs[i], seam_data->seam_puvs[i], ibuf_inv); + } - /* Handle convergent normals (can self-intersect). */ - if ((ang[0] + ang[1]) < M_PI) { - if (isect_seg_seg_v2_simple(orig_uv[fidx[0]], seam_uvs[0], orig_uv[fidx[1]], seam_uvs[1])) { - float isect_co[2]; + /* Handle convergent normals (can self-intersect). */ + if ((ang[0] + ang[1]) < M_PI) { + if (isect_seg_seg_v2_simple(orig_uv[fidx[0]], seam_uvs[0], orig_uv[fidx[1]], seam_uvs[1])) { + float isect_co[2]; - isect_seg_seg_v2_point(orig_uv[fidx[0]], seam_uvs[0], orig_uv[fidx[1]], seam_uvs[1], isect_co); + isect_seg_seg_v2_point( + orig_uv[fidx[0]], seam_uvs[0], orig_uv[fidx[1]], seam_uvs[1], isect_co); - copy_v2_v2(seam_uvs[0], isect_co); - copy_v2_v2(seam_uvs[1], isect_co); - } - } - - } + copy_v2_v2(seam_uvs[0], isect_co); + copy_v2_v2(seam_uvs[1], isect_co); + } + } + } } -static void insert_seam_vert_array( - const ProjPaintState *ps, MemArena *arena, const int tri_index, - const int fidx1, const int ibuf_x, const int ibuf_y) +static void insert_seam_vert_array(const ProjPaintState *ps, + MemArena *arena, + const int tri_index, + const int fidx1, + const int ibuf_x, + const int ibuf_y) { - const MLoopTri *lt = &ps->mlooptri_eval[tri_index]; - const float *lt_tri_uv[3] = {PS_LOOPTRI_AS_UV_3(ps->poly_to_loop_uv, lt)}; - const int fidx[2] = {fidx1, ((fidx1 + 1) % 3)}; - float vec[2]; + const MLoopTri *lt = &ps->mlooptri_eval[tri_index]; + const float *lt_tri_uv[3] = {PS_LOOPTRI_AS_UV_3(ps->poly_to_loop_uv, lt)}; + const int fidx[2] = {fidx1, ((fidx1 + 1) % 3)}; + float vec[2]; - VertSeam *vseam = BLI_memarena_alloc(arena, sizeof(VertSeam) * 2); + VertSeam *vseam = BLI_memarena_alloc(arena, sizeof(VertSeam) * 2); - vseam->prev = NULL; - vseam->next = NULL; + vseam->prev = NULL; + vseam->next = NULL; - vseam->tri = tri_index; - vseam->loop = lt->tri[fidx[0]]; + vseam->tri = tri_index; + vseam->loop = lt->tri[fidx[0]]; - sub_v2_v2v2(vec, lt_tri_uv[fidx[1]], lt_tri_uv[fidx[0]]); - vec[0] *= ibuf_x; - vec[1] *= ibuf_y; - vseam->angle = atan2f(vec[1], vec[0]); + sub_v2_v2v2(vec, lt_tri_uv[fidx[1]], lt_tri_uv[fidx[0]]); + vec[0] *= ibuf_x; + vec[1] *= ibuf_y; + vseam->angle = atan2f(vec[1], vec[0]); - /* If face windings are not initialized, something must be wrong. */ - BLI_assert((ps->faceWindingFlags[tri_index] & PROJ_FACE_WINDING_INIT) != 0); - vseam->normal_cw = (ps->faceWindingFlags[tri_index] & PROJ_FACE_WINDING_CW); + /* If face windings are not initialized, something must be wrong. */ + BLI_assert((ps->faceWindingFlags[tri_index] & PROJ_FACE_WINDING_INIT) != 0); + vseam->normal_cw = (ps->faceWindingFlags[tri_index] & PROJ_FACE_WINDING_CW); - copy_v2_v2(vseam->uv, lt_tri_uv[fidx[0]]); + copy_v2_v2(vseam->uv, lt_tri_uv[fidx[0]]); - vseam[1] = vseam[0]; - vseam[1].angle += vseam[1].angle > 0.0f ? -M_PI : M_PI; - vseam[1].normal_cw = !vseam[1].normal_cw; - copy_v2_v2(vseam[1].uv, lt_tri_uv[fidx[1]]); + vseam[1] = vseam[0]; + vseam[1].angle += vseam[1].angle > 0.0f ? -M_PI : M_PI; + vseam[1].normal_cw = !vseam[1].normal_cw; + copy_v2_v2(vseam[1].uv, lt_tri_uv[fidx[1]]); - for (uint i = 0; i < 2; i++) { - uint vert = ps->mloop_eval[lt->tri[fidx[i]]].v; - ListBase *list = &ps->vertSeams[vert]; - VertSeam *item = list->first; + for (uint i = 0; i < 2; i++) { + uint vert = ps->mloop_eval[lt->tri[fidx[i]]].v; + ListBase *list = &ps->vertSeams[vert]; + VertSeam *item = list->first; - while (item && item->angle < vseam[i].angle) { - item = item->next; - } + while (item && item->angle < vseam[i].angle) { + item = item->next; + } - BLI_insertlinkbefore(list, item, &vseam[i]); - } + BLI_insertlinkbefore(list, item, &vseam[i]); + } } /* @@ -1402,144 +1398,151 @@ static void insert_seam_vert_array( * * If we're multithreadng, make sure threads are locked when this is called */ -static void project_face_seams_init( - const ProjPaintState *ps, MemArena *arena, const int tri_index, const uint vert_index, - bool init_all, const int ibuf_x, const int ibuf_y) +static void project_face_seams_init(const ProjPaintState *ps, + MemArena *arena, + const int tri_index, + const uint vert_index, + bool init_all, + const int ibuf_x, + const int ibuf_y) { - /* vars for the other face, we also set its flag */ - int other_face, other_fidx; - /* next fidx in the face (0,1,2,3) -> (1,2,3,0) or (0,1,2) -> (1,2,0) for a tri */ - int fidx[2] = {2, 0}; - const MLoopTri *lt = &ps->mlooptri_eval[tri_index]; - LinkNode *node; - - /* initialize face winding if needed */ - if ((ps->faceWindingFlags[tri_index] & PROJ_FACE_WINDING_INIT) == 0) - project_face_winding_init(ps, tri_index); - - do { - if (init_all || - (ps->mloop_eval[lt->tri[fidx[0]]].v == vert_index) || - (ps->mloop_eval[lt->tri[fidx[1]]].v == vert_index)) - { - if ((ps->faceSeamFlags[tri_index] & (PROJ_FACE_SEAM0 << fidx[0] | PROJ_FACE_NOSEAM0 << fidx[0])) == 0) { - if (check_seam(ps, tri_index, fidx[0], fidx[1], &other_face, &other_fidx)) { - ps->faceSeamFlags[tri_index] |= PROJ_FACE_SEAM0 << fidx[0]; - insert_seam_vert_array(ps, arena, tri_index, fidx[0], ibuf_x, ibuf_y); - - if (other_face != -1) { - /* Check if the other seam is already set. We don't want to insert it in the list twice. */ - if ((ps->faceSeamFlags[other_face] & (PROJ_FACE_SEAM0 << other_fidx)) == 0) { - ps->faceSeamFlags[other_face] |= PROJ_FACE_SEAM0 << other_fidx; - insert_seam_vert_array(ps, arena, other_face, other_fidx, ibuf_x, ibuf_y); - } - } - } - else { - ps->faceSeamFlags[tri_index] |= PROJ_FACE_NOSEAM0 << fidx[0]; - ps->faceSeamFlags[tri_index] |= PROJ_FACE_SEAM_INIT0 << fidx[0]; - - if (other_face != -1) { - /* second 4 bits for disabled */ - ps->faceSeamFlags[other_face] |= PROJ_FACE_NOSEAM0 << other_fidx; - ps->faceSeamFlags[other_face] |= PROJ_FACE_SEAM_INIT0 << other_fidx; - } - } - } - } - - fidx[1] = fidx[0]; - } while (fidx[0]--); - - if (init_all) { - char checked_verts = 0; - - fidx[0] = 2; - fidx[1] = 0; - - do { - if ((ps->faceSeamFlags[tri_index] & (PROJ_FACE_SEAM_INIT0 << fidx[0])) == 0) { - for (uint i = 0; i < 2; i++) { - uint vert; - - if ((checked_verts & (1 << fidx[i])) != 0) { - continue; - } - - vert = ps->mloop_eval[lt->tri[fidx[i]]].v; - - for (node = ps->vertFaces[vert]; node; node = node->next) { - const int tri = POINTER_AS_INT(node->link); - - project_face_seams_init(ps, arena, tri, vert, false, ibuf_x, ibuf_y); - } - - checked_verts |= 1 << fidx[i]; - } - - ps->faceSeamFlags[tri_index] |= PROJ_FACE_SEAM_INIT0 << fidx[0]; - } - - fidx[1] = fidx[0]; - } while (fidx[0]--); - } + /* vars for the other face, we also set its flag */ + int other_face, other_fidx; + /* next fidx in the face (0,1,2,3) -> (1,2,3,0) or (0,1,2) -> (1,2,0) for a tri */ + int fidx[2] = {2, 0}; + const MLoopTri *lt = &ps->mlooptri_eval[tri_index]; + LinkNode *node; + + /* initialize face winding if needed */ + if ((ps->faceWindingFlags[tri_index] & PROJ_FACE_WINDING_INIT) == 0) + project_face_winding_init(ps, tri_index); + + do { + if (init_all || (ps->mloop_eval[lt->tri[fidx[0]]].v == vert_index) || + (ps->mloop_eval[lt->tri[fidx[1]]].v == vert_index)) { + if ((ps->faceSeamFlags[tri_index] & + (PROJ_FACE_SEAM0 << fidx[0] | PROJ_FACE_NOSEAM0 << fidx[0])) == 0) { + if (check_seam(ps, tri_index, fidx[0], fidx[1], &other_face, &other_fidx)) { + ps->faceSeamFlags[tri_index] |= PROJ_FACE_SEAM0 << fidx[0]; + insert_seam_vert_array(ps, arena, tri_index, fidx[0], ibuf_x, ibuf_y); + + if (other_face != -1) { + /* Check if the other seam is already set. We don't want to insert it in the list twice. */ + if ((ps->faceSeamFlags[other_face] & (PROJ_FACE_SEAM0 << other_fidx)) == 0) { + ps->faceSeamFlags[other_face] |= PROJ_FACE_SEAM0 << other_fidx; + insert_seam_vert_array(ps, arena, other_face, other_fidx, ibuf_x, ibuf_y); + } + } + } + else { + ps->faceSeamFlags[tri_index] |= PROJ_FACE_NOSEAM0 << fidx[0]; + ps->faceSeamFlags[tri_index] |= PROJ_FACE_SEAM_INIT0 << fidx[0]; + + if (other_face != -1) { + /* second 4 bits for disabled */ + ps->faceSeamFlags[other_face] |= PROJ_FACE_NOSEAM0 << other_fidx; + ps->faceSeamFlags[other_face] |= PROJ_FACE_SEAM_INIT0 << other_fidx; + } + } + } + } + + fidx[1] = fidx[0]; + } while (fidx[0]--); + + if (init_all) { + char checked_verts = 0; + + fidx[0] = 2; + fidx[1] = 0; + + do { + if ((ps->faceSeamFlags[tri_index] & (PROJ_FACE_SEAM_INIT0 << fidx[0])) == 0) { + for (uint i = 0; i < 2; i++) { + uint vert; + + if ((checked_verts & (1 << fidx[i])) != 0) { + continue; + } + + vert = ps->mloop_eval[lt->tri[fidx[i]]].v; + + for (node = ps->vertFaces[vert]; node; node = node->next) { + const int tri = POINTER_AS_INT(node->link); + + project_face_seams_init(ps, arena, tri, vert, false, ibuf_x, ibuf_y); + } + + checked_verts |= 1 << fidx[i]; + } + + ps->faceSeamFlags[tri_index] |= PROJ_FACE_SEAM_INIT0 << fidx[0]; + } + + fidx[1] = fidx[0]; + } while (fidx[0]--); + } } -#endif // PROJ_DEBUG_NOSEAMBLEED - +#endif // PROJ_DEBUG_NOSEAMBLEED /* 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( - const float uv[2], - const float v1co[3], const float v2co[3], const float v3co[3], /* Screenspace coords */ - const float uv1co[2], const float uv2co[2], const float uv3co[2], - float pixelScreenCo[4], - float w[3]) +static void screen_px_from_ortho(const float uv[2], + const float v1co[3], + const float v2co[3], + const float v3co[3], /* Screenspace coords */ + const float uv1co[2], + const float uv2co[2], + const float uv3co[2], + float pixelScreenCo[4], + float w[3]) { - barycentric_weights_v2(uv1co, uv2co, uv3co, uv, w); - interp_v3_v3v3v3(pixelScreenCo, v1co, v2co, v3co, w); + barycentric_weights_v2(uv1co, uv2co, uv3co, uv, w); + interp_v3_v3v3v3(pixelScreenCo, v1co, v2co, v3co, w); } /* same as screen_px_from_ortho except we * do perspective correction on the pixel coordinate */ -static void screen_px_from_persp( - const float uv[2], - const float v1co[4], const float v2co[4], const float v3co[4], /* screenspace coords */ - const float uv1co[2], const float uv2co[2], const float uv3co[2], - float pixelScreenCo[4], - float w[3]) +static void screen_px_from_persp(const float uv[2], + const float v1co[4], + const float v2co[4], + const float v3co[4], /* screenspace coords */ + const float uv1co[2], + const float uv2co[2], + const float uv3co[2], + float pixelScreenCo[4], + float w[3]) { - float w_int[3]; - float wtot_inv, wtot; - barycentric_weights_v2(uv1co, uv2co, uv3co, uv, w); - - /* re-weight from the 4th coord of each screen vert */ - w_int[0] = w[0] * v1co[3]; - w_int[1] = w[1] * v2co[3]; - w_int[2] = w[2] * v3co[3]; - - wtot = w_int[0] + w_int[1] + w_int[2]; - - if (wtot > 0.0f) { - wtot_inv = 1.0f / wtot; - w_int[0] *= wtot_inv; - w_int[1] *= wtot_inv; - w_int[2] *= wtot_inv; - } - else { - w[0] = w[1] = w[2] = - /* dummy values for zero area face */ - w_int[0] = w_int[1] = w_int[2] = 1.0f / 3.0f; - } - /* done re-weighting */ - - /* do interpolation based on projected weight */ - interp_v3_v3v3v3(pixelScreenCo, v1co, v2co, v3co, w_int); + float w_int[3]; + float wtot_inv, wtot; + barycentric_weights_v2(uv1co, uv2co, uv3co, uv, w); + + /* re-weight from the 4th coord of each screen vert */ + w_int[0] = w[0] * v1co[3]; + w_int[1] = w[1] * v2co[3]; + w_int[2] = w[2] * v3co[3]; + + wtot = w_int[0] + w_int[1] + w_int[2]; + + if (wtot > 0.0f) { + wtot_inv = 1.0f / wtot; + w_int[0] *= wtot_inv; + w_int[1] *= wtot_inv; + w_int[2] *= wtot_inv; + } + else { + w[0] = w[1] = w[2] = + /* dummy values for zero area face */ + w_int[0] = w_int[1] = w_int[2] = 1.0f / 3.0f; + } + /* done re-weighting */ + + /* do interpolation based on projected weight */ + interp_v3_v3v3v3(pixelScreenCo, v1co, v2co, v3co, w_int); } - /** * Set a direction vector based on a screen location. * (use for perspective view, else we can simply use `ps->viewDir`) @@ -1548,16 +1551,18 @@ static void screen_px_from_persp( * * \param r_dir: Resulting direction (length is undefined). */ -static void screen_px_to_vector_persp( - int winx, int winy, const float projmat_inv[4][4], const float view_pos[3], - const float co_px[2], - float r_dir[3]) +static void screen_px_to_vector_persp(int winx, + int winy, + const float projmat_inv[4][4], + const float view_pos[3], + const float co_px[2], + float r_dir[3]) { - r_dir[0] = 2.0f * (co_px[0] / winx) - 1.0f; - r_dir[1] = 2.0f * (co_px[1] / winy) - 1.0f; - r_dir[2] = -0.5f; - mul_project_m4_v3((float(*)[4])projmat_inv, r_dir); - sub_v3_v3(r_dir, view_pos); + r_dir[0] = 2.0f * (co_px[0] / winx) - 1.0f; + r_dir[1] = 2.0f * (co_px[1] / winy) - 1.0f; + r_dir[2] = -0.5f; + mul_project_m4_v3((float(*)[4])projmat_inv, r_dir); + sub_v3_v3(r_dir, view_pos); } /** @@ -1568,578 +1573,601 @@ static void screen_px_to_vector_persp( * \param p: 2D screen-space location. * \param v1, v2: 3D object-space locations. */ -static float screen_px_line_point_factor_v2_persp( - const ProjPaintState *ps, - const float p[2], - const float v1[3], const float v2[3]) +static float screen_px_line_point_factor_v2_persp(const ProjPaintState *ps, + const float p[2], + const float v1[3], + const float v2[3]) { - const float zero[3] = {0}; - float v1_proj[3], v2_proj[3]; - float dir[3]; + const float zero[3] = {0}; + float v1_proj[3], v2_proj[3]; + float dir[3]; - screen_px_to_vector_persp(ps->winx, ps->winy, ps->projectMatInv, ps->viewPos, p, dir); + screen_px_to_vector_persp(ps->winx, ps->winy, ps->projectMatInv, ps->viewPos, p, dir); - sub_v3_v3v3(v1_proj, v1, ps->viewPos); - sub_v3_v3v3(v2_proj, v2, ps->viewPos); + sub_v3_v3v3(v1_proj, v1, ps->viewPos); + sub_v3_v3v3(v2_proj, v2, ps->viewPos); - project_plane_v3_v3v3(v1_proj, v1_proj, dir); - project_plane_v3_v3v3(v2_proj, v2_proj, dir); + project_plane_v3_v3v3(v1_proj, v1_proj, dir); + project_plane_v3_v3v3(v2_proj, v2_proj, dir); - return line_point_factor_v2(zero, v1_proj, v2_proj); + return line_point_factor_v2(zero, v1_proj, v2_proj); } - -static void project_face_pixel( - const float *lt_tri_uv[3], ImBuf *ibuf_other, const float w[3], - unsigned char rgba_ub[4], float rgba_f[4]) +static void project_face_pixel(const float *lt_tri_uv[3], + ImBuf *ibuf_other, + const float w[3], + unsigned char rgba_ub[4], + float rgba_f[4]) { - float uv_other[2], x, y; - - interp_v2_v2v2v2(uv_other, UNPACK3(lt_tri_uv), w); + float uv_other[2], x, y; - /* use */ - uvco_to_wrapped_pxco(uv_other, ibuf_other->x, ibuf_other->y, &x, &y); + interp_v2_v2v2v2(uv_other, UNPACK3(lt_tri_uv), w); - if (ibuf_other->rect_float) { /* from float to float */ - bilinear_interpolation_color_wrap(ibuf_other, NULL, rgba_f, x, y); - } - else { /* from char to float */ - bilinear_interpolation_color_wrap(ibuf_other, rgba_ub, NULL, x, y); - } + /* 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_wrap(ibuf_other, NULL, rgba_f, x, y); + } + else { /* from char to float */ + bilinear_interpolation_color_wrap(ibuf_other, rgba_ub, NULL, x, y); + } } /* run this outside project_paint_uvpixel_init since pixels with mask 0 don't need init */ -static float project_paint_uvpixel_mask( - const ProjPaintState *ps, - const int tri_index, - const float w[3]) +static float project_paint_uvpixel_mask(const ProjPaintState *ps, + const int tri_index, + const float w[3]) { - float mask; - - /* Image Mask */ - if (ps->do_layer_stencil) { - /* another UV maps image is masking this one's */ - ImBuf *ibuf_other; - Image *other_tpage = ps->stencil_ima; - - if (other_tpage && (ibuf_other = BKE_image_acquire_ibuf(other_tpage, NULL, NULL))) { - const MLoopTri *lt_other = &ps->mlooptri_eval[tri_index]; - const float *lt_other_tri_uv[3] = { PS_LOOPTRI_AS_UV_3(ps->poly_to_loop_uv, lt_other) }; - - /* BKE_image_acquire_ibuf - TODO - this may be slow */ - unsigned char rgba_ub[4]; - float rgba_f[4]; - - project_face_pixel(lt_other_tri_uv, ibuf_other, w, rgba_ub, rgba_f); - - if (ibuf_other->rect_float) { /* from float to float */ - mask = ((rgba_f[0] + rgba_f[1] + rgba_f[2]) * (1.0f / 3.0f)) * rgba_f[3]; - } - else { /* from char to float */ - mask = ((rgba_ub[0] + rgba_ub[1] + rgba_ub[2]) * (1.0f / (255.0f * 3.0f))) * (rgba_ub[3] * (1.0f / 255.0f)); - } - - BKE_image_release_ibuf(other_tpage, ibuf_other, NULL); - - if (!ps->do_layer_stencil_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; - } - - if (ps->do_mask_cavity) { - const MLoopTri *lt = &ps->mlooptri_eval[tri_index]; - const int lt_vtri[3] = { PS_LOOPTRI_AS_VERT_INDEX_3(ps, lt) }; - float ca1, ca2, ca3, ca_mask; - ca1 = ps->cavities[lt_vtri[0]]; - ca2 = ps->cavities[lt_vtri[1]]; - ca3 = ps->cavities[lt_vtri[2]]; - - ca_mask = w[0] * ca1 + w[1] * ca2 + w[2] * ca3; - ca_mask = curvemapping_evaluateF(ps->cavity_curve, 0, ca_mask); - CLAMP(ca_mask, 0.0f, 1.0f); - mask *= ca_mask; - } - - /* calculate mask */ - if (ps->do_mask_normal) { - const MLoopTri *lt = &ps->mlooptri_eval[tri_index]; - const int lt_vtri[3] = { PS_LOOPTRI_AS_VERT_INDEX_3(ps, lt) }; - const MPoly *mp = &ps->mpoly_eval[lt->poly]; - float no[3], angle_cos; - - if (mp->flag & ME_SMOOTH) { - const short *no1, *no2, *no3; - no1 = ps->mvert_eval[lt_vtri[0]].no; - no2 = ps->mvert_eval[lt_vtri[1]].no; - no3 = ps->mvert_eval[lt_vtri[2]].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_v3(no); - } - else { - /* incase the */ + float mask; + + /* Image Mask */ + if (ps->do_layer_stencil) { + /* another UV maps image is masking this one's */ + ImBuf *ibuf_other; + Image *other_tpage = ps->stencil_ima; + + if (other_tpage && (ibuf_other = BKE_image_acquire_ibuf(other_tpage, NULL, NULL))) { + const MLoopTri *lt_other = &ps->mlooptri_eval[tri_index]; + const float *lt_other_tri_uv[3] = {PS_LOOPTRI_AS_UV_3(ps->poly_to_loop_uv, lt_other)}; + + /* BKE_image_acquire_ibuf - TODO - this may be slow */ + unsigned char rgba_ub[4]; + float rgba_f[4]; + + project_face_pixel(lt_other_tri_uv, ibuf_other, w, rgba_ub, rgba_f); + + if (ibuf_other->rect_float) { /* from float to float */ + mask = ((rgba_f[0] + rgba_f[1] + rgba_f[2]) * (1.0f / 3.0f)) * rgba_f[3]; + } + else { /* from char to float */ + mask = ((rgba_ub[0] + rgba_ub[1] + rgba_ub[2]) * (1.0f / (255.0f * 3.0f))) * + (rgba_ub[3] * (1.0f / 255.0f)); + } + + BKE_image_release_ibuf(other_tpage, ibuf_other, NULL); + + if (!ps->do_layer_stencil_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; + } + + if (ps->do_mask_cavity) { + const MLoopTri *lt = &ps->mlooptri_eval[tri_index]; + const int lt_vtri[3] = {PS_LOOPTRI_AS_VERT_INDEX_3(ps, lt)}; + float ca1, ca2, ca3, ca_mask; + ca1 = ps->cavities[lt_vtri[0]]; + ca2 = ps->cavities[lt_vtri[1]]; + ca3 = ps->cavities[lt_vtri[2]]; + + ca_mask = w[0] * ca1 + w[1] * ca2 + w[2] * ca3; + ca_mask = curvemapping_evaluateF(ps->cavity_curve, 0, ca_mask); + CLAMP(ca_mask, 0.0f, 1.0f); + mask *= ca_mask; + } + + /* calculate mask */ + if (ps->do_mask_normal) { + const MLoopTri *lt = &ps->mlooptri_eval[tri_index]; + const int lt_vtri[3] = {PS_LOOPTRI_AS_VERT_INDEX_3(ps, lt)}; + const MPoly *mp = &ps->mpoly_eval[lt->poly]; + float no[3], angle_cos; + + if (mp->flag & ME_SMOOTH) { + const short *no1, *no2, *no3; + no1 = ps->mvert_eval[lt_vtri[0]].no; + no2 = ps->mvert_eval[lt_vtri[1]].no; + no3 = ps->mvert_eval[lt_vtri[2]].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_v3(no); + } + else { + /* incase the */ #if 1 - /* normalizing per pixel isn't optimal, we could cache or check ps->*/ - normal_tri_v3(no, - ps->mvert_eval[lt_vtri[0]].co, - ps->mvert_eval[lt_vtri[1]].co, - ps->mvert_eval[lt_vtri[2]].co); + /* normalizing per pixel isn't optimal, we could cache or check ps->*/ + normal_tri_v3(no, + ps->mvert_eval[lt_vtri[0]].co, + ps->mvert_eval[lt_vtri[1]].co, + ps->mvert_eval[lt_vtri[2]].co); #else - /* don't use because some modifiers dont have normal data (subsurf for eg) */ - copy_v3_v3(no, (float *)ps->dm->getTessFaceData(ps->dm, tri_index, CD_NORMAL)); + /* don't use because some modifiers dont have normal data (subsurf for eg) */ + copy_v3_v3(no, (float *)ps->dm->getTessFaceData(ps->dm, tri_index, CD_NORMAL)); #endif - } - - if (UNLIKELY(ps->is_flip_object)) { - negate_v3(no); - } - - /* now we can use the normal as a mask */ - if (ps->is_ortho) { - angle_cos = dot_v3v3(ps->viewDir, no); - } - else { - /* Annoying but for the perspective view we need to get the pixels location in 3D space :/ */ - float viewDirPersp[3]; - const float *co1, *co2, *co3; - co1 = ps->mvert_eval[lt_vtri[0]].co; - co2 = ps->mvert_eval[lt_vtri[1]].co; - co3 = ps->mvert_eval[lt_vtri[2]].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_v3(viewDirPersp); - if (UNLIKELY(ps->is_flip_object)) { - negate_v3(viewDirPersp); - } - - angle_cos = dot_v3v3(viewDirPersp, no); - } - - /* If backface culling is disabled, allow painting on back faces. */ - if (!ps->do_backfacecull) { - angle_cos = fabsf(angle_cos); - } - - if (angle_cos <= ps->normal_angle__cos) { - /* outsize the normal limit*/ - return 0.0f; - } - else if (angle_cos < ps->normal_angle_inner__cos) { - mask *= (ps->normal_angle - acosf(angle_cos)) / ps->normal_angle_range; - } /* otherwise no mask normal is needed, were within the limit */ - } - - /* This only works when the opacity doesn't change while painting, stylus pressure messes with this - * so don't use it. */ - // if (ps->is_airbrush == 0) mask *= BKE_brush_alpha_get(ps->brush); - - return mask; + } + + if (UNLIKELY(ps->is_flip_object)) { + negate_v3(no); + } + + /* now we can use the normal as a mask */ + if (ps->is_ortho) { + angle_cos = dot_v3v3(ps->viewDir, no); + } + else { + /* Annoying but for the perspective view we need to get the pixels location in 3D space :/ */ + float viewDirPersp[3]; + const float *co1, *co2, *co3; + co1 = ps->mvert_eval[lt_vtri[0]].co; + co2 = ps->mvert_eval[lt_vtri[1]].co; + co3 = ps->mvert_eval[lt_vtri[2]].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_v3(viewDirPersp); + if (UNLIKELY(ps->is_flip_object)) { + negate_v3(viewDirPersp); + } + + angle_cos = dot_v3v3(viewDirPersp, no); + } + + /* If backface culling is disabled, allow painting on back faces. */ + if (!ps->do_backfacecull) { + angle_cos = fabsf(angle_cos); + } + + if (angle_cos <= ps->normal_angle__cos) { + /* outsize the normal limit*/ + return 0.0f; + } + else if (angle_cos < ps->normal_angle_inner__cos) { + mask *= (ps->normal_angle - acosf(angle_cos)) / ps->normal_angle_range; + } /* otherwise no mask normal is needed, were within the limit */ + } + + /* This only works when the opacity doesn't change while painting, stylus pressure messes with this + * so don't use it. */ + // if (ps->is_airbrush == 0) mask *= BKE_brush_alpha_get(ps->brush); + + return mask; } static int project_paint_pixel_sizeof(const short tool) { - if ((tool == PAINT_TOOL_CLONE) || (tool == PAINT_TOOL_SMEAR)) { - return sizeof(ProjPixelClone); - } - else { - return sizeof(ProjPixel); - } + if ((tool == PAINT_TOOL_CLONE) || (tool == PAINT_TOOL_SMEAR)) { + return sizeof(ProjPixelClone); + } + else { + return sizeof(ProjPixel); + } } static int project_paint_undo_subtiles(const TileInfo *tinf, int tx, int ty) { - ProjPaintImage *pjIma = tinf->pjima; - int tile_index = tx + ty * tinf->tile_width; - bool generate_tile = false; - - /* double check lock to avoid locking */ - if (UNLIKELY(!pjIma->undoRect[tile_index])) { - if (tinf->lock) - BLI_spin_lock(tinf->lock); - if (LIKELY(!pjIma->undoRect[tile_index])) { - pjIma->undoRect[tile_index] = TILE_PENDING; - generate_tile = true; - } - if (tinf->lock) - BLI_spin_unlock(tinf->lock); - } - - - if (generate_tile) { - ListBase *undo_tiles = ED_image_undo_get_tiles(); - volatile void *undorect; - if (tinf->masked) { - undorect = image_undo_push_tile( - undo_tiles, pjIma->ima, pjIma->ibuf, tinf->tmpibuf, - tx, ty, &pjIma->maskRect[tile_index], &pjIma->valid[tile_index], true, false); - } - else { - undorect = image_undo_push_tile( - undo_tiles, pjIma->ima, pjIma->ibuf, tinf->tmpibuf, - tx, ty, NULL, &pjIma->valid[tile_index], true, false); - } - - pjIma->ibuf->userflags |= IB_BITMAPDIRTY; - /* tile ready, publish */ - if (tinf->lock) - BLI_spin_lock(tinf->lock); - pjIma->undoRect[tile_index] = undorect; - if (tinf->lock) - BLI_spin_unlock(tinf->lock); - - } - - return tile_index; + ProjPaintImage *pjIma = tinf->pjima; + int tile_index = tx + ty * tinf->tile_width; + bool generate_tile = false; + + /* double check lock to avoid locking */ + if (UNLIKELY(!pjIma->undoRect[tile_index])) { + if (tinf->lock) + BLI_spin_lock(tinf->lock); + if (LIKELY(!pjIma->undoRect[tile_index])) { + pjIma->undoRect[tile_index] = TILE_PENDING; + generate_tile = true; + } + if (tinf->lock) + BLI_spin_unlock(tinf->lock); + } + + if (generate_tile) { + ListBase *undo_tiles = ED_image_undo_get_tiles(); + volatile void *undorect; + if (tinf->masked) { + undorect = image_undo_push_tile(undo_tiles, + pjIma->ima, + pjIma->ibuf, + tinf->tmpibuf, + tx, + ty, + &pjIma->maskRect[tile_index], + &pjIma->valid[tile_index], + true, + false); + } + else { + undorect = image_undo_push_tile(undo_tiles, + pjIma->ima, + pjIma->ibuf, + tinf->tmpibuf, + tx, + ty, + NULL, + &pjIma->valid[tile_index], + true, + false); + } + + pjIma->ibuf->userflags |= IB_BITMAPDIRTY; + /* tile ready, publish */ + if (tinf->lock) + BLI_spin_lock(tinf->lock); + pjIma->undoRect[tile_index] = undorect; + if (tinf->lock) + BLI_spin_unlock(tinf->lock); + } + + return tile_index; } /* 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 TileInfo *tinf, - int x_px, int y_px, - const float mask, - const int tri_index, - const float pixelScreenCo[4], - const float world_spaceCo[3], - const float w[3]) +static ProjPixel *project_paint_uvpixel_init(const ProjPaintState *ps, + MemArena *arena, + const TileInfo *tinf, + int x_px, + int y_px, + const float mask, + const int tri_index, + const float pixelScreenCo[4], + const float world_spaceCo[3], + const float w[3]) { - ProjPixel *projPixel; - int x_tile, y_tile; - int x_round, y_round; - int tile_offset; - /* volatile is important here to ensure pending check is not optimized away by compiler*/ - volatile int tile_index; - - ProjPaintImage *projima = tinf->pjima; - ImBuf *ibuf = projima->ibuf; - /* wrap pixel location */ - - x_px = mod_i(x_px, ibuf->x); - y_px = mod_i(y_px, ibuf->y); - - BLI_assert(ps->pixel_sizeof == project_paint_pixel_sizeof(ps->tool)); - projPixel = BLI_memarena_alloc(arena, ps->pixel_sizeof); - - /* calculate the undo tile offset of the pixel, used to store the original - * pixel color and accumulated mask if any */ - x_tile = x_px >> IMAPAINT_TILE_BITS; - y_tile = y_px >> IMAPAINT_TILE_BITS; - - x_round = x_tile * IMAPAINT_TILE_SIZE; - y_round = y_tile * IMAPAINT_TILE_SIZE; - //memset(projPixel, 0, size); - - tile_offset = (x_px - x_round) + (y_px - y_round) * IMAPAINT_TILE_SIZE; - tile_index = project_paint_undo_subtiles(tinf, x_tile, y_tile); - - /* other thread may be initializing the tile so wait here */ - while (projima->undoRect[tile_index] == TILE_PENDING) - ; - - BLI_assert(tile_index < (IMAPAINT_TILE_NUMBER(ibuf->x) * IMAPAINT_TILE_NUMBER(ibuf->y))); - BLI_assert(tile_offset < (IMAPAINT_TILE_SIZE * IMAPAINT_TILE_SIZE)); - - projPixel->valid = projima->valid[tile_index]; - - if (ibuf->rect_float) { - projPixel->pixel.f_pt = ibuf->rect_float + ((x_px + y_px * ibuf->x) * 4); - projPixel->origColor.f_pt = (float *)projima->undoRect[tile_index] + 4 * tile_offset; - zero_v4(projPixel->newColor.f); - } - else { - projPixel->pixel.ch_pt = (unsigned char *)(ibuf->rect + (x_px + y_px * ibuf->x)); - projPixel->origColor.uint_pt = (unsigned int *)projima->undoRect[tile_index] + tile_offset; - projPixel->newColor.uint = 0; - } - - /* screenspace unclamped, we could keep its z and w values but don't need them at the moment */ - if (ps->brush->mtex.brush_map_mode == MTEX_MAP_MODE_3D) { - copy_v3_v3(projPixel->worldCoSS, world_spaceCo); - } - - copy_v2_v2(projPixel->projCoSS, pixelScreenCo); - - projPixel->x_px = x_px; - projPixel->y_px = y_px; - - projPixel->mask = (unsigned short)(mask * 65535); - if (ps->do_masking) - projPixel->mask_accum = projima->maskRect[tile_index] + tile_offset; - else - projPixel->mask_accum = NULL; - - /* 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->poly_to_loop_uv_clone) { - ImBuf *ibuf_other; - Image *other_tpage = project_paint_face_clone_image(ps, tri_index); - - if (other_tpage && (ibuf_other = BKE_image_acquire_ibuf(other_tpage, NULL, NULL))) { - const MLoopTri *lt_other = &ps->mlooptri_eval[tri_index]; - const float *lt_other_tri_uv[3] = { PS_LOOPTRI_AS_UV_3(ps->poly_to_loop_uv_clone, lt_other) }; - - /* BKE_image_acquire_ibuf - TODO - this may be slow */ - - if (ibuf->rect_float) { - if (ibuf_other->rect_float) { /* from float to float */ - project_face_pixel(lt_other_tri_uv, ibuf_other, w, NULL, ((ProjPixelClone *)projPixel)->clonepx.f); - } - else { /* from char to float */ - unsigned char rgba_ub[4]; - float rgba[4]; - project_face_pixel(lt_other_tri_uv, ibuf_other, w, rgba_ub, NULL); - if (ps->use_colormanagement) { - srgb_to_linearrgb_uchar4(rgba, rgba_ub); - } - else { - rgba_uchar_to_float(rgba, rgba_ub); - } - straight_to_premul_v4_v4(((ProjPixelClone *)projPixel)->clonepx.f, rgba); - } - } - else { - if (ibuf_other->rect_float) { /* float to char */ - float rgba[4]; - project_face_pixel(lt_other_tri_uv, ibuf_other, w, NULL, rgba); - premul_to_straight_v4(rgba); - if (ps->use_colormanagement) { - linearrgb_to_srgb_uchar3(((ProjPixelClone *)projPixel)->clonepx.ch, rgba); - } - else { - rgb_float_to_uchar(((ProjPixelClone *)projPixel)->clonepx.ch, rgba); - } - ((ProjPixelClone *)projPixel)->clonepx.ch[3] = rgba[3] * 255; - } - else { /* char to char */ - project_face_pixel(lt_other_tri_uv, ibuf_other, w, ((ProjPixelClone *)projPixel)->clonepx.ch, NULL); - } - } - - BKE_image_release_ibuf(other_tpage, ibuf_other, NULL); - } - else { - if (ibuf->rect_float) { - ((ProjPixelClone *)projPixel)->clonepx.f[3] = 0; - } - else { - ((ProjPixelClone *)projPixel)->clonepx.ch[3] = 0; - } - } - - } - else { - float co[2]; - sub_v2_v2v2(co, projPixel->projCoSS, ps->cloneOffset); - - /* no need to initialize the bucket, we're only checking buckets faces and for this - * the faces are already initialized in project_paint_delayed_face_init(...) */ - if (ibuf->rect_float) { - if (!project_paint_PickColor(ps, co, ((ProjPixelClone *)projPixel)->clonepx.f, NULL, 1)) { - /* zero alpha - ignore */ - ((ProjPixelClone *)projPixel)->clonepx.f[3] = 0; - } - } - else { - if (!project_paint_PickColor(ps, co, NULL, ((ProjPixelClone *)projPixel)->clonepx.ch, 1)) { - /* zero alpha - ignore */ - ((ProjPixelClone *)projPixel)->clonepx.ch[3] = 0; - } - } - } - } + ProjPixel *projPixel; + int x_tile, y_tile; + int x_round, y_round; + int tile_offset; + /* volatile is important here to ensure pending check is not optimized away by compiler*/ + volatile int tile_index; + + ProjPaintImage *projima = tinf->pjima; + ImBuf *ibuf = projima->ibuf; + /* wrap pixel location */ + + x_px = mod_i(x_px, ibuf->x); + y_px = mod_i(y_px, ibuf->y); + + BLI_assert(ps->pixel_sizeof == project_paint_pixel_sizeof(ps->tool)); + projPixel = BLI_memarena_alloc(arena, ps->pixel_sizeof); + + /* calculate the undo tile offset of the pixel, used to store the original + * pixel color and accumulated mask if any */ + x_tile = x_px >> IMAPAINT_TILE_BITS; + y_tile = y_px >> IMAPAINT_TILE_BITS; + + x_round = x_tile * IMAPAINT_TILE_SIZE; + y_round = y_tile * IMAPAINT_TILE_SIZE; + //memset(projPixel, 0, size); + + tile_offset = (x_px - x_round) + (y_px - y_round) * IMAPAINT_TILE_SIZE; + tile_index = project_paint_undo_subtiles(tinf, x_tile, y_tile); + + /* other thread may be initializing the tile so wait here */ + while (projima->undoRect[tile_index] == TILE_PENDING) + ; + + BLI_assert(tile_index < (IMAPAINT_TILE_NUMBER(ibuf->x) * IMAPAINT_TILE_NUMBER(ibuf->y))); + BLI_assert(tile_offset < (IMAPAINT_TILE_SIZE * IMAPAINT_TILE_SIZE)); + + projPixel->valid = projima->valid[tile_index]; + + if (ibuf->rect_float) { + projPixel->pixel.f_pt = ibuf->rect_float + ((x_px + y_px * ibuf->x) * 4); + projPixel->origColor.f_pt = (float *)projima->undoRect[tile_index] + 4 * tile_offset; + zero_v4(projPixel->newColor.f); + } + else { + projPixel->pixel.ch_pt = (unsigned char *)(ibuf->rect + (x_px + y_px * ibuf->x)); + projPixel->origColor.uint_pt = (unsigned int *)projima->undoRect[tile_index] + tile_offset; + projPixel->newColor.uint = 0; + } + + /* screenspace unclamped, we could keep its z and w values but don't need them at the moment */ + if (ps->brush->mtex.brush_map_mode == MTEX_MAP_MODE_3D) { + copy_v3_v3(projPixel->worldCoSS, world_spaceCo); + } + + copy_v2_v2(projPixel->projCoSS, pixelScreenCo); + + projPixel->x_px = x_px; + projPixel->y_px = y_px; + + projPixel->mask = (unsigned short)(mask * 65535); + if (ps->do_masking) + projPixel->mask_accum = projima->maskRect[tile_index] + tile_offset; + else + projPixel->mask_accum = NULL; + + /* 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->poly_to_loop_uv_clone) { + ImBuf *ibuf_other; + Image *other_tpage = project_paint_face_clone_image(ps, tri_index); + + if (other_tpage && (ibuf_other = BKE_image_acquire_ibuf(other_tpage, NULL, NULL))) { + const MLoopTri *lt_other = &ps->mlooptri_eval[tri_index]; + const float *lt_other_tri_uv[3] = { + PS_LOOPTRI_AS_UV_3(ps->poly_to_loop_uv_clone, lt_other)}; + + /* BKE_image_acquire_ibuf - TODO - this may be slow */ + + if (ibuf->rect_float) { + if (ibuf_other->rect_float) { /* from float to float */ + project_face_pixel( + lt_other_tri_uv, ibuf_other, w, NULL, ((ProjPixelClone *)projPixel)->clonepx.f); + } + else { /* from char to float */ + unsigned char rgba_ub[4]; + float rgba[4]; + project_face_pixel(lt_other_tri_uv, ibuf_other, w, rgba_ub, NULL); + if (ps->use_colormanagement) { + srgb_to_linearrgb_uchar4(rgba, rgba_ub); + } + else { + rgba_uchar_to_float(rgba, rgba_ub); + } + straight_to_premul_v4_v4(((ProjPixelClone *)projPixel)->clonepx.f, rgba); + } + } + else { + if (ibuf_other->rect_float) { /* float to char */ + float rgba[4]; + project_face_pixel(lt_other_tri_uv, ibuf_other, w, NULL, rgba); + premul_to_straight_v4(rgba); + if (ps->use_colormanagement) { + linearrgb_to_srgb_uchar3(((ProjPixelClone *)projPixel)->clonepx.ch, rgba); + } + else { + rgb_float_to_uchar(((ProjPixelClone *)projPixel)->clonepx.ch, rgba); + } + ((ProjPixelClone *)projPixel)->clonepx.ch[3] = rgba[3] * 255; + } + else { /* char to char */ + project_face_pixel( + lt_other_tri_uv, ibuf_other, w, ((ProjPixelClone *)projPixel)->clonepx.ch, NULL); + } + } + + BKE_image_release_ibuf(other_tpage, ibuf_other, NULL); + } + else { + if (ibuf->rect_float) { + ((ProjPixelClone *)projPixel)->clonepx.f[3] = 0; + } + else { + ((ProjPixelClone *)projPixel)->clonepx.ch[3] = 0; + } + } + } + else { + float co[2]; + sub_v2_v2v2(co, projPixel->projCoSS, ps->cloneOffset); + + /* no need to initialize the bucket, we're only checking buckets faces and for this + * the faces are already initialized in project_paint_delayed_face_init(...) */ + if (ibuf->rect_float) { + if (!project_paint_PickColor(ps, co, ((ProjPixelClone *)projPixel)->clonepx.f, NULL, 1)) { + /* zero alpha - ignore */ + ((ProjPixelClone *)projPixel)->clonepx.f[3] = 0; + } + } + else { + if (!project_paint_PickColor(ps, co, NULL, ((ProjPixelClone *)projPixel)->clonepx.ch, 1)) { + /* zero alpha - ignore */ + ((ProjPixelClone *)projPixel)->clonepx.ch[3] = 0; + } + } + } + } #ifdef PROJ_DEBUG_PAINT - if (ibuf->rect_float) projPixel->pixel.f_pt[0] = 0; - else projPixel->pixel.ch_pt[0] = 0; + if (ibuf->rect_float) + projPixel->pixel.f_pt[0] = 0; + else + projPixel->pixel.ch_pt[0] = 0; #endif - /* pointer arithmetic */ - projPixel->image_index = projima - ps->projImages; + /* pointer arithmetic */ + projPixel->image_index = projima - ps->projImages; - return projPixel; + return projPixel; } -static bool line_clip_rect2f( - const rctf *cliprect, - const rctf *rect, - const float l1[2], const float l2[2], - float l1_clip[2], float l2_clip[2]) +static bool line_clip_rect2f(const rctf *cliprect, + const 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 (fabsf(l1[1] - l2[1]) < PROJ_PIXEL_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; - } - - - /* this is a single point (or close to)*/ - if (fabsf(l1[0] - l2[0]) < PROJ_PIXEL_TOLERANCE) { - if (BLI_rctf_isect_pt_v(rect, l1)) { - copy_v2_v2(l1_clip, l1); - copy_v2_v2(l2_clip, l2); - return 1; - } - else { - return 0; - } - } - - copy_v2_v2(l1_clip, l1); - copy_v2_v2(l2_clip, l2); - CLAMP(l1_clip[0], rect->xmin, rect->xmax); - CLAMP(l2_clip[0], rect->xmin, rect->xmax); - return 1; - } - else if (fabsf(l1[0] - l2[0]) < PROJ_PIXEL_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; - } - - /* this is a single point (or close to)*/ - if (fabsf(l1[1] - l2[1]) < PROJ_PIXEL_TOLERANCE) { - if (BLI_rctf_isect_pt_v(rect, l1)) { - copy_v2_v2(l1_clip, l1); - copy_v2_v2(l2_clip, l2); - return 1; - } - else { - return 0; - } - } - - copy_v2_v2(l1_clip, l1); - copy_v2_v2(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_rctf_isect_pt_v(rect, l1)) { - copy_v2_v2(l1_clip, l1); - ok1 = 1; - } - - if (BLI_rctf_isect_pt_v(rect, l2)) { - copy_v2_v2(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 >= cliprect->xmin) && (isect <= cliprect->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 >= cliprect->xmin) && (isect <= cliprect->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 >= cliprect->ymin) && (isect <= cliprect->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 >= cliprect->ymin) && (isect <= cliprect->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; - } - } + /* first account for horizontal, then vertical lines */ + /* horiz */ + if (fabsf(l1[1] - l2[1]) < PROJ_PIXEL_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; + } + + /* this is a single point (or close to)*/ + if (fabsf(l1[0] - l2[0]) < PROJ_PIXEL_TOLERANCE) { + if (BLI_rctf_isect_pt_v(rect, l1)) { + copy_v2_v2(l1_clip, l1); + copy_v2_v2(l2_clip, l2); + return 1; + } + else { + return 0; + } + } + + copy_v2_v2(l1_clip, l1); + copy_v2_v2(l2_clip, l2); + CLAMP(l1_clip[0], rect->xmin, rect->xmax); + CLAMP(l2_clip[0], rect->xmin, rect->xmax); + return 1; + } + else if (fabsf(l1[0] - l2[0]) < PROJ_PIXEL_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; + } + + /* this is a single point (or close to)*/ + if (fabsf(l1[1] - l2[1]) < PROJ_PIXEL_TOLERANCE) { + if (BLI_rctf_isect_pt_v(rect, l1)) { + copy_v2_v2(l1_clip, l1); + copy_v2_v2(l2_clip, l2); + return 1; + } + else { + return 0; + } + } + + copy_v2_v2(l1_clip, l1); + copy_v2_v2(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_rctf_isect_pt_v(rect, l1)) { + copy_v2_v2(l1_clip, l1); + ok1 = 1; + } + + if (BLI_rctf_isect_pt_v(rect, l2)) { + copy_v2_v2(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 >= cliprect->xmin) && + (isect <= cliprect->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 >= cliprect->xmin) && + (isect <= cliprect->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 >= cliprect->ymin) && + (isect <= cliprect->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 >= cliprect->ymin) && + (isect <= cliprect->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 tri about its center * scaling by #PROJ_FACE_SCALE_SEAM (0.99x) is used for getting fake UV pixel coords that are on the @@ -2149,82 +2177,93 @@ static bool line_clip_rect2f( static void scale_tri(float insetCos[3][3], const float *origCos[4], const float inset) { - float cent[3]; - cent[0] = (origCos[0][0] + origCos[1][0] + origCos[2][0]) * (1.0f / 3.0f); - cent[1] = (origCos[0][1] + origCos[1][1] + origCos[2][1]) * (1.0f / 3.0f); - cent[2] = (origCos[0][2] + origCos[1][2] + origCos[2][2]) * (1.0f / 3.0f); - - sub_v3_v3v3(insetCos[0], origCos[0], cent); - sub_v3_v3v3(insetCos[1], origCos[1], cent); - sub_v3_v3v3(insetCos[2], origCos[2], cent); - - mul_v3_fl(insetCos[0], inset); - mul_v3_fl(insetCos[1], inset); - mul_v3_fl(insetCos[2], inset); - - add_v3_v3(insetCos[0], cent); - add_v3_v3(insetCos[1], cent); - add_v3_v3(insetCos[2], cent); + float cent[3]; + cent[0] = (origCos[0][0] + origCos[1][0] + origCos[2][0]) * (1.0f / 3.0f); + cent[1] = (origCos[0][1] + origCos[1][1] + origCos[2][1]) * (1.0f / 3.0f); + cent[2] = (origCos[0][2] + origCos[1][2] + origCos[2][2]) * (1.0f / 3.0f); + + sub_v3_v3v3(insetCos[0], origCos[0], cent); + sub_v3_v3v3(insetCos[1], origCos[1], cent); + sub_v3_v3v3(insetCos[2], origCos[2], cent); + + mul_v3_fl(insetCos[0], inset); + mul_v3_fl(insetCos[1], inset); + mul_v3_fl(insetCos[2], inset); + + add_v3_v3(insetCos[0], cent); + add_v3_v3(insetCos[1], cent); + add_v3_v3(insetCos[2], cent); } -#endif //PROJ_DEBUG_NOSEAMBLEED +#endif //PROJ_DEBUG_NOSEAMBLEED static float len_squared_v2v2_alt(const float v1[2], const float v2_1, const float v2_2) { - float x, y; + float x, y; - x = v1[0] - v2_1; - y = v1[1] - v2_2; - return x * x + y * 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 len_squared_v2v2 * 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 bool project_bucket_isect_circle(const float cent[2], const float radius_squared, const rctf *bucket_bounds) +static bool project_bucket_isect_circle(const float cent[2], + const float radius_squared, + const rctf *bucket_bounds) { - /* Would normally to a simple intersection test, however we know the bounds of these 2 already 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 - */ + /* Would normally to a simple intersection test, however we know the bounds of these 2 already 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 0 - if (BLI_rctf_isect_pt_v(bucket_bounds, cent)) - return 1; + if (BLI_rctf_isect_pt_v(bucket_bounds, cent)) + return 1; #endif - 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 (len_squared_v2v2_alt(cent, bucket_bounds->xmin, bucket_bounds->ymin) < radius_squared) ? 1 : 0; - } - /* top left test */ - else if (cent[1] > bucket_bounds->ymax) { - return (len_squared_v2v2_alt(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 (len_squared_v2v2_alt(cent, bucket_bounds->xmax, bucket_bounds->ymin) < radius_squared) ? 1 : 0; - } - /* top right test */ - else if (cent[1] > bucket_bounds->ymax) { - return (len_squared_v2v2_alt(cent, bucket_bounds->xmax, bucket_bounds->ymax) < radius_squared) ? 1 : 0; - } - } - - return 0; + 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 (len_squared_v2v2_alt(cent, bucket_bounds->xmin, bucket_bounds->ymin) < + radius_squared) ? + 1 : + 0; + } + /* top left test */ + else if (cent[1] > bucket_bounds->ymax) { + return (len_squared_v2v2_alt(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 (len_squared_v2v2_alt(cent, bucket_bounds->xmax, bucket_bounds->ymin) < + radius_squared) ? + 1 : + 0; + } + /* top right test */ + else if (cent[1] > bucket_bounds->ymax) { + return (len_squared_v2v2_alt(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 @@ -2234,70 +2273,75 @@ static bool project_bucket_isect_circle(const float cent[2], const float radius_ * 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( - const rctf *bucket_bounds, - const float *v1coSS, const float *v2coSS, const float *v3coSS, - const float *uv1co, const float *uv2co, const float *uv3co, - float bucket_bounds_uv[4][2], - const int flip) +static void rect_to_uvspace_ortho(const rctf *bucket_bounds, + const float *v1coSS, + const float *v2coSS, + const float *v3coSS, + const float *uv1co, + const float *uv2co, + const 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; - barycentric_weights_v2(v1coSS, v2coSS, v3coSS, uv, w); - interp_v2_v2v2v2(bucket_bounds_uv[flip ? 3 : 0], uv1co, uv2co, uv3co, w); - - //uv[0] = bucket_bounds->xmax; // set above - uv[1] = bucket_bounds->ymax; - barycentric_weights_v2(v1coSS, v2coSS, v3coSS, uv, w); - interp_v2_v2v2v2(bucket_bounds_uv[flip ? 2 : 1], uv1co, uv2co, uv3co, w); - - uv[0] = bucket_bounds->xmin; - //uv[1] = bucket_bounds->ymax; // set above - barycentric_weights_v2(v1coSS, v2coSS, v3coSS, uv, w); - interp_v2_v2v2v2(bucket_bounds_uv[flip ? 1 : 2], uv1co, uv2co, uv3co, w); - - //uv[0] = bucket_bounds->xmin; // set above - uv[1] = bucket_bounds->ymin; - barycentric_weights_v2(v1coSS, v2coSS, v3coSS, uv, w); - interp_v2_v2v2v2(bucket_bounds_uv[flip ? 0 : 3], uv1co, uv2co, uv3co, w); + float uv[2]; + float w[3]; + + /* get the UV space bounding box */ + uv[0] = bucket_bounds->xmax; + uv[1] = bucket_bounds->ymin; + barycentric_weights_v2(v1coSS, v2coSS, v3coSS, uv, w); + interp_v2_v2v2v2(bucket_bounds_uv[flip ? 3 : 0], uv1co, uv2co, uv3co, w); + + //uv[0] = bucket_bounds->xmax; // set above + uv[1] = bucket_bounds->ymax; + barycentric_weights_v2(v1coSS, v2coSS, v3coSS, uv, w); + interp_v2_v2v2v2(bucket_bounds_uv[flip ? 2 : 1], uv1co, uv2co, uv3co, w); + + uv[0] = bucket_bounds->xmin; + //uv[1] = bucket_bounds->ymax; // set above + barycentric_weights_v2(v1coSS, v2coSS, v3coSS, uv, w); + interp_v2_v2v2v2(bucket_bounds_uv[flip ? 1 : 2], uv1co, uv2co, uv3co, w); + + //uv[0] = bucket_bounds->xmin; // set above + uv[1] = bucket_bounds->ymin; + barycentric_weights_v2(v1coSS, v2coSS, v3coSS, uv, w); + interp_v2_v2v2v2(bucket_bounds_uv[flip ? 0 : 3], uv1co, uv2co, uv3co, w); } /* same as above but use barycentric_weights_v2_persp */ -static void rect_to_uvspace_persp( - const rctf *bucket_bounds, - const float *v1coSS, const float *v2coSS, const float *v3coSS, - const float *uv1co, const float *uv2co, const float *uv3co, - float bucket_bounds_uv[4][2], - const int flip - ) +static void rect_to_uvspace_persp(const rctf *bucket_bounds, + const float *v1coSS, + const float *v2coSS, + const float *v3coSS, + const float *uv1co, + const float *uv2co, + const 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; - barycentric_weights_v2_persp(v1coSS, v2coSS, v3coSS, uv, w); - interp_v2_v2v2v2(bucket_bounds_uv[flip ? 3 : 0], uv1co, uv2co, uv3co, w); - - //uv[0] = bucket_bounds->xmax; // set above - uv[1] = bucket_bounds->ymax; - barycentric_weights_v2_persp(v1coSS, v2coSS, v3coSS, uv, w); - interp_v2_v2v2v2(bucket_bounds_uv[flip ? 2 : 1], uv1co, uv2co, uv3co, w); - - uv[0] = bucket_bounds->xmin; - //uv[1] = bucket_bounds->ymax; // set above - barycentric_weights_v2_persp(v1coSS, v2coSS, v3coSS, uv, w); - interp_v2_v2v2v2(bucket_bounds_uv[flip ? 1 : 2], uv1co, uv2co, uv3co, w); - - //uv[0] = bucket_bounds->xmin; // set above - uv[1] = bucket_bounds->ymin; - barycentric_weights_v2_persp(v1coSS, v2coSS, v3coSS, uv, w); - interp_v2_v2v2v2(bucket_bounds_uv[flip ? 0 : 3], uv1co, uv2co, uv3co, w); + float uv[2]; + float w[3]; + + /* get the UV space bounding box */ + uv[0] = bucket_bounds->xmax; + uv[1] = bucket_bounds->ymin; + barycentric_weights_v2_persp(v1coSS, v2coSS, v3coSS, uv, w); + interp_v2_v2v2v2(bucket_bounds_uv[flip ? 3 : 0], uv1co, uv2co, uv3co, w); + + //uv[0] = bucket_bounds->xmax; // set above + uv[1] = bucket_bounds->ymax; + barycentric_weights_v2_persp(v1coSS, v2coSS, v3coSS, uv, w); + interp_v2_v2v2v2(bucket_bounds_uv[flip ? 2 : 1], uv1co, uv2co, uv3co, w); + + uv[0] = bucket_bounds->xmin; + //uv[1] = bucket_bounds->ymax; // set above + barycentric_weights_v2_persp(v1coSS, v2coSS, v3coSS, uv, w); + interp_v2_v2v2v2(bucket_bounds_uv[flip ? 1 : 2], uv1co, uv2co, uv3co, w); + + //uv[0] = bucket_bounds->xmin; // set above + uv[1] = bucket_bounds->ymin; + barycentric_weights_v2_persp(v1coSS, v2coSS, v3coSS, uv, w); + interp_v2_v2v2v2(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 */ @@ -2305,12 +2349,12 @@ static void rect_to_uvspace_persp( #if 0 static float angle_2d_clockwise(const float p1[2], const float p2[2], const float p3[2]) { - float v1[2], v2[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]; + 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 -atan2f(v1[0] * v2[1] - v1[1] * v2[0], v1[0] * v2[0] + v1[1] * v2[1]); + return -atan2f(v1[0] * v2[1] - v1[1] * v2[0], v1[0] * v2[0] + v1[1] * v2[1]); } #endif @@ -2323,13 +2367,10 @@ static float angle_2d_clockwise(const float p1[2], const float p2[2], const floa /* limit must be a fraction over 1.0f */ static bool IsectPT2Df_limit( - const float pt[2], - const float v1[2], const float v2[2], const float v3[2], - const float limit) + const float pt[2], const float v1[2], const float v2[2], const float v3[2], const float limit) { - return ((area_tri_v2(pt, v1, v2) + - area_tri_v2(pt, v2, v3) + - area_tri_v2(pt, v3, v1)) / (area_tri_v2(v1, v2, v3))) < limit; + return ((area_tri_v2(pt, v1, v2) + area_tri_v2(pt, v2, v3) + area_tri_v2(pt, v3, v1)) / + (area_tri_v2(v1, v2, v3))) < limit; } /* Clip the face by a bucket and set the uv-space bucket_bounds_uv @@ -2337,360 +2378,445 @@ static bool IsectPT2Df_limit( * */ static int float_z_sort_flip(const void *p1, const void *p2) { - return (((float *)p1)[2] < ((float *)p2)[2] ? 1 : -1); + 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); + return (((float *)p1)[2] < ((float *)p2)[2] ? -1 : 1); } /* assumes one point is within the rectangle */ -static bool line_rect_clip( - const rctf *rect, - const float l1[4], const float l2[4], - const float uv1[2], const float uv2[2], - float uv[2], bool is_ortho) +static bool line_rect_clip(const rctf *rect, + const float l1[4], + const float l2[4], + const float uv1[2], + const float uv2[2], + float uv[2], + bool is_ortho) { - float min = FLT_MAX, tmp; - float xlen = l2[0] - l1[0]; - float ylen = l2[1] - l1[1]; - - /* 0.1 might seem too much, but remember, this is pixels! */ - if (xlen > 0.1f) { - if ((l1[0] - rect->xmin) * (l2[0] - rect->xmin) <= 0) { - tmp = rect->xmin; - min = min_ff((tmp - l1[0]) / xlen, min); - } - else if ((l1[0] - rect->xmax) * (l2[0] - rect->xmax) < 0) { - tmp = rect->xmax; - min = min_ff((tmp - l1[0]) / xlen, min); - } - } - - if (ylen > 0.1f) { - if ((l1[1] - rect->ymin) * (l2[1] - rect->ymin) <= 0) { - tmp = rect->ymin; - min = min_ff((tmp - l1[1]) / ylen, min); - } - else if ((l1[1] - rect->ymax) * (l2[1] - rect->ymax) < 0) { - tmp = rect->ymax; - min = min_ff((tmp - l1[1]) / ylen, min); - } - } - - if (min == FLT_MAX) - return false; - - tmp = (is_ortho) ? 1.0f : (l1[3] + min * (l2[3] - l1[3])); - - uv[0] = (uv1[0] + min / tmp * (uv2[0] - uv1[0])); - uv[1] = (uv1[1] + min / tmp * (uv2[1] - uv1[1])); - - return true; + float min = FLT_MAX, tmp; + float xlen = l2[0] - l1[0]; + float ylen = l2[1] - l1[1]; + + /* 0.1 might seem too much, but remember, this is pixels! */ + if (xlen > 0.1f) { + if ((l1[0] - rect->xmin) * (l2[0] - rect->xmin) <= 0) { + tmp = rect->xmin; + min = min_ff((tmp - l1[0]) / xlen, min); + } + else if ((l1[0] - rect->xmax) * (l2[0] - rect->xmax) < 0) { + tmp = rect->xmax; + min = min_ff((tmp - l1[0]) / xlen, min); + } + } + + if (ylen > 0.1f) { + if ((l1[1] - rect->ymin) * (l2[1] - rect->ymin) <= 0) { + tmp = rect->ymin; + min = min_ff((tmp - l1[1]) / ylen, min); + } + else if ((l1[1] - rect->ymax) * (l2[1] - rect->ymax) < 0) { + tmp = rect->ymax; + min = min_ff((tmp - l1[1]) / ylen, min); + } + } + + if (min == FLT_MAX) + return false; + + tmp = (is_ortho) ? 1.0f : (l1[3] + min * (l2[3] - l1[3])); + + uv[0] = (uv1[0] + min / tmp * (uv2[0] - uv1[0])); + uv[1] = (uv1[1] + min / tmp * (uv2[1] - uv1[1])); + + return true; } - -static void project_bucket_clip_face( - const bool is_ortho, const bool is_flip_object, - const rctf *cliprect, - const rctf *bucket_bounds, - const float *v1coSS, const float *v2coSS, const float *v3coSS, - const float *uv1co, const float *uv2co, const float *uv3co, - float bucket_bounds_uv[8][2], - int *tot, bool cull) +static void project_bucket_clip_face(const bool is_ortho, + const bool is_flip_object, + const rctf *cliprect, + const rctf *bucket_bounds, + const float *v1coSS, + const float *v2coSS, + const float *v3coSS, + const float *uv1co, + const float *uv2co, + const float *uv3co, + float bucket_bounds_uv[8][2], + int *tot, + bool cull) { - int inside_bucket_flag = 0; - int inside_face_flag = 0; - int flip; - bool collinear = false; - - float bucket_bounds_ss[4][2]; - - /* detect pathological case where face the three vertices are almost collinear in screen space. - * mostly those will be culled but when flood filling or with - * smooth shading it's a possibility */ - if (min_fff(dist_squared_to_line_v2(v1coSS, v2coSS, v3coSS), - dist_squared_to_line_v2(v2coSS, v3coSS, v1coSS), - dist_squared_to_line_v2(v3coSS, v1coSS, v2coSS)) < PROJ_PIXEL_TOLERANCE) - { - collinear = true; - } - - /* get the UV space bounding box */ - inside_bucket_flag |= BLI_rctf_isect_pt_v(bucket_bounds, v1coSS); - inside_bucket_flag |= BLI_rctf_isect_pt_v(bucket_bounds, v2coSS) << 1; - inside_bucket_flag |= BLI_rctf_isect_pt_v(bucket_bounds, v3coSS) << 2; - - if (inside_bucket_flag == ISECT_ALL3) { - /* is_flip_object is used here because we use the face winding */ - flip = (((line_point_side_v2(v1coSS, v2coSS, v3coSS) > 0.0f) != is_flip_object) != - (line_point_side_v2(uv1co, uv2co, uv3co) > 0.0f)); - - /* all screenspace points are inside the bucket bounding box, - * this means we don't need to clip and can simply return the UVs */ - if (flip) { /* facing the back? */ - copy_v2_v2(bucket_bounds_uv[0], uv3co); - copy_v2_v2(bucket_bounds_uv[1], uv2co); - copy_v2_v2(bucket_bounds_uv[2], uv1co); - } - else { - copy_v2_v2(bucket_bounds_uv[0], uv1co); - copy_v2_v2(bucket_bounds_uv[1], uv2co); - copy_v2_v2(bucket_bounds_uv[2], uv3co); - } - - *tot = 3; - return; - } - /* Handle pathological case here, - * no need for further intersections below since triangle area is almost zero. */ - if (collinear) { - int flag; - - (*tot) = 0; - - if (cull) - return; - - if (inside_bucket_flag & ISECT_1) { copy_v2_v2(bucket_bounds_uv[*tot], uv1co); (*tot)++; } - - flag = inside_bucket_flag & (ISECT_1 | ISECT_2); - if (flag && flag != (ISECT_1 | ISECT_2)) { - if (line_rect_clip(bucket_bounds, v1coSS, v2coSS, uv1co, uv2co, bucket_bounds_uv[*tot], is_ortho)) - (*tot)++; - } - - if (inside_bucket_flag & ISECT_2) { copy_v2_v2(bucket_bounds_uv[*tot], uv2co); (*tot)++; } - - flag = inside_bucket_flag & (ISECT_2 | ISECT_3); - if (flag && flag != (ISECT_2 | ISECT_3)) { - if (line_rect_clip(bucket_bounds, v2coSS, v3coSS, uv2co, uv3co, bucket_bounds_uv[*tot], is_ortho)) - (*tot)++; - } - - if (inside_bucket_flag & ISECT_3) { copy_v2_v2(bucket_bounds_uv[*tot], uv3co); (*tot)++; } - - flag = inside_bucket_flag & (ISECT_3 | ISECT_1); - if (flag && flag != (ISECT_3 | ISECT_1)) { - if (line_rect_clip(bucket_bounds, v3coSS, v1coSS, uv3co, uv1co, bucket_bounds_uv[*tot], is_ortho)) - (*tot)++; - } - - if ((*tot) < 3) { - /* no intersections to speak of, but more probable is that all face is just outside the - * rectangle and culled due to float precision issues. Since above tests have failed, - * just dump triangle as is for painting */ - *tot = 0; - copy_v2_v2(bucket_bounds_uv[*tot], uv1co); (*tot)++; - copy_v2_v2(bucket_bounds_uv[*tot], uv2co); (*tot)++; - copy_v2_v2(bucket_bounds_uv[*tot], uv3co); (*tot)++; - return; - } - - 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); - - flip = ((line_point_side_v2(v1coSS, v2coSS, v3coSS) > 0.0f) != - (line_point_side_v2(uv1co, uv2co, uv3co) > 0.0f)); - - 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 */ - - /* The 3rd float is used to store angle for qsort(), NOT as a Z location */ - float isectVCosSS[8][3]; - float v1_clipSS[2], v2_clipSS[2]; - float w[3]; - - /* calc center */ - float cent[2] = {0.0f, 0.0f}; - /*float up[2] = {0.0f, 1.0f};*/ - int i; - bool doubles; - - (*tot) = 0; - - if (inside_face_flag & ISECT_1) { copy_v2_v2(isectVCosSS[*tot], bucket_bounds_ss[0]); (*tot)++; } - if (inside_face_flag & ISECT_2) { copy_v2_v2(isectVCosSS[*tot], bucket_bounds_ss[1]); (*tot)++; } - if (inside_face_flag & ISECT_3) { copy_v2_v2(isectVCosSS[*tot], bucket_bounds_ss[2]); (*tot)++; } - if (inside_face_flag & ISECT_4) { copy_v2_v2(isectVCosSS[*tot], bucket_bounds_ss[3]); (*tot)++; } - - if (inside_bucket_flag & ISECT_1) { copy_v2_v2(isectVCosSS[*tot], v1coSS); (*tot)++; } - if (inside_bucket_flag & ISECT_2) { copy_v2_v2(isectVCosSS[*tot], v2coSS); (*tot)++; } - if (inside_bucket_flag & ISECT_3) { copy_v2_v2(isectVCosSS[*tot], v3coSS); (*tot)++; } - - if ((inside_bucket_flag & (ISECT_1 | ISECT_2)) != (ISECT_1 | ISECT_2)) { - if (line_clip_rect2f(cliprect, bucket_bounds, v1coSS, v2coSS, v1_clipSS, v2_clipSS)) { - if ((inside_bucket_flag & ISECT_1) == 0) { copy_v2_v2(isectVCosSS[*tot], v1_clipSS); (*tot)++; } - if ((inside_bucket_flag & ISECT_2) == 0) { copy_v2_v2(isectVCosSS[*tot], v2_clipSS); (*tot)++; } - } - } - - if ((inside_bucket_flag & (ISECT_2 | ISECT_3)) != (ISECT_2 | ISECT_3)) { - if (line_clip_rect2f(cliprect, bucket_bounds, v2coSS, v3coSS, v1_clipSS, v2_clipSS)) { - if ((inside_bucket_flag & ISECT_2) == 0) { copy_v2_v2(isectVCosSS[*tot], v1_clipSS); (*tot)++; } - if ((inside_bucket_flag & ISECT_3) == 0) { copy_v2_v2(isectVCosSS[*tot], v2_clipSS); (*tot)++; } - } - } - - if ((inside_bucket_flag & (ISECT_3 | ISECT_1)) != (ISECT_3 | ISECT_1)) { - if (line_clip_rect2f(cliprect, bucket_bounds, v3coSS, v1coSS, v1_clipSS, v2_clipSS)) { - if ((inside_bucket_flag & ISECT_3) == 0) { copy_v2_v2(isectVCosSS[*tot], v1_clipSS); (*tot)++; } - if ((inside_bucket_flag & ISECT_1) == 0) { copy_v2_v2(isectVCosSS[*tot], v2_clipSS); (*tot)++; } - } - } - - - if ((*tot) < 3) { /* no intersections to speak of */ - *tot = 0; - return; - } - - /* 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]); - } + int inside_bucket_flag = 0; + int inside_face_flag = 0; + int flip; + bool collinear = false; + + float bucket_bounds_ss[4][2]; + + /* detect pathological case where face the three vertices are almost collinear in screen space. + * mostly those will be culled but when flood filling or with + * smooth shading it's a possibility */ + if (min_fff(dist_squared_to_line_v2(v1coSS, v2coSS, v3coSS), + dist_squared_to_line_v2(v2coSS, v3coSS, v1coSS), + dist_squared_to_line_v2(v3coSS, v1coSS, v2coSS)) < PROJ_PIXEL_TOLERANCE) { + collinear = true; + } + + /* get the UV space bounding box */ + inside_bucket_flag |= BLI_rctf_isect_pt_v(bucket_bounds, v1coSS); + inside_bucket_flag |= BLI_rctf_isect_pt_v(bucket_bounds, v2coSS) << 1; + inside_bucket_flag |= BLI_rctf_isect_pt_v(bucket_bounds, v3coSS) << 2; + + if (inside_bucket_flag == ISECT_ALL3) { + /* is_flip_object is used here because we use the face winding */ + flip = (((line_point_side_v2(v1coSS, v2coSS, v3coSS) > 0.0f) != is_flip_object) != + (line_point_side_v2(uv1co, uv2co, uv3co) > 0.0f)); + + /* all screenspace points are inside the bucket bounding box, + * this means we don't need to clip and can simply return the UVs */ + if (flip) { /* facing the back? */ + copy_v2_v2(bucket_bounds_uv[0], uv3co); + copy_v2_v2(bucket_bounds_uv[1], uv2co); + copy_v2_v2(bucket_bounds_uv[2], uv1co); + } + else { + copy_v2_v2(bucket_bounds_uv[0], uv1co); + copy_v2_v2(bucket_bounds_uv[1], uv2co); + copy_v2_v2(bucket_bounds_uv[2], uv3co); + } + + *tot = 3; + return; + } + /* Handle pathological case here, + * no need for further intersections below since triangle area is almost zero. */ + if (collinear) { + int flag; + + (*tot) = 0; + + if (cull) + return; + + if (inside_bucket_flag & ISECT_1) { + copy_v2_v2(bucket_bounds_uv[*tot], uv1co); + (*tot)++; + } + + flag = inside_bucket_flag & (ISECT_1 | ISECT_2); + if (flag && flag != (ISECT_1 | ISECT_2)) { + if (line_rect_clip( + bucket_bounds, v1coSS, v2coSS, uv1co, uv2co, bucket_bounds_uv[*tot], is_ortho)) + (*tot)++; + } + + if (inside_bucket_flag & ISECT_2) { + copy_v2_v2(bucket_bounds_uv[*tot], uv2co); + (*tot)++; + } + + flag = inside_bucket_flag & (ISECT_2 | ISECT_3); + if (flag && flag != (ISECT_2 | ISECT_3)) { + if (line_rect_clip( + bucket_bounds, v2coSS, v3coSS, uv2co, uv3co, bucket_bounds_uv[*tot], is_ortho)) + (*tot)++; + } + + if (inside_bucket_flag & ISECT_3) { + copy_v2_v2(bucket_bounds_uv[*tot], uv3co); + (*tot)++; + } + + flag = inside_bucket_flag & (ISECT_3 | ISECT_1); + if (flag && flag != (ISECT_3 | ISECT_1)) { + if (line_rect_clip( + bucket_bounds, v3coSS, v1coSS, uv3co, uv1co, bucket_bounds_uv[*tot], is_ortho)) + (*tot)++; + } + + if ((*tot) < 3) { + /* no intersections to speak of, but more probable is that all face is just outside the + * rectangle and culled due to float precision issues. Since above tests have failed, + * just dump triangle as is for painting */ + *tot = 0; + copy_v2_v2(bucket_bounds_uv[*tot], uv1co); + (*tot)++; + copy_v2_v2(bucket_bounds_uv[*tot], uv2co); + (*tot)++; + copy_v2_v2(bucket_bounds_uv[*tot], uv3co); + (*tot)++; + return; + } + + 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); + + flip = ((line_point_side_v2(v1coSS, v2coSS, v3coSS) > 0.0f) != + (line_point_side_v2(uv1co, uv2co, uv3co) > 0.0f)); + + 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 */ + + /* The 3rd float is used to store angle for qsort(), NOT as a Z location */ + float isectVCosSS[8][3]; + float v1_clipSS[2], v2_clipSS[2]; + float w[3]; + + /* calc center */ + float cent[2] = {0.0f, 0.0f}; + /*float up[2] = {0.0f, 1.0f};*/ + int i; + bool doubles; + + (*tot) = 0; + + if (inside_face_flag & ISECT_1) { + copy_v2_v2(isectVCosSS[*tot], bucket_bounds_ss[0]); + (*tot)++; + } + if (inside_face_flag & ISECT_2) { + copy_v2_v2(isectVCosSS[*tot], bucket_bounds_ss[1]); + (*tot)++; + } + if (inside_face_flag & ISECT_3) { + copy_v2_v2(isectVCosSS[*tot], bucket_bounds_ss[2]); + (*tot)++; + } + if (inside_face_flag & ISECT_4) { + copy_v2_v2(isectVCosSS[*tot], bucket_bounds_ss[3]); + (*tot)++; + } + + if (inside_bucket_flag & ISECT_1) { + copy_v2_v2(isectVCosSS[*tot], v1coSS); + (*tot)++; + } + if (inside_bucket_flag & ISECT_2) { + copy_v2_v2(isectVCosSS[*tot], v2coSS); + (*tot)++; + } + if (inside_bucket_flag & ISECT_3) { + copy_v2_v2(isectVCosSS[*tot], v3coSS); + (*tot)++; + } + + if ((inside_bucket_flag & (ISECT_1 | ISECT_2)) != (ISECT_1 | ISECT_2)) { + if (line_clip_rect2f(cliprect, bucket_bounds, v1coSS, v2coSS, v1_clipSS, v2_clipSS)) { + if ((inside_bucket_flag & ISECT_1) == 0) { + copy_v2_v2(isectVCosSS[*tot], v1_clipSS); + (*tot)++; + } + if ((inside_bucket_flag & ISECT_2) == 0) { + copy_v2_v2(isectVCosSS[*tot], v2_clipSS); + (*tot)++; + } + } + } + + if ((inside_bucket_flag & (ISECT_2 | ISECT_3)) != (ISECT_2 | ISECT_3)) { + if (line_clip_rect2f(cliprect, bucket_bounds, v2coSS, v3coSS, v1_clipSS, v2_clipSS)) { + if ((inside_bucket_flag & ISECT_2) == 0) { + copy_v2_v2(isectVCosSS[*tot], v1_clipSS); + (*tot)++; + } + if ((inside_bucket_flag & ISECT_3) == 0) { + copy_v2_v2(isectVCosSS[*tot], v2_clipSS); + (*tot)++; + } + } + } + + if ((inside_bucket_flag & (ISECT_3 | ISECT_1)) != (ISECT_3 | ISECT_1)) { + if (line_clip_rect2f(cliprect, bucket_bounds, v3coSS, v1coSS, v1_clipSS, v2_clipSS)) { + if ((inside_bucket_flag & ISECT_3) == 0) { + copy_v2_v2(isectVCosSS[*tot], v1_clipSS); + (*tot)++; + } + if ((inside_bucket_flag & ISECT_1) == 0) { + copy_v2_v2(isectVCosSS[*tot], v2_clipSS); + (*tot)++; + } + } + } + + if ((*tot) < 3) { /* no intersections to speak of */ + *tot = 0; + return; + } + + /* 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 - /* Abuse this var for the loop below */ - v1_clipSS[0] = cent[0]; - 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] = atan2f(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); - - doubles = true; - while (doubles == true) { - doubles = false; - - for (i = 0; i < (*tot); i++) { - if (fabsf(isectVCosSS[(i + 1) % *tot][0] - isectVCosSS[i][0]) < PROJ_PIXEL_TOLERANCE && - fabsf(isectVCosSS[(i + 1) % *tot][1] - isectVCosSS[i][1]) < PROJ_PIXEL_TOLERANCE) - { - int j; - for (j = i; j < (*tot) - 1; j++) { - isectVCosSS[j][0] = isectVCosSS[j + 1][0]; - isectVCosSS[j][1] = isectVCosSS[j + 1][1]; - } - /* keep looking for more doubles */ - doubles = true; - (*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++) { - barycentric_weights_v2(v1coSS, v2coSS, v3coSS, isectVCosSS[i], w); - interp_v2_v2v2v2(bucket_bounds_uv[i], uv1co, uv2co, uv3co, w); - } - } - else { - for (i = 0; i < (*tot); i++) { - barycentric_weights_v2_persp(v1coSS, v2coSS, v3coSS, isectVCosSS[i], w); - interp_v2_v2v2v2(bucket_bounds_uv[i], uv1co, uv2co, uv3co, w); - } - } - } + /* Abuse this var for the loop below */ + v1_clipSS[0] = cent[0]; + 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] = atan2f(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); + + doubles = true; + while (doubles == true) { + doubles = false; + + for (i = 0; i < (*tot); i++) { + if (fabsf(isectVCosSS[(i + 1) % *tot][0] - isectVCosSS[i][0]) < PROJ_PIXEL_TOLERANCE && + fabsf(isectVCosSS[(i + 1) % *tot][1] - isectVCosSS[i][1]) < PROJ_PIXEL_TOLERANCE) { + int j; + for (j = i; j < (*tot) - 1; j++) { + isectVCosSS[j][0] = isectVCosSS[j + 1][0]; + isectVCosSS[j][1] = isectVCosSS[j + 1][1]; + } + /* keep looking for more doubles */ + doubles = true; + (*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++) { + barycentric_weights_v2(v1coSS, v2coSS, v3coSS, isectVCosSS[i], w); + interp_v2_v2v2v2(bucket_bounds_uv[i], uv1co, uv2co, uv3co, w); + } + } + else { + for (i = 0; i < (*tot); i++) { + barycentric_weights_v2_persp(v1coSS, v2coSS, v3coSS, isectVCosSS[i], w); + interp_v2_v2v2v2(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"); - } + /* 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 } @@ -2740,7 +2866,6 @@ static void project_bucket_clip_face( * main() */ - #undef ISECT_1 #undef ISECT_2 #undef ISECT_3 @@ -2748,35 +2873,32 @@ static void project_bucket_clip_face( #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 (line_point_side_v2 > 0.0f) cases */ static bool IsectPoly2Df(const float pt[2], float uv[][2], const int tot) { - int i; - if (line_point_side_v2(uv[tot - 1], uv[0], pt) < 0.0f) - return 0; + int i; + if (line_point_side_v2(uv[tot - 1], uv[0], pt) < 0.0f) + return 0; - for (i = 1; i < tot; i++) { - if (line_point_side_v2(uv[i - 1], uv[i], pt) < 0.0f) - return 0; + for (i = 1; i < tot; i++) { + if (line_point_side_v2(uv[i - 1], uv[i], pt) < 0.0f) + return 0; + } - } - - return 1; + return 1; } static bool IsectPoly2Df_twoside(const float pt[2], float uv[][2], const int tot) { - int i; - bool side = (line_point_side_v2(uv[tot - 1], uv[0], pt) > 0.0f); - - for (i = 1; i < tot; i++) { - if ((line_point_side_v2(uv[i - 1], uv[i], pt) > 0.0f) != side) - return 0; + int i; + bool side = (line_point_side_v2(uv[tot - 1], uv[0], pt) > 0.0f); - } + for (i = 1; i < tot; i++) { + if ((line_point_side_v2(uv[i - 1], uv[i], pt) > 0.0f) != side) + return 0; + } - return 1; + return 1; } /* One of the most important function for projection painting, @@ -2784,1824 +2906,1853 @@ static bool IsectPoly2Df_twoside(const float pt[2], float uv[][2], const int tot * * 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 tri_index, const int image_index, - const rctf *clip_rect, const rctf *bucket_bounds, ImBuf *ibuf, ImBuf **tmpibuf) +static void project_paint_face_init(const ProjPaintState *ps, + const int thread_index, + const int bucket_index, + const int tri_index, + const int image_index, + const rctf *clip_rect, + const rctf *bucket_bounds, + ImBuf *ibuf, + ImBuf **tmpibuf) { - /* 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]; - bool threaded = (ps->thread_tot > 1); - - TileInfo tinf = { - ps->tile_lock, - ps->do_masking, - IMAPAINT_TILE_NUMBER(ibuf->x), - tmpibuf, - ps->projImages + image_index, - }; - - const MLoopTri *lt = &ps->mlooptri_eval[tri_index]; - const int lt_vtri[3] = { PS_LOOPTRI_AS_VERT_INDEX_3(ps, lt) }; - const float *lt_tri_uv[3] = { PS_LOOPTRI_AS_UV_3(ps->poly_to_loop_uv, lt) }; - - /* UV/pixel seeking data */ - /* Image X/Y-Pixel */ - int x, y; - float mask; - /* Image floating point UV - same as x, y but from 0.0-1.0 */ - float uv[2]; - - /* vert co screen-space, these will be assigned to lt_vtri[0-2] */ - const float *v1coSS, *v2coSS, *v3coSS; - - /* vertex screenspace coords */ - const float *vCo[3]; - - float w[3], wco[3]; - - /* for convenience only, these will be assigned to lt_tri_uv[0],1,2 or lt_tri_uv[0],2,3 */ - float *uv1co, *uv2co, *uv3co; - float pixelScreenCo[4]; - bool do_3d_mapping = ps->brush->mtex.brush_map_mode == MTEX_MAP_MODE_3D; - - /* ispace bounds */ - rcti bounds_px; - /* vars for getting uvspace bounds */ - - /* bucket bounds in UV space so we can init pixels only for this face, */ - float lt_uv_pxoffset[3][2]; - float xhalfpx, yhalfpx; - const float ibuf_xf = (float)ibuf->x, ibuf_yf = (float)ibuf->y; - - /* for early loop exit */ - int has_x_isect = 0, has_isect = 0; - - float uv_clip[8][2]; - int uv_clip_tot; - const bool is_ortho = ps->is_ortho; - const bool is_flip_object = ps->is_flip_object; - const bool do_backfacecull = ps->do_backfacecull; - const bool do_clip = ps->rv3d ? ps->rv3d->rflag & RV3D_CLIPPING : 0; - - vCo[0] = ps->mvert_eval[lt_vtri[0]].co; - vCo[1] = ps->mvert_eval[lt_vtri[1]].co; - vCo[2] = ps->mvert_eval[lt_vtri[2]].co; - - - /* Use lt_uv_pxoffset instead of lt_tri_uv so we can offset the UV half a pixel - * this is done so we can avoid offsetting all the pixels by 0.5 which causes - * problems when wrapping negative coords */ - xhalfpx = (0.5f + (PROJ_PIXEL_TOLERANCE * (1.0f / 3.0f))) / ibuf_xf; - yhalfpx = (0.5f + (PROJ_PIXEL_TOLERANCE * (1.0f / 4.0f))) / ibuf_yf; - - /* Note about (PROJ_GEOM_TOLERANCE/x) above... - * Needed to add this offset since UV coords are often quads aligned to pixels. - * In this case pixels can be exactly between 2 triangles causing nasty - * artifacts. - * - * This workaround can be removed and painting will still work on most cases - * but since the first thing most people try is painting onto a quad- better make it work. - */ - - lt_uv_pxoffset[0][0] = lt_tri_uv[0][0] - xhalfpx; - lt_uv_pxoffset[0][1] = lt_tri_uv[0][1] - yhalfpx; - - lt_uv_pxoffset[1][0] = lt_tri_uv[1][0] - xhalfpx; - lt_uv_pxoffset[1][1] = lt_tri_uv[1][1] - yhalfpx; - - lt_uv_pxoffset[2][0] = lt_tri_uv[2][0] - xhalfpx; - lt_uv_pxoffset[2][1] = lt_tri_uv[2][1] - yhalfpx; - - { - uv1co = lt_uv_pxoffset[0]; // was lt_tri_uv[i1]; - uv2co = lt_uv_pxoffset[1]; // was lt_tri_uv[i2]; - uv3co = lt_uv_pxoffset[2]; // was lt_tri_uv[i3]; - - v1coSS = ps->screenCoords[lt_vtri[0]]; - v2coSS = ps->screenCoords[lt_vtri[1]]; - v3coSS = ps->screenCoords[lt_vtri[2]]; - - /* This function gives is a concave polyline in UV space from the clipped tri*/ - project_bucket_clip_face( - is_ortho, is_flip_object, - clip_rect, bucket_bounds, - v1coSS, v2coSS, v3coSS, - uv1co, uv2co, uv3co, - uv_clip, &uv_clip_tot, - do_backfacecull || ps->do_occlude); - - /* sometimes this happens, better just allow for 8 intersectiosn even though there should be max 6 */ + /* 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]; + bool threaded = (ps->thread_tot > 1); + + TileInfo tinf = { + ps->tile_lock, + ps->do_masking, + IMAPAINT_TILE_NUMBER(ibuf->x), + tmpibuf, + ps->projImages + image_index, + }; + + const MLoopTri *lt = &ps->mlooptri_eval[tri_index]; + const int lt_vtri[3] = {PS_LOOPTRI_AS_VERT_INDEX_3(ps, lt)}; + const float *lt_tri_uv[3] = {PS_LOOPTRI_AS_UV_3(ps->poly_to_loop_uv, lt)}; + + /* UV/pixel seeking data */ + /* Image X/Y-Pixel */ + int x, y; + float mask; + /* Image floating point UV - same as x, y but from 0.0-1.0 */ + float uv[2]; + + /* vert co screen-space, these will be assigned to lt_vtri[0-2] */ + const float *v1coSS, *v2coSS, *v3coSS; + + /* vertex screenspace coords */ + const float *vCo[3]; + + float w[3], wco[3]; + + /* for convenience only, these will be assigned to lt_tri_uv[0],1,2 or lt_tri_uv[0],2,3 */ + float *uv1co, *uv2co, *uv3co; + float pixelScreenCo[4]; + bool do_3d_mapping = ps->brush->mtex.brush_map_mode == MTEX_MAP_MODE_3D; + + /* ispace bounds */ + rcti bounds_px; + /* vars for getting uvspace bounds */ + + /* bucket bounds in UV space so we can init pixels only for this face, */ + float lt_uv_pxoffset[3][2]; + float xhalfpx, yhalfpx; + const float ibuf_xf = (float)ibuf->x, ibuf_yf = (float)ibuf->y; + + /* for early loop exit */ + int has_x_isect = 0, has_isect = 0; + + float uv_clip[8][2]; + int uv_clip_tot; + const bool is_ortho = ps->is_ortho; + const bool is_flip_object = ps->is_flip_object; + const bool do_backfacecull = ps->do_backfacecull; + const bool do_clip = ps->rv3d ? ps->rv3d->rflag & RV3D_CLIPPING : 0; + + vCo[0] = ps->mvert_eval[lt_vtri[0]].co; + vCo[1] = ps->mvert_eval[lt_vtri[1]].co; + vCo[2] = ps->mvert_eval[lt_vtri[2]].co; + + /* Use lt_uv_pxoffset instead of lt_tri_uv so we can offset the UV half a pixel + * this is done so we can avoid offsetting all the pixels by 0.5 which causes + * problems when wrapping negative coords */ + xhalfpx = (0.5f + (PROJ_PIXEL_TOLERANCE * (1.0f / 3.0f))) / ibuf_xf; + yhalfpx = (0.5f + (PROJ_PIXEL_TOLERANCE * (1.0f / 4.0f))) / ibuf_yf; + + /* Note about (PROJ_GEOM_TOLERANCE/x) above... + * Needed to add this offset since UV coords are often quads aligned to pixels. + * In this case pixels can be exactly between 2 triangles causing nasty + * artifacts. + * + * This workaround can be removed and painting will still work on most cases + * but since the first thing most people try is painting onto a quad- better make it work. + */ + + lt_uv_pxoffset[0][0] = lt_tri_uv[0][0] - xhalfpx; + lt_uv_pxoffset[0][1] = lt_tri_uv[0][1] - yhalfpx; + + lt_uv_pxoffset[1][0] = lt_tri_uv[1][0] - xhalfpx; + lt_uv_pxoffset[1][1] = lt_tri_uv[1][1] - yhalfpx; + + lt_uv_pxoffset[2][0] = lt_tri_uv[2][0] - xhalfpx; + lt_uv_pxoffset[2][1] = lt_tri_uv[2][1] - yhalfpx; + + { + uv1co = lt_uv_pxoffset[0]; // was lt_tri_uv[i1]; + uv2co = lt_uv_pxoffset[1]; // was lt_tri_uv[i2]; + uv3co = lt_uv_pxoffset[2]; // was lt_tri_uv[i3]; + + v1coSS = ps->screenCoords[lt_vtri[0]]; + v2coSS = ps->screenCoords[lt_vtri[1]]; + v3coSS = ps->screenCoords[lt_vtri[2]]; + + /* This function gives is a concave polyline in UV space from the clipped tri*/ + project_bucket_clip_face(is_ortho, + is_flip_object, + clip_rect, + bucket_bounds, + v1coSS, + v2coSS, + v3coSS, + uv1co, + uv2co, + uv3co, + uv_clip, + &uv_clip_tot, + do_backfacecull || ps->do_occlude); + + /* sometimes this happens, better just allow for 8 intersectiosn even though there should be max 6 */ #if 0 - if (uv_clip_tot > 6) { - printf("this should never happen! %d\n", uv_clip_tot); - } + if (uv_clip_tot > 6) { + printf("this should never happen! %d\n", uv_clip_tot); + } #endif - if (pixel_bounds_array(uv_clip, &bounds_px, ibuf->x, ibuf->y, uv_clip_tot)) { + if (pixel_bounds_array(uv_clip, &bounds_px, ibuf->x, ibuf->y, uv_clip_tot)) { #if 0 - project_paint_undo_tiles_init( - &bounds_px, ps->projImages + image_index, tmpibuf, - tile_width, threaded, ps->do_masking); + project_paint_undo_tiles_init( + &bounds_px, ps->projImages + image_index, tmpibuf, + tile_width, threaded, ps->do_masking); #endif - /* 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; - /* use pixel offset UV coords instead */ - uv[1] = (float)y / ibuf_yf; - - has_x_isect = 0; - for (x = bounds_px.xmin; x < bounds_px.xmax; x++) { - //uv[0] = (((float)x) + 0.5f) / ibuf->x; - /* use pixel offset UV coords instead */ - uv[0] = (float)x / ibuf_xf; - - /* Note about IsectPoly2Df_twoside, checking the face or uv flipping doesn't work, - * could check the poly direction but better to do this */ - if ((do_backfacecull == true && IsectPoly2Df(uv, uv_clip, uv_clip_tot)) || - (do_backfacecull == false && IsectPoly2Df_twoside(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 pity we need to get the worldspace pixel location here */ - if (do_clip || do_3d_mapping) { - interp_v3_v3v3v3( - wco, - ps->mvert_eval[lt_vtri[0]].co, - ps->mvert_eval[lt_vtri[1]].co, - ps->mvert_eval[lt_vtri[2]].co, - w); - if (do_clip && ED_view3d_clipping_test(ps->rv3d, wco, true)) { - /* Watch out that no code below this needs to run */ - continue; - } - } - - /* 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) == tri_index) { - if ((ps->do_occlude == false) || - !project_bucket_point_occluded(ps, bucketFaceNodes, tri_index, pixelScreenCo)) - { - mask = project_paint_uvpixel_mask(ps, tri_index, w); - - if (mask > 0.0f) { - BLI_linklist_prepend_arena( - bucketPixelNodes, - project_paint_uvpixel_init( - ps, arena, &tinf, x, y, mask, tri_index, - pixelScreenCo, wco, 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; - } + /* 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; + /* use pixel offset UV coords instead */ + uv[1] = (float)y / ibuf_yf; + + has_x_isect = 0; + for (x = bounds_px.xmin; x < bounds_px.xmax; x++) { + //uv[0] = (((float)x) + 0.5f) / ibuf->x; + /* use pixel offset UV coords instead */ + uv[0] = (float)x / ibuf_xf; + + /* Note about IsectPoly2Df_twoside, checking the face or uv flipping doesn't work, + * could check the poly direction but better to do this */ + if ((do_backfacecull == true && IsectPoly2Df(uv, uv_clip, uv_clip_tot)) || + (do_backfacecull == false && IsectPoly2Df_twoside(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 pity we need to get the worldspace pixel location here */ + if (do_clip || do_3d_mapping) { + interp_v3_v3v3v3(wco, + ps->mvert_eval[lt_vtri[0]].co, + ps->mvert_eval[lt_vtri[1]].co, + ps->mvert_eval[lt_vtri[2]].co, + w); + if (do_clip && ED_view3d_clipping_test(ps->rv3d, wco, true)) { + /* Watch out that no code below this needs to run */ + continue; + } + } + + /* 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) == tri_index) { + if ((ps->do_occlude == false) || + !project_bucket_point_occluded(ps, bucketFaceNodes, tri_index, pixelScreenCo)) { + mask = project_paint_uvpixel_mask(ps, tri_index, w); + + if (mask > 0.0f) { + BLI_linklist_prepend_arena( + bucketPixelNodes, + project_paint_uvpixel_init( + ps, arena, &tinf, x, y, mask, tri_index, pixelScreenCo, wco, 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 - } - } - } - + } + } + } #ifndef PROJ_DEBUG_NOSEAMBLEED - if (ps->seam_bleed_px > 0.0f && !(ps->faceSeamFlags[tri_index] & PROJ_FACE_DEGENERATE)) { - int face_seam_flag; - - if (threaded) { - /* Other threads could be modifying these vars. */ - BLI_thread_lock(LOCK_CUSTOM1); - } - - face_seam_flag = ps->faceSeamFlags[tri_index]; - - /* are any of our edges un-initialized? */ - if ((face_seam_flag & PROJ_FACE_SEAM_INIT0) == 0 || - (face_seam_flag & PROJ_FACE_SEAM_INIT1) == 0 || - (face_seam_flag & PROJ_FACE_SEAM_INIT2) == 0) - { - project_face_seams_init(ps, arena, tri_index, 0, true, ibuf->x, ibuf->y); - face_seam_flag = ps->faceSeamFlags[tri_index]; - //printf("seams - %d %d %d %d\n", flag&PROJ_FACE_SEAM0, flag&PROJ_FACE_SEAM1, flag&PROJ_FACE_SEAM2); - } - - if ((face_seam_flag & (PROJ_FACE_SEAM0 | PROJ_FACE_SEAM1 | PROJ_FACE_SEAM2)) == 0) { - - if (threaded) { - /* Other threads could be modifying these vars. */ - BLI_thread_unlock(LOCK_CUSTOM1); - } - - } - else { - /* we have a seam - deal with it! */ - - /* inset face coords. NOTE!!! ScreenSace for ortho, Worldspace in perspective view */ - float insetCos[3][3]; - - /* vertex screenspace coords */ - const float *vCoSS[3]; - - /* Store the screenspace coords of the face, - * clipped by the bucket's screen aligned rectangle. */ - float bucket_clip_edges[2][2]; - float edge_verts_inset_clip[2][3]; - /* face edge pairs - loop throuh these: - * ((0,1), (1,2), (2,3), (3,0)) or ((0,1), (1,2), (2,0)) for a tri */ - int fidx1, fidx2; - - float seam_subsection[4][2]; - float fac1, fac2; - - /* Pixelspace UVs. */ - float lt_puv[3][2]; - - lt_puv[0][0] = lt_uv_pxoffset[0][0] * ibuf->x; - lt_puv[0][1] = lt_uv_pxoffset[0][1] * ibuf->y; - - lt_puv[1][0] = lt_uv_pxoffset[1][0] * ibuf->x; - lt_puv[1][1] = lt_uv_pxoffset[1][1] * ibuf->y; - - lt_puv[2][0] = lt_uv_pxoffset[2][0] * ibuf->x; - lt_puv[2][1] = lt_uv_pxoffset[2][1] * ibuf->y; - - if ((ps->faceSeamFlags[tri_index] & PROJ_FACE_SEAM0) || - (ps->faceSeamFlags[tri_index] & PROJ_FACE_SEAM1) || - (ps->faceSeamFlags[tri_index] & PROJ_FACE_SEAM2)) - { - uv_image_outset(ps, lt_uv_pxoffset, lt_puv, tri_index, ibuf->x, ibuf->y); - } - - /* ps->loopSeamUVs cant be modified when threading, now this is done we can unlock. */ - if (threaded) { - /* Other threads could be modifying these vars */ - BLI_thread_unlock(LOCK_CUSTOM1); - } - - vCoSS[0] = ps->screenCoords[lt_vtri[0]]; - vCoSS[1] = ps->screenCoords[lt_vtri[1]]; - vCoSS[2] = ps->screenCoords[lt_vtri[2]]; - - /* PROJ_FACE_SCALE_SEAM must be slightly less then 1.0f */ - if (is_ortho) { - scale_tri(insetCos, vCoSS, PROJ_FACE_SCALE_SEAM); - } - else { - scale_tri(insetCos, vCo, PROJ_FACE_SCALE_SEAM); - } - - for (fidx1 = 0; fidx1 < 3; fidx1++) { - /* next fidx in the face (0,1,2) -> (1,2,0) */ - fidx2 = (fidx1 == 2) ? 0 : fidx1 + 1; - - if ((face_seam_flag & (1 << fidx1)) && /* 1<<fidx1 -> PROJ_FACE_SEAM# */ - line_clip_rect2f(clip_rect, bucket_bounds, vCoSS[fidx1], vCoSS[fidx2], bucket_clip_edges[0], bucket_clip_edges[1])) - { - /* Avoid div by zero. */ - if (len_squared_v2v2(vCoSS[fidx1], vCoSS[fidx2]) > FLT_EPSILON) { - uint loop_idx = ps->mlooptri_eval[tri_index].tri[fidx1]; - LoopSeamData *seam_data = &ps->loopSeamData[loop_idx]; - float (*seam_uvs)[2] = seam_data->seam_uvs; - - if (is_ortho) { - fac1 = line_point_factor_v2(bucket_clip_edges[0], vCoSS[fidx1], vCoSS[fidx2]); - fac2 = line_point_factor_v2(bucket_clip_edges[1], vCoSS[fidx1], vCoSS[fidx2]); - } - else { - fac1 = screen_px_line_point_factor_v2_persp(ps, bucket_clip_edges[0], vCo[fidx1], vCo[fidx2]); - fac2 = screen_px_line_point_factor_v2_persp(ps, bucket_clip_edges[1], vCo[fidx1], vCo[fidx2]); - } - - interp_v2_v2v2(seam_subsection[0], lt_uv_pxoffset[fidx1], lt_uv_pxoffset[fidx2], fac1); - interp_v2_v2v2(seam_subsection[1], lt_uv_pxoffset[fidx1], lt_uv_pxoffset[fidx2], fac2); - - interp_v2_v2v2(seam_subsection[2], seam_uvs[0], seam_uvs[1], fac2); - interp_v2_v2v2(seam_subsection[3], seam_uvs[0], seam_uvs[1], 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 */ - interp_v3_v3v3(edge_verts_inset_clip[0], insetCos[fidx1], insetCos[fidx2], fac1); - interp_v3_v3v3(edge_verts_inset_clip[1], insetCos[fidx1], insetCos[fidx2], fac2); - - - if (pixel_bounds_uv(seam_subsection, &bounds_px, ibuf->x, ibuf->y)) { - /* 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; - /* use offset uvs instead */ - uv[1] = (float)y / ibuf_yf; - - has_x_isect = 0; - for (x = bounds_px.xmin; x < bounds_px.xmax; x++) { - float puv[2] = {(float)x, (float)y}; - bool in_bounds; - //uv[0] = (((float)x) + 0.5f) / (float)ibuf->x; - /* use offset uvs instead */ - uv[0] = (float)x / ibuf_xf; - - /* test we're inside uvspace bucket and triangle bounds */ - if (equals_v2v2(seam_uvs[0], seam_uvs[1])) { - in_bounds = isect_point_tri_v2(uv, UNPACK3(seam_subsection)); - } - else { - in_bounds = isect_point_quad_v2(uv, UNPACK4(seam_subsection)); - } - - if (in_bounds) { - if ((seam_data->corner_dist_sq[0] > 0.0f) && - (len_squared_v2v2(puv, seam_data->seam_puvs[0]) < seam_data->corner_dist_sq[0]) && - (len_squared_v2v2(puv, lt_puv[fidx1]) > ps->seam_bleed_px_sq)) - { - in_bounds = false; - } - else if ((seam_data->corner_dist_sq[1] > 0.0f) && - (len_squared_v2v2(puv, seam_data->seam_puvs[1]) < seam_data->corner_dist_sq[1]) && - (len_squared_v2v2(puv, lt_puv[fidx2]) > ps->seam_bleed_px_sq)) - { - in_bounds = false; - } - } - - if (in_bounds) { - float pixel_on_edge[4]; - float fac; - - 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); - } - - /* We need the coord of the pixel on the edge, for the occlusion query. */ - fac = resolve_quad_u_v2(uv, UNPACK4(seam_subsection)); - interp_v3_v3v3(pixel_on_edge, edge_verts_inset_clip[0], edge_verts_inset_clip[1], fac); - - if (!is_ortho) { - pixel_on_edge[3] = 1.0f; - /* cast because of const */ - mul_m4_v4((float(*)[4])ps->projectMat, pixel_on_edge); - pixel_on_edge[0] = (float)(ps->winx * 0.5f) + (ps->winx * 0.5f) * pixel_on_edge[0] / pixel_on_edge[3]; - pixel_on_edge[1] = (float)(ps->winy * 0.5f) + (ps->winy * 0.5f) * pixel_on_edge[1] / pixel_on_edge[3]; - /* Use the depth for bucket point occlusion */ - pixel_on_edge[2] = pixel_on_edge[2] / pixel_on_edge[3]; - } - - if ((ps->do_occlude == false) || - !project_bucket_point_occluded(ps, bucketFaceNodes, tri_index, pixel_on_edge)) - { - /* a pity we need to get the worldspace - * pixel location here */ - if (do_clip || do_3d_mapping) { - interp_v3_v3v3v3(wco, vCo[0], vCo[1], vCo[2], w); - - if (do_clip && ED_view3d_clipping_test(ps->rv3d, wco, true)) { - /* Watch out that no code below - * this needs to run */ - continue; - } - } - - mask = project_paint_uvpixel_mask(ps, tri_index, w); - - if (mask > 0.0f) { - BLI_linklist_prepend_arena( - bucketPixelNodes, - project_paint_uvpixel_init(ps, arena, &tinf, x, y, mask, tri_index, - pixelScreenCo, wco, 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 - } - } - } - } - } - } - } + if (ps->seam_bleed_px > 0.0f && !(ps->faceSeamFlags[tri_index] & PROJ_FACE_DEGENERATE)) { + int face_seam_flag; + + if (threaded) { + /* Other threads could be modifying these vars. */ + BLI_thread_lock(LOCK_CUSTOM1); + } + + face_seam_flag = ps->faceSeamFlags[tri_index]; + + /* are any of our edges un-initialized? */ + if ((face_seam_flag & PROJ_FACE_SEAM_INIT0) == 0 || + (face_seam_flag & PROJ_FACE_SEAM_INIT1) == 0 || + (face_seam_flag & PROJ_FACE_SEAM_INIT2) == 0) { + project_face_seams_init(ps, arena, tri_index, 0, true, ibuf->x, ibuf->y); + face_seam_flag = ps->faceSeamFlags[tri_index]; + //printf("seams - %d %d %d %d\n", flag&PROJ_FACE_SEAM0, flag&PROJ_FACE_SEAM1, flag&PROJ_FACE_SEAM2); + } + + if ((face_seam_flag & (PROJ_FACE_SEAM0 | PROJ_FACE_SEAM1 | PROJ_FACE_SEAM2)) == 0) { + + if (threaded) { + /* Other threads could be modifying these vars. */ + BLI_thread_unlock(LOCK_CUSTOM1); + } + } + else { + /* we have a seam - deal with it! */ + + /* inset face coords. NOTE!!! ScreenSace for ortho, Worldspace in perspective view */ + float insetCos[3][3]; + + /* vertex screenspace coords */ + const float *vCoSS[3]; + + /* Store the screenspace coords of the face, + * clipped by the bucket's screen aligned rectangle. */ + float bucket_clip_edges[2][2]; + float edge_verts_inset_clip[2][3]; + /* face edge pairs - loop throuh these: + * ((0,1), (1,2), (2,3), (3,0)) or ((0,1), (1,2), (2,0)) for a tri */ + int fidx1, fidx2; + + float seam_subsection[4][2]; + float fac1, fac2; + + /* Pixelspace UVs. */ + float lt_puv[3][2]; + + lt_puv[0][0] = lt_uv_pxoffset[0][0] * ibuf->x; + lt_puv[0][1] = lt_uv_pxoffset[0][1] * ibuf->y; + + lt_puv[1][0] = lt_uv_pxoffset[1][0] * ibuf->x; + lt_puv[1][1] = lt_uv_pxoffset[1][1] * ibuf->y; + + lt_puv[2][0] = lt_uv_pxoffset[2][0] * ibuf->x; + lt_puv[2][1] = lt_uv_pxoffset[2][1] * ibuf->y; + + if ((ps->faceSeamFlags[tri_index] & PROJ_FACE_SEAM0) || + (ps->faceSeamFlags[tri_index] & PROJ_FACE_SEAM1) || + (ps->faceSeamFlags[tri_index] & PROJ_FACE_SEAM2)) { + uv_image_outset(ps, lt_uv_pxoffset, lt_puv, tri_index, ibuf->x, ibuf->y); + } + + /* ps->loopSeamUVs cant be modified when threading, now this is done we can unlock. */ + if (threaded) { + /* Other threads could be modifying these vars */ + BLI_thread_unlock(LOCK_CUSTOM1); + } + + vCoSS[0] = ps->screenCoords[lt_vtri[0]]; + vCoSS[1] = ps->screenCoords[lt_vtri[1]]; + vCoSS[2] = ps->screenCoords[lt_vtri[2]]; + + /* PROJ_FACE_SCALE_SEAM must be slightly less then 1.0f */ + if (is_ortho) { + scale_tri(insetCos, vCoSS, PROJ_FACE_SCALE_SEAM); + } + else { + scale_tri(insetCos, vCo, PROJ_FACE_SCALE_SEAM); + } + + for (fidx1 = 0; fidx1 < 3; fidx1++) { + /* next fidx in the face (0,1,2) -> (1,2,0) */ + fidx2 = (fidx1 == 2) ? 0 : fidx1 + 1; + + if ((face_seam_flag & (1 << fidx1)) && /* 1<<fidx1 -> PROJ_FACE_SEAM# */ + line_clip_rect2f(clip_rect, + bucket_bounds, + vCoSS[fidx1], + vCoSS[fidx2], + bucket_clip_edges[0], + bucket_clip_edges[1])) { + /* Avoid div by zero. */ + if (len_squared_v2v2(vCoSS[fidx1], vCoSS[fidx2]) > FLT_EPSILON) { + uint loop_idx = ps->mlooptri_eval[tri_index].tri[fidx1]; + LoopSeamData *seam_data = &ps->loopSeamData[loop_idx]; + float(*seam_uvs)[2] = seam_data->seam_uvs; + + if (is_ortho) { + fac1 = line_point_factor_v2(bucket_clip_edges[0], vCoSS[fidx1], vCoSS[fidx2]); + fac2 = line_point_factor_v2(bucket_clip_edges[1], vCoSS[fidx1], vCoSS[fidx2]); + } + else { + fac1 = screen_px_line_point_factor_v2_persp( + ps, bucket_clip_edges[0], vCo[fidx1], vCo[fidx2]); + fac2 = screen_px_line_point_factor_v2_persp( + ps, bucket_clip_edges[1], vCo[fidx1], vCo[fidx2]); + } + + interp_v2_v2v2(seam_subsection[0], lt_uv_pxoffset[fidx1], lt_uv_pxoffset[fidx2], fac1); + interp_v2_v2v2(seam_subsection[1], lt_uv_pxoffset[fidx1], lt_uv_pxoffset[fidx2], fac2); + + interp_v2_v2v2(seam_subsection[2], seam_uvs[0], seam_uvs[1], fac2); + interp_v2_v2v2(seam_subsection[3], seam_uvs[0], seam_uvs[1], 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 */ + interp_v3_v3v3(edge_verts_inset_clip[0], insetCos[fidx1], insetCos[fidx2], fac1); + interp_v3_v3v3(edge_verts_inset_clip[1], insetCos[fidx1], insetCos[fidx2], fac2); + + if (pixel_bounds_uv(seam_subsection, &bounds_px, ibuf->x, ibuf->y)) { + /* 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; + /* use offset uvs instead */ + uv[1] = (float)y / ibuf_yf; + + has_x_isect = 0; + for (x = bounds_px.xmin; x < bounds_px.xmax; x++) { + float puv[2] = {(float)x, (float)y}; + bool in_bounds; + //uv[0] = (((float)x) + 0.5f) / (float)ibuf->x; + /* use offset uvs instead */ + uv[0] = (float)x / ibuf_xf; + + /* test we're inside uvspace bucket and triangle bounds */ + if (equals_v2v2(seam_uvs[0], seam_uvs[1])) { + in_bounds = isect_point_tri_v2(uv, UNPACK3(seam_subsection)); + } + else { + in_bounds = isect_point_quad_v2(uv, UNPACK4(seam_subsection)); + } + + if (in_bounds) { + if ((seam_data->corner_dist_sq[0] > 0.0f) && + (len_squared_v2v2(puv, seam_data->seam_puvs[0]) < + seam_data->corner_dist_sq[0]) && + (len_squared_v2v2(puv, lt_puv[fidx1]) > ps->seam_bleed_px_sq)) { + in_bounds = false; + } + else if ((seam_data->corner_dist_sq[1] > 0.0f) && + (len_squared_v2v2(puv, seam_data->seam_puvs[1]) < + seam_data->corner_dist_sq[1]) && + (len_squared_v2v2(puv, lt_puv[fidx2]) > ps->seam_bleed_px_sq)) { + in_bounds = false; + } + } + + if (in_bounds) { + float pixel_on_edge[4]; + float fac; + + 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); + } + + /* We need the coord of the pixel on the edge, for the occlusion query. */ + fac = resolve_quad_u_v2(uv, UNPACK4(seam_subsection)); + interp_v3_v3v3( + pixel_on_edge, edge_verts_inset_clip[0], edge_verts_inset_clip[1], fac); + + if (!is_ortho) { + pixel_on_edge[3] = 1.0f; + /* cast because of const */ + mul_m4_v4((float(*)[4])ps->projectMat, pixel_on_edge); + pixel_on_edge[0] = (float)(ps->winx * 0.5f) + + (ps->winx * 0.5f) * pixel_on_edge[0] / pixel_on_edge[3]; + pixel_on_edge[1] = (float)(ps->winy * 0.5f) + + (ps->winy * 0.5f) * pixel_on_edge[1] / pixel_on_edge[3]; + /* Use the depth for bucket point occlusion */ + pixel_on_edge[2] = pixel_on_edge[2] / pixel_on_edge[3]; + } + + if ((ps->do_occlude == false) || + !project_bucket_point_occluded( + ps, bucketFaceNodes, tri_index, pixel_on_edge)) { + /* a pity we need to get the worldspace + * pixel location here */ + if (do_clip || do_3d_mapping) { + interp_v3_v3v3v3(wco, vCo[0], vCo[1], vCo[2], w); + + if (do_clip && ED_view3d_clipping_test(ps->rv3d, wco, true)) { + /* Watch out that no code below + * this needs to run */ + continue; + } + } + + mask = project_paint_uvpixel_mask(ps, tri_index, w); + + if (mask > 0.0f) { + BLI_linklist_prepend_arena( + bucketPixelNodes, + project_paint_uvpixel_init( + ps, arena, &tinf, x, y, mask, tri_index, pixelScreenCo, wco, 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 + } + } + } + } + } + } + } #else - UNUSED_VARS(vCo, threaded); -#endif // PROJ_DEBUG_NOSEAMBLEED + UNUSED_VARS(vCo, threaded); +#endif // PROJ_DEBUG_NOSEAMBLEED } - /** * Takes floating point screenspace min/max and * returns int min/max to be used as indices 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]) +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 */ - - /* XXX: the offset of 0.5 is always truncated to zero and the offset of 1.5f - * is always truncated to 1, is this really correct?? - jwilkins */ - - /* these offsets of 0.5 and 1.5 seem odd but they are correct */ - bucketMin[0] = (int)((int)(((float)(min[0] - ps->screenMin[0]) / ps->screen_width) * ps->buckets_x) + 0.5f); - bucketMin[1] = (int)((int)(((float)(min[1] - ps->screenMin[1]) / ps->screen_height) * ps->buckets_y) + 0.5f); - - bucketMax[0] = (int)((int)(((float)(max[0] - ps->screenMin[0]) / ps->screen_width) * ps->buckets_x) + 1.5f); - bucketMax[1] = (int)((int)(((float)(max[1] - ps->screenMin[1]) / ps->screen_height) * ps->buckets_y) + 1.5f); - - /* in case 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); + /* divide by bucketWidth & bucketHeight so the bounds are offset in bucket grid units */ + + /* XXX: the offset of 0.5 is always truncated to zero and the offset of 1.5f + * is always truncated to 1, is this really correct?? - jwilkins */ + + /* these offsets of 0.5 and 1.5 seem odd but they are correct */ + bucketMin[0] = + (int)((int)(((float)(min[0] - ps->screenMin[0]) / ps->screen_width) * ps->buckets_x) + 0.5f); + bucketMin[1] = (int)((int)(((float)(min[1] - ps->screenMin[1]) / ps->screen_height) * + ps->buckets_y) + + 0.5f); + + bucketMax[0] = + (int)((int)(((float)(max[0] - ps->screenMin[0]) / ps->screen_width) * ps->buckets_x) + 1.5f); + bucketMax[1] = (int)((int)(((float)(max[1] - ps->screenMin[1]) / ps->screen_height) * + ps->buckets_y) + + 1.5f); + + /* in case 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) +static void project_bucket_bounds(const ProjPaintState *ps, + const int bucket_x, + const int bucket_y, + rctf *bucket_bounds) { - /* left */ - bucket_bounds->xmin = ( - ps->screenMin[0] + ((bucket_x) * (ps->screen_width / ps->buckets_x))); - /* right */ - bucket_bounds->xmax = ( - ps->screenMin[0] + ((bucket_x + 1) * (ps->screen_width / ps->buckets_x))); - - /* bottom */ - bucket_bounds->ymin = ( - ps->screenMin[1] + ((bucket_y) * (ps->screen_height / ps->buckets_y))); - /* top */ - bucket_bounds->ymax = ( - ps->screenMin[1] + ((bucket_y + 1) * (ps->screen_height / ps->buckets_y))); + /* left */ + bucket_bounds->xmin = (ps->screenMin[0] + ((bucket_x) * (ps->screen_width / ps->buckets_x))); + /* right */ + bucket_bounds->xmax = (ps->screenMin[0] + ((bucket_x + 1) * (ps->screen_width / ps->buckets_x))); + + /* bottom */ + bucket_bounds->ymin = (ps->screenMin[1] + ((bucket_y) * (ps->screen_height / ps->buckets_y))); + /* top */ + bucket_bounds->ymax = (ps->screenMin[1] + + ((bucket_y + 1) * (ps->screen_height / ps->buckets_y))); } /* 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, - const rctf *clip_rect, const rctf *bucket_bounds) +static void project_bucket_init(const ProjPaintState *ps, + const int thread_index, + const int bucket_index, + const rctf *clip_rect, + const rctf *bucket_bounds) { - LinkNode *node; - int tri_index, image_index = 0; - ImBuf *ibuf = NULL; - Image *tpage_last = NULL, *tpage; - ImBuf *tmpibuf = 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, POINTER_AS_INT(node->link), 0, - clip_rect, bucket_bounds, ibuf, &tmpibuf); - } - } - else { - - /* More complicated loop, switch between images */ - for (node = ps->bucketFaces[bucket_index]; node; node = node->next) { - tri_index = POINTER_AS_INT(node->link); - - /* Image context switching */ - tpage = project_paint_face_paint_image(ps, tri_index); - if (tpage_last != tpage) { - tpage_last = tpage; - - 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, tri_index, image_index, - clip_rect, bucket_bounds, ibuf, &tmpibuf); - } - } - - if (tmpibuf) - IMB_freeImBuf(tmpibuf); - - ps->bucketFlags[bucket_index] |= PROJ_BUCKET_INIT; + LinkNode *node; + int tri_index, image_index = 0; + ImBuf *ibuf = NULL; + Image *tpage_last = NULL, *tpage; + ImBuf *tmpibuf = 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, + POINTER_AS_INT(node->link), + 0, + clip_rect, + bucket_bounds, + ibuf, + &tmpibuf); + } + } + else { + + /* More complicated loop, switch between images */ + for (node = ps->bucketFaces[bucket_index]; node; node = node->next) { + tri_index = POINTER_AS_INT(node->link); + + /* Image context switching */ + tpage = project_paint_face_paint_image(ps, tri_index); + if (tpage_last != tpage) { + tpage_last = tpage; + + 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, + tri_index, + image_index, + clip_rect, + bucket_bounds, + ibuf, + &tmpibuf); + } + } + + if (tmpibuf) + IMB_freeImBuf(tmpibuf); + + 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 bool project_bucket_face_isect(ProjPaintState *ps, int bucket_x, int bucket_y, const MLoopTri *lt) +static bool project_bucket_face_isect(ProjPaintState *ps, + int bucket_x, + int bucket_y, + const MLoopTri *lt) { - /* TODO - replace this with a tricker method that uses sideofline for all - * screenCoords's edges against the closest bucket corner */ - const int lt_vtri[3] = { PS_LOOPTRI_AS_VERT_INDEX_3(ps, lt) }; - rctf bucket_bounds; - float p1[2], p2[2], p3[2], p4[2]; - const float *v, *v1, *v2, *v3; - int fidx; - - project_bucket_bounds(ps, bucket_x, bucket_y, &bucket_bounds); - - /* Is one of the faces verts in the bucket bounds? */ - - fidx = 2; - do { - v = ps->screenCoords[lt_vtri[fidx]]; - if (BLI_rctf_isect_pt_v(&bucket_bounds, v)) { - return 1; - } - } while (fidx--); - - v1 = ps->screenCoords[lt_vtri[0]]; - v2 = ps->screenCoords[lt_vtri[1]]; - v3 = ps->screenCoords[lt_vtri[2]]; - - 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 (isect_point_tri_v2(p1, v1, v2, v3) || - isect_point_tri_v2(p2, v1, v2, v3) || - isect_point_tri_v2(p3, v1, v2, v3) || - isect_point_tri_v2(p4, v1, v2, v3) || - /* we can avoid testing v3,v1 because another intersection MUST exist if this intersects */ - (isect_seg_seg_v2(p1, p2, v1, v2) || isect_seg_seg_v2(p1, p2, v2, v3)) || - (isect_seg_seg_v2(p2, p3, v1, v2) || isect_seg_seg_v2(p2, p3, v2, v3)) || - (isect_seg_seg_v2(p3, p4, v1, v2) || isect_seg_seg_v2(p3, p4, v2, v3)) || - (isect_seg_seg_v2(p4, p1, v1, v2) || isect_seg_seg_v2(p4, p1, v2, v3))) - { - return 1; - } - - return 0; + /* TODO - replace this with a tricker method that uses sideofline for all + * screenCoords's edges against the closest bucket corner */ + const int lt_vtri[3] = {PS_LOOPTRI_AS_VERT_INDEX_3(ps, lt)}; + rctf bucket_bounds; + float p1[2], p2[2], p3[2], p4[2]; + const float *v, *v1, *v2, *v3; + int fidx; + + project_bucket_bounds(ps, bucket_x, bucket_y, &bucket_bounds); + + /* Is one of the faces verts in the bucket bounds? */ + + fidx = 2; + do { + v = ps->screenCoords[lt_vtri[fidx]]; + if (BLI_rctf_isect_pt_v(&bucket_bounds, v)) { + return 1; + } + } while (fidx--); + + v1 = ps->screenCoords[lt_vtri[0]]; + v2 = ps->screenCoords[lt_vtri[1]]; + v3 = ps->screenCoords[lt_vtri[2]]; + + 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 (isect_point_tri_v2(p1, v1, v2, v3) || isect_point_tri_v2(p2, v1, v2, v3) || + isect_point_tri_v2(p3, v1, v2, v3) || isect_point_tri_v2(p4, v1, v2, v3) || + /* we can avoid testing v3,v1 because another intersection MUST exist if this intersects */ + (isect_seg_seg_v2(p1, p2, v1, v2) || isect_seg_seg_v2(p1, p2, v2, v3)) || + (isect_seg_seg_v2(p2, p3, v1, v2) || isect_seg_seg_v2(p2, p3, v2, v3)) || + (isect_seg_seg_v2(p3, p4, v1, v2) || isect_seg_seg_v2(p3, p4, v2, v3)) || + (isect_seg_seg_v2(p4, p1, v1, v2) || isect_seg_seg_v2(p4, p1, v2, v3))) { + return 1; + } + + return 0; } /* Add faces to the bucket but don't 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 MLoopTri *lt, const int tri_index) +static void project_paint_delayed_face_init(ProjPaintState *ps, + const MLoopTri *lt, + const int tri_index) { - const int lt_vtri[3] = { PS_LOOPTRI_AS_VERT_INDEX_3(ps, lt) }; - float min[2], max[2], *vCoSS; - /* for ps->bucketRect indexing */ - int bucketMin[2], bucketMax[2]; - int fidx, bucket_x, bucket_y; - /* for early loop exit */ - int has_x_isect = -1, has_isect = 0; - /* just use the first thread arena since threading has not started yet */ - MemArena *arena = ps->arena_mt[0]; - - INIT_MINMAX2(min, max); - - fidx = 2; - do { - vCoSS = ps->screenCoords[lt_vtri[fidx]]; - minmax_v2v2_v2(min, max, vCoSS); - } 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++) { - if (project_bucket_face_isect(ps, bucket_x, bucket_y, lt)) { - int bucket_index = bucket_x + (bucket_y * ps->buckets_x); - BLI_linklist_prepend_arena( - &ps->bucketFaces[bucket_index], - /* cast to a pointer to shut up the compiler */ - POINTER_FROM_INT(tri_index), - 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; - } - } + const int lt_vtri[3] = {PS_LOOPTRI_AS_VERT_INDEX_3(ps, lt)}; + float min[2], max[2], *vCoSS; + /* for ps->bucketRect indexing */ + int bucketMin[2], bucketMax[2]; + int fidx, bucket_x, bucket_y; + /* for early loop exit */ + int has_x_isect = -1, has_isect = 0; + /* just use the first thread arena since threading has not started yet */ + MemArena *arena = ps->arena_mt[0]; + + INIT_MINMAX2(min, max); + + fidx = 2; + do { + vCoSS = ps->screenCoords[lt_vtri[fidx]]; + minmax_v2v2_v2(min, max, vCoSS); + } 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++) { + if (project_bucket_face_isect(ps, bucket_x, bucket_y, lt)) { + int bucket_index = bucket_x + (bucket_y * ps->buckets_x); + BLI_linklist_prepend_arena(&ps->bucketFaces[bucket_index], + /* cast to a pointer to shut up the compiler */ + POINTER_FROM_INT(tri_index), + 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) { - /* set as uninitialized */ - ps->loopSeamData[lt->tri[0]].seam_uvs[0][0] = FLT_MAX; - ps->loopSeamData[lt->tri[1]].seam_uvs[0][0] = FLT_MAX; - ps->loopSeamData[lt->tri[2]].seam_uvs[0][0] = FLT_MAX; - } + if (ps->seam_bleed_px > 0.0f) { + /* set as uninitialized */ + ps->loopSeamData[lt->tri[0]].seam_uvs[0][0] = FLT_MAX; + ps->loopSeamData[lt->tri[1]].seam_uvs[0][0] = FLT_MAX; + ps->loopSeamData[lt->tri[2]].seam_uvs[0][0] = FLT_MAX; + } #endif } static void proj_paint_state_viewport_init(ProjPaintState *ps, const char symmetry_flag) { - float mat[3][3]; - float viewmat[4][4]; - float viewinv[4][4]; - - ps->viewDir[0] = 0.0f; - ps->viewDir[1] = 0.0f; - ps->viewDir[2] = 1.0f; - - copy_m4_m4(ps->obmat, ps->ob->obmat); - - if (symmetry_flag) { - int i; - for (i = 0; i < 3; i++) { - if ((symmetry_flag >> i) & 1) { - negate_v3(ps->obmat[i]); - ps->is_flip_object = !ps->is_flip_object; - } - } - } - - invert_m4_m4(ps->obmat_imat, ps->obmat); - - if (ELEM(ps->source, PROJ_SRC_VIEW, PROJ_SRC_VIEW_FILL)) { - /* normal drawing */ - ps->winx = ps->ar->winx; - ps->winy = ps->ar->winy; - - copy_m4_m4(viewmat, ps->rv3d->viewmat); - copy_m4_m4(viewinv, ps->rv3d->viewinv); - - ED_view3d_ob_project_mat_get_from_obmat(ps->rv3d, ps->obmat, ps->projectMat); - - ps->is_ortho = ED_view3d_clip_range_get(ps->depsgraph, ps->v3d, ps->rv3d, &ps->clip_start, &ps->clip_end, true); - } - else { - /* re-projection */ - float winmat[4][4]; - float vmat[4][4]; - - ps->winx = ps->reproject_ibuf->x; - ps->winy = ps->reproject_ibuf->y; - - if (ps->source == PROJ_SRC_IMAGE_VIEW) { - /* image stores camera data, tricky */ - IDProperty *idgroup = IDP_GetProperties(&ps->reproject_image->id, 0); - IDProperty *view_data = IDP_GetPropertyFromGroup(idgroup, PROJ_VIEW_DATA_ID); - - const float *array = (float *)IDP_Array(view_data); - - /* use image array, written when creating image */ - memcpy(winmat, array, sizeof(winmat)); array += sizeof(winmat) / sizeof(float); - memcpy(viewmat, array, sizeof(viewmat)); array += sizeof(viewmat) / sizeof(float); - ps->clip_start = array[0]; - ps->clip_end = array[1]; - ps->is_ortho = array[2] ? 1 : 0; - - invert_m4_m4(viewinv, viewmat); - } - else if (ps->source == PROJ_SRC_IMAGE_CAM) { - Object *cam_ob_eval = DEG_get_evaluated_object(ps->depsgraph, ps->scene->camera); - CameraParams params; - - /* viewmat & viewinv */ - copy_m4_m4(viewinv, cam_ob_eval->obmat); - normalize_m4(viewinv); - invert_m4_m4(viewmat, viewinv); - - /* window matrix, clipping and ortho */ - BKE_camera_params_init(¶ms); - BKE_camera_params_from_object(¶ms, cam_ob_eval); - BKE_camera_params_compute_viewplane(¶ms, ps->winx, ps->winy, 1.0f, 1.0f); - BKE_camera_params_compute_matrix(¶ms); - - copy_m4_m4(winmat, params.winmat); - ps->clip_start = params.clip_start; - ps->clip_end = params.clip_end; - ps->is_ortho = params.is_ortho; - } - else { - BLI_assert(0); - } - - /* same as #ED_view3d_ob_project_mat_get */ - mul_m4_m4m4(vmat, viewmat, ps->obmat); - mul_m4_m4m4(ps->projectMat, winmat, vmat); - } - - invert_m4_m4(ps->projectMatInv, ps->projectMat); - - /* viewDir - object relative */ - copy_m3_m4(mat, viewinv); - mul_m3_v3(mat, ps->viewDir); - copy_m3_m4(mat, ps->obmat_imat); - mul_m3_v3(mat, ps->viewDir); - normalize_v3(ps->viewDir); - - if (UNLIKELY(ps->is_flip_object)) { - negate_v3(ps->viewDir); - } - - /* viewPos - object relative */ - copy_v3_v3(ps->viewPos, viewinv[3]); - copy_m3_m4(mat, ps->obmat_imat); - mul_m3_v3(mat, ps->viewPos); - add_v3_v3(ps->viewPos, ps->obmat_imat[3]); + float mat[3][3]; + float viewmat[4][4]; + float viewinv[4][4]; + + ps->viewDir[0] = 0.0f; + ps->viewDir[1] = 0.0f; + ps->viewDir[2] = 1.0f; + + copy_m4_m4(ps->obmat, ps->ob->obmat); + + if (symmetry_flag) { + int i; + for (i = 0; i < 3; i++) { + if ((symmetry_flag >> i) & 1) { + negate_v3(ps->obmat[i]); + ps->is_flip_object = !ps->is_flip_object; + } + } + } + + invert_m4_m4(ps->obmat_imat, ps->obmat); + + if (ELEM(ps->source, PROJ_SRC_VIEW, PROJ_SRC_VIEW_FILL)) { + /* normal drawing */ + ps->winx = ps->ar->winx; + ps->winy = ps->ar->winy; + + copy_m4_m4(viewmat, ps->rv3d->viewmat); + copy_m4_m4(viewinv, ps->rv3d->viewinv); + + ED_view3d_ob_project_mat_get_from_obmat(ps->rv3d, ps->obmat, ps->projectMat); + + ps->is_ortho = ED_view3d_clip_range_get( + ps->depsgraph, ps->v3d, ps->rv3d, &ps->clip_start, &ps->clip_end, true); + } + else { + /* re-projection */ + float winmat[4][4]; + float vmat[4][4]; + + ps->winx = ps->reproject_ibuf->x; + ps->winy = ps->reproject_ibuf->y; + + if (ps->source == PROJ_SRC_IMAGE_VIEW) { + /* image stores camera data, tricky */ + IDProperty *idgroup = IDP_GetProperties(&ps->reproject_image->id, 0); + IDProperty *view_data = IDP_GetPropertyFromGroup(idgroup, PROJ_VIEW_DATA_ID); + + const float *array = (float *)IDP_Array(view_data); + + /* use image array, written when creating image */ + memcpy(winmat, array, sizeof(winmat)); + array += sizeof(winmat) / sizeof(float); + memcpy(viewmat, array, sizeof(viewmat)); + array += sizeof(viewmat) / sizeof(float); + ps->clip_start = array[0]; + ps->clip_end = array[1]; + ps->is_ortho = array[2] ? 1 : 0; + + invert_m4_m4(viewinv, viewmat); + } + else if (ps->source == PROJ_SRC_IMAGE_CAM) { + Object *cam_ob_eval = DEG_get_evaluated_object(ps->depsgraph, ps->scene->camera); + CameraParams params; + + /* viewmat & viewinv */ + copy_m4_m4(viewinv, cam_ob_eval->obmat); + normalize_m4(viewinv); + invert_m4_m4(viewmat, viewinv); + + /* window matrix, clipping and ortho */ + BKE_camera_params_init(¶ms); + BKE_camera_params_from_object(¶ms, cam_ob_eval); + BKE_camera_params_compute_viewplane(¶ms, ps->winx, ps->winy, 1.0f, 1.0f); + BKE_camera_params_compute_matrix(¶ms); + + copy_m4_m4(winmat, params.winmat); + ps->clip_start = params.clip_start; + ps->clip_end = params.clip_end; + ps->is_ortho = params.is_ortho; + } + else { + BLI_assert(0); + } + + /* same as #ED_view3d_ob_project_mat_get */ + mul_m4_m4m4(vmat, viewmat, ps->obmat); + mul_m4_m4m4(ps->projectMat, winmat, vmat); + } + + invert_m4_m4(ps->projectMatInv, ps->projectMat); + + /* viewDir - object relative */ + copy_m3_m4(mat, viewinv); + mul_m3_v3(mat, ps->viewDir); + copy_m3_m4(mat, ps->obmat_imat); + mul_m3_v3(mat, ps->viewDir); + normalize_v3(ps->viewDir); + + if (UNLIKELY(ps->is_flip_object)) { + negate_v3(ps->viewDir); + } + + /* viewPos - object relative */ + copy_v3_v3(ps->viewPos, viewinv[3]); + copy_m3_m4(mat, ps->obmat_imat); + mul_m3_v3(mat, ps->viewPos); + add_v3_v3(ps->viewPos, ps->obmat_imat[3]); } static void proj_paint_state_screen_coords_init(ProjPaintState *ps, const int diameter) { - const MVert *mv; - float *projScreenCo; - float projMargin; - int a; - - INIT_MINMAX2(ps->screenMin, ps->screenMax); - - ps->screenCoords = MEM_mallocN(sizeof(float) * ps->totvert_eval * 4, "ProjectPaint ScreenVerts"); - projScreenCo = *ps->screenCoords; - - if (ps->is_ortho) { - for (a = 0, mv = ps->mvert_eval; a < ps->totvert_eval; a++, mv++, projScreenCo += 4) { - mul_v3_m4v3(projScreenCo, ps->projectMat, mv->co); - - /* screen space, not clamped */ - projScreenCo[0] = (float)(ps->winx * 0.5f) + (ps->winx * 0.5f) * projScreenCo[0]; - projScreenCo[1] = (float)(ps->winy * 0.5f) + (ps->winy * 0.5f) * projScreenCo[1]; - minmax_v2v2_v2(ps->screenMin, ps->screenMax, projScreenCo); - } - } - else { - for (a = 0, mv = ps->mvert_eval; a < ps->totvert_eval; a++, mv++, projScreenCo += 4) { - copy_v3_v3(projScreenCo, mv->co); - projScreenCo[3] = 1.0f; - - mul_m4_v4(ps->projectMat, projScreenCo); - - if (projScreenCo[3] > ps->clip_start) { - /* screen space, not clamped */ - projScreenCo[0] = (float)(ps->winx * 0.5f) + (ps->winx * 0.5f) * projScreenCo[0] / projScreenCo[3]; - projScreenCo[1] = (float)(ps->winy * 0.5f) + (ps->winy * 0.5f) * projScreenCo[1] / projScreenCo[3]; - /* Use the depth for bucket point occlusion */ - projScreenCo[2] = projScreenCo[2] / projScreenCo[3]; - minmax_v2v2_v2(ps->screenMin, ps->screenMax, projScreenCo); - } - 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 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; - - if (ps->source == PROJ_SRC_VIEW) { + const MVert *mv; + float *projScreenCo; + float projMargin; + int a; + + INIT_MINMAX2(ps->screenMin, ps->screenMax); + + ps->screenCoords = MEM_mallocN(sizeof(float) * ps->totvert_eval * 4, "ProjectPaint ScreenVerts"); + projScreenCo = *ps->screenCoords; + + if (ps->is_ortho) { + for (a = 0, mv = ps->mvert_eval; a < ps->totvert_eval; a++, mv++, projScreenCo += 4) { + mul_v3_m4v3(projScreenCo, ps->projectMat, mv->co); + + /* screen space, not clamped */ + projScreenCo[0] = (float)(ps->winx * 0.5f) + (ps->winx * 0.5f) * projScreenCo[0]; + projScreenCo[1] = (float)(ps->winy * 0.5f) + (ps->winy * 0.5f) * projScreenCo[1]; + minmax_v2v2_v2(ps->screenMin, ps->screenMax, projScreenCo); + } + } + else { + for (a = 0, mv = ps->mvert_eval; a < ps->totvert_eval; a++, mv++, projScreenCo += 4) { + copy_v3_v3(projScreenCo, mv->co); + projScreenCo[3] = 1.0f; + + mul_m4_v4(ps->projectMat, projScreenCo); + + if (projScreenCo[3] > ps->clip_start) { + /* screen space, not clamped */ + projScreenCo[0] = (float)(ps->winx * 0.5f) + + (ps->winx * 0.5f) * projScreenCo[0] / projScreenCo[3]; + projScreenCo[1] = (float)(ps->winy * 0.5f) + + (ps->winy * 0.5f) * projScreenCo[1] / projScreenCo[3]; + /* Use the depth for bucket point occlusion */ + projScreenCo[2] = projScreenCo[2] / projScreenCo[3]; + minmax_v2v2_v2(ps->screenMin, ps->screenMax, projScreenCo); + } + 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 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; + + if (ps->source == PROJ_SRC_VIEW) { #ifdef PROJ_DEBUG_WINCLIP - CLAMP(ps->screenMin[0], (float)(-diameter), (float)(ps->winx + diameter)); - CLAMP(ps->screenMax[0], (float)(-diameter), (float)(ps->winx + diameter)); + CLAMP(ps->screenMin[0], (float)(-diameter), (float)(ps->winx + diameter)); + CLAMP(ps->screenMax[0], (float)(-diameter), (float)(ps->winx + diameter)); - CLAMP(ps->screenMin[1], (float)(-diameter), (float)(ps->winy + diameter)); - CLAMP(ps->screenMax[1], (float)(-diameter), (float)(ps->winy + diameter)); + CLAMP(ps->screenMin[1], (float)(-diameter), (float)(ps->winy + diameter)); + CLAMP(ps->screenMax[1], (float)(-diameter), (float)(ps->winy + diameter)); #else - UNUSED_VARS(diameter); + UNUSED_VARS(diameter); #endif - } - else if (ps->source != PROJ_SRC_VIEW_FILL) { /* re-projection, use bounds */ - ps->screenMin[0] = 0; - ps->screenMax[0] = (float)(ps->winx); - - ps->screenMin[1] = 0; - ps->screenMax[1] = (float)(ps->winy); - } + } + else if (ps->source != PROJ_SRC_VIEW_FILL) { /* re-projection, use bounds */ + ps->screenMin[0] = 0; + ps->screenMax[0] = (float)(ps->winx); + + ps->screenMin[1] = 0; + ps->screenMax[1] = (float)(ps->winy); + } } static void proj_paint_state_cavity_init(ProjPaintState *ps) { - const MVert *mv; - const MEdge *me; - float *cavities; - int a; - - if (ps->do_mask_cavity) { - int *counter = MEM_callocN(sizeof(int) * ps->totvert_eval, "counter"); - float (*edges)[3] = MEM_callocN(sizeof(float) * 3 * ps->totvert_eval, "edges"); - ps->cavities = MEM_mallocN(sizeof(float) * ps->totvert_eval, "ProjectPaint Cavities"); - cavities = ps->cavities; - - for (a = 0, me = ps->medge_eval; a < ps->totedge_eval; a++, me++) { - float e[3]; - sub_v3_v3v3(e, ps->mvert_eval[me->v1].co, ps->mvert_eval[me->v2].co); - normalize_v3(e); - add_v3_v3(edges[me->v2], e); - counter[me->v2]++; - sub_v3_v3(edges[me->v1], e); - counter[me->v1]++; - } - for (a = 0, mv = ps->mvert_eval; a < ps->totvert_eval; a++, mv++) { - if (counter[a] > 0) { - float no[3]; - mul_v3_fl(edges[a], 1.0f / counter[a]); - normal_short_to_float_v3(no, mv->no); - /* augment the diffe*/ - cavities[a] = saacos(10.0f * dot_v3v3(no, edges[a])) * (float)M_1_PI; - } - else - cavities[a] = 0.0; - } - - MEM_freeN(counter); - MEM_freeN(edges); - } + const MVert *mv; + const MEdge *me; + float *cavities; + int a; + + if (ps->do_mask_cavity) { + int *counter = MEM_callocN(sizeof(int) * ps->totvert_eval, "counter"); + float(*edges)[3] = MEM_callocN(sizeof(float) * 3 * ps->totvert_eval, "edges"); + ps->cavities = MEM_mallocN(sizeof(float) * ps->totvert_eval, "ProjectPaint Cavities"); + cavities = ps->cavities; + + for (a = 0, me = ps->medge_eval; a < ps->totedge_eval; a++, me++) { + float e[3]; + sub_v3_v3v3(e, ps->mvert_eval[me->v1].co, ps->mvert_eval[me->v2].co); + normalize_v3(e); + add_v3_v3(edges[me->v2], e); + counter[me->v2]++; + sub_v3_v3(edges[me->v1], e); + counter[me->v1]++; + } + for (a = 0, mv = ps->mvert_eval; a < ps->totvert_eval; a++, mv++) { + if (counter[a] > 0) { + float no[3]; + mul_v3_fl(edges[a], 1.0f / counter[a]); + normal_short_to_float_v3(no, mv->no); + /* augment the diffe*/ + cavities[a] = saacos(10.0f * dot_v3v3(no, edges[a])) * (float)M_1_PI; + } + else + cavities[a] = 0.0; + } + + MEM_freeN(counter); + MEM_freeN(edges); + } } #ifndef PROJ_DEBUG_NOSEAMBLEED static void proj_paint_state_seam_bleed_init(ProjPaintState *ps) { - if (ps->seam_bleed_px > 0.0f) { - ps->vertFaces = MEM_callocN(sizeof(LinkNode *) * ps->totvert_eval, "paint-vertFaces"); - ps->faceSeamFlags = MEM_callocN(sizeof(ushort) * ps->totlooptri_eval, "paint-faceSeamFlags"); - ps->faceWindingFlags = MEM_callocN(sizeof(char) * ps->totlooptri_eval, "paint-faceWindindFlags"); - ps->loopSeamData = MEM_mallocN(sizeof(LoopSeamData) * ps->totloop_eval, "paint-loopSeamUVs"); - ps->vertSeams = MEM_callocN(sizeof(ListBase) * ps->totvert_eval, "paint-vertSeams"); - } + if (ps->seam_bleed_px > 0.0f) { + ps->vertFaces = MEM_callocN(sizeof(LinkNode *) * ps->totvert_eval, "paint-vertFaces"); + ps->faceSeamFlags = MEM_callocN(sizeof(ushort) * ps->totlooptri_eval, "paint-faceSeamFlags"); + ps->faceWindingFlags = MEM_callocN(sizeof(char) * ps->totlooptri_eval, + "paint-faceWindindFlags"); + ps->loopSeamData = MEM_mallocN(sizeof(LoopSeamData) * ps->totloop_eval, "paint-loopSeamUVs"); + ps->vertSeams = MEM_callocN(sizeof(ListBase) * ps->totvert_eval, "paint-vertSeams"); + } } #endif static void proj_paint_state_thread_init(ProjPaintState *ps, const bool reset_threads) { - int a; + int a; - /* 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. */ + /* 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. */ - ps->thread_tot = BKE_scene_num_threads(ps->scene); + ps->thread_tot = BKE_scene_num_threads(ps->scene); - /* workaround for #35057, disable threading if diameter is less than is possible for - * optimum bucket number generation */ - if (reset_threads) - ps->thread_tot = 1; + /* workaround for #35057, disable threading if diameter is less than is possible for + * optimum bucket number generation */ + if (reset_threads) + ps->thread_tot = 1; - if (ps->is_shared_user == false) { - if (ps->thread_tot > 1) { - ps->tile_lock = MEM_mallocN(sizeof(SpinLock), "projpaint_tile_lock"); - BLI_spin_init(ps->tile_lock); - } + if (ps->is_shared_user == false) { + if (ps->thread_tot > 1) { + ps->tile_lock = MEM_mallocN(sizeof(SpinLock), "projpaint_tile_lock"); + BLI_spin_init(ps->tile_lock); + } - image_undo_init_locks(); - } + image_undo_init_locks(); + } - for (a = 0; a < ps->thread_tot; a++) { - ps->arena_mt[a] = BLI_memarena_new(MEM_SIZE_OPTIMAL(1 << 16), "project paint arena"); - } + for (a = 0; a < ps->thread_tot; a++) { + ps->arena_mt[a] = BLI_memarena_new(MEM_SIZE_OPTIMAL(1 << 16), "project paint arena"); + } } static void proj_paint_state_vert_flags_init(ProjPaintState *ps) { - if (ps->do_backfacecull && ps->do_mask_normal) { - float viewDirPersp[3]; - const MVert *mv; - float no[3]; - int a; - - ps->vertFlags = MEM_callocN(sizeof(char) * ps->totvert_eval, "paint-vertFlags"); - - for (a = 0, mv = ps->mvert_eval; a < ps->totvert_eval; a++, mv++) { - normal_short_to_float_v3(no, mv->no); - if (UNLIKELY(ps->is_flip_object)) { - negate_v3(no); - } - - if (ps->is_ortho) { - if (dot_v3v3(ps->viewDir, no) <= ps->normal_angle__cos) { - /* 1 vert of this face is towards us */ - ps->vertFlags[a] |= PROJ_VERT_CULL; - } - } - else { - sub_v3_v3v3(viewDirPersp, ps->viewPos, mv->co); - normalize_v3(viewDirPersp); - if (UNLIKELY(ps->is_flip_object)) { - negate_v3(viewDirPersp); - } - if (dot_v3v3(viewDirPersp, no) <= ps->normal_angle__cos) { - /* 1 vert of this face is towards us */ - ps->vertFlags[a] |= PROJ_VERT_CULL; - } - } - } - } - else { - ps->vertFlags = NULL; - } + if (ps->do_backfacecull && ps->do_mask_normal) { + float viewDirPersp[3]; + const MVert *mv; + float no[3]; + int a; + + ps->vertFlags = MEM_callocN(sizeof(char) * ps->totvert_eval, "paint-vertFlags"); + + for (a = 0, mv = ps->mvert_eval; a < ps->totvert_eval; a++, mv++) { + normal_short_to_float_v3(no, mv->no); + if (UNLIKELY(ps->is_flip_object)) { + negate_v3(no); + } + + if (ps->is_ortho) { + if (dot_v3v3(ps->viewDir, no) <= ps->normal_angle__cos) { + /* 1 vert of this face is towards us */ + ps->vertFlags[a] |= PROJ_VERT_CULL; + } + } + else { + sub_v3_v3v3(viewDirPersp, ps->viewPos, mv->co); + normalize_v3(viewDirPersp); + if (UNLIKELY(ps->is_flip_object)) { + negate_v3(viewDirPersp); + } + if (dot_v3v3(viewDirPersp, no) <= ps->normal_angle__cos) { + /* 1 vert of this face is towards us */ + ps->vertFlags[a] |= PROJ_VERT_CULL; + } + } + } + } + else { + ps->vertFlags = NULL; + } } #ifndef PROJ_DEBUG_NOSEAMBLEED -static void project_paint_bleed_add_face_user( - const ProjPaintState *ps, MemArena *arena, - const MLoopTri *lt, const int tri_index) +static void project_paint_bleed_add_face_user(const ProjPaintState *ps, + MemArena *arena, + const MLoopTri *lt, + const int tri_index) { - /* 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) { - const float *lt_tri_uv[3] = { PS_LOOPTRI_AS_UV_3(ps->poly_to_loop_uv, lt) }; - - /* Check for degenerate triangles. Degenerate faces cause trouble with bleed computations. - * Ideally this would be checked later, not to add to the cost of computing non-degenerate - * triangles, but that would allow other triangles to still find adjacent seams on degenerate - * triangles, potentially causing incorrect results. */ - if (area_tri_v2(UNPACK3(lt_tri_uv)) > FLT_EPSILON) { - const int lt_vtri[3] = { PS_LOOPTRI_AS_VERT_INDEX_3(ps, lt) }; - void *tri_index_p = POINTER_FROM_INT(tri_index); - - BLI_linklist_prepend_arena(&ps->vertFaces[lt_vtri[0]], tri_index_p, arena); - BLI_linklist_prepend_arena(&ps->vertFaces[lt_vtri[1]], tri_index_p, arena); - BLI_linklist_prepend_arena(&ps->vertFaces[lt_vtri[2]], tri_index_p, arena); - } - else { - ps->faceSeamFlags[tri_index] |= PROJ_FACE_DEGENERATE; - } - } + /* 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) { + const float *lt_tri_uv[3] = {PS_LOOPTRI_AS_UV_3(ps->poly_to_loop_uv, lt)}; + + /* Check for degenerate triangles. Degenerate faces cause trouble with bleed computations. + * Ideally this would be checked later, not to add to the cost of computing non-degenerate + * triangles, but that would allow other triangles to still find adjacent seams on degenerate + * triangles, potentially causing incorrect results. */ + if (area_tri_v2(UNPACK3(lt_tri_uv)) > FLT_EPSILON) { + const int lt_vtri[3] = {PS_LOOPTRI_AS_VERT_INDEX_3(ps, lt)}; + void *tri_index_p = POINTER_FROM_INT(tri_index); + + BLI_linklist_prepend_arena(&ps->vertFaces[lt_vtri[0]], tri_index_p, arena); + BLI_linklist_prepend_arena(&ps->vertFaces[lt_vtri[1]], tri_index_p, arena); + BLI_linklist_prepend_arena(&ps->vertFaces[lt_vtri[2]], tri_index_p, arena); + } + else { + ps->faceSeamFlags[tri_index] |= PROJ_FACE_DEGENERATE; + } + } } #endif /* Return true if evaluated mesh can be painted on, false otherwise */ static bool proj_paint_state_mesh_eval_init(const bContext *C, ProjPaintState *ps) { - Depsgraph *depsgraph = CTX_data_depsgraph(C); - Object *ob = ps->ob; - - Scene *scene_eval = DEG_get_evaluated_scene(depsgraph); - Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob); - - if (scene_eval == NULL || ob_eval == NULL) { - return false; - } - - CustomData_MeshMasks cddata_masks = scene_eval->customdata_mask; - cddata_masks.fmask |= CD_MASK_MTFACE; - cddata_masks.lmask |= CD_MASK_MLOOPUV; - - /* Workaround for subsurf selection, try the display mesh first */ - if (ps->source == PROJ_SRC_IMAGE_CAM) { - /* using render mesh, assume only camera was rendered from */ - ps->me_eval = mesh_create_eval_final_render( - depsgraph, scene_eval, ob_eval, &cddata_masks); - ps->me_eval_free = true; - } - else { - if (ps->do_face_sel) { - cddata_masks.vmask |= CD_MASK_ORIGINDEX; - cddata_masks.emask |= CD_MASK_ORIGINDEX; - cddata_masks.pmask |= CD_MASK_ORIGINDEX; - } - ps->me_eval = mesh_get_eval_final( - depsgraph, scene_eval, ob_eval, - &cddata_masks); - ps->me_eval_free = false; - } - - if (!CustomData_has_layer(&ps->me_eval->ldata, CD_MLOOPUV)) { - if (ps->me_eval_free) { - BKE_id_free(NULL, ps->me_eval); - } - ps->me_eval = NULL; - return false; - } - - /* Build final material array, we use this a lot here. */ - /* materials start from 1, default material is 0 */ - const int totmat = ob->totcol + 1; - ps->mat_array = MEM_malloc_arrayN(totmat, sizeof(*ps->mat_array), __func__); - /* We leave last material as empty - rationale here is being able to index - * the materials by using the mf->mat_nr directly and leaving the last - * material as NULL in case no materials exist on mesh, so indexing will not fail. */ - for (int i = 0; i < totmat - 1; i++) { - ps->mat_array[i] = give_current_material(ob, i + 1); - } - ps->mat_array[totmat - 1] = NULL; - - ps->mvert_eval = ps->me_eval->mvert; - if (ps->do_mask_cavity) { - ps->medge_eval = ps->me_eval->medge; - } - ps->mloop_eval = ps->me_eval->mloop; - ps->mpoly_eval = ps->me_eval->mpoly; - - ps->totvert_eval = ps->me_eval->totvert; - ps->totedge_eval = ps->me_eval->totedge; - ps->totpoly_eval = ps->me_eval->totpoly; - ps->totloop_eval = ps->me_eval->totloop; - - ps->mlooptri_eval = BKE_mesh_runtime_looptri_ensure(ps->me_eval); - ps->totlooptri_eval = ps->me_eval->runtime.looptris.len; - - ps->poly_to_loop_uv = MEM_mallocN(ps->totpoly_eval * sizeof(MLoopUV *), "proj_paint_mtfaces"); - - return true; + Depsgraph *depsgraph = CTX_data_depsgraph(C); + Object *ob = ps->ob; + + Scene *scene_eval = DEG_get_evaluated_scene(depsgraph); + Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob); + + if (scene_eval == NULL || ob_eval == NULL) { + return false; + } + + CustomData_MeshMasks cddata_masks = scene_eval->customdata_mask; + cddata_masks.fmask |= CD_MASK_MTFACE; + cddata_masks.lmask |= CD_MASK_MLOOPUV; + + /* Workaround for subsurf selection, try the display mesh first */ + if (ps->source == PROJ_SRC_IMAGE_CAM) { + /* using render mesh, assume only camera was rendered from */ + ps->me_eval = mesh_create_eval_final_render(depsgraph, scene_eval, ob_eval, &cddata_masks); + ps->me_eval_free = true; + } + else { + if (ps->do_face_sel) { + cddata_masks.vmask |= CD_MASK_ORIGINDEX; + cddata_masks.emask |= CD_MASK_ORIGINDEX; + cddata_masks.pmask |= CD_MASK_ORIGINDEX; + } + ps->me_eval = mesh_get_eval_final(depsgraph, scene_eval, ob_eval, &cddata_masks); + ps->me_eval_free = false; + } + + if (!CustomData_has_layer(&ps->me_eval->ldata, CD_MLOOPUV)) { + if (ps->me_eval_free) { + BKE_id_free(NULL, ps->me_eval); + } + ps->me_eval = NULL; + return false; + } + + /* Build final material array, we use this a lot here. */ + /* materials start from 1, default material is 0 */ + const int totmat = ob->totcol + 1; + ps->mat_array = MEM_malloc_arrayN(totmat, sizeof(*ps->mat_array), __func__); + /* We leave last material as empty - rationale here is being able to index + * the materials by using the mf->mat_nr directly and leaving the last + * material as NULL in case no materials exist on mesh, so indexing will not fail. */ + for (int i = 0; i < totmat - 1; i++) { + ps->mat_array[i] = give_current_material(ob, i + 1); + } + ps->mat_array[totmat - 1] = NULL; + + ps->mvert_eval = ps->me_eval->mvert; + if (ps->do_mask_cavity) { + ps->medge_eval = ps->me_eval->medge; + } + ps->mloop_eval = ps->me_eval->mloop; + ps->mpoly_eval = ps->me_eval->mpoly; + + ps->totvert_eval = ps->me_eval->totvert; + ps->totedge_eval = ps->me_eval->totedge; + ps->totpoly_eval = ps->me_eval->totpoly; + ps->totloop_eval = ps->me_eval->totloop; + + ps->mlooptri_eval = BKE_mesh_runtime_looptri_ensure(ps->me_eval); + ps->totlooptri_eval = ps->me_eval->runtime.looptris.len; + + ps->poly_to_loop_uv = MEM_mallocN(ps->totpoly_eval * sizeof(MLoopUV *), "proj_paint_mtfaces"); + + return true; } typedef struct { - const MLoopUV *mloopuv_clone_base; - const TexPaintSlot *slot_last_clone; - const TexPaintSlot *slot_clone; + const MLoopUV *mloopuv_clone_base; + const TexPaintSlot *slot_last_clone; + const TexPaintSlot *slot_clone; } ProjPaintLayerClone; -static void proj_paint_layer_clone_init( - ProjPaintState *ps, - ProjPaintLayerClone *layer_clone) +static void proj_paint_layer_clone_init(ProjPaintState *ps, ProjPaintLayerClone *layer_clone) { - MLoopUV *mloopuv_clone_base = NULL; + MLoopUV *mloopuv_clone_base = NULL; - /* use clone mtface? */ - if (ps->do_layer_clone) { - const int layer_num = CustomData_get_clone_layer(&((Mesh *)ps->ob->data)->ldata, CD_MLOOPUV); + /* use clone mtface? */ + if (ps->do_layer_clone) { + const int layer_num = CustomData_get_clone_layer(&((Mesh *)ps->ob->data)->ldata, CD_MLOOPUV); - ps->poly_to_loop_uv_clone = MEM_mallocN(ps->totpoly_eval * sizeof(MLoopUV *), "proj_paint_mtfaces"); + ps->poly_to_loop_uv_clone = MEM_mallocN(ps->totpoly_eval * sizeof(MLoopUV *), + "proj_paint_mtfaces"); - if (layer_num != -1) - mloopuv_clone_base = CustomData_get_layer_n(&ps->me_eval->ldata, CD_MLOOPUV, layer_num); + if (layer_num != -1) + mloopuv_clone_base = CustomData_get_layer_n(&ps->me_eval->ldata, CD_MLOOPUV, layer_num); - if (mloopuv_clone_base == NULL) { - /* get active instead */ - mloopuv_clone_base = CustomData_get_layer(&ps->me_eval->ldata, CD_MLOOPUV); - } + if (mloopuv_clone_base == NULL) { + /* get active instead */ + mloopuv_clone_base = CustomData_get_layer(&ps->me_eval->ldata, CD_MLOOPUV); + } + } - } - - memset(layer_clone, 0, sizeof(*layer_clone)); - layer_clone->mloopuv_clone_base = mloopuv_clone_base; + memset(layer_clone, 0, sizeof(*layer_clone)); + layer_clone->mloopuv_clone_base = mloopuv_clone_base; } /* Return true if face should be skipped, false otherwise */ -static bool project_paint_clone_face_skip( - ProjPaintState *ps, - ProjPaintLayerClone *lc, - const TexPaintSlot *slot, - const int tri_index) +static bool project_paint_clone_face_skip(ProjPaintState *ps, + ProjPaintLayerClone *lc, + const TexPaintSlot *slot, + const int tri_index) { - if (ps->do_layer_clone) { - if (ps->do_material_slots) { - lc->slot_clone = project_paint_face_clone_slot(ps, tri_index); - /* all faces should have a valid slot, reassert here */ - if (ELEM(lc->slot_clone, NULL, slot)) - return true; - } - else if (ps->clone_ima == ps->canvas_ima) - return true; - - if (ps->do_material_slots) { - if (lc->slot_clone != lc->slot_last_clone) { - if (!slot->uvname || - !(lc->mloopuv_clone_base = CustomData_get_layer_named( - &ps->me_eval->ldata, CD_MLOOPUV, - lc->slot_clone->uvname))) - { - lc->mloopuv_clone_base = CustomData_get_layer(&ps->me_eval->ldata, CD_MLOOPUV); - } - lc->slot_last_clone = lc->slot_clone; - } - } - - /* will set multiple times for 4+ sided poly */ - ps->poly_to_loop_uv_clone[ps->mlooptri_eval[tri_index].poly] = lc->mloopuv_clone_base; - } - return false; + if (ps->do_layer_clone) { + if (ps->do_material_slots) { + lc->slot_clone = project_paint_face_clone_slot(ps, tri_index); + /* all faces should have a valid slot, reassert here */ + if (ELEM(lc->slot_clone, NULL, slot)) + return true; + } + else if (ps->clone_ima == ps->canvas_ima) + return true; + + if (ps->do_material_slots) { + if (lc->slot_clone != lc->slot_last_clone) { + if (!slot->uvname || !(lc->mloopuv_clone_base = CustomData_get_layer_named( + &ps->me_eval->ldata, CD_MLOOPUV, lc->slot_clone->uvname))) { + lc->mloopuv_clone_base = CustomData_get_layer(&ps->me_eval->ldata, CD_MLOOPUV); + } + lc->slot_last_clone = lc->slot_clone; + } + } + + /* will set multiple times for 4+ sided poly */ + ps->poly_to_loop_uv_clone[ps->mlooptri_eval[tri_index].poly] = lc->mloopuv_clone_base; + } + return false; } typedef struct { - const MPoly *mpoly_orig; + const MPoly *mpoly_orig; - const int *index_mp_to_orig; + const int *index_mp_to_orig; } ProjPaintFaceLookup; -static void proj_paint_face_lookup_init( - const ProjPaintState *ps, - ProjPaintFaceLookup *face_lookup) +static void proj_paint_face_lookup_init(const ProjPaintState *ps, ProjPaintFaceLookup *face_lookup) { - memset(face_lookup, 0, sizeof(*face_lookup)); - if (ps->do_face_sel) { - face_lookup->index_mp_to_orig = CustomData_get_layer(&ps->me_eval->pdata, CD_ORIGINDEX); - face_lookup->mpoly_orig = ((Mesh *)ps->ob->data)->mpoly; - } + memset(face_lookup, 0, sizeof(*face_lookup)); + if (ps->do_face_sel) { + face_lookup->index_mp_to_orig = CustomData_get_layer(&ps->me_eval->pdata, CD_ORIGINDEX); + face_lookup->mpoly_orig = ((Mesh *)ps->ob->data)->mpoly; + } } /* Return true if face should be considered selected, false otherwise */ -static bool project_paint_check_face_sel( - const ProjPaintState *ps, - const ProjPaintFaceLookup *face_lookup, - const MLoopTri *lt) +static bool project_paint_check_face_sel(const ProjPaintState *ps, + const ProjPaintFaceLookup *face_lookup, + const MLoopTri *lt) { - if (ps->do_face_sel) { - int orig_index; - const MPoly *mp; - - if ((face_lookup->index_mp_to_orig != NULL) && - (((orig_index = (face_lookup->index_mp_to_orig[lt->poly]))) != ORIGINDEX_NONE)) - { - mp = &face_lookup->mpoly_orig[orig_index]; - } - else { - mp = &ps->mpoly_eval[lt->poly]; - } - - return ((mp->flag & ME_FACE_SEL) != 0); - } - else { - return true; - } + if (ps->do_face_sel) { + int orig_index; + const MPoly *mp; + + if ((face_lookup->index_mp_to_orig != NULL) && + (((orig_index = (face_lookup->index_mp_to_orig[lt->poly]))) != ORIGINDEX_NONE)) { + mp = &face_lookup->mpoly_orig[orig_index]; + } + else { + mp = &ps->mpoly_eval[lt->poly]; + } + + return ((mp->flag & ME_FACE_SEL) != 0); + } + else { + return true; + } } typedef struct { - const float *v1; - const float *v2; - const float *v3; + const float *v1; + const float *v2; + const float *v3; } ProjPaintFaceCoSS; -static void proj_paint_face_coSS_init( - const ProjPaintState *ps, const MLoopTri *lt, - ProjPaintFaceCoSS *coSS) +static void proj_paint_face_coSS_init(const ProjPaintState *ps, + const MLoopTri *lt, + ProjPaintFaceCoSS *coSS) { - const int lt_vtri[3] = { PS_LOOPTRI_AS_VERT_INDEX_3(ps, lt) }; - coSS->v1 = ps->screenCoords[lt_vtri[0]]; - coSS->v2 = ps->screenCoords[lt_vtri[1]]; - coSS->v3 = ps->screenCoords[lt_vtri[2]]; + const int lt_vtri[3] = {PS_LOOPTRI_AS_VERT_INDEX_3(ps, lt)}; + coSS->v1 = ps->screenCoords[lt_vtri[0]]; + coSS->v2 = ps->screenCoords[lt_vtri[1]]; + coSS->v3 = ps->screenCoords[lt_vtri[2]]; } /* Return true if face should be culled, false otherwise */ -static bool project_paint_flt_max_cull( - const ProjPaintState *ps, - const ProjPaintFaceCoSS *coSS) +static bool project_paint_flt_max_cull(const ProjPaintState *ps, const ProjPaintFaceCoSS *coSS) { - if (!ps->is_ortho) { - if (coSS->v1[0] == FLT_MAX || - coSS->v2[0] == FLT_MAX || - coSS->v3[0] == FLT_MAX) - { - return true; - } - } - return false; + if (!ps->is_ortho) { + if (coSS->v1[0] == FLT_MAX || coSS->v2[0] == FLT_MAX || coSS->v3[0] == FLT_MAX) { + return true; + } + } + return false; } #ifdef PROJ_DEBUG_WINCLIP /* Return true if face should be culled, false otherwise */ -static bool project_paint_winclip( - const ProjPaintState *ps, - const ProjPaintFaceCoSS *coSS) +static bool project_paint_winclip(const ProjPaintState *ps, const ProjPaintFaceCoSS *coSS) { - /* ignore faces outside the view */ - return ((ps->source != PROJ_SRC_VIEW_FILL) && - ((coSS->v1[0] < ps->screenMin[0] && - coSS->v2[0] < ps->screenMin[0] && - coSS->v3[0] < ps->screenMin[0]) || - - (coSS->v1[0] > ps->screenMax[0] && - coSS->v2[0] > ps->screenMax[0] && - coSS->v3[0] > ps->screenMax[0]) || - - (coSS->v1[1] < ps->screenMin[1] && - coSS->v2[1] < ps->screenMin[1] && - coSS->v3[1] < ps->screenMin[1]) || - - (coSS->v1[1] > ps->screenMax[1] && - coSS->v2[1] > ps->screenMax[1] && - coSS->v3[1] > ps->screenMax[1]))); -} -#endif //PROJ_DEBUG_WINCLIP + /* ignore faces outside the view */ + return ((ps->source != PROJ_SRC_VIEW_FILL) && + ((coSS->v1[0] < ps->screenMin[0] && coSS->v2[0] < ps->screenMin[0] && + coSS->v3[0] < ps->screenMin[0]) || + (coSS->v1[0] > ps->screenMax[0] && coSS->v2[0] > ps->screenMax[0] && + coSS->v3[0] > ps->screenMax[0]) || -static void project_paint_build_proj_ima( - ProjPaintState *ps, MemArena *arena, - LinkNode *image_LinkList) + (coSS->v1[1] < ps->screenMin[1] && coSS->v2[1] < ps->screenMin[1] && + coSS->v3[1] < ps->screenMin[1]) || + + (coSS->v1[1] > ps->screenMax[1] && coSS->v2[1] > ps->screenMax[1] && + coSS->v3[1] > ps->screenMax[1]))); +} +#endif //PROJ_DEBUG_WINCLIP + +static void project_paint_build_proj_ima(ProjPaintState *ps, + MemArena *arena, + LinkNode *image_LinkList) { - ProjPaintImage *projIma; - LinkNode *node; - int i; - - /* build an array of images we use */ - projIma = ps->projImages = BLI_memarena_alloc(arena, sizeof(ProjPaintImage) * ps->image_tot); - - for (node = image_LinkList, i = 0; node; node = node->next, i++, projIma++) { - int size; - projIma->ima = node->link; - projIma->touch = 0; - projIma->ibuf = BKE_image_acquire_ibuf(projIma->ima, NULL, NULL); - size = sizeof(void **) * IMAPAINT_TILE_NUMBER(projIma->ibuf->x) * IMAPAINT_TILE_NUMBER(projIma->ibuf->y); - projIma->partRedrawRect = BLI_memarena_alloc(arena, sizeof(ImagePaintPartialRedraw) * PROJ_BOUNDBOX_SQUARED); - partial_redraw_array_init(projIma->partRedrawRect); - projIma->undoRect = (volatile void **) BLI_memarena_alloc(arena, size); - memset((void *)projIma->undoRect, 0, size); - projIma->maskRect = BLI_memarena_alloc(arena, size); - memset(projIma->maskRect, 0, size); - projIma->valid = BLI_memarena_alloc(arena, size); - memset(projIma->valid, 0, size); - } + ProjPaintImage *projIma; + LinkNode *node; + int i; + + /* build an array of images we use */ + projIma = ps->projImages = BLI_memarena_alloc(arena, sizeof(ProjPaintImage) * ps->image_tot); + + for (node = image_LinkList, i = 0; node; node = node->next, i++, projIma++) { + int size; + projIma->ima = node->link; + projIma->touch = 0; + projIma->ibuf = BKE_image_acquire_ibuf(projIma->ima, NULL, NULL); + size = sizeof(void **) * IMAPAINT_TILE_NUMBER(projIma->ibuf->x) * + IMAPAINT_TILE_NUMBER(projIma->ibuf->y); + projIma->partRedrawRect = BLI_memarena_alloc( + arena, sizeof(ImagePaintPartialRedraw) * PROJ_BOUNDBOX_SQUARED); + partial_redraw_array_init(projIma->partRedrawRect); + projIma->undoRect = (volatile void **)BLI_memarena_alloc(arena, size); + memset((void *)projIma->undoRect, 0, size); + projIma->maskRect = BLI_memarena_alloc(arena, size); + memset(projIma->maskRect, 0, size); + projIma->valid = BLI_memarena_alloc(arena, size); + memset(projIma->valid, 0, size); + } } -static void project_paint_prepare_all_faces( - ProjPaintState *ps, MemArena *arena, - const ProjPaintFaceLookup *face_lookup, - ProjPaintLayerClone *layer_clone, - const MLoopUV *mloopuv_base, - const bool is_multi_view) +static void project_paint_prepare_all_faces(ProjPaintState *ps, + MemArena *arena, + const ProjPaintFaceLookup *face_lookup, + ProjPaintLayerClone *layer_clone, + const MLoopUV *mloopuv_base, + const bool is_multi_view) { - /* Image Vars - keep track of images we have used */ - LinkNodePair image_LinkList = {NULL, NULL}; - - Image *tpage_last = NULL, *tpage; - TexPaintSlot *slot_last = NULL; - TexPaintSlot *slot = NULL; - const MLoopTri *lt; - int image_index = -1, tri_index; - int prev_poly = -1; - - for (tri_index = 0, lt = ps->mlooptri_eval; tri_index < ps->totlooptri_eval; tri_index++, lt++) { - bool is_face_sel; - bool skip_tri = false; - - is_face_sel = project_paint_check_face_sel(ps, face_lookup, lt); - - if (!ps->do_stencil_brush) { - slot = project_paint_face_paint_slot(ps, tri_index); - /* all faces should have a valid slot, reassert here */ - if (slot == NULL) { - mloopuv_base = CustomData_get_layer(&ps->me_eval->ldata, CD_MLOOPUV); - tpage = ps->canvas_ima; - } - else { - if (slot != slot_last) { - if (!slot->uvname || !(mloopuv_base = CustomData_get_layer_named(&ps->me_eval->ldata, CD_MLOOPUV, slot->uvname))) - mloopuv_base = CustomData_get_layer(&ps->me_eval->ldata, CD_MLOOPUV); - slot_last = slot; - } - - /* don't allow using the same inage for painting and stencilling */ - if (slot->ima == ps->stencil_ima) { - /* Delay continuing the loop until after loop_uvs and bleed faces are initialized. - * While this shouldn't be used, face-winding reads all polys. - * It's less trouble to set all faces to valid UV's, - * avoiding NULL checks all over. */ - skip_tri = true; - } - else { - tpage = slot->ima; - } - } - } - else { - tpage = ps->stencil_ima; - } - - ps->poly_to_loop_uv[lt->poly] = mloopuv_base; + /* Image Vars - keep track of images we have used */ + LinkNodePair image_LinkList = {NULL, NULL}; + + Image *tpage_last = NULL, *tpage; + TexPaintSlot *slot_last = NULL; + TexPaintSlot *slot = NULL; + const MLoopTri *lt; + int image_index = -1, tri_index; + int prev_poly = -1; + + for (tri_index = 0, lt = ps->mlooptri_eval; tri_index < ps->totlooptri_eval; tri_index++, lt++) { + bool is_face_sel; + bool skip_tri = false; + + is_face_sel = project_paint_check_face_sel(ps, face_lookup, lt); + + if (!ps->do_stencil_brush) { + slot = project_paint_face_paint_slot(ps, tri_index); + /* all faces should have a valid slot, reassert here */ + if (slot == NULL) { + mloopuv_base = CustomData_get_layer(&ps->me_eval->ldata, CD_MLOOPUV); + tpage = ps->canvas_ima; + } + else { + if (slot != slot_last) { + if (!slot->uvname || !(mloopuv_base = CustomData_get_layer_named( + &ps->me_eval->ldata, CD_MLOOPUV, slot->uvname))) + mloopuv_base = CustomData_get_layer(&ps->me_eval->ldata, CD_MLOOPUV); + slot_last = slot; + } + + /* don't allow using the same inage for painting and stencilling */ + if (slot->ima == ps->stencil_ima) { + /* Delay continuing the loop until after loop_uvs and bleed faces are initialized. + * While this shouldn't be used, face-winding reads all polys. + * It's less trouble to set all faces to valid UV's, + * avoiding NULL checks all over. */ + skip_tri = true; + } + else { + tpage = slot->ima; + } + } + } + else { + tpage = ps->stencil_ima; + } + + ps->poly_to_loop_uv[lt->poly] = mloopuv_base; #ifndef PROJ_DEBUG_NOSEAMBLEED - project_paint_bleed_add_face_user(ps, arena, lt, tri_index); + project_paint_bleed_add_face_user(ps, arena, lt, tri_index); #endif - if (skip_tri || project_paint_clone_face_skip(ps, layer_clone, slot, tri_index)) { - continue; - } + if (skip_tri || project_paint_clone_face_skip(ps, layer_clone, slot, tri_index)) { + continue; + } - /* tfbase here should be non-null! */ - BLI_assert(mloopuv_base != NULL); + /* tfbase here should be non-null! */ + BLI_assert(mloopuv_base != NULL); - if (is_face_sel && tpage) { - ProjPaintFaceCoSS coSS; - proj_paint_face_coSS_init(ps, lt, &coSS); + if (is_face_sel && tpage) { + ProjPaintFaceCoSS coSS; + proj_paint_face_coSS_init(ps, lt, &coSS); - if (is_multi_view == false) { - if (project_paint_flt_max_cull(ps, &coSS)) { - continue; - } + if (is_multi_view == false) { + if (project_paint_flt_max_cull(ps, &coSS)) { + continue; + } #ifdef PROJ_DEBUG_WINCLIP - if (project_paint_winclip(ps, &coSS)) { - continue; - } - -#endif //PROJ_DEBUG_WINCLIP - - /* backface culls individual triangles but mask normal will use polygon */ - if (ps->do_backfacecull) { - if (ps->do_mask_normal) { - if (prev_poly != lt->poly) { - int iloop; - bool culled = true; - const MPoly *poly = ps->mpoly_eval + lt->poly; - int poly_loops = poly->totloop; - prev_poly = lt->poly; - for (iloop = 0; iloop < poly_loops; iloop++) { - if (!(ps->vertFlags[ps->mloop_eval[poly->loopstart + iloop].v] & PROJ_VERT_CULL)) { - culled = false; - break; - } - } - - if (culled) { - /* poly loops - 2 is number of triangles for poly, - * but counter gets incremented when continuing, so decrease by 3 */ - int poly_tri = poly_loops - 3; - tri_index += poly_tri; - lt += poly_tri; - continue; - } - } - } - else { - if ((line_point_side_v2(coSS.v1, coSS.v2, coSS.v3) < 0.0f) != ps->is_flip_object) { - continue; - } - } - } - } - - if (tpage_last != tpage) { - - image_index = BLI_linklist_index(image_LinkList.list, tpage); - - if (image_index == -1 && BKE_image_has_ibuf(tpage, NULL)) { - /* MemArena dosnt have an append func */ - BLI_linklist_append(&image_LinkList, tpage); - image_index = ps->image_tot; - ps->image_tot++; - } - - tpage_last = tpage; - } - - if (image_index != -1) { - /* Initialize the faces screen pixels */ - /* Add this to a list to initialize later */ - project_paint_delayed_face_init(ps, lt, tri_index); - } - } - } - - /* build an array of images we use*/ - if (ps->is_shared_user == false) { - project_paint_build_proj_ima(ps, arena, image_LinkList.list); - } - - /* we have built the array, discard the linked list */ - BLI_linklist_free(image_LinkList.list, NULL); + if (project_paint_winclip(ps, &coSS)) { + continue; + } + +#endif //PROJ_DEBUG_WINCLIP + + /* backface culls individual triangles but mask normal will use polygon */ + if (ps->do_backfacecull) { + if (ps->do_mask_normal) { + if (prev_poly != lt->poly) { + int iloop; + bool culled = true; + const MPoly *poly = ps->mpoly_eval + lt->poly; + int poly_loops = poly->totloop; + prev_poly = lt->poly; + for (iloop = 0; iloop < poly_loops; iloop++) { + if (!(ps->vertFlags[ps->mloop_eval[poly->loopstart + iloop].v] & PROJ_VERT_CULL)) { + culled = false; + break; + } + } + + if (culled) { + /* poly loops - 2 is number of triangles for poly, + * but counter gets incremented when continuing, so decrease by 3 */ + int poly_tri = poly_loops - 3; + tri_index += poly_tri; + lt += poly_tri; + continue; + } + } + } + else { + if ((line_point_side_v2(coSS.v1, coSS.v2, coSS.v3) < 0.0f) != ps->is_flip_object) { + continue; + } + } + } + } + + if (tpage_last != tpage) { + + image_index = BLI_linklist_index(image_LinkList.list, tpage); + + if (image_index == -1 && BKE_image_has_ibuf(tpage, NULL)) { + /* MemArena dosnt have an append func */ + BLI_linklist_append(&image_LinkList, tpage); + image_index = ps->image_tot; + ps->image_tot++; + } + + tpage_last = tpage; + } + + if (image_index != -1) { + /* Initialize the faces screen pixels */ + /* Add this to a list to initialize later */ + project_paint_delayed_face_init(ps, lt, tri_index); + } + } + } + + /* build an array of images we use*/ + if (ps->is_shared_user == false) { + project_paint_build_proj_ima(ps, arena, image_LinkList.list); + } + + /* we have built the array, discard the linked list */ + BLI_linklist_free(image_LinkList.list, NULL); } /* run once per stroke before projection painting */ -static void project_paint_begin( - const bContext *C, ProjPaintState *ps, - const bool is_multi_view, const char symmetry_flag) +static void project_paint_begin(const bContext *C, + ProjPaintState *ps, + const bool is_multi_view, + const char symmetry_flag) { - ProjPaintLayerClone layer_clone; - ProjPaintFaceLookup face_lookup; - const MLoopUV *mloopuv_base = NULL; + ProjPaintLayerClone layer_clone; + ProjPaintFaceLookup face_lookup; + const MLoopUV *mloopuv_base = NULL; - /* at the moment this is just ps->arena_mt[0], but use this to show were not multithreading */ - MemArena *arena; + /* at the moment this is just ps->arena_mt[0], but use this to show were not multithreading */ + MemArena *arena; - const int diameter = 2 * BKE_brush_size_get(ps->scene, ps->brush); + const int diameter = 2 * BKE_brush_size_get(ps->scene, ps->brush); - bool reset_threads = false; + bool reset_threads = false; - /* ---- end defines ---- */ + /* ---- end defines ---- */ - if (ps->source == PROJ_SRC_VIEW) { - /* faster clipping lookups */ - ED_view3d_clipping_local(ps->rv3d, ps->ob->obmat); - } + if (ps->source == PROJ_SRC_VIEW) { + /* faster clipping lookups */ + ED_view3d_clipping_local(ps->rv3d, ps->ob->obmat); + } - ps->do_face_sel = ((((Mesh *)ps->ob->data)->editflag & ME_EDIT_PAINT_FACE_SEL) != 0); - ps->is_flip_object = (ps->ob->transflag & OB_NEG_SCALE) != 0; + ps->do_face_sel = ((((Mesh *)ps->ob->data)->editflag & ME_EDIT_PAINT_FACE_SEL) != 0); + ps->is_flip_object = (ps->ob->transflag & OB_NEG_SCALE) != 0; - /* paint onto the derived mesh */ - if (ps->is_shared_user == false) { - if (!proj_paint_state_mesh_eval_init(C, ps)) { - return; - } - } + /* paint onto the derived mesh */ + if (ps->is_shared_user == false) { + if (!proj_paint_state_mesh_eval_init(C, ps)) { + return; + } + } - proj_paint_face_lookup_init(ps, &face_lookup); - proj_paint_layer_clone_init(ps, &layer_clone); + proj_paint_face_lookup_init(ps, &face_lookup); + proj_paint_layer_clone_init(ps, &layer_clone); - if (ps->do_layer_stencil || ps->do_stencil_brush) { - //int layer_num = CustomData_get_stencil_layer(&ps->me_eval->ldata, CD_MLOOPUV); - int layer_num = CustomData_get_stencil_layer(&((Mesh *)ps->ob->data)->ldata, CD_MLOOPUV); - if (layer_num != -1) - ps->mloopuv_stencil_eval = CustomData_get_layer_n(&ps->me_eval->ldata, CD_MLOOPUV, layer_num); + if (ps->do_layer_stencil || ps->do_stencil_brush) { + //int layer_num = CustomData_get_stencil_layer(&ps->me_eval->ldata, CD_MLOOPUV); + int layer_num = CustomData_get_stencil_layer(&((Mesh *)ps->ob->data)->ldata, CD_MLOOPUV); + if (layer_num != -1) + ps->mloopuv_stencil_eval = CustomData_get_layer_n( + &ps->me_eval->ldata, CD_MLOOPUV, layer_num); - if (ps->mloopuv_stencil_eval == NULL) { - /* get active instead */ - ps->mloopuv_stencil_eval = CustomData_get_layer(&ps->me_eval->ldata, CD_MLOOPUV); - } + if (ps->mloopuv_stencil_eval == NULL) { + /* get active instead */ + ps->mloopuv_stencil_eval = CustomData_get_layer(&ps->me_eval->ldata, CD_MLOOPUV); + } - if (ps->do_stencil_brush) - mloopuv_base = ps->mloopuv_stencil_eval; - } + if (ps->do_stencil_brush) + mloopuv_base = ps->mloopuv_stencil_eval; + } - /* when using subsurf or multires, mface arrays are thrown away, we need to keep a copy */ - if (ps->is_shared_user == false) { - proj_paint_state_cavity_init(ps); - } + /* when using subsurf or multires, mface arrays are thrown away, we need to keep a copy */ + if (ps->is_shared_user == false) { + proj_paint_state_cavity_init(ps); + } - proj_paint_state_viewport_init(ps, symmetry_flag); + proj_paint_state_viewport_init(ps, symmetry_flag); - /* calculate vert screen coords - * run this early so we can calculate the x/y resolution of our bucket rect */ - proj_paint_state_screen_coords_init(ps, diameter); + /* calculate vert screen coords + * run this early so we can calculate the x/y resolution of our bucket rect */ + proj_paint_state_screen_coords_init(ps, diameter); - /* only for convenience */ - ps->screen_width = ps->screenMax[0] - ps->screenMin[0]; - ps->screen_height = ps->screenMax[1] - ps->screenMin[1]; + /* 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)diameter) / PROJ_BUCKET_BRUSH_DIV)); - ps->buckets_y = (int)(ps->screen_height / (((float)diameter) / PROJ_BUCKET_BRUSH_DIV)); + ps->buckets_x = (int)(ps->screen_width / (((float)diameter) / PROJ_BUCKET_BRUSH_DIV)); + ps->buckets_y = (int)(ps->screen_height / (((float)diameter) / PROJ_BUCKET_BRUSH_DIV)); - /* printf("\tscreenspace bucket division x:%d y:%d\n", ps->buckets_x, ps->buckets_y); */ + /* printf("\tscreenspace bucket division x:%d y:%d\n", ps->buckets_x, ps->buckets_y); */ - if (ps->buckets_x > PROJ_BUCKET_RECT_MAX || ps->buckets_y > PROJ_BUCKET_RECT_MAX) { - reset_threads = true; - } + if (ps->buckets_x > PROJ_BUCKET_RECT_MAX || ps->buckets_y > PROJ_BUCKET_RECT_MAX) { + reset_threads = true; + } - /* 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); + /* 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 = MEM_callocN(sizeof(LinkNode *) * ps->buckets_x * ps->buckets_y, "paint-bucketRect"); - ps->bucketFaces = MEM_callocN(sizeof(LinkNode *) * ps->buckets_x * ps->buckets_y, "paint-bucketFaces"); + ps->bucketRect = MEM_callocN(sizeof(LinkNode *) * ps->buckets_x * ps->buckets_y, + "paint-bucketRect"); + ps->bucketFaces = MEM_callocN(sizeof(LinkNode *) * ps->buckets_x * ps->buckets_y, + "paint-bucketFaces"); - ps->bucketFlags = MEM_callocN(sizeof(char) * ps->buckets_x * ps->buckets_y, "paint-bucketFaces"); + ps->bucketFlags = MEM_callocN(sizeof(char) * ps->buckets_x * ps->buckets_y, "paint-bucketFaces"); #ifndef PROJ_DEBUG_NOSEAMBLEED - if (ps->is_shared_user == false) { - proj_paint_state_seam_bleed_init(ps); - } + if (ps->is_shared_user == false) { + proj_paint_state_seam_bleed_init(ps); + } #endif - proj_paint_state_thread_init(ps, reset_threads); - arena = ps->arena_mt[0]; + proj_paint_state_thread_init(ps, reset_threads); + arena = ps->arena_mt[0]; - proj_paint_state_vert_flags_init(ps); + proj_paint_state_vert_flags_init(ps); - project_paint_prepare_all_faces(ps, arena, &face_lookup, &layer_clone, mloopuv_base, is_multi_view); + project_paint_prepare_all_faces( + ps, arena, &face_lookup, &layer_clone, mloopuv_base, is_multi_view); } static void paint_proj_begin_clone(ProjPaintState *ps, const float mouse[2]) { - /* setup clone offset */ - if (ps->tool == PAINT_TOOL_CLONE) { - float projCo[4]; - copy_v3_v3(projCo, ps->scene->cursor.location); - mul_m4_v3(ps->obmat_imat, projCo); - - projCo[3] = 1.0f; - mul_m4_v4(ps->projectMat, projCo); - ps->cloneOffset[0] = mouse[0] - ((float)(ps->winx * 0.5f) + (ps->winx * 0.5f) * projCo[0] / projCo[3]); - ps->cloneOffset[1] = mouse[1] - ((float)(ps->winy * 0.5f) + (ps->winy * 0.5f) * projCo[1] / projCo[3]); - } + /* setup clone offset */ + if (ps->tool == PAINT_TOOL_CLONE) { + float projCo[4]; + copy_v3_v3(projCo, ps->scene->cursor.location); + mul_m4_v3(ps->obmat_imat, projCo); + + projCo[3] = 1.0f; + mul_m4_v4(ps->projectMat, projCo); + ps->cloneOffset[0] = mouse[0] - + ((float)(ps->winx * 0.5f) + (ps->winx * 0.5f) * projCo[0] / projCo[3]); + ps->cloneOffset[1] = mouse[1] - + ((float)(ps->winy * 0.5f) + (ps->winy * 0.5f) * projCo[1] / projCo[3]); + } } static void project_paint_end(ProjPaintState *ps) { - int a; - - image_undo_remove_masks(); - - /* dereference used image buffers */ - if (ps->is_shared_user == false) { - ProjPaintImage *projIma; - for (a = 0, projIma = ps->projImages; a < ps->image_tot; a++, projIma++) { - BKE_image_release_ibuf(projIma->ima, projIma->ibuf, NULL); - DEG_id_tag_update(&projIma->ima->id, 0); - } - } - - if (ps->reproject_ibuf_free_float) { - imb_freerectfloatImBuf(ps->reproject_ibuf); - } - if (ps->reproject_ibuf_free_uchar) { - imb_freerectImBuf(ps->reproject_ibuf); - } - BKE_image_release_ibuf(ps->reproject_image, ps->reproject_ibuf, NULL); - - MEM_freeN(ps->screenCoords); - MEM_freeN(ps->bucketRect); - MEM_freeN(ps->bucketFaces); - MEM_freeN(ps->bucketFlags); - - if (ps->is_shared_user == false) { - if (ps->mat_array != NULL) { - MEM_freeN(ps->mat_array); - } - - /* must be set for non-shared */ - BLI_assert(ps->poly_to_loop_uv || ps->is_shared_user); - if (ps->poly_to_loop_uv) - MEM_freeN((void *)ps->poly_to_loop_uv); - - if (ps->do_layer_clone) - MEM_freeN((void *)ps->poly_to_loop_uv_clone); - if (ps->thread_tot > 1) { - BLI_spin_end(ps->tile_lock); - MEM_freeN((void *)ps->tile_lock); - } - - image_undo_end_locks(); + int a; + + image_undo_remove_masks(); + + /* dereference used image buffers */ + if (ps->is_shared_user == false) { + ProjPaintImage *projIma; + for (a = 0, projIma = ps->projImages; a < ps->image_tot; a++, projIma++) { + BKE_image_release_ibuf(projIma->ima, projIma->ibuf, NULL); + DEG_id_tag_update(&projIma->ima->id, 0); + } + } + + if (ps->reproject_ibuf_free_float) { + imb_freerectfloatImBuf(ps->reproject_ibuf); + } + if (ps->reproject_ibuf_free_uchar) { + imb_freerectImBuf(ps->reproject_ibuf); + } + BKE_image_release_ibuf(ps->reproject_image, ps->reproject_ibuf, NULL); + + MEM_freeN(ps->screenCoords); + MEM_freeN(ps->bucketRect); + MEM_freeN(ps->bucketFaces); + MEM_freeN(ps->bucketFlags); + + if (ps->is_shared_user == false) { + if (ps->mat_array != NULL) { + MEM_freeN(ps->mat_array); + } + + /* must be set for non-shared */ + BLI_assert(ps->poly_to_loop_uv || ps->is_shared_user); + if (ps->poly_to_loop_uv) + MEM_freeN((void *)ps->poly_to_loop_uv); + + if (ps->do_layer_clone) + MEM_freeN((void *)ps->poly_to_loop_uv_clone); + if (ps->thread_tot > 1) { + BLI_spin_end(ps->tile_lock); + MEM_freeN((void *)ps->tile_lock); + } + + image_undo_end_locks(); #ifndef PROJ_DEBUG_NOSEAMBLEED - if (ps->seam_bleed_px > 0.0f) { - MEM_freeN(ps->vertFaces); - MEM_freeN(ps->faceSeamFlags); - MEM_freeN(ps->faceWindingFlags); - MEM_freeN(ps->loopSeamData); - MEM_freeN(ps->vertSeams); - } + if (ps->seam_bleed_px > 0.0f) { + MEM_freeN(ps->vertFaces); + MEM_freeN(ps->faceSeamFlags); + MEM_freeN(ps->faceWindingFlags); + MEM_freeN(ps->loopSeamData); + MEM_freeN(ps->vertSeams); + } #endif - if (ps->do_mask_cavity) { - MEM_freeN(ps->cavities); - } + if (ps->do_mask_cavity) { + MEM_freeN(ps->cavities); + } - if (ps->me_eval_free) { - BKE_id_free(NULL, ps->me_eval); - } - ps->me_eval = NULL; - } + if (ps->me_eval_free) { + BKE_id_free(NULL, ps->me_eval); + } + ps->me_eval = NULL; + } - if (ps->blurkernel) { - paint_delete_blur_kernel(ps->blurkernel); - MEM_freeN(ps->blurkernel); - } + if (ps->blurkernel) { + paint_delete_blur_kernel(ps->blurkernel); + MEM_freeN(ps->blurkernel); + } - if (ps->vertFlags) MEM_freeN(ps->vertFlags); + if (ps->vertFlags) + MEM_freeN(ps->vertFlags); - for (a = 0; a < ps->thread_tot; a++) { - BLI_memarena_free(ps->arena_mt[a]); - } + for (a = 0; a < ps->thread_tot; a++) { + BLI_memarena_free(ps->arena_mt[a]); + } } /* 1 = an undo, -1 is a redo. */ static void partial_redraw_single_init(ImagePaintPartialRedraw *pr) { - pr->x1 = INT_MAX; - pr->y1 = INT_MAX; + pr->x1 = INT_MAX; + pr->y1 = INT_MAX; - pr->x2 = -1; - pr->y2 = -1; + pr->x2 = -1; + pr->y2 = -1; - pr->enabled = 1; + pr->enabled = 1; } static void partial_redraw_array_init(ImagePaintPartialRedraw *pr) { - int tot = PROJ_BOUNDBOX_SQUARED; - while (tot--) { - partial_redraw_single_init(pr); - pr++; - } + int tot = PROJ_BOUNDBOX_SQUARED; + while (tot--) { + partial_redraw_single_init(pr); + pr++; + } } - -static bool partial_redraw_array_merge(ImagePaintPartialRedraw *pr, ImagePaintPartialRedraw *pr_other, int tot) +static bool partial_redraw_array_merge(ImagePaintPartialRedraw *pr, + ImagePaintPartialRedraw *pr_other, + int tot) { - bool touch = 0; - while (tot--) { - pr->x1 = min_ii(pr->x1, pr_other->x1); - pr->y1 = min_ii(pr->y1, pr_other->y1); + bool touch = 0; + while (tot--) { + pr->x1 = min_ii(pr->x1, pr_other->x1); + pr->y1 = min_ii(pr->y1, pr_other->y1); - pr->x2 = max_ii(pr->x2, pr_other->x2); - pr->y2 = max_ii(pr->y2, pr_other->y2); + pr->x2 = max_ii(pr->x2, pr_other->x2); + pr->y2 = max_ii(pr->y2, pr_other->y2); - if (pr->x2 != -1) - touch = 1; + if (pr->x2 != -1) + touch = 1; - pr++; pr_other++; - } + pr++; + pr_other++; + } - return touch; + return touch; } /* Loop over all images on this mesh and update any we have touched */ static bool project_image_refresh_tagged(ProjPaintState *ps) { - ImagePaintPartialRedraw *pr; - ProjPaintImage *projIma; - int a, i; - bool redraw = false; - - - 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' ? */ - set_imapaintpartial(pr); - imapaint_image_update(NULL, projIma->ima, projIma->ibuf, true); - redraw = 1; - } - - partial_redraw_single_init(pr); - } - - /* clear for reuse */ - projIma->touch = 0; - } - } - - return redraw; + ImagePaintPartialRedraw *pr; + ProjPaintImage *projIma; + int a, i; + bool redraw = false; + + 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' ? */ + set_imapaintpartial(pr); + imapaint_image_update(NULL, projIma->ima, projIma->ibuf, true); + redraw = 1; + } + + partial_redraw_single_init(pr); + } + + /* clear for reuse */ + projIma->touch = 0; + } + } + + return redraw; } /* run this per painting onto each mouse location */ static bool project_bucket_iter_init(ProjPaintState *ps, const float mval_f[2]) { - if (ps->source == PROJ_SRC_VIEW) { - float min_brush[2], max_brush[2]; - const float radius = ps->brush_size; + if (ps->source == PROJ_SRC_VIEW) { + float min_brush[2], max_brush[2]; + const float radius = ps->brush_size; - /* so we don't have a bucket bounds that is way too small to paint into */ - // if (radius < 1.0f) radius = 1.0f; // this doesn't work yet :/ + /* so we don't have a bucket bounds that is way too small to paint into */ + // if (radius < 1.0f) radius = 1.0f; // this doesn't work yet :/ - min_brush[0] = mval_f[0] - radius; - min_brush[1] = mval_f[1] - radius; + min_brush[0] = mval_f[0] - radius; + min_brush[1] = mval_f[1] - radius; - max_brush[0] = mval_f[0] + radius; - max_brush[1] = mval_f[1] + radius; + max_brush[0] = mval_f[0] + radius; + max_brush[1] = mval_f[1] + radius; - /* offset to make this a valid bucket index */ - project_paint_bucket_bounds(ps, min_brush, max_brush, ps->bucketMin, ps->bucketMax); + /* 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; - } + /* 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]; - } - else { /* reproject: PROJ_SRC_* */ - ps->bucketMin[0] = 0; - ps->bucketMin[1] = 0; + ps->context_bucket_x = ps->bucketMin[0]; + ps->context_bucket_y = ps->bucketMin[1]; + } + else { /* reproject: PROJ_SRC_* */ + ps->bucketMin[0] = 0; + ps->bucketMin[1] = 0; - ps->bucketMax[0] = ps->buckets_x; - ps->bucketMax[1] = ps->buckets_y; + ps->bucketMax[0] = ps->buckets_x; + ps->bucketMax[1] = ps->buckets_y; - ps->context_bucket_x = 0; - ps->context_bucket_y = 0; - } - return 1; + ps->context_bucket_x = 0; + ps->context_bucket_y = 0; + } + return 1; } - -static bool project_bucket_iter_next( - ProjPaintState *ps, int *bucket_index, - rctf *bucket_bounds, const float mval[2]) +static bool project_bucket_iter_next(ProjPaintState *ps, + int *bucket_index, + rctf *bucket_bounds, + const float mval[2]) { - const int diameter = 2 * ps->brush_size; + const int diameter = 2 * ps->brush_size; - if (ps->thread_tot > 1) - BLI_thread_lock(LOCK_CUSTOM1); + if (ps->thread_tot > 1) + BLI_thread_lock(LOCK_CUSTOM1); - //printf("%d %d\n", ps->context_bucket_x, ps->context_bucket_y); + //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++) { + 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); + /* 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 ((ps->source != PROJ_SRC_VIEW) || - project_bucket_isect_circle(mval, (float)(diameter * diameter), bucket_bounds)) - { - *bucket_index = ps->context_bucket_x + (ps->context_bucket_y * ps->buckets_x); - ps->context_bucket_x++; + if ((ps->source != PROJ_SRC_VIEW) || + project_bucket_isect_circle(mval, (float)(diameter * diameter), 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_thread_unlock(LOCK_CUSTOM1); + if (ps->thread_tot > 1) + BLI_thread_unlock(LOCK_CUSTOM1); - return 1; - } - } - ps->context_bucket_x = ps->bucketMin[0]; - } + return 1; + } + } + ps->context_bucket_x = ps->bucketMin[0]; + } - if (ps->thread_tot > 1) - BLI_thread_unlock(LOCK_CUSTOM1); - return 0; + if (ps->thread_tot > 1) + BLI_thread_unlock(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]; + /* args */ + ProjPaintState *ps; + float prevmval[2]; + float mval[2]; - /* Annoying but we need to have image bounds per thread, - * then merge into ps->projectPartialRedraws. */ + /* Annoying but we need to have image bounds per thread, + * then merge into ps->projectPartialRedraws. */ - /* array of partial redraws */ - ProjPaintImage *projImages; + /* array of partial redraws */ + ProjPaintImage *projImages; - /* thread settings */ - int thread_index; + /* thread settings */ + int thread_index; - struct ImagePool *pool; + struct ImagePool *pool; } ProjectHandle; static void do_projectpaint_clone(ProjPaintState *ps, ProjPixel *projPixel, float mask) { - const unsigned char *clone_pt = ((ProjPixelClone *)projPixel)->clonepx.ch; - - if (clone_pt[3]) { - unsigned char clone_rgba[4]; - - clone_rgba[0] = clone_pt[0]; - clone_rgba[1] = clone_pt[1]; - clone_rgba[2] = clone_pt[2]; - clone_rgba[3] = (unsigned char)(clone_pt[3] * mask); - - if (ps->do_masking) { - IMB_blend_color_byte(projPixel->pixel.ch_pt, projPixel->origColor.ch_pt, clone_rgba, ps->blend); - } - else { - IMB_blend_color_byte(projPixel->pixel.ch_pt, projPixel->pixel.ch_pt, clone_rgba, ps->blend); - } - } + const unsigned char *clone_pt = ((ProjPixelClone *)projPixel)->clonepx.ch; + + if (clone_pt[3]) { + unsigned char clone_rgba[4]; + + clone_rgba[0] = clone_pt[0]; + clone_rgba[1] = clone_pt[1]; + clone_rgba[2] = clone_pt[2]; + clone_rgba[3] = (unsigned char)(clone_pt[3] * mask); + + if (ps->do_masking) { + IMB_blend_color_byte( + projPixel->pixel.ch_pt, projPixel->origColor.ch_pt, clone_rgba, ps->blend); + } + else { + IMB_blend_color_byte(projPixel->pixel.ch_pt, projPixel->pixel.ch_pt, clone_rgba, ps->blend); + } + } } static void do_projectpaint_clone_f(ProjPaintState *ps, ProjPixel *projPixel, float mask) { - const float *clone_pt = ((ProjPixelClone *)projPixel)->clonepx.f; + const float *clone_pt = ((ProjPixelClone *)projPixel)->clonepx.f; - if (clone_pt[3]) { - float clone_rgba[4]; + if (clone_pt[3]) { + float clone_rgba[4]; - mul_v4_v4fl(clone_rgba, clone_pt, mask); + mul_v4_v4fl(clone_rgba, clone_pt, mask); - if (ps->do_masking) { - IMB_blend_color_float(projPixel->pixel.f_pt, projPixel->origColor.f_pt, clone_rgba, ps->blend); - } - else { - IMB_blend_color_float(projPixel->pixel.f_pt, projPixel->pixel.f_pt, clone_rgba, ps->blend); - } - } + if (ps->do_masking) { + IMB_blend_color_float( + projPixel->pixel.f_pt, projPixel->origColor.f_pt, clone_rgba, ps->blend); + } + else { + IMB_blend_color_float(projPixel->pixel.f_pt, projPixel->pixel.f_pt, clone_rgba, ps->blend); + } + } } /** @@ -4609,1342 +4760,1396 @@ static void do_projectpaint_clone_f(ProjPaintState *ps, ProjPixel *projPixel, fl * accumulation of color greater than '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 mask, - MemArena *smearArena, LinkNode **smearPixels, const float co[2]) +static void do_projectpaint_smear(ProjPaintState *ps, + ProjPixel *projPixel, + float mask, + MemArena *smearArena, + LinkNode **smearPixels, + const float co[2]) { - unsigned char rgba_ub[4]; + unsigned char rgba_ub[4]; - if (project_paint_PickColor(ps, co, NULL, rgba_ub, 1) == 0) - return; + if (project_paint_PickColor(ps, co, NULL, rgba_ub, 1) == 0) + return; - blend_color_interpolate_byte(((ProjPixelClone *)projPixel)->clonepx.ch, projPixel->pixel.ch_pt, rgba_ub, mask); - BLI_linklist_prepend_arena(smearPixels, (void *)projPixel, smearArena); + blend_color_interpolate_byte( + ((ProjPixelClone *)projPixel)->clonepx.ch, projPixel->pixel.ch_pt, rgba_ub, mask); + BLI_linklist_prepend_arena(smearPixels, (void *)projPixel, smearArena); } -static void do_projectpaint_smear_f( - ProjPaintState *ps, ProjPixel *projPixel, float mask, - MemArena *smearArena, LinkNode **smearPixels_f, const float co[2]) +static void do_projectpaint_smear_f(ProjPaintState *ps, + ProjPixel *projPixel, + float mask, + MemArena *smearArena, + LinkNode **smearPixels_f, + const float co[2]) { - float rgba[4]; + float rgba[4]; - if (project_paint_PickColor(ps, co, rgba, NULL, 1) == 0) - return; + if (project_paint_PickColor(ps, co, rgba, NULL, 1) == 0) + return; - blend_color_interpolate_float(((ProjPixelClone *)projPixel)->clonepx.f, projPixel->pixel.f_pt, rgba, mask); - BLI_linklist_prepend_arena(smearPixels_f, (void *)projPixel, smearArena); + blend_color_interpolate_float( + ((ProjPixelClone *)projPixel)->clonepx.f, projPixel->pixel.f_pt, rgba, mask); + BLI_linklist_prepend_arena(smearPixels_f, (void *)projPixel, smearArena); } -static void do_projectpaint_soften_f( - ProjPaintState *ps, ProjPixel *projPixel, float mask, - MemArena *softenArena, LinkNode **softenPixels) +static void do_projectpaint_soften_f(ProjPaintState *ps, + ProjPixel *projPixel, + float mask, + MemArena *softenArena, + LinkNode **softenPixels) { - float accum_tot = 0.0f; - int xk, yk; - BlurKernel *kernel = ps->blurkernel; - float *rgba = projPixel->newColor.f; - - /* rather then painting, accumulate surrounding colors */ - zero_v4(rgba); - - for (yk = 0; yk < kernel->side; yk++) { - for (xk = 0; xk < kernel->side; xk++) { - float rgba_tmp[4]; - float co_ofs[2] = {2.0f * xk - 1.0f, 2.0f * yk - 1.0f}; - - add_v2_v2(co_ofs, projPixel->projCoSS); - - if (project_paint_PickColor(ps, co_ofs, rgba_tmp, NULL, true)) { - float weight = kernel->wdata[xk + yk * kernel->side]; - mul_v4_fl(rgba_tmp, weight); - add_v4_v4(rgba, rgba_tmp); - accum_tot += weight; - } - } - } - - if (LIKELY(accum_tot != 0)) { - mul_v4_fl(rgba, 1.0f / (float)accum_tot); - - if (ps->mode == BRUSH_STROKE_INVERT) { - /* subtract blurred image from normal image gives high pass filter */ - sub_v3_v3v3(rgba, projPixel->pixel.f_pt, rgba); - - /* now rgba_ub contains the edge result, but this should be converted to luminance to avoid - * colored speckles appearing in final image, and also to check for threshold */ - rgba[0] = rgba[1] = rgba[2] = IMB_colormanagement_get_luminance(rgba); - if (fabsf(rgba[0]) > ps->brush->sharp_threshold) { - float alpha = projPixel->pixel.f_pt[3]; - projPixel->pixel.f_pt[3] = rgba[3] = mask; - - /* add to enhance edges */ - blend_color_add_float(rgba, projPixel->pixel.f_pt, rgba); - rgba[3] = alpha; - } - else - return; - } - else { - blend_color_interpolate_float(rgba, projPixel->pixel.f_pt, rgba, mask); - } - - BLI_linklist_prepend_arena(softenPixels, (void *)projPixel, softenArena); - } + float accum_tot = 0.0f; + int xk, yk; + BlurKernel *kernel = ps->blurkernel; + float *rgba = projPixel->newColor.f; + + /* rather then painting, accumulate surrounding colors */ + zero_v4(rgba); + + for (yk = 0; yk < kernel->side; yk++) { + for (xk = 0; xk < kernel->side; xk++) { + float rgba_tmp[4]; + float co_ofs[2] = {2.0f * xk - 1.0f, 2.0f * yk - 1.0f}; + + add_v2_v2(co_ofs, projPixel->projCoSS); + + if (project_paint_PickColor(ps, co_ofs, rgba_tmp, NULL, true)) { + float weight = kernel->wdata[xk + yk * kernel->side]; + mul_v4_fl(rgba_tmp, weight); + add_v4_v4(rgba, rgba_tmp); + accum_tot += weight; + } + } + } + + if (LIKELY(accum_tot != 0)) { + mul_v4_fl(rgba, 1.0f / (float)accum_tot); + + if (ps->mode == BRUSH_STROKE_INVERT) { + /* subtract blurred image from normal image gives high pass filter */ + sub_v3_v3v3(rgba, projPixel->pixel.f_pt, rgba); + + /* now rgba_ub contains the edge result, but this should be converted to luminance to avoid + * colored speckles appearing in final image, and also to check for threshold */ + rgba[0] = rgba[1] = rgba[2] = IMB_colormanagement_get_luminance(rgba); + if (fabsf(rgba[0]) > ps->brush->sharp_threshold) { + float alpha = projPixel->pixel.f_pt[3]; + projPixel->pixel.f_pt[3] = rgba[3] = mask; + + /* add to enhance edges */ + blend_color_add_float(rgba, projPixel->pixel.f_pt, rgba); + rgba[3] = alpha; + } + else + return; + } + else { + blend_color_interpolate_float(rgba, projPixel->pixel.f_pt, rgba, mask); + } + + BLI_linklist_prepend_arena(softenPixels, (void *)projPixel, softenArena); + } } -static void do_projectpaint_soften( - ProjPaintState *ps, ProjPixel *projPixel, float mask, - MemArena *softenArena, LinkNode **softenPixels) +static void do_projectpaint_soften(ProjPaintState *ps, + ProjPixel *projPixel, + float mask, + MemArena *softenArena, + LinkNode **softenPixels) { - float accum_tot = 0; - int xk, yk; - BlurKernel *kernel = ps->blurkernel; - /* convert to byte after */ - float rgba[4]; - - /* rather then painting, accumulate surrounding colors */ - zero_v4(rgba); - - for (yk = 0; yk < kernel->side; yk++) { - for (xk = 0; xk < kernel->side; xk++) { - float rgba_tmp[4]; - float co_ofs[2] = {2.0f * xk - 1.0f, 2.0f * yk - 1.0f}; - - add_v2_v2(co_ofs, projPixel->projCoSS); - - if (project_paint_PickColor(ps, co_ofs, rgba_tmp, NULL, true)) { - float weight = kernel->wdata[xk + yk * kernel->side]; - mul_v4_fl(rgba_tmp, weight); - add_v4_v4(rgba, rgba_tmp); - accum_tot += weight; - } - } - } - - if (LIKELY(accum_tot != 0)) { - unsigned char *rgba_ub = projPixel->newColor.ch; - - mul_v4_fl(rgba, 1.0f / (float)accum_tot); - - if (ps->mode == BRUSH_STROKE_INVERT) { - float rgba_pixel[4]; - - straight_uchar_to_premul_float(rgba_pixel, projPixel->pixel.ch_pt); - - /* subtract blurred image from normal image gives high pass filter */ - sub_v3_v3v3(rgba, rgba_pixel, rgba); - /* now rgba_ub contains the edge result, but this should be converted to luminance to avoid - * colored speckles appearing in final image, and also to check for threshold */ - rgba[0] = rgba[1] = rgba[2] = IMB_colormanagement_get_luminance(rgba); - if (fabsf(rgba[0]) > ps->brush->sharp_threshold) { - float alpha = rgba_pixel[3]; - rgba[3] = rgba_pixel[3] = mask; - - /* add to enhance edges */ - blend_color_add_float(rgba, rgba_pixel, rgba); - - rgba[3] = alpha; - premul_float_to_straight_uchar(rgba_ub, rgba); - } - else - return; - } - else { - premul_float_to_straight_uchar(rgba_ub, rgba); - blend_color_interpolate_byte(rgba_ub, projPixel->pixel.ch_pt, rgba_ub, mask); - } - BLI_linklist_prepend_arena(softenPixels, (void *)projPixel, softenArena); - } + float accum_tot = 0; + int xk, yk; + BlurKernel *kernel = ps->blurkernel; + /* convert to byte after */ + float rgba[4]; + + /* rather then painting, accumulate surrounding colors */ + zero_v4(rgba); + + for (yk = 0; yk < kernel->side; yk++) { + for (xk = 0; xk < kernel->side; xk++) { + float rgba_tmp[4]; + float co_ofs[2] = {2.0f * xk - 1.0f, 2.0f * yk - 1.0f}; + + add_v2_v2(co_ofs, projPixel->projCoSS); + + if (project_paint_PickColor(ps, co_ofs, rgba_tmp, NULL, true)) { + float weight = kernel->wdata[xk + yk * kernel->side]; + mul_v4_fl(rgba_tmp, weight); + add_v4_v4(rgba, rgba_tmp); + accum_tot += weight; + } + } + } + + if (LIKELY(accum_tot != 0)) { + unsigned char *rgba_ub = projPixel->newColor.ch; + + mul_v4_fl(rgba, 1.0f / (float)accum_tot); + + if (ps->mode == BRUSH_STROKE_INVERT) { + float rgba_pixel[4]; + + straight_uchar_to_premul_float(rgba_pixel, projPixel->pixel.ch_pt); + + /* subtract blurred image from normal image gives high pass filter */ + sub_v3_v3v3(rgba, rgba_pixel, rgba); + /* now rgba_ub contains the edge result, but this should be converted to luminance to avoid + * colored speckles appearing in final image, and also to check for threshold */ + rgba[0] = rgba[1] = rgba[2] = IMB_colormanagement_get_luminance(rgba); + if (fabsf(rgba[0]) > ps->brush->sharp_threshold) { + float alpha = rgba_pixel[3]; + rgba[3] = rgba_pixel[3] = mask; + + /* add to enhance edges */ + blend_color_add_float(rgba, rgba_pixel, rgba); + + rgba[3] = alpha; + premul_float_to_straight_uchar(rgba_ub, rgba); + } + else + return; + } + else { + premul_float_to_straight_uchar(rgba_ub, rgba); + blend_color_interpolate_byte(rgba_ub, projPixel->pixel.ch_pt, rgba_ub, mask); + } + BLI_linklist_prepend_arena(softenPixels, (void *)projPixel, softenArena); + } } -static void do_projectpaint_draw( - ProjPaintState *ps, ProjPixel *projPixel, const float texrgb[3], float mask, - float dither, float u, float v) +static void do_projectpaint_draw(ProjPaintState *ps, + ProjPixel *projPixel, + const float texrgb[3], + float mask, + float dither, + float u, + float v) { - float rgb[3]; - unsigned char rgba_ub[4]; - - if (ps->is_texbrush) { - mul_v3_v3v3(rgb, texrgb, ps->paint_color_linear); - /* TODO(sergey): Support texture paint color space. */ - if (ps->use_colormanagement) { - linearrgb_to_srgb_v3_v3(rgb, rgb); - } - else { - copy_v3_v3(rgb, rgb); - } - } - else { - copy_v3_v3(rgb, ps->paint_color); - } - - if (dither > 0.0f) { - float_to_byte_dither_v3(rgba_ub, rgb, dither, u, v); - } - else { - unit_float_to_uchar_clamp_v3(rgba_ub, rgb); - } - rgba_ub[3] = f_to_char(mask); - - if (ps->do_masking) { - IMB_blend_color_byte(projPixel->pixel.ch_pt, projPixel->origColor.ch_pt, rgba_ub, ps->blend); - } - else { - IMB_blend_color_byte(projPixel->pixel.ch_pt, projPixel->pixel.ch_pt, rgba_ub, ps->blend); - } + float rgb[3]; + unsigned char rgba_ub[4]; + + if (ps->is_texbrush) { + mul_v3_v3v3(rgb, texrgb, ps->paint_color_linear); + /* TODO(sergey): Support texture paint color space. */ + if (ps->use_colormanagement) { + linearrgb_to_srgb_v3_v3(rgb, rgb); + } + else { + copy_v3_v3(rgb, rgb); + } + } + else { + copy_v3_v3(rgb, ps->paint_color); + } + + if (dither > 0.0f) { + float_to_byte_dither_v3(rgba_ub, rgb, dither, u, v); + } + else { + unit_float_to_uchar_clamp_v3(rgba_ub, rgb); + } + rgba_ub[3] = f_to_char(mask); + + if (ps->do_masking) { + IMB_blend_color_byte(projPixel->pixel.ch_pt, projPixel->origColor.ch_pt, rgba_ub, ps->blend); + } + else { + IMB_blend_color_byte(projPixel->pixel.ch_pt, projPixel->pixel.ch_pt, rgba_ub, ps->blend); + } } -static void do_projectpaint_draw_f(ProjPaintState *ps, ProjPixel *projPixel, const float texrgb[3], float mask) +static void do_projectpaint_draw_f(ProjPaintState *ps, + ProjPixel *projPixel, + const float texrgb[3], + float mask) { - float rgba[4]; + float rgba[4]; - copy_v3_v3(rgba, ps->paint_color_linear); + copy_v3_v3(rgba, ps->paint_color_linear); - if (ps->is_texbrush) - mul_v3_v3(rgba, texrgb); + if (ps->is_texbrush) + mul_v3_v3(rgba, texrgb); - mul_v3_fl(rgba, mask); - rgba[3] = mask; + mul_v3_fl(rgba, mask); + rgba[3] = mask; - if (ps->do_masking) { - IMB_blend_color_float(projPixel->pixel.f_pt, projPixel->origColor.f_pt, rgba, ps->blend); - } - else { - IMB_blend_color_float(projPixel->pixel.f_pt, projPixel->pixel.f_pt, rgba, ps->blend); - } + if (ps->do_masking) { + IMB_blend_color_float(projPixel->pixel.f_pt, projPixel->origColor.f_pt, rgba, ps->blend); + } + else { + IMB_blend_color_float(projPixel->pixel.f_pt, projPixel->pixel.f_pt, rgba, ps->blend); + } } static void do_projectpaint_mask(ProjPaintState *ps, ProjPixel *projPixel, float mask) { - unsigned char rgba_ub[4]; - rgba_ub[0] = rgba_ub[1] = rgba_ub[2] = ps->stencil_value * 255.0f; - rgba_ub[3] = f_to_char(mask); - - if (ps->do_masking) { - IMB_blend_color_byte(projPixel->pixel.ch_pt, projPixel->origColor.ch_pt, rgba_ub, ps->blend); - } - else { - IMB_blend_color_byte(projPixel->pixel.ch_pt, projPixel->pixel.ch_pt, rgba_ub, ps->blend); - } + unsigned char rgba_ub[4]; + rgba_ub[0] = rgba_ub[1] = rgba_ub[2] = ps->stencil_value * 255.0f; + rgba_ub[3] = f_to_char(mask); + + if (ps->do_masking) { + IMB_blend_color_byte(projPixel->pixel.ch_pt, projPixel->origColor.ch_pt, rgba_ub, ps->blend); + } + else { + IMB_blend_color_byte(projPixel->pixel.ch_pt, projPixel->pixel.ch_pt, rgba_ub, ps->blend); + } } static void do_projectpaint_mask_f(ProjPaintState *ps, ProjPixel *projPixel, float mask) { - float rgba[4]; - rgba[0] = rgba[1] = rgba[2] = ps->stencil_value; - rgba[3] = mask; - - if (ps->do_masking) { - IMB_blend_color_float(projPixel->pixel.f_pt, projPixel->origColor.f_pt, rgba, ps->blend); - } - else { - IMB_blend_color_float(projPixel->pixel.f_pt, projPixel->pixel.f_pt, rgba, ps->blend); - } + float rgba[4]; + rgba[0] = rgba[1] = rgba[2] = ps->stencil_value; + rgba[3] = mask; + + if (ps->do_masking) { + IMB_blend_color_float(projPixel->pixel.f_pt, projPixel->origColor.f_pt, rgba, ps->blend); + } + else { + IMB_blend_color_float(projPixel->pixel.f_pt, projPixel->pixel.f_pt, rgba, ps->blend); + } } -static void image_paint_partial_redraw_expand( - ImagePaintPartialRedraw *cell, - const ProjPixel *projPixel) +static void image_paint_partial_redraw_expand(ImagePaintPartialRedraw *cell, + const ProjPixel *projPixel) { - cell->x1 = min_ii(cell->x1, (int)projPixel->x_px); - cell->y1 = min_ii(cell->y1, (int)projPixel->y_px); + cell->x1 = min_ii(cell->x1, (int)projPixel->x_px); + cell->y1 = min_ii(cell->y1, (int)projPixel->y_px); - cell->x2 = max_ii(cell->x2, (int)projPixel->x_px + 1); - cell->y2 = max_ii(cell->y2, (int)projPixel->y_px + 1); + cell->x2 = max_ii(cell->x2, (int)projPixel->x_px + 1); + cell->y2 = max_ii(cell->y2, (int)projPixel->y_px + 1); } /* 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; - struct ImagePool *pool = ((ProjectHandle *)ph_v)->pool; - /* Done with args from ProjectHandle */ - - LinkNode *node; - ProjPixel *projPixel; - Brush *brush = ps->brush; - - int last_index = -1; - ProjPaintImage *last_projIma = NULL; - ImagePaintPartialRedraw *last_partial_redraw_cell; - - float dist_sq, dist; - - float falloff; - int bucket_index; - bool is_floatbuf = false; - const short tool = ps->tool; - rctf bucket_bounds; - - /* for smear only */ - float pos_ofs[2] = {0}; - float co[2]; - unsigned short mask_short; - const float brush_alpha = BKE_brush_alpha_get(ps->scene, brush); - const float brush_radius = ps->brush_size; - /* avoid a square root with every dist comparison */ - const float brush_radius_sq = brush_radius * brush_radius; - - const bool lock_alpha = ELEM(brush->blend, IMB_BLEND_ERASE_ALPHA, IMB_BLEND_ADD_ALPHA) ? - 0 : (brush->flag & BRUSH_LOCK_ALPHA) != 0; - - LinkNode *smearPixels = NULL; - LinkNode *smearPixels_f = NULL; - /* mem arena for this brush projection only */ - MemArena *smearArena = NULL; - - LinkNode *softenPixels = NULL; - LinkNode *softenPixels_f = NULL; - /* mem arena for this brush projection only */ - MemArena *softenArena = NULL; - - if (tool == PAINT_TOOL_SMEAR) { - pos_ofs[0] = pos[0] - lastpos[0]; - pos_ofs[1] = pos[1] - lastpos[1]; - - smearArena = BLI_memarena_new(MEM_SIZE_OPTIMAL(1 << 16), "paint smear arena"); - } - else if (tool == PAINT_TOOL_SOFTEN) { - softenArena = BLI_memarena_new(MEM_SIZE_OPTIMAL(1 << 16), "paint soften arena"); - } - - /* 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) { - rctf clip_rect = bucket_bounds; - clip_rect.xmin -= PROJ_PIXEL_TOLERANCE; - clip_rect.xmax += PROJ_PIXEL_TOLERANCE; - clip_rect.ymin -= PROJ_PIXEL_TOLERANCE; - clip_rect.ymax += PROJ_PIXEL_TOLERANCE; - /* No pixels initialized */ - project_bucket_init(ps, thread_index, bucket_index, &clip_rect, &bucket_bounds); - } - - if (ps->source != PROJ_SRC_VIEW) { - - /* Re-Projection, simple, no brushes! */ - - for (node = ps->bucketRect[bucket_index]; node; node = node->next) { - projPixel = (ProjPixel *)node->link; - - /* copy of code below */ - 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 != NULL); - } - /* end copy */ - - /* fill tools */ - if (ps->source == PROJ_SRC_VIEW_FILL) { - if (brush->flag & BRUSH_USE_GRADIENT) { - /* these could probably be cached instead of being done per pixel */ - float tangent[2]; - float line_len_sq_inv, line_len; - float f; - float color_f[4]; - float p[2] = {projPixel->projCoSS[0] - lastpos[0], projPixel->projCoSS[1] - lastpos[1]}; - - sub_v2_v2v2(tangent, pos, lastpos); - line_len = len_squared_v2(tangent); - line_len_sq_inv = 1.0f / line_len; - line_len = sqrtf(line_len); - - switch (brush->gradient_fill_mode) { - case BRUSH_GRADIENT_LINEAR: - { - f = dot_v2v2(p, tangent) * line_len_sq_inv; - break; - } - case BRUSH_GRADIENT_RADIAL: - default: - { - f = len_v2(p) / line_len; - break; - } - } - BKE_colorband_evaluate(brush->gradient, f, color_f); - color_f[3] *= ((float)projPixel->mask) * (1.0f / 65535.0f) * brush->alpha; - - if (is_floatbuf) { - /* convert to premultipied */ - mul_v3_fl(color_f, color_f[3]); - IMB_blend_color_float( - projPixel->pixel.f_pt, projPixel->origColor.f_pt, - color_f, ps->blend); - } - else { - linearrgb_to_srgb_v3_v3(color_f, color_f); - - if (ps->dither > 0.0f) { - float_to_byte_dither_v3(projPixel->newColor.ch, color_f, ps->dither, projPixel->x_px, projPixel->y_px); - } - else { - unit_float_to_uchar_clamp_v3(projPixel->newColor.ch, color_f); - } - projPixel->newColor.ch[3] = unit_float_to_uchar_clamp(color_f[3]); - IMB_blend_color_byte( - projPixel->pixel.ch_pt, projPixel->origColor.ch_pt, - projPixel->newColor.ch, ps->blend); - } - } - else { - if (is_floatbuf) { - float newColor_f[4]; - newColor_f[3] = ((float)projPixel->mask) * (1.0f / 65535.0f) * brush->alpha; - copy_v3_v3(newColor_f, ps->paint_color_linear); - - IMB_blend_color_float( - projPixel->pixel.f_pt, projPixel->origColor.f_pt, - newColor_f, ps->blend); - } - else { - float mask = ((float)projPixel->mask) * (1.0f / 65535.0f); - projPixel->newColor.ch[3] = mask * 255 * brush->alpha; - - rgb_float_to_uchar(projPixel->newColor.ch, ps->paint_color); - IMB_blend_color_byte( - projPixel->pixel.ch_pt, projPixel->origColor.ch_pt, - projPixel->newColor.ch, ps->blend); - } - } - - if (lock_alpha) { - if (is_floatbuf) { - /* slightly more involved case since floats are in premultiplied space we need - * to make sure alpha is consistent, see T44627 */ - float rgb_straight[4]; - premul_to_straight_v4_v4(rgb_straight, projPixel->pixel.f_pt); - rgb_straight[3] = projPixel->origColor.f_pt[3]; - straight_to_premul_v4_v4(projPixel->pixel.f_pt, rgb_straight); - } - else { - projPixel->pixel.ch_pt[3] = projPixel->origColor.ch_pt[3]; - } - } - - last_partial_redraw_cell = last_projIma->partRedrawRect + projPixel->bb_cell_index; - image_paint_partial_redraw_expand(last_partial_redraw_cell, projPixel); - } - else { - if (is_floatbuf) { - if (UNLIKELY(ps->reproject_ibuf->rect_float == NULL)) { - IMB_float_from_rect(ps->reproject_ibuf); - ps->reproject_ibuf_free_float = true; - } - - bicubic_interpolation_color( - ps->reproject_ibuf, NULL, projPixel->newColor.f, - projPixel->projCoSS[0], projPixel->projCoSS[1]); - if (projPixel->newColor.f[3]) { - float mask = ((float)projPixel->mask) * (1.0f / 65535.0f); - - mul_v4_v4fl(projPixel->newColor.f, projPixel->newColor.f, mask); - - blend_color_mix_float( - projPixel->pixel.f_pt, projPixel->origColor.f_pt, - projPixel->newColor.f); - } - } - else { - if (UNLIKELY(ps->reproject_ibuf->rect == NULL)) { - IMB_rect_from_float(ps->reproject_ibuf); - ps->reproject_ibuf_free_uchar = true; - } - - bicubic_interpolation_color( - ps->reproject_ibuf, projPixel->newColor.ch, NULL, - projPixel->projCoSS[0], projPixel->projCoSS[1]); - if (projPixel->newColor.ch[3]) { - float mask = ((float)projPixel->mask) * (1.0f / 65535.0f); - projPixel->newColor.ch[3] *= mask; - - blend_color_mix_byte( - projPixel->pixel.ch_pt, projPixel->origColor.ch_pt, - projPixel->newColor.ch); - } - } - } - } - } - else { - /* Normal brush painting */ - - for (node = ps->bucketRect[bucket_index]; node; node = node->next) { - - projPixel = (ProjPixel *)node->link; - - dist_sq = len_squared_v2v2(projPixel->projCoSS, pos); - - /*if (dist < radius) {*/ /* correct but uses a sqrtf */ - if (dist_sq <= brush_radius_sq) { - dist = sqrtf(dist_sq); - - falloff = BKE_brush_curve_strength_clamped(ps->brush, dist, brush_radius); - - if (falloff > 0.0f) { - float texrgb[3]; - float mask; - - /* Extra mask for normal, layer stencil, .. */ - float custom_mask = ((float)projPixel->mask) * (1.0f / 65535.0f); - - /* Mask texture. */ - if (ps->is_maskbrush) { - float texmask = BKE_brush_sample_masktex(ps->scene, ps->brush, projPixel->projCoSS, thread_index, pool); - CLAMP(texmask, 0.0f, 1.0f); - custom_mask *= texmask; - } - - /* Color texture (alpha used as mask). */ - if (ps->is_texbrush) { - MTex *mtex = &brush->mtex; - float samplecos[3]; - float texrgba[4]; - - /* taking 3d copy to account for 3D mapping too. - * It gets concatenated during sampling */ - if (mtex->brush_map_mode == MTEX_MAP_MODE_3D) { - copy_v3_v3(samplecos, projPixel->worldCoSS); - } - else { - copy_v2_v2(samplecos, projPixel->projCoSS); - samplecos[2] = 0.0f; - } - - /* note, for clone and smear, - * we only use the alpha, could be a special function */ - BKE_brush_sample_tex_3d(ps->scene, brush, samplecos, texrgba, thread_index, pool); - - copy_v3_v3(texrgb, texrgba); - custom_mask *= texrgba[3]; - } - else { - zero_v3(texrgb); - } - - if (ps->do_masking) { - /* masking to keep brush contribution to a pixel limited. note we do not do - * a simple max(mask, mask_accum), as this is very sensitive to spacing and - * gives poor results for strokes crossing themselves. - * - * Instead we use a formula that adds up but approaches brush_alpha slowly - * and never exceeds it, which gives nice smooth results. */ - float mask_accum = *projPixel->mask_accum; - float max_mask = brush_alpha * custom_mask * falloff * 65535.0f; - - if (brush->flag & BRUSH_ACCUMULATE) - mask = mask_accum + max_mask; - else - mask = mask_accum + (max_mask - mask_accum * falloff); - - mask = min_ff(mask, 65535.0f); - mask_short = (unsigned short)mask; - - if (mask_short > *projPixel->mask_accum) { - *projPixel->mask_accum = mask_short; - mask = mask_short * (1.0f / 65535.0f); - } - else { - /* Go onto the next pixel */ - continue; - } - } - else { - mask = brush_alpha * custom_mask * falloff; - } - - if (mask > 0.0f) { - - /* copy of code above */ - 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 != NULL); - } - /* end copy */ - - /* validate undo tile, since we will modify t*/ - *projPixel->valid = true; - - last_partial_redraw_cell = last_projIma->partRedrawRect + projPixel->bb_cell_index; - image_paint_partial_redraw_expand(last_partial_redraw_cell, projPixel); - - /* texrgb is not used for clone, smear or soften */ - switch (tool) { - case PAINT_TOOL_CLONE: - if (is_floatbuf) do_projectpaint_clone_f(ps, projPixel, mask); - else do_projectpaint_clone(ps, projPixel, mask); - break; - case PAINT_TOOL_SMEAR: - sub_v2_v2v2(co, projPixel->projCoSS, pos_ofs); - - if (is_floatbuf) do_projectpaint_smear_f(ps, projPixel, mask, smearArena, &smearPixels_f, co); - else do_projectpaint_smear(ps, projPixel, mask, smearArena, &smearPixels, co); - break; - case PAINT_TOOL_SOFTEN: - if (is_floatbuf) do_projectpaint_soften_f(ps, projPixel, mask, softenArena, &softenPixels_f); - else do_projectpaint_soften(ps, projPixel, mask, softenArena, &softenPixels); - break; - case PAINT_TOOL_MASK: - if (is_floatbuf) do_projectpaint_mask_f(ps, projPixel, mask); - else do_projectpaint_mask(ps, projPixel, mask); - break; - default: - if (is_floatbuf) do_projectpaint_draw_f(ps, projPixel, texrgb, mask); - else do_projectpaint_draw(ps, projPixel, texrgb, mask, ps->dither, projPixel->x_px, projPixel->y_px); - break; - } - - if (lock_alpha) { - if (is_floatbuf) { - /* slightly more involved case since floats are in premultiplied space we need - * to make sure alpha is consistent, see T44627 */ - float rgb_straight[4]; - premul_to_straight_v4_v4(rgb_straight, projPixel->pixel.f_pt); - rgb_straight[3] = projPixel->origColor.f_pt[3]; - straight_to_premul_v4_v4(projPixel->pixel.f_pt, rgb_straight); - } - else { - projPixel->pixel.ch_pt[3] = projPixel->origColor.ch_pt[3]; - } - } - } - - /* 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) { - projPixel = node->link; - copy_v4_v4(projPixel->pixel.f_pt, ((ProjPixelClone *)projPixel)->clonepx.f); - } - - BLI_memarena_free(smearArena); - } - else if (tool == PAINT_TOOL_SOFTEN) { - - for (node = softenPixels; node; node = node->next) { /* this wont run for a float image */ - projPixel = node->link; - *projPixel->pixel.uint_pt = projPixel->newColor.uint; - } - - for (node = softenPixels_f; node; node = node->next) { - projPixel = node->link; - copy_v4_v4(projPixel->pixel.f_pt, projPixel->newColor.f); - } - - BLI_memarena_free(softenArena); - } - - return NULL; + /* 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; + struct ImagePool *pool = ((ProjectHandle *)ph_v)->pool; + /* Done with args from ProjectHandle */ + + LinkNode *node; + ProjPixel *projPixel; + Brush *brush = ps->brush; + + int last_index = -1; + ProjPaintImage *last_projIma = NULL; + ImagePaintPartialRedraw *last_partial_redraw_cell; + + float dist_sq, dist; + + float falloff; + int bucket_index; + bool is_floatbuf = false; + const short tool = ps->tool; + rctf bucket_bounds; + + /* for smear only */ + float pos_ofs[2] = {0}; + float co[2]; + unsigned short mask_short; + const float brush_alpha = BKE_brush_alpha_get(ps->scene, brush); + const float brush_radius = ps->brush_size; + /* avoid a square root with every dist comparison */ + const float brush_radius_sq = brush_radius * brush_radius; + + const bool lock_alpha = ELEM(brush->blend, IMB_BLEND_ERASE_ALPHA, IMB_BLEND_ADD_ALPHA) ? + 0 : + (brush->flag & BRUSH_LOCK_ALPHA) != 0; + + LinkNode *smearPixels = NULL; + LinkNode *smearPixels_f = NULL; + /* mem arena for this brush projection only */ + MemArena *smearArena = NULL; + + LinkNode *softenPixels = NULL; + LinkNode *softenPixels_f = NULL; + /* mem arena for this brush projection only */ + MemArena *softenArena = NULL; + + if (tool == PAINT_TOOL_SMEAR) { + pos_ofs[0] = pos[0] - lastpos[0]; + pos_ofs[1] = pos[1] - lastpos[1]; + + smearArena = BLI_memarena_new(MEM_SIZE_OPTIMAL(1 << 16), "paint smear arena"); + } + else if (tool == PAINT_TOOL_SOFTEN) { + softenArena = BLI_memarena_new(MEM_SIZE_OPTIMAL(1 << 16), "paint soften arena"); + } + + /* 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) { + rctf clip_rect = bucket_bounds; + clip_rect.xmin -= PROJ_PIXEL_TOLERANCE; + clip_rect.xmax += PROJ_PIXEL_TOLERANCE; + clip_rect.ymin -= PROJ_PIXEL_TOLERANCE; + clip_rect.ymax += PROJ_PIXEL_TOLERANCE; + /* No pixels initialized */ + project_bucket_init(ps, thread_index, bucket_index, &clip_rect, &bucket_bounds); + } + + if (ps->source != PROJ_SRC_VIEW) { + + /* Re-Projection, simple, no brushes! */ + + for (node = ps->bucketRect[bucket_index]; node; node = node->next) { + projPixel = (ProjPixel *)node->link; + + /* copy of code below */ + 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 != NULL); + } + /* end copy */ + + /* fill tools */ + if (ps->source == PROJ_SRC_VIEW_FILL) { + if (brush->flag & BRUSH_USE_GRADIENT) { + /* these could probably be cached instead of being done per pixel */ + float tangent[2]; + float line_len_sq_inv, line_len; + float f; + float color_f[4]; + float p[2] = {projPixel->projCoSS[0] - lastpos[0], + projPixel->projCoSS[1] - lastpos[1]}; + + sub_v2_v2v2(tangent, pos, lastpos); + line_len = len_squared_v2(tangent); + line_len_sq_inv = 1.0f / line_len; + line_len = sqrtf(line_len); + + switch (brush->gradient_fill_mode) { + case BRUSH_GRADIENT_LINEAR: { + f = dot_v2v2(p, tangent) * line_len_sq_inv; + break; + } + case BRUSH_GRADIENT_RADIAL: + default: { + f = len_v2(p) / line_len; + break; + } + } + BKE_colorband_evaluate(brush->gradient, f, color_f); + color_f[3] *= ((float)projPixel->mask) * (1.0f / 65535.0f) * brush->alpha; + + if (is_floatbuf) { + /* convert to premultipied */ + mul_v3_fl(color_f, color_f[3]); + IMB_blend_color_float( + projPixel->pixel.f_pt, projPixel->origColor.f_pt, color_f, ps->blend); + } + else { + linearrgb_to_srgb_v3_v3(color_f, color_f); + + if (ps->dither > 0.0f) { + float_to_byte_dither_v3( + projPixel->newColor.ch, color_f, ps->dither, projPixel->x_px, projPixel->y_px); + } + else { + unit_float_to_uchar_clamp_v3(projPixel->newColor.ch, color_f); + } + projPixel->newColor.ch[3] = unit_float_to_uchar_clamp(color_f[3]); + IMB_blend_color_byte(projPixel->pixel.ch_pt, + projPixel->origColor.ch_pt, + projPixel->newColor.ch, + ps->blend); + } + } + else { + if (is_floatbuf) { + float newColor_f[4]; + newColor_f[3] = ((float)projPixel->mask) * (1.0f / 65535.0f) * brush->alpha; + copy_v3_v3(newColor_f, ps->paint_color_linear); + + IMB_blend_color_float( + projPixel->pixel.f_pt, projPixel->origColor.f_pt, newColor_f, ps->blend); + } + else { + float mask = ((float)projPixel->mask) * (1.0f / 65535.0f); + projPixel->newColor.ch[3] = mask * 255 * brush->alpha; + + rgb_float_to_uchar(projPixel->newColor.ch, ps->paint_color); + IMB_blend_color_byte(projPixel->pixel.ch_pt, + projPixel->origColor.ch_pt, + projPixel->newColor.ch, + ps->blend); + } + } + + if (lock_alpha) { + if (is_floatbuf) { + /* slightly more involved case since floats are in premultiplied space we need + * to make sure alpha is consistent, see T44627 */ + float rgb_straight[4]; + premul_to_straight_v4_v4(rgb_straight, projPixel->pixel.f_pt); + rgb_straight[3] = projPixel->origColor.f_pt[3]; + straight_to_premul_v4_v4(projPixel->pixel.f_pt, rgb_straight); + } + else { + projPixel->pixel.ch_pt[3] = projPixel->origColor.ch_pt[3]; + } + } + + last_partial_redraw_cell = last_projIma->partRedrawRect + projPixel->bb_cell_index; + image_paint_partial_redraw_expand(last_partial_redraw_cell, projPixel); + } + else { + if (is_floatbuf) { + if (UNLIKELY(ps->reproject_ibuf->rect_float == NULL)) { + IMB_float_from_rect(ps->reproject_ibuf); + ps->reproject_ibuf_free_float = true; + } + + bicubic_interpolation_color(ps->reproject_ibuf, + NULL, + projPixel->newColor.f, + projPixel->projCoSS[0], + projPixel->projCoSS[1]); + if (projPixel->newColor.f[3]) { + float mask = ((float)projPixel->mask) * (1.0f / 65535.0f); + + mul_v4_v4fl(projPixel->newColor.f, projPixel->newColor.f, mask); + + blend_color_mix_float( + projPixel->pixel.f_pt, projPixel->origColor.f_pt, projPixel->newColor.f); + } + } + else { + if (UNLIKELY(ps->reproject_ibuf->rect == NULL)) { + IMB_rect_from_float(ps->reproject_ibuf); + ps->reproject_ibuf_free_uchar = true; + } + + bicubic_interpolation_color(ps->reproject_ibuf, + projPixel->newColor.ch, + NULL, + projPixel->projCoSS[0], + projPixel->projCoSS[1]); + if (projPixel->newColor.ch[3]) { + float mask = ((float)projPixel->mask) * (1.0f / 65535.0f); + projPixel->newColor.ch[3] *= mask; + + blend_color_mix_byte( + projPixel->pixel.ch_pt, projPixel->origColor.ch_pt, projPixel->newColor.ch); + } + } + } + } + } + else { + /* Normal brush painting */ + + for (node = ps->bucketRect[bucket_index]; node; node = node->next) { + + projPixel = (ProjPixel *)node->link; + + dist_sq = len_squared_v2v2(projPixel->projCoSS, pos); + + /*if (dist < radius) {*/ /* correct but uses a sqrtf */ + if (dist_sq <= brush_radius_sq) { + dist = sqrtf(dist_sq); + + falloff = BKE_brush_curve_strength_clamped(ps->brush, dist, brush_radius); + + if (falloff > 0.0f) { + float texrgb[3]; + float mask; + + /* Extra mask for normal, layer stencil, .. */ + float custom_mask = ((float)projPixel->mask) * (1.0f / 65535.0f); + + /* Mask texture. */ + if (ps->is_maskbrush) { + float texmask = BKE_brush_sample_masktex( + ps->scene, ps->brush, projPixel->projCoSS, thread_index, pool); + CLAMP(texmask, 0.0f, 1.0f); + custom_mask *= texmask; + } + + /* Color texture (alpha used as mask). */ + if (ps->is_texbrush) { + MTex *mtex = &brush->mtex; + float samplecos[3]; + float texrgba[4]; + + /* taking 3d copy to account for 3D mapping too. + * It gets concatenated during sampling */ + if (mtex->brush_map_mode == MTEX_MAP_MODE_3D) { + copy_v3_v3(samplecos, projPixel->worldCoSS); + } + else { + copy_v2_v2(samplecos, projPixel->projCoSS); + samplecos[2] = 0.0f; + } + + /* note, for clone and smear, + * we only use the alpha, could be a special function */ + BKE_brush_sample_tex_3d(ps->scene, brush, samplecos, texrgba, thread_index, pool); + + copy_v3_v3(texrgb, texrgba); + custom_mask *= texrgba[3]; + } + else { + zero_v3(texrgb); + } + + if (ps->do_masking) { + /* masking to keep brush contribution to a pixel limited. note we do not do + * a simple max(mask, mask_accum), as this is very sensitive to spacing and + * gives poor results for strokes crossing themselves. + * + * Instead we use a formula that adds up but approaches brush_alpha slowly + * and never exceeds it, which gives nice smooth results. */ + float mask_accum = *projPixel->mask_accum; + float max_mask = brush_alpha * custom_mask * falloff * 65535.0f; + + if (brush->flag & BRUSH_ACCUMULATE) + mask = mask_accum + max_mask; + else + mask = mask_accum + (max_mask - mask_accum * falloff); + + mask = min_ff(mask, 65535.0f); + mask_short = (unsigned short)mask; + + if (mask_short > *projPixel->mask_accum) { + *projPixel->mask_accum = mask_short; + mask = mask_short * (1.0f / 65535.0f); + } + else { + /* Go onto the next pixel */ + continue; + } + } + else { + mask = brush_alpha * custom_mask * falloff; + } + + if (mask > 0.0f) { + + /* copy of code above */ + 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 != NULL); + } + /* end copy */ + + /* validate undo tile, since we will modify t*/ + *projPixel->valid = true; + + last_partial_redraw_cell = last_projIma->partRedrawRect + projPixel->bb_cell_index; + image_paint_partial_redraw_expand(last_partial_redraw_cell, projPixel); + + /* texrgb is not used for clone, smear or soften */ + switch (tool) { + case PAINT_TOOL_CLONE: + if (is_floatbuf) + do_projectpaint_clone_f(ps, projPixel, mask); + else + do_projectpaint_clone(ps, projPixel, mask); + break; + case PAINT_TOOL_SMEAR: + sub_v2_v2v2(co, projPixel->projCoSS, pos_ofs); + + if (is_floatbuf) + do_projectpaint_smear_f(ps, projPixel, mask, smearArena, &smearPixels_f, co); + else + do_projectpaint_smear(ps, projPixel, mask, smearArena, &smearPixels, co); + break; + case PAINT_TOOL_SOFTEN: + if (is_floatbuf) + do_projectpaint_soften_f(ps, projPixel, mask, softenArena, &softenPixels_f); + else + do_projectpaint_soften(ps, projPixel, mask, softenArena, &softenPixels); + break; + case PAINT_TOOL_MASK: + if (is_floatbuf) + do_projectpaint_mask_f(ps, projPixel, mask); + else + do_projectpaint_mask(ps, projPixel, mask); + break; + default: + if (is_floatbuf) + do_projectpaint_draw_f(ps, projPixel, texrgb, mask); + else + do_projectpaint_draw( + ps, projPixel, texrgb, mask, ps->dither, projPixel->x_px, projPixel->y_px); + break; + } + + if (lock_alpha) { + if (is_floatbuf) { + /* slightly more involved case since floats are in premultiplied space we need + * to make sure alpha is consistent, see T44627 */ + float rgb_straight[4]; + premul_to_straight_v4_v4(rgb_straight, projPixel->pixel.f_pt); + rgb_straight[3] = projPixel->origColor.f_pt[3]; + straight_to_premul_v4_v4(projPixel->pixel.f_pt, rgb_straight); + } + else { + projPixel->pixel.ch_pt[3] = projPixel->origColor.ch_pt[3]; + } + } + } + + /* 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) { + projPixel = node->link; + copy_v4_v4(projPixel->pixel.f_pt, ((ProjPixelClone *)projPixel)->clonepx.f); + } + + BLI_memarena_free(smearArena); + } + else if (tool == PAINT_TOOL_SOFTEN) { + + for (node = softenPixels; node; node = node->next) { /* this wont run for a float image */ + projPixel = node->link; + *projPixel->pixel.uint_pt = projPixel->newColor.uint; + } + + for (node = softenPixels_f; node; node = node->next) { + projPixel = node->link; + copy_v4_v4(projPixel->pixel.f_pt, projPixel->newColor.f); + } + + BLI_memarena_free(softenArena); + } + + return NULL; } static bool project_paint_op(void *state, const float lastpos[2], const float pos[2]) { - /* First unpack args from the struct */ - ProjPaintState *ps = (ProjPaintState *)state; - bool touch_any = false; - - ProjectHandle handles[BLENDER_MAX_THREADS]; - ListBase threads; - int a, i; - - struct ImagePool *pool; - - if (!project_bucket_iter_init(ps, pos)) { - return touch_any; - } - - if (ps->thread_tot > 1) - BLI_threadpool_init(&threads, do_projectpaint_thread, ps->thread_tot); - - pool = BKE_image_pool_new(); - - /* 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; - copy_v2_v2(handles[a].mval, pos); - copy_v2_v2(handles[a].prevmval, lastpos); - - /* thread specific */ - handles[a].thread_index = a; - - handles[a].projImages = 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 = 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); - } - - handles[a].pool = pool; - - if (ps->thread_tot > 1) - BLI_threadpool_insert(&threads, &handles[a]); - } - - if (ps->thread_tot > 1) /* wait for everything to be done */ - BLI_threadpool_end(&threads); - else - do_projectpaint_thread(&handles[0]); - - - BKE_image_pool_free(pool); - - /* 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; - } - } - - /* calculate pivot for rotation around seletion if needed */ - if (U.uiflag & USER_ORBIT_SELECTION) { - float w[3]; - int tri_index; - - tri_index = project_paint_PickFace(ps, pos, w); - - if (tri_index != -1) { - const MLoopTri *lt = &ps->mlooptri_eval[tri_index]; - const int lt_vtri[3] = { PS_LOOPTRI_AS_VERT_INDEX_3(ps, lt) }; - float world[3]; - UnifiedPaintSettings *ups = &ps->scene->toolsettings->unified_paint_settings; - - interp_v3_v3v3v3( - world, - ps->mvert_eval[lt_vtri[0]].co, - ps->mvert_eval[lt_vtri[1]].co, - ps->mvert_eval[lt_vtri[2]].co, - w); - - ups->average_stroke_counter++; - mul_m4_v3(ps->obmat, world); - add_v3_v3(ups->average_stroke_accum, world); - ups->last_stroke_valid = true; - } - } - - return touch_any; + /* First unpack args from the struct */ + ProjPaintState *ps = (ProjPaintState *)state; + bool touch_any = false; + + ProjectHandle handles[BLENDER_MAX_THREADS]; + ListBase threads; + int a, i; + + struct ImagePool *pool; + + if (!project_bucket_iter_init(ps, pos)) { + return touch_any; + } + + if (ps->thread_tot > 1) + BLI_threadpool_init(&threads, do_projectpaint_thread, ps->thread_tot); + + pool = BKE_image_pool_new(); + + /* 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; + copy_v2_v2(handles[a].mval, pos); + copy_v2_v2(handles[a].prevmval, lastpos); + + /* thread specific */ + handles[a].thread_index = a; + + handles[a].projImages = 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 = 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); + } + + handles[a].pool = pool; + + if (ps->thread_tot > 1) + BLI_threadpool_insert(&threads, &handles[a]); + } + + if (ps->thread_tot > 1) /* wait for everything to be done */ + BLI_threadpool_end(&threads); + else + do_projectpaint_thread(&handles[0]); + + BKE_image_pool_free(pool); + + /* 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; + } + } + + /* calculate pivot for rotation around seletion if needed */ + if (U.uiflag & USER_ORBIT_SELECTION) { + float w[3]; + int tri_index; + + tri_index = project_paint_PickFace(ps, pos, w); + + if (tri_index != -1) { + const MLoopTri *lt = &ps->mlooptri_eval[tri_index]; + const int lt_vtri[3] = {PS_LOOPTRI_AS_VERT_INDEX_3(ps, lt)}; + float world[3]; + UnifiedPaintSettings *ups = &ps->scene->toolsettings->unified_paint_settings; + + interp_v3_v3v3v3(world, + ps->mvert_eval[lt_vtri[0]].co, + ps->mvert_eval[lt_vtri[1]].co, + ps->mvert_eval[lt_vtri[2]].co, + w); + + ups->average_stroke_counter++; + mul_m4_v3(ps->obmat, world); + add_v3_v3(ups->average_stroke_accum, world); + ups->last_stroke_valid = true; + } + } + + return touch_any; } - -static void paint_proj_stroke_ps( - const bContext *UNUSED(C), void *ps_handle_p, const float prev_pos[2], const float pos[2], - const bool eraser, float pressure, float distance, float size, - /* extra view */ - ProjPaintState *ps - ) +static void paint_proj_stroke_ps(const bContext *UNUSED(C), + void *ps_handle_p, + const float prev_pos[2], + const float pos[2], + const bool eraser, + float pressure, + float distance, + float size, + /* extra view */ + ProjPaintState *ps) { - ProjStrokeHandle *ps_handle = ps_handle_p; - Brush *brush = ps->brush; - Scene *scene = ps->scene; - - ps->brush_size = size; - ps->blend = brush->blend; - if (eraser) - ps->blend = IMB_BLEND_ERASE_ALPHA; - - /* handle gradient and inverted stroke color here */ - if (ELEM(ps->tool, PAINT_TOOL_DRAW, PAINT_TOOL_FILL)) { - paint_brush_color_get(scene, brush, false, ps->mode == BRUSH_STROKE_INVERT, distance, pressure, ps->paint_color, NULL); - if (ps->use_colormanagement) { - srgb_to_linearrgb_v3_v3(ps->paint_color_linear, ps->paint_color); - } - else { - copy_v3_v3(ps->paint_color_linear, ps->paint_color); - } - } - else if (ps->tool == PAINT_TOOL_MASK) { - ps->stencil_value = brush->weight; - - if ((ps->mode == BRUSH_STROKE_INVERT) ^ - ((scene->toolsettings->imapaint.flag & IMAGEPAINT_PROJECT_LAYER_STENCIL_INV) != 0)) - { - ps->stencil_value = 1.0f - ps->stencil_value; - } - } - - if (project_paint_op(ps, prev_pos, pos)) { - ps_handle->need_redraw = true; - project_image_refresh_tagged(ps); - } + ProjStrokeHandle *ps_handle = ps_handle_p; + Brush *brush = ps->brush; + Scene *scene = ps->scene; + + ps->brush_size = size; + ps->blend = brush->blend; + if (eraser) + ps->blend = IMB_BLEND_ERASE_ALPHA; + + /* handle gradient and inverted stroke color here */ + if (ELEM(ps->tool, PAINT_TOOL_DRAW, PAINT_TOOL_FILL)) { + paint_brush_color_get(scene, + brush, + false, + ps->mode == BRUSH_STROKE_INVERT, + distance, + pressure, + ps->paint_color, + NULL); + if (ps->use_colormanagement) { + srgb_to_linearrgb_v3_v3(ps->paint_color_linear, ps->paint_color); + } + else { + copy_v3_v3(ps->paint_color_linear, ps->paint_color); + } + } + else if (ps->tool == PAINT_TOOL_MASK) { + ps->stencil_value = brush->weight; + + if ((ps->mode == BRUSH_STROKE_INVERT) ^ + ((scene->toolsettings->imapaint.flag & IMAGEPAINT_PROJECT_LAYER_STENCIL_INV) != 0)) { + ps->stencil_value = 1.0f - ps->stencil_value; + } + } + + if (project_paint_op(ps, prev_pos, pos)) { + ps_handle->need_redraw = true; + project_image_refresh_tagged(ps); + } } - -void paint_proj_stroke( - const bContext *C, void *ps_handle_p, const float prev_pos[2], const float pos[2], - const bool eraser, float pressure, float distance, float size) +void paint_proj_stroke(const bContext *C, + void *ps_handle_p, + const float prev_pos[2], + const float pos[2], + const bool eraser, + float pressure, + float distance, + float size) { - int i; - ProjStrokeHandle *ps_handle = ps_handle_p; + int i; + ProjStrokeHandle *ps_handle = ps_handle_p; - /* clone gets special treatment here to avoid going through image initialization */ - if (ps_handle->is_clone_cursor_pick) { - Scene *scene = ps_handle->scene; - struct Depsgraph *depsgraph = CTX_data_depsgraph(C); - View3D *v3d = CTX_wm_view3d(C); - ARegion *ar = CTX_wm_region(C); - float *cursor = scene->cursor.location; - int mval_i[2] = {(int)pos[0], (int)pos[1]}; + /* clone gets special treatment here to avoid going through image initialization */ + if (ps_handle->is_clone_cursor_pick) { + Scene *scene = ps_handle->scene; + struct Depsgraph *depsgraph = CTX_data_depsgraph(C); + View3D *v3d = CTX_wm_view3d(C); + ARegion *ar = CTX_wm_region(C); + float *cursor = scene->cursor.location; + int mval_i[2] = {(int)pos[0], (int)pos[1]}; - view3d_operator_needs_opengl(C); + view3d_operator_needs_opengl(C); - if (!ED_view3d_autodist(depsgraph, ar, v3d, mval_i, cursor, false, NULL)) { - return; - } + if (!ED_view3d_autodist(depsgraph, ar, v3d, mval_i, cursor, false, NULL)) { + return; + } - ED_region_tag_redraw(ar); + ED_region_tag_redraw(ar); - return; - } + return; + } - for (i = 0; i < ps_handle->ps_views_tot; i++) { - ProjPaintState *ps = ps_handle->ps_views[i]; - paint_proj_stroke_ps(C, ps_handle_p, prev_pos, pos, eraser, pressure, distance, size, ps); - } + for (i = 0; i < ps_handle->ps_views_tot; i++) { + ProjPaintState *ps = ps_handle->ps_views[i]; + paint_proj_stroke_ps(C, ps_handle_p, prev_pos, pos, eraser, pressure, distance, size, ps); + } } - /* initialize project paint settings from context */ static void project_state_init(bContext *C, Object *ob, ProjPaintState *ps, int mode) { - Scene *scene = CTX_data_scene(C); - ToolSettings *settings = scene->toolsettings; - - /* brush */ - ps->mode = mode; - ps->brush = BKE_paint_brush(&settings->imapaint.paint); - if (ps->brush) { - Brush *brush = ps->brush; - ps->tool = brush->imagepaint_tool; - ps->blend = brush->blend; - /* only check for inversion for the soften tool, elsewhere, - * a resident brush inversion flag can cause issues */ - if (brush->imagepaint_tool == PAINT_TOOL_SOFTEN) { - ps->mode = (((ps->mode == BRUSH_STROKE_INVERT) ^ ((brush->flag & BRUSH_DIR_IN) != 0)) ? - BRUSH_STROKE_INVERT : BRUSH_STROKE_NORMAL); - - ps->blurkernel = paint_new_blur_kernel(brush, true); - } - - /* disable for 3d mapping also because painting on mirrored mesh can create "stripes" */ - ps->do_masking = paint_use_opacity_masking(brush); - ps->is_texbrush = (brush->mtex.tex && brush->imagepaint_tool == PAINT_TOOL_DRAW) ? true : false; - ps->is_maskbrush = (brush->mask_mtex.tex) ? true : false; - } - else { - /* brush may be NULL*/ - ps->do_masking = false; - ps->is_texbrush = false; - ps->is_maskbrush = false; - } - - /* sizeof(ProjPixel), since we alloc this a _lot_ */ - ps->pixel_sizeof = project_paint_pixel_sizeof(ps->tool); - BLI_assert(ps->pixel_sizeof >= sizeof(ProjPixel)); - - /* these can be NULL */ - ps->v3d = CTX_wm_view3d(C); - ps->rv3d = CTX_wm_region_view3d(C); - ps->ar = CTX_wm_region(C); - - ps->depsgraph = CTX_data_depsgraph(C); - ps->scene = scene; - /* allow override of active object */ - ps->ob = ob; - - ps->do_material_slots = (settings->imapaint.mode == IMAGEPAINT_MODE_MATERIAL); - ps->stencil_ima = settings->imapaint.stencil; - ps->canvas_ima = (!ps->do_material_slots) ? - settings->imapaint.canvas : NULL; - ps->clone_ima = (!ps->do_material_slots) ? - settings->imapaint.clone : NULL; - - ps->do_mask_cavity = (settings->imapaint.paint.flags & PAINT_USE_CAVITY_MASK) ? true : false; - ps->cavity_curve = settings->imapaint.paint.cavity_curve; - - /* setup projection painting data */ - if (ps->tool != PAINT_TOOL_FILL) { - ps->do_backfacecull = (settings->imapaint.flag & IMAGEPAINT_PROJECT_BACKFACE) ? false : true; - ps->do_occlude = (settings->imapaint.flag & IMAGEPAINT_PROJECT_XRAY) ? false : true; - ps->do_mask_normal = (settings->imapaint.flag & IMAGEPAINT_PROJECT_FLAT) ? false : true; - } - else { - ps->do_backfacecull = ps->do_occlude = ps->do_mask_normal = 0; - } - - if (ps->tool == PAINT_TOOL_CLONE) - ps->do_layer_clone = (settings->imapaint.flag & IMAGEPAINT_PROJECT_LAYER_CLONE) ? 1 : 0; - - - ps->do_stencil_brush = (ps->brush && ps->brush->imagepaint_tool == PAINT_TOOL_MASK); - /* deactivate stenciling for the stencil brush :) */ - ps->do_layer_stencil = ((settings->imapaint.flag & IMAGEPAINT_PROJECT_LAYER_STENCIL) && - !(ps->do_stencil_brush) && ps->stencil_ima); - ps->do_layer_stencil_inv = ((settings->imapaint.flag & IMAGEPAINT_PROJECT_LAYER_STENCIL_INV) != 0); - + Scene *scene = CTX_data_scene(C); + ToolSettings *settings = scene->toolsettings; + + /* brush */ + ps->mode = mode; + ps->brush = BKE_paint_brush(&settings->imapaint.paint); + if (ps->brush) { + Brush *brush = ps->brush; + ps->tool = brush->imagepaint_tool; + ps->blend = brush->blend; + /* only check for inversion for the soften tool, elsewhere, + * a resident brush inversion flag can cause issues */ + if (brush->imagepaint_tool == PAINT_TOOL_SOFTEN) { + ps->mode = (((ps->mode == BRUSH_STROKE_INVERT) ^ ((brush->flag & BRUSH_DIR_IN) != 0)) ? + BRUSH_STROKE_INVERT : + BRUSH_STROKE_NORMAL); + + ps->blurkernel = paint_new_blur_kernel(brush, true); + } + + /* disable for 3d mapping also because painting on mirrored mesh can create "stripes" */ + ps->do_masking = paint_use_opacity_masking(brush); + ps->is_texbrush = (brush->mtex.tex && brush->imagepaint_tool == PAINT_TOOL_DRAW) ? true : + false; + ps->is_maskbrush = (brush->mask_mtex.tex) ? true : false; + } + else { + /* brush may be NULL*/ + ps->do_masking = false; + ps->is_texbrush = false; + ps->is_maskbrush = false; + } + + /* sizeof(ProjPixel), since we alloc this a _lot_ */ + ps->pixel_sizeof = project_paint_pixel_sizeof(ps->tool); + BLI_assert(ps->pixel_sizeof >= sizeof(ProjPixel)); + + /* these can be NULL */ + ps->v3d = CTX_wm_view3d(C); + ps->rv3d = CTX_wm_region_view3d(C); + ps->ar = CTX_wm_region(C); + + ps->depsgraph = CTX_data_depsgraph(C); + ps->scene = scene; + /* allow override of active object */ + ps->ob = ob; + + ps->do_material_slots = (settings->imapaint.mode == IMAGEPAINT_MODE_MATERIAL); + ps->stencil_ima = settings->imapaint.stencil; + ps->canvas_ima = (!ps->do_material_slots) ? settings->imapaint.canvas : NULL; + ps->clone_ima = (!ps->do_material_slots) ? settings->imapaint.clone : NULL; + + ps->do_mask_cavity = (settings->imapaint.paint.flags & PAINT_USE_CAVITY_MASK) ? true : false; + ps->cavity_curve = settings->imapaint.paint.cavity_curve; + + /* setup projection painting data */ + if (ps->tool != PAINT_TOOL_FILL) { + ps->do_backfacecull = (settings->imapaint.flag & IMAGEPAINT_PROJECT_BACKFACE) ? false : true; + ps->do_occlude = (settings->imapaint.flag & IMAGEPAINT_PROJECT_XRAY) ? false : true; + ps->do_mask_normal = (settings->imapaint.flag & IMAGEPAINT_PROJECT_FLAT) ? false : true; + } + else { + ps->do_backfacecull = ps->do_occlude = ps->do_mask_normal = 0; + } + + if (ps->tool == PAINT_TOOL_CLONE) + ps->do_layer_clone = (settings->imapaint.flag & IMAGEPAINT_PROJECT_LAYER_CLONE) ? 1 : 0; + + ps->do_stencil_brush = (ps->brush && ps->brush->imagepaint_tool == PAINT_TOOL_MASK); + /* deactivate stenciling for the stencil brush :) */ + ps->do_layer_stencil = ((settings->imapaint.flag & IMAGEPAINT_PROJECT_LAYER_STENCIL) && + !(ps->do_stencil_brush) && ps->stencil_ima); + ps->do_layer_stencil_inv = ((settings->imapaint.flag & IMAGEPAINT_PROJECT_LAYER_STENCIL_INV) != + 0); #ifndef PROJ_DEBUG_NOSEAMBLEED - /* pixel num to bleed */ - ps->seam_bleed_px = settings->imapaint.seam_bleed; - ps->seam_bleed_px_sq = SQUARE(settings->imapaint.seam_bleed); + /* pixel num to bleed */ + ps->seam_bleed_px = settings->imapaint.seam_bleed; + ps->seam_bleed_px_sq = SQUARE(settings->imapaint.seam_bleed); #endif - if (ps->do_mask_normal) { - ps->normal_angle_inner = settings->imapaint.normal_angle; - ps->normal_angle = (ps->normal_angle_inner + 90.0f) * 0.5f; - } - else { - ps->normal_angle_inner = ps->normal_angle = settings->imapaint.normal_angle; - } + if (ps->do_mask_normal) { + ps->normal_angle_inner = settings->imapaint.normal_angle; + ps->normal_angle = (ps->normal_angle_inner + 90.0f) * 0.5f; + } + else { + ps->normal_angle_inner = ps->normal_angle = settings->imapaint.normal_angle; + } - ps->normal_angle_inner *= (float)(M_PI_2 / 90); - ps->normal_angle *= (float)(M_PI_2 / 90); - ps->normal_angle_range = ps->normal_angle - ps->normal_angle_inner; + ps->normal_angle_inner *= (float)(M_PI_2 / 90); + ps->normal_angle *= (float)(M_PI_2 / 90); + ps->normal_angle_range = ps->normal_angle - ps->normal_angle_inner; - if (ps->normal_angle_range <= 0.0f) { - /* no need to do blending */ - ps->do_mask_normal = false; - } + if (ps->normal_angle_range <= 0.0f) { + /* no need to do blending */ + ps->do_mask_normal = false; + } - ps->normal_angle__cos = cosf(ps->normal_angle); - ps->normal_angle_inner__cos = cosf(ps->normal_angle_inner); + ps->normal_angle__cos = cosf(ps->normal_angle); + ps->normal_angle_inner__cos = cosf(ps->normal_angle_inner); - ps->dither = settings->imapaint.dither; + ps->dither = settings->imapaint.dither; - ps->use_colormanagement = BKE_scene_check_color_management_enabled(CTX_data_scene(C)); + ps->use_colormanagement = BKE_scene_check_color_management_enabled(CTX_data_scene(C)); - return; + return; } void *paint_proj_new_stroke(bContext *C, Object *ob, const float mouse[2], int mode) { - ProjStrokeHandle *ps_handle; - Scene *scene = CTX_data_scene(C); - ToolSettings *settings = scene->toolsettings; - int i; - bool is_multi_view; - char symmetry_flag_views[ARRAY_SIZE(ps_handle->ps_views)] = {0}; - - ps_handle = MEM_callocN(sizeof(ProjStrokeHandle), "ProjStrokeHandle"); - ps_handle->scene = scene; - ps_handle->brush = BKE_paint_brush(&settings->imapaint.paint); - - /* bypass regular stroke logic */ - if ((ps_handle->brush->imagepaint_tool == PAINT_TOOL_CLONE) && - (mode == BRUSH_STROKE_INVERT)) - { - view3d_operator_needs_opengl(C); - ps_handle->is_clone_cursor_pick = true; - return ps_handle; - } - - ps_handle->orig_brush_size = BKE_brush_size_get(scene, ps_handle->brush); - - ps_handle->symmetry_flags = settings->imapaint.paint.symmetry_flags & PAINT_SYMM_AXIS_ALL; - ps_handle->ps_views_tot = 1 + (pow_i(2, count_bits_i(ps_handle->symmetry_flags)) - 1); - is_multi_view = (ps_handle->ps_views_tot != 1); - - for (i = 0; i < ps_handle->ps_views_tot; i++) { - ProjPaintState *ps = MEM_callocN(sizeof(ProjPaintState), "ProjectionPaintState"); - ps_handle->ps_views[i] = ps; - } - - if (ps_handle->symmetry_flags) { - int index = 0; - - int x = 0; - do { - int y = 0; - do { - int z = 0; - do { - symmetry_flag_views[index++] = ( - (x ? PAINT_SYMM_X : 0) | - (y ? PAINT_SYMM_Y : 0) | - (z ? PAINT_SYMM_Z : 0)); - BLI_assert(index <= ps_handle->ps_views_tot); - } while ((z++ == 0) && (ps_handle->symmetry_flags & PAINT_SYMM_Z)); - } while ((y++ == 0) && (ps_handle->symmetry_flags & PAINT_SYMM_Y)); - } while ((x++ == 0) && (ps_handle->symmetry_flags & PAINT_SYMM_X)); - BLI_assert(index == ps_handle->ps_views_tot); - } - - for (i = 0; i < ps_handle->ps_views_tot; i++) { - ProjPaintState *ps = ps_handle->ps_views[i]; - - project_state_init(C, ob, ps, mode); - - if (ps->ob == NULL) { - ps_handle->ps_views_tot = i + 1; - goto fail; - } - } - - /* Don't allow brush size below 2 */ - if (BKE_brush_size_get(scene, ps_handle->brush) < 2) - BKE_brush_size_set(scene, ps_handle->brush, 2 * U.pixelsize); - - /* allocate and initialize spatial data structures */ - - for (i = 0; i < ps_handle->ps_views_tot; i++) { - ProjPaintState *ps = ps_handle->ps_views[i]; - - ps->source = (ps->tool == PAINT_TOOL_FILL) ? PROJ_SRC_VIEW_FILL : PROJ_SRC_VIEW; - project_image_refresh_tagged(ps); - - /* re-use! */ - if (i != 0) { - ps->is_shared_user = true; - PROJ_PAINT_STATE_SHARED_MEMCPY(ps, ps_handle->ps_views[0]); - } - - project_paint_begin(C, ps, is_multi_view, symmetry_flag_views[i]); - if (ps->me_eval == NULL) { - goto fail; - } - - paint_proj_begin_clone(ps, mouse); - } - - paint_brush_init_tex(ps_handle->brush); - - return ps_handle; - + ProjStrokeHandle *ps_handle; + Scene *scene = CTX_data_scene(C); + ToolSettings *settings = scene->toolsettings; + int i; + bool is_multi_view; + char symmetry_flag_views[ARRAY_SIZE(ps_handle->ps_views)] = {0}; + + ps_handle = MEM_callocN(sizeof(ProjStrokeHandle), "ProjStrokeHandle"); + ps_handle->scene = scene; + ps_handle->brush = BKE_paint_brush(&settings->imapaint.paint); + + /* bypass regular stroke logic */ + if ((ps_handle->brush->imagepaint_tool == PAINT_TOOL_CLONE) && (mode == BRUSH_STROKE_INVERT)) { + view3d_operator_needs_opengl(C); + ps_handle->is_clone_cursor_pick = true; + return ps_handle; + } + + ps_handle->orig_brush_size = BKE_brush_size_get(scene, ps_handle->brush); + + ps_handle->symmetry_flags = settings->imapaint.paint.symmetry_flags & PAINT_SYMM_AXIS_ALL; + ps_handle->ps_views_tot = 1 + (pow_i(2, count_bits_i(ps_handle->symmetry_flags)) - 1); + is_multi_view = (ps_handle->ps_views_tot != 1); + + for (i = 0; i < ps_handle->ps_views_tot; i++) { + ProjPaintState *ps = MEM_callocN(sizeof(ProjPaintState), "ProjectionPaintState"); + ps_handle->ps_views[i] = ps; + } + + if (ps_handle->symmetry_flags) { + int index = 0; + + int x = 0; + do { + int y = 0; + do { + int z = 0; + do { + symmetry_flag_views[index++] = ((x ? PAINT_SYMM_X : 0) | (y ? PAINT_SYMM_Y : 0) | + (z ? PAINT_SYMM_Z : 0)); + BLI_assert(index <= ps_handle->ps_views_tot); + } while ((z++ == 0) && (ps_handle->symmetry_flags & PAINT_SYMM_Z)); + } while ((y++ == 0) && (ps_handle->symmetry_flags & PAINT_SYMM_Y)); + } while ((x++ == 0) && (ps_handle->symmetry_flags & PAINT_SYMM_X)); + BLI_assert(index == ps_handle->ps_views_tot); + } + + for (i = 0; i < ps_handle->ps_views_tot; i++) { + ProjPaintState *ps = ps_handle->ps_views[i]; + + project_state_init(C, ob, ps, mode); + + if (ps->ob == NULL) { + ps_handle->ps_views_tot = i + 1; + goto fail; + } + } + + /* Don't allow brush size below 2 */ + if (BKE_brush_size_get(scene, ps_handle->brush) < 2) + BKE_brush_size_set(scene, ps_handle->brush, 2 * U.pixelsize); + + /* allocate and initialize spatial data structures */ + + for (i = 0; i < ps_handle->ps_views_tot; i++) { + ProjPaintState *ps = ps_handle->ps_views[i]; + + ps->source = (ps->tool == PAINT_TOOL_FILL) ? PROJ_SRC_VIEW_FILL : PROJ_SRC_VIEW; + project_image_refresh_tagged(ps); + + /* re-use! */ + if (i != 0) { + ps->is_shared_user = true; + PROJ_PAINT_STATE_SHARED_MEMCPY(ps, ps_handle->ps_views[0]); + } + + project_paint_begin(C, ps, is_multi_view, symmetry_flag_views[i]); + if (ps->me_eval == NULL) { + goto fail; + } + + paint_proj_begin_clone(ps, mouse); + } + + paint_brush_init_tex(ps_handle->brush); + + return ps_handle; fail: - for (i = 0; i < ps_handle->ps_views_tot; i++) { - ProjPaintState *ps = ps_handle->ps_views[i]; - MEM_freeN(ps); - } - MEM_freeN(ps_handle); - return NULL; + for (i = 0; i < ps_handle->ps_views_tot; i++) { + ProjPaintState *ps = ps_handle->ps_views[i]; + MEM_freeN(ps); + } + MEM_freeN(ps_handle); + return NULL; } void paint_proj_redraw(const bContext *C, void *ps_handle_p, bool final) { - ProjStrokeHandle *ps_handle = ps_handle_p; - - if (ps_handle->need_redraw) { - ps_handle->need_redraw = false; - } - else if (!final) { - return; - } - - if (final) { - /* compositor listener deals with updating */ - WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, NULL); - } - else { - ED_region_tag_redraw(CTX_wm_region(C)); - } + ProjStrokeHandle *ps_handle = ps_handle_p; + + if (ps_handle->need_redraw) { + ps_handle->need_redraw = false; + } + else if (!final) { + return; + } + + if (final) { + /* compositor listener deals with updating */ + WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, NULL); + } + else { + ED_region_tag_redraw(CTX_wm_region(C)); + } } void paint_proj_stroke_done(void *ps_handle_p) { - ProjStrokeHandle *ps_handle = ps_handle_p; - Scene *scene = ps_handle->scene; - int i; + ProjStrokeHandle *ps_handle = ps_handle_p; + Scene *scene = ps_handle->scene; + int i; - if (ps_handle->is_clone_cursor_pick) { - MEM_freeN(ps_handle); - return; - } + if (ps_handle->is_clone_cursor_pick) { + MEM_freeN(ps_handle); + return; + } - for (i = 1; i < ps_handle->ps_views_tot; i++) { - PROJ_PAINT_STATE_SHARED_CLEAR(ps_handle->ps_views[i]); - } + for (i = 1; i < ps_handle->ps_views_tot; i++) { + PROJ_PAINT_STATE_SHARED_CLEAR(ps_handle->ps_views[i]); + } - BKE_brush_size_set(scene, ps_handle->brush, ps_handle->orig_brush_size); + BKE_brush_size_set(scene, ps_handle->brush, ps_handle->orig_brush_size); - paint_brush_exit_tex(ps_handle->brush); + paint_brush_exit_tex(ps_handle->brush); - for (i = 0; i < ps_handle->ps_views_tot; i++) { - ProjPaintState *ps; - ps = ps_handle->ps_views[i]; - project_paint_end(ps); - MEM_freeN(ps); + for (i = 0; i < ps_handle->ps_views_tot; i++) { + ProjPaintState *ps; + ps = ps_handle->ps_views[i]; + project_paint_end(ps); + MEM_freeN(ps); + } - } - - MEM_freeN(ps_handle); + MEM_freeN(ps_handle); } /* use project paint to re-apply an image */ static int texture_paint_camera_project_exec(bContext *C, wmOperator *op) { - Image *image = BLI_findlink(&CTX_data_main(C)->images, RNA_enum_get(op->ptr, "image")); - Scene *scene = CTX_data_scene(C); - ViewLayer *view_layer = CTX_data_view_layer(C); - ProjPaintState ps = {NULL}; - int orig_brush_size; - IDProperty *idgroup; - IDProperty *view_data = NULL; - Object *ob = OBACT(view_layer); - bool uvs, mat, tex; - - if (ob == NULL || ob->type != OB_MESH) { - BKE_report(op->reports, RPT_ERROR, "No active mesh object"); - return OPERATOR_CANCELLED; - } - - if (!BKE_paint_proj_mesh_data_check(scene, ob, &uvs, &mat, &tex, NULL)) { - BKE_paint_data_warning(op->reports, uvs, mat, tex, true); - WM_event_add_notifier(C, NC_SCENE | ND_TOOLSETTINGS, NULL); - return OPERATOR_CANCELLED; - } - - project_state_init(C, ob, &ps, BRUSH_STROKE_NORMAL); - - if (image == NULL) { - BKE_report(op->reports, RPT_ERROR, "Image could not be found"); - return OPERATOR_CANCELLED; - } - - ps.reproject_image = image; - ps.reproject_ibuf = BKE_image_acquire_ibuf(image, NULL, NULL); - - if ((ps.reproject_ibuf == NULL) || - ((ps.reproject_ibuf->rect || ps.reproject_ibuf->rect_float) == false)) - { - BKE_report(op->reports, RPT_ERROR, "Image data could not be found"); - return OPERATOR_CANCELLED; - } - - idgroup = IDP_GetProperties(&image->id, 0); - - if (idgroup) { - view_data = IDP_GetPropertyTypeFromGroup(idgroup, PROJ_VIEW_DATA_ID, IDP_ARRAY); - - /* type check to make sure its ok */ - if (view_data->len != PROJ_VIEW_DATA_SIZE || view_data->subtype != IDP_FLOAT) { - BKE_report(op->reports, RPT_ERROR, "Image project data invalid"); - return OPERATOR_CANCELLED; - } - } - - if (view_data) { - /* image has stored view projection info */ - ps.source = PROJ_SRC_IMAGE_VIEW; - } - else { - ps.source = PROJ_SRC_IMAGE_CAM; - - if (scene->camera == NULL) { - BKE_report(op->reports, RPT_ERROR, "No active camera set"); - return OPERATOR_CANCELLED; - } - } - - /* override */ - ps.is_texbrush = false; - ps.is_maskbrush = false; - ps.do_masking = false; - orig_brush_size = BKE_brush_size_get(scene, ps.brush); - /* cover the whole image */ - BKE_brush_size_set(scene, ps.brush, 32 * U.pixelsize); - - /* so pixels are initialized with minimal info */ - ps.tool = PAINT_TOOL_DRAW; - - scene->toolsettings->imapaint.flag |= IMAGEPAINT_DRAWING; - - ED_image_undo_push_begin(op->type->name, PAINT_MODE_TEXTURE_3D); - - /* allocate and initialize spatial data structures */ - project_paint_begin(C, &ps, false, 0); - - if (ps.me_eval == NULL) { - BKE_brush_size_set(scene, ps.brush, orig_brush_size); - BKE_report(op->reports, RPT_ERROR, "Could not get valid evaluated mesh"); - return OPERATOR_CANCELLED; - } - else { - float pos[2] = {0.0, 0.0}; - float lastpos[2] = {0.0, 0.0}; - int a; - - project_paint_op(&ps, lastpos, pos); - - project_image_refresh_tagged(&ps); - - for (a = 0; a < ps.image_tot; a++) { - GPU_free_image(ps.projImages[a].ima); - WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, ps.projImages[a].ima); - } - } - - project_paint_end(&ps); - - scene->toolsettings->imapaint.flag &= ~IMAGEPAINT_DRAWING; - BKE_brush_size_set(scene, ps.brush, orig_brush_size); - - return OPERATOR_FINISHED; + Image *image = BLI_findlink(&CTX_data_main(C)->images, RNA_enum_get(op->ptr, "image")); + Scene *scene = CTX_data_scene(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + ProjPaintState ps = {NULL}; + int orig_brush_size; + IDProperty *idgroup; + IDProperty *view_data = NULL; + Object *ob = OBACT(view_layer); + bool uvs, mat, tex; + + if (ob == NULL || ob->type != OB_MESH) { + BKE_report(op->reports, RPT_ERROR, "No active mesh object"); + return OPERATOR_CANCELLED; + } + + if (!BKE_paint_proj_mesh_data_check(scene, ob, &uvs, &mat, &tex, NULL)) { + BKE_paint_data_warning(op->reports, uvs, mat, tex, true); + WM_event_add_notifier(C, NC_SCENE | ND_TOOLSETTINGS, NULL); + return OPERATOR_CANCELLED; + } + + project_state_init(C, ob, &ps, BRUSH_STROKE_NORMAL); + + if (image == NULL) { + BKE_report(op->reports, RPT_ERROR, "Image could not be found"); + return OPERATOR_CANCELLED; + } + + ps.reproject_image = image; + ps.reproject_ibuf = BKE_image_acquire_ibuf(image, NULL, NULL); + + if ((ps.reproject_ibuf == NULL) || + ((ps.reproject_ibuf->rect || ps.reproject_ibuf->rect_float) == false)) { + BKE_report(op->reports, RPT_ERROR, "Image data could not be found"); + return OPERATOR_CANCELLED; + } + + idgroup = IDP_GetProperties(&image->id, 0); + + if (idgroup) { + view_data = IDP_GetPropertyTypeFromGroup(idgroup, PROJ_VIEW_DATA_ID, IDP_ARRAY); + + /* type check to make sure its ok */ + if (view_data->len != PROJ_VIEW_DATA_SIZE || view_data->subtype != IDP_FLOAT) { + BKE_report(op->reports, RPT_ERROR, "Image project data invalid"); + return OPERATOR_CANCELLED; + } + } + + if (view_data) { + /* image has stored view projection info */ + ps.source = PROJ_SRC_IMAGE_VIEW; + } + else { + ps.source = PROJ_SRC_IMAGE_CAM; + + if (scene->camera == NULL) { + BKE_report(op->reports, RPT_ERROR, "No active camera set"); + return OPERATOR_CANCELLED; + } + } + + /* override */ + ps.is_texbrush = false; + ps.is_maskbrush = false; + ps.do_masking = false; + orig_brush_size = BKE_brush_size_get(scene, ps.brush); + /* cover the whole image */ + BKE_brush_size_set(scene, ps.brush, 32 * U.pixelsize); + + /* so pixels are initialized with minimal info */ + ps.tool = PAINT_TOOL_DRAW; + + scene->toolsettings->imapaint.flag |= IMAGEPAINT_DRAWING; + + ED_image_undo_push_begin(op->type->name, PAINT_MODE_TEXTURE_3D); + + /* allocate and initialize spatial data structures */ + project_paint_begin(C, &ps, false, 0); + + if (ps.me_eval == NULL) { + BKE_brush_size_set(scene, ps.brush, orig_brush_size); + BKE_report(op->reports, RPT_ERROR, "Could not get valid evaluated mesh"); + return OPERATOR_CANCELLED; + } + else { + float pos[2] = {0.0, 0.0}; + float lastpos[2] = {0.0, 0.0}; + int a; + + project_paint_op(&ps, lastpos, pos); + + project_image_refresh_tagged(&ps); + + for (a = 0; a < ps.image_tot; a++) { + GPU_free_image(ps.projImages[a].ima); + WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, ps.projImages[a].ima); + } + } + + project_paint_end(&ps); + + scene->toolsettings->imapaint.flag &= ~IMAGEPAINT_DRAWING; + BKE_brush_size_set(scene, ps.brush, orig_brush_size); + + return OPERATOR_FINISHED; } void PAINT_OT_project_image(wmOperatorType *ot) { - PropertyRNA *prop; + PropertyRNA *prop; - /* identifiers */ - ot->name = "Project Image"; - ot->idname = "PAINT_OT_project_image"; - ot->description = "Project an edited render from the active camera back onto the object"; + /* identifiers */ + ot->name = "Project Image"; + ot->idname = "PAINT_OT_project_image"; + ot->description = "Project an edited render from the active camera back onto the object"; - /* api callbacks */ - ot->invoke = WM_enum_search_invoke; - ot->exec = texture_paint_camera_project_exec; + /* api callbacks */ + ot->invoke = WM_enum_search_invoke; + ot->exec = texture_paint_camera_project_exec; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - prop = RNA_def_enum(ot->srna, "image", DummyRNA_NULL_items, 0, "Image", ""); - RNA_def_enum_funcs(prop, RNA_image_itemf); - RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE); - ot->prop = prop; + prop = RNA_def_enum(ot->srna, "image", DummyRNA_NULL_items, 0, "Image", ""); + RNA_def_enum_funcs(prop, RNA_image_itemf); + RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE); + ot->prop = prop; } static bool texture_paint_image_from_view_poll(bContext *C) { - if (BKE_screen_find_big_area(CTX_wm_screen(C), SPACE_VIEW3D, 0) == NULL) { - CTX_wm_operator_poll_msg_set(C, "No 3D viewport found to create image from"); - return false; - } - return true; + if (BKE_screen_find_big_area(CTX_wm_screen(C), SPACE_VIEW3D, 0) == NULL) { + CTX_wm_operator_poll_msg_set(C, "No 3D viewport found to create image from"); + return false; + } + return true; } static int texture_paint_image_from_view_exec(bContext *C, wmOperator *op) { - Image *image; - ImBuf *ibuf; - char filename[FILE_MAX]; - - Main *bmain = CTX_data_main(C); - Depsgraph *depsgraph = CTX_data_depsgraph(C); - Scene *scene = CTX_data_scene(C); - ToolSettings *settings = scene->toolsettings; - int w = settings->imapaint.screen_grab_size[0]; - int h = settings->imapaint.screen_grab_size[1]; - int maxsize; - char err_out[256] = "unknown"; - - ScrArea *sa = BKE_screen_find_big_area(CTX_wm_screen(C), SPACE_VIEW3D, 0); - if (!sa) { - BKE_report(op->reports, RPT_ERROR, "No 3D viewport found to create image from"); - return OPERATOR_CANCELLED; - } - View3D *v3d = sa->spacedata.first; - ARegion *ar = BKE_area_find_region_active_win(sa); - if (!ar) { - BKE_report(op->reports, RPT_ERROR, "No 3D viewport found to create image from"); - return OPERATOR_CANCELLED; - } - RegionView3D *rv3d = ar->regiondata; - - RNA_string_get(op->ptr, "filepath", filename); - - maxsize = GPU_max_texture_size(); - - if (w > maxsize) w = maxsize; - if (h > maxsize) h = maxsize; - - ibuf = ED_view3d_draw_offscreen_imbuf( - depsgraph, scene, v3d->shading.type, - v3d, ar, - w, h, IB_rect, V3D_OFSDRAW_NONE, R_ALPHAPREMUL, 0, NULL, - NULL, err_out); - if (!ibuf) { - /* Mostly happens when OpenGL offscreen buffer was failed to create, */ - /* but could be other reasons. Should be handled in the future. nazgul */ - BKE_reportf(op->reports, RPT_ERROR, "Failed to create OpenGL off-screen buffer: %s", err_out); - return OPERATOR_CANCELLED; - } - - image = BKE_image_add_from_imbuf(bmain, ibuf, "image_view"); - - /* Drop reference to ibuf so that the image owns it */ - IMB_freeImBuf(ibuf); - - if (image) { - /* now for the trickiness. store the view projection here! - * re-projection will reuse this */ - IDPropertyTemplate val; - IDProperty *idgroup = IDP_GetProperties(&image->id, 1); - IDProperty *view_data; - bool is_ortho; - float *array; - - val.array.len = PROJ_VIEW_DATA_SIZE; - val.array.type = IDP_FLOAT; - view_data = IDP_New(IDP_ARRAY, &val, PROJ_VIEW_DATA_ID); - - array = (float *)IDP_Array(view_data); - memcpy(array, rv3d->winmat, sizeof(rv3d->winmat)); array += sizeof(rv3d->winmat) / sizeof(float); - memcpy(array, rv3d->viewmat, sizeof(rv3d->viewmat)); array += sizeof(rv3d->viewmat) / sizeof(float); - is_ortho = ED_view3d_clip_range_get(CTX_data_depsgraph(C), v3d, rv3d, &array[0], &array[1], true); - /* using float for a bool is dodgy but since its an extra member in the array... - * easier then adding a single bool prop */ - array[2] = is_ortho ? 1.0f : 0.0f; - - IDP_AddToGroup(idgroup, view_data); - } - - return OPERATOR_FINISHED; + Image *image; + ImBuf *ibuf; + char filename[FILE_MAX]; + + Main *bmain = CTX_data_main(C); + Depsgraph *depsgraph = CTX_data_depsgraph(C); + Scene *scene = CTX_data_scene(C); + ToolSettings *settings = scene->toolsettings; + int w = settings->imapaint.screen_grab_size[0]; + int h = settings->imapaint.screen_grab_size[1]; + int maxsize; + char err_out[256] = "unknown"; + + ScrArea *sa = BKE_screen_find_big_area(CTX_wm_screen(C), SPACE_VIEW3D, 0); + if (!sa) { + BKE_report(op->reports, RPT_ERROR, "No 3D viewport found to create image from"); + return OPERATOR_CANCELLED; + } + View3D *v3d = sa->spacedata.first; + ARegion *ar = BKE_area_find_region_active_win(sa); + if (!ar) { + BKE_report(op->reports, RPT_ERROR, "No 3D viewport found to create image from"); + return OPERATOR_CANCELLED; + } + RegionView3D *rv3d = ar->regiondata; + + RNA_string_get(op->ptr, "filepath", filename); + + maxsize = GPU_max_texture_size(); + + if (w > maxsize) + w = maxsize; + if (h > maxsize) + h = maxsize; + + ibuf = ED_view3d_draw_offscreen_imbuf(depsgraph, + scene, + v3d->shading.type, + v3d, + ar, + w, + h, + IB_rect, + V3D_OFSDRAW_NONE, + R_ALPHAPREMUL, + 0, + NULL, + NULL, + err_out); + if (!ibuf) { + /* Mostly happens when OpenGL offscreen buffer was failed to create, */ + /* but could be other reasons. Should be handled in the future. nazgul */ + BKE_reportf(op->reports, RPT_ERROR, "Failed to create OpenGL off-screen buffer: %s", err_out); + return OPERATOR_CANCELLED; + } + + image = BKE_image_add_from_imbuf(bmain, ibuf, "image_view"); + + /* Drop reference to ibuf so that the image owns it */ + IMB_freeImBuf(ibuf); + + if (image) { + /* now for the trickiness. store the view projection here! + * re-projection will reuse this */ + IDPropertyTemplate val; + IDProperty *idgroup = IDP_GetProperties(&image->id, 1); + IDProperty *view_data; + bool is_ortho; + float *array; + + val.array.len = PROJ_VIEW_DATA_SIZE; + val.array.type = IDP_FLOAT; + view_data = IDP_New(IDP_ARRAY, &val, PROJ_VIEW_DATA_ID); + + array = (float *)IDP_Array(view_data); + memcpy(array, rv3d->winmat, sizeof(rv3d->winmat)); + array += sizeof(rv3d->winmat) / sizeof(float); + memcpy(array, rv3d->viewmat, sizeof(rv3d->viewmat)); + array += sizeof(rv3d->viewmat) / sizeof(float); + is_ortho = ED_view3d_clip_range_get( + CTX_data_depsgraph(C), v3d, rv3d, &array[0], &array[1], true); + /* using float for a bool is dodgy but since its an extra member in the array... + * easier then adding a single bool prop */ + array[2] = is_ortho ? 1.0f : 0.0f; + + IDP_AddToGroup(idgroup, view_data); + } + + return OPERATOR_FINISHED; } void PAINT_OT_image_from_view(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Image from View"; - ot->idname = "PAINT_OT_image_from_view"; - ot->description = "Make an image from biggest 3D view for re-projection"; + /* identifiers */ + ot->name = "Image from View"; + ot->idname = "PAINT_OT_image_from_view"; + ot->description = "Make an image from biggest 3D view for re-projection"; - /* api callbacks */ - ot->exec = texture_paint_image_from_view_exec; - ot->poll = texture_paint_image_from_view_poll; + /* api callbacks */ + ot->exec = texture_paint_image_from_view_exec; + ot->poll = texture_paint_image_from_view_poll; - /* flags */ - ot->flag = OPTYPE_REGISTER; + /* flags */ + ot->flag = OPTYPE_REGISTER; - RNA_def_string_file_name(ot->srna, "filepath", NULL, FILE_MAX, "File Path", "Name of the file"); + RNA_def_string_file_name(ot->srna, "filepath", NULL, FILE_MAX, "File Path", "Name of the file"); } /********************************************* @@ -5953,462 +6158,476 @@ void PAINT_OT_image_from_view(wmOperatorType *ot) void BKE_paint_data_warning(struct ReportList *reports, bool uvs, bool mat, bool tex, bool stencil) { - BKE_reportf( - reports, RPT_WARNING, "Missing%s%s%s%s detected!", - !uvs ? " UVs," : "", - !mat ? " Materials," : "", - !tex ? " Textures," : "", - !stencil ? " Stencil," : "" - ); + BKE_reportf(reports, + RPT_WARNING, + "Missing%s%s%s%s detected!", + !uvs ? " UVs," : "", + !mat ? " Materials," : "", + !tex ? " Textures," : "", + !stencil ? " Stencil," : ""); } /* Make sure that active object has a material, * and assign UVs and image layers if they do not exist */ -bool BKE_paint_proj_mesh_data_check(Scene *scene, Object *ob, bool *uvs, bool *mat, bool *tex, bool *stencil) +bool BKE_paint_proj_mesh_data_check( + Scene *scene, Object *ob, bool *uvs, bool *mat, bool *tex, bool *stencil) { - Mesh *me; - int layernum; - ImagePaintSettings *imapaint = &scene->toolsettings->imapaint; - Brush *br = BKE_paint_brush(&imapaint->paint); - bool hasmat = true; - bool hastex = true; - bool hasstencil = true; - bool hasuvs = true; - - imapaint->missing_data = 0; - - BLI_assert(ob->type == OB_MESH); - - if (imapaint->mode == IMAGEPAINT_MODE_MATERIAL) { - /* no material, add one */ - if (ob->totcol == 0) { - hasmat = false; - hastex = false; - } - else { - /* there may be material slots but they may be empty, check */ - int i; - hasmat = false; - hastex = false; - - for (i = 1; i < ob->totcol + 1; i++) { - Material *ma = give_current_material(ob, i); - - if (ma) { - hasmat = true; - if (!ma->texpaintslot) { - /* refresh here just in case */ - BKE_texpaint_slot_refresh_cache(scene, ma); - - /* if still no slots, we have to add */ - if (ma->texpaintslot) { - hastex = true; - break; - } - } - else { - hastex = true; - break; - } - } - } - } - } - else if (imapaint->mode == IMAGEPAINT_MODE_IMAGE) { - if (imapaint->canvas == NULL) { - hastex = false; - } - } - - me = BKE_mesh_from_object(ob); - layernum = CustomData_number_of_layers(&me->ldata, CD_MLOOPUV); - - if (layernum == 0) { - hasuvs = false; - } - - /* Make sure we have a stencil to paint on! */ - if (br && br->imagepaint_tool == PAINT_TOOL_MASK) { - imapaint->flag |= IMAGEPAINT_PROJECT_LAYER_STENCIL; - - if (imapaint->stencil == NULL) { - hasstencil = false; - } - } - - if (!hasuvs) imapaint->missing_data |= IMAGEPAINT_MISSING_UVS; - if (!hasmat) imapaint->missing_data |= IMAGEPAINT_MISSING_MATERIAL; - if (!hastex) imapaint->missing_data |= IMAGEPAINT_MISSING_TEX; - if (!hasstencil) imapaint->missing_data |= IMAGEPAINT_MISSING_STENCIL; - - if (uvs) { - *uvs = hasuvs; - } - if (mat) { - *mat = hasmat; - } - if (tex) { - *tex = hastex; - } - if (stencil) { - *stencil = hasstencil; - } - - return hasuvs && hasmat && hastex && hasstencil; + Mesh *me; + int layernum; + ImagePaintSettings *imapaint = &scene->toolsettings->imapaint; + Brush *br = BKE_paint_brush(&imapaint->paint); + bool hasmat = true; + bool hastex = true; + bool hasstencil = true; + bool hasuvs = true; + + imapaint->missing_data = 0; + + BLI_assert(ob->type == OB_MESH); + + if (imapaint->mode == IMAGEPAINT_MODE_MATERIAL) { + /* no material, add one */ + if (ob->totcol == 0) { + hasmat = false; + hastex = false; + } + else { + /* there may be material slots but they may be empty, check */ + int i; + hasmat = false; + hastex = false; + + for (i = 1; i < ob->totcol + 1; i++) { + Material *ma = give_current_material(ob, i); + + if (ma) { + hasmat = true; + if (!ma->texpaintslot) { + /* refresh here just in case */ + BKE_texpaint_slot_refresh_cache(scene, ma); + + /* if still no slots, we have to add */ + if (ma->texpaintslot) { + hastex = true; + break; + } + } + else { + hastex = true; + break; + } + } + } + } + } + else if (imapaint->mode == IMAGEPAINT_MODE_IMAGE) { + if (imapaint->canvas == NULL) { + hastex = false; + } + } + + me = BKE_mesh_from_object(ob); + layernum = CustomData_number_of_layers(&me->ldata, CD_MLOOPUV); + + if (layernum == 0) { + hasuvs = false; + } + + /* Make sure we have a stencil to paint on! */ + if (br && br->imagepaint_tool == PAINT_TOOL_MASK) { + imapaint->flag |= IMAGEPAINT_PROJECT_LAYER_STENCIL; + + if (imapaint->stencil == NULL) { + hasstencil = false; + } + } + + if (!hasuvs) + imapaint->missing_data |= IMAGEPAINT_MISSING_UVS; + if (!hasmat) + imapaint->missing_data |= IMAGEPAINT_MISSING_MATERIAL; + if (!hastex) + imapaint->missing_data |= IMAGEPAINT_MISSING_TEX; + if (!hasstencil) + imapaint->missing_data |= IMAGEPAINT_MISSING_STENCIL; + + if (uvs) { + *uvs = hasuvs; + } + if (mat) { + *mat = hasmat; + } + if (tex) { + *tex = hastex; + } + if (stencil) { + *stencil = hasstencil; + } + + return hasuvs && hasmat && hastex && hasstencil; } /* Add layer operator */ enum { - LAYER_BASE_COLOR, - LAYER_SPECULAR, - LAYER_ROUGHNESS, - LAYER_METALLIC, - LAYER_NORMAL, - LAYER_BUMP, - LAYER_DISPLACEMENT, + LAYER_BASE_COLOR, + LAYER_SPECULAR, + LAYER_ROUGHNESS, + LAYER_METALLIC, + LAYER_NORMAL, + LAYER_BUMP, + LAYER_DISPLACEMENT, }; static const EnumPropertyItem layer_type_items[] = { - {LAYER_BASE_COLOR, "BASE_COLOR", 0, "Base Color", ""}, - {LAYER_SPECULAR, "SPECULAR", 0, "Specular", ""}, - {LAYER_ROUGHNESS, "ROUGHNESS", 0, "Roughness", ""}, - {LAYER_METALLIC, "METALLIC", 0, "Metallic", ""}, - {LAYER_NORMAL, "NORMAL", 0, "Normal", ""}, - {LAYER_BUMP, "BUMP", 0, "Bump", ""}, - {LAYER_DISPLACEMENT, "DISPLACEMENT", 0, "Displacement", ""}, - {0, NULL, 0, NULL, NULL}, + {LAYER_BASE_COLOR, "BASE_COLOR", 0, "Base Color", ""}, + {LAYER_SPECULAR, "SPECULAR", 0, "Specular", ""}, + {LAYER_ROUGHNESS, "ROUGHNESS", 0, "Roughness", ""}, + {LAYER_METALLIC, "METALLIC", 0, "Metallic", ""}, + {LAYER_NORMAL, "NORMAL", 0, "Normal", ""}, + {LAYER_BUMP, "BUMP", 0, "Bump", ""}, + {LAYER_DISPLACEMENT, "DISPLACEMENT", 0, "Displacement", ""}, + {0, NULL, 0, NULL, NULL}, }; static Image *proj_paint_image_create(wmOperator *op, Main *bmain) { - Image *ima; - float color[4] = {0.0f, 0.0f, 0.0f, 1.0f}; - char imagename[MAX_ID_NAME - 2] = "Material Diffuse Color"; - int width = 1024; - int height = 1024; - bool use_float = false; - short gen_type = IMA_GENTYPE_BLANK; - bool alpha = false; - - if (op) { - width = RNA_int_get(op->ptr, "width"); - height = RNA_int_get(op->ptr, "height"); - use_float = RNA_boolean_get(op->ptr, "float"); - gen_type = RNA_enum_get(op->ptr, "generated_type"); - RNA_float_get_array(op->ptr, "color", color); - alpha = RNA_boolean_get(op->ptr, "alpha"); - RNA_string_get(op->ptr, "name", imagename); - } - ima = BKE_image_add_generated( - bmain, width, height, imagename, alpha ? 32 : 24, use_float, - gen_type, color, false); - - return ima; + Image *ima; + float color[4] = {0.0f, 0.0f, 0.0f, 1.0f}; + char imagename[MAX_ID_NAME - 2] = "Material Diffuse Color"; + int width = 1024; + int height = 1024; + bool use_float = false; + short gen_type = IMA_GENTYPE_BLANK; + bool alpha = false; + + if (op) { + width = RNA_int_get(op->ptr, "width"); + height = RNA_int_get(op->ptr, "height"); + use_float = RNA_boolean_get(op->ptr, "float"); + gen_type = RNA_enum_get(op->ptr, "generated_type"); + RNA_float_get_array(op->ptr, "color", color); + alpha = RNA_boolean_get(op->ptr, "alpha"); + RNA_string_get(op->ptr, "name", imagename); + } + ima = BKE_image_add_generated( + bmain, width, height, imagename, alpha ? 32 : 24, use_float, gen_type, color, false); + + return ima; } static void proj_paint_default_color(wmOperator *op, int type, Material *ma) { - if (RNA_struct_property_is_set(op->ptr, "color")) { - return; - } - - bNode *in_node = ntreeFindType(ma->nodetree, SH_NODE_BSDF_PRINCIPLED); - if (in_node == NULL) { - return; - } - - float color[4]; - - if (type >= LAYER_BASE_COLOR && type < LAYER_NORMAL) { - /* Copy color from node, so result is unchanged after assigning textures. */ - bNodeSocket *in_sock = nodeFindSocket(in_node, SOCK_IN, layer_type_items[type].name); - - switch (in_sock->type) { - case SOCK_FLOAT: { - bNodeSocketValueFloat *socket_data = in_sock->default_value; - copy_v3_fl(color, socket_data->value); - color[3] = 1.0f; - break; - } - case SOCK_VECTOR: - case SOCK_RGBA: { - bNodeSocketValueRGBA *socket_data = in_sock->default_value; - copy_v3_v3(color, socket_data->value); - color[3] = 1.0f; - break; - } - default: { - return; - } - } - } - else if (type == LAYER_NORMAL) { - /* Neutral tangent space normal map. */ - rgba_float_args_set(color, 0.5f, 0.5f, 1.0f, 1.0f); - } - else if (ELEM(type, LAYER_BUMP, LAYER_DISPLACEMENT)) { - /* Neutral displacement and bump map. */ - rgba_float_args_set(color, 0.5f, 0.5f, 0.5f, 1.0f); - } - else { - return; - } - - RNA_float_set_array(op->ptr, "color", color); + if (RNA_struct_property_is_set(op->ptr, "color")) { + return; + } + + bNode *in_node = ntreeFindType(ma->nodetree, SH_NODE_BSDF_PRINCIPLED); + if (in_node == NULL) { + return; + } + + float color[4]; + + if (type >= LAYER_BASE_COLOR && type < LAYER_NORMAL) { + /* Copy color from node, so result is unchanged after assigning textures. */ + bNodeSocket *in_sock = nodeFindSocket(in_node, SOCK_IN, layer_type_items[type].name); + + switch (in_sock->type) { + case SOCK_FLOAT: { + bNodeSocketValueFloat *socket_data = in_sock->default_value; + copy_v3_fl(color, socket_data->value); + color[3] = 1.0f; + break; + } + case SOCK_VECTOR: + case SOCK_RGBA: { + bNodeSocketValueRGBA *socket_data = in_sock->default_value; + copy_v3_v3(color, socket_data->value); + color[3] = 1.0f; + break; + } + default: { + return; + } + } + } + else if (type == LAYER_NORMAL) { + /* Neutral tangent space normal map. */ + rgba_float_args_set(color, 0.5f, 0.5f, 1.0f, 1.0f); + } + else if (ELEM(type, LAYER_BUMP, LAYER_DISPLACEMENT)) { + /* Neutral displacement and bump map. */ + rgba_float_args_set(color, 0.5f, 0.5f, 0.5f, 1.0f); + } + else { + return; + } + + RNA_float_set_array(op->ptr, "color", color); } static bool proj_paint_add_slot(bContext *C, wmOperator *op) { - Object *ob = ED_object_active_context(C); - Scene *scene = CTX_data_scene(C); - Material *ma; - Image *ima = NULL; - - if (!ob) - return false; - - ma = give_current_material(ob, ob->actcol); + Object *ob = ED_object_active_context(C); + Scene *scene = CTX_data_scene(C); + Material *ma; + Image *ima = NULL; + + if (!ob) + return false; + + ma = give_current_material(ob, ob->actcol); - if (ma) { - Main *bmain = CTX_data_main(C); - int type = RNA_enum_get(op->ptr, "type"); + if (ma) { + Main *bmain = CTX_data_main(C); + int type = RNA_enum_get(op->ptr, "type"); - bNode *imanode; - bNodeTree *ntree = ma->nodetree; + bNode *imanode; + bNodeTree *ntree = ma->nodetree; - if (!ntree) { - ED_node_shader_default(C, &ma->id); - ntree = ma->nodetree; - } + if (!ntree) { + ED_node_shader_default(C, &ma->id); + ntree = ma->nodetree; + } - ma->use_nodes = true; + ma->use_nodes = true; - /* try to add an image node */ - imanode = nodeAddStaticNode(C, ntree, SH_NODE_TEX_IMAGE); + /* try to add an image node */ + imanode = nodeAddStaticNode(C, ntree, SH_NODE_TEX_IMAGE); - ima = proj_paint_image_create(op, bmain); - imanode->id = &ima->id; + ima = proj_paint_image_create(op, bmain); + imanode->id = &ima->id; - nodeSetActive(ntree, imanode); + nodeSetActive(ntree, imanode); - /* Connect to first available principled bsdf node. */ - bNode *in_node = ntreeFindType(ntree, SH_NODE_BSDF_PRINCIPLED); - bNode *out_node = imanode; + /* Connect to first available principled bsdf node. */ + bNode *in_node = ntreeFindType(ntree, SH_NODE_BSDF_PRINCIPLED); + bNode *out_node = imanode; - if (in_node != NULL) { - bNodeSocket *out_sock = nodeFindSocket(out_node, SOCK_OUT, "Color"); - bNodeSocket *in_sock = NULL; + if (in_node != NULL) { + bNodeSocket *out_sock = nodeFindSocket(out_node, SOCK_OUT, "Color"); + bNodeSocket *in_sock = NULL; - if (type >= LAYER_BASE_COLOR && type < LAYER_NORMAL) { - in_sock = nodeFindSocket(in_node, SOCK_IN, layer_type_items[type].name); - } - else if (type == LAYER_NORMAL) { - bNode *nor_node; - nor_node = nodeAddStaticNode(C, ntree, SH_NODE_NORMAL_MAP); + if (type >= LAYER_BASE_COLOR && type < LAYER_NORMAL) { + in_sock = nodeFindSocket(in_node, SOCK_IN, layer_type_items[type].name); + } + else if (type == LAYER_NORMAL) { + bNode *nor_node; + nor_node = nodeAddStaticNode(C, ntree, SH_NODE_NORMAL_MAP); - in_sock = nodeFindSocket(nor_node, SOCK_IN, "Color"); - nodeAddLink(ntree, out_node, out_sock, nor_node, in_sock); + in_sock = nodeFindSocket(nor_node, SOCK_IN, "Color"); + nodeAddLink(ntree, out_node, out_sock, nor_node, in_sock); - in_sock = nodeFindSocket(in_node, SOCK_IN, "Normal"); - out_sock = nodeFindSocket(nor_node, SOCK_OUT, "Normal"); + in_sock = nodeFindSocket(in_node, SOCK_IN, "Normal"); + out_sock = nodeFindSocket(nor_node, SOCK_OUT, "Normal"); - out_node = nor_node; - } - else if (type == LAYER_BUMP) { - bNode *bump_node; - bump_node = nodeAddStaticNode(C, ntree, SH_NODE_BUMP); + out_node = nor_node; + } + else if (type == LAYER_BUMP) { + bNode *bump_node; + bump_node = nodeAddStaticNode(C, ntree, SH_NODE_BUMP); - in_sock = nodeFindSocket(bump_node, SOCK_IN, "Height"); - nodeAddLink(ntree, out_node, out_sock, bump_node, in_sock); + in_sock = nodeFindSocket(bump_node, SOCK_IN, "Height"); + nodeAddLink(ntree, out_node, out_sock, bump_node, in_sock); - in_sock = nodeFindSocket(in_node, SOCK_IN, "Normal"); - out_sock = nodeFindSocket(bump_node, SOCK_OUT, "Normal"); + in_sock = nodeFindSocket(in_node, SOCK_IN, "Normal"); + out_sock = nodeFindSocket(bump_node, SOCK_OUT, "Normal"); - out_node = bump_node; - } - else if (type == LAYER_DISPLACEMENT) { - /* Connect to the displacement output socket */ - in_node = ntreeFindType(ntree, SH_NODE_OUTPUT_MATERIAL); + out_node = bump_node; + } + else if (type == LAYER_DISPLACEMENT) { + /* Connect to the displacement output socket */ + in_node = ntreeFindType(ntree, SH_NODE_OUTPUT_MATERIAL); - if (in_node != NULL) { - in_sock = nodeFindSocket(in_node, SOCK_IN, layer_type_items[type].name); - } - else { - in_sock = NULL; - } - } + if (in_node != NULL) { + in_sock = nodeFindSocket(in_node, SOCK_IN, layer_type_items[type].name); + } + else { + in_sock = NULL; + } + } - if (type > LAYER_BASE_COLOR) { - /* This is a "non color data" image */ - NodeTexImage *tex = imanode->storage; - tex->color_space = SHD_COLORSPACE_NONE; - } + if (type > LAYER_BASE_COLOR) { + /* This is a "non color data" image */ + NodeTexImage *tex = imanode->storage; + tex->color_space = SHD_COLORSPACE_NONE; + } - /* Check if the socket in already connected to something */ - bNodeLink *link = in_sock ? in_sock->link : NULL; - if (in_sock != NULL && link == NULL) { - nodeAddLink(ntree, out_node, out_sock, in_node, in_sock); + /* Check if the socket in already connected to something */ + bNodeLink *link = in_sock ? in_sock->link : NULL; + if (in_sock != NULL && link == NULL) { + nodeAddLink(ntree, out_node, out_sock, in_node, in_sock); - nodePositionRelative(out_node, in_node, out_sock, in_sock); - } - } + nodePositionRelative(out_node, in_node, out_sock, in_sock); + } + } - ntreeUpdateTree(CTX_data_main(C), ntree); - /* In case we added more than one node, position them too. */ - nodePositionPropagate(out_node); + ntreeUpdateTree(CTX_data_main(C), ntree); + /* In case we added more than one node, position them too. */ + nodePositionPropagate(out_node); - if (ima) { - BKE_texpaint_slot_refresh_cache(scene, ma); - BKE_image_signal(bmain, ima, NULL, IMA_SIGNAL_USER_NEW_IMAGE); - WM_event_add_notifier(C, NC_IMAGE | NA_ADDED, ima); - } + if (ima) { + BKE_texpaint_slot_refresh_cache(scene, ma); + BKE_image_signal(bmain, ima, NULL, IMA_SIGNAL_USER_NEW_IMAGE); + WM_event_add_notifier(C, NC_IMAGE | NA_ADDED, ima); + } - DEG_id_tag_update(&ntree->id, 0); - DEG_id_tag_update(&ma->id, ID_RECALC_SHADING); - ED_area_tag_redraw(CTX_wm_area(C)); + DEG_id_tag_update(&ntree->id, 0); + DEG_id_tag_update(&ma->id, ID_RECALC_SHADING); + ED_area_tag_redraw(CTX_wm_area(C)); - BKE_paint_proj_mesh_data_check(scene, ob, NULL, NULL, NULL, NULL); + BKE_paint_proj_mesh_data_check(scene, ob, NULL, NULL, NULL, NULL); - return true; - } + return true; + } - return false; + return false; } static int get_texture_layer_type(wmOperator *op, const char *prop_name) { - int type_value = RNA_enum_get(op->ptr, prop_name); - int type = RNA_enum_from_value(layer_type_items, type_value); - BLI_assert(type != -1); - return type; + int type_value = RNA_enum_get(op->ptr, prop_name); + int type = RNA_enum_from_value(layer_type_items, type_value); + BLI_assert(type != -1); + return type; } static Material *get_or_create_current_material(bContext *C, Object *ob) { - Material *ma = give_current_material(ob, ob->actcol); - if (!ma) { - Main *bmain = CTX_data_main(C); - ma = BKE_material_add(bmain, "Material"); - assign_material(bmain, ob, ma, ob->actcol, BKE_MAT_ASSIGN_USERPREF); - } - return ma; + Material *ma = give_current_material(ob, ob->actcol); + if (!ma) { + Main *bmain = CTX_data_main(C); + ma = BKE_material_add(bmain, "Material"); + assign_material(bmain, ob, ma, ob->actcol, BKE_MAT_ASSIGN_USERPREF); + } + return ma; } static int texture_paint_add_texture_paint_slot_exec(bContext *C, wmOperator *op) { - Object *ob = ED_object_active_context(C); - Material *ma = get_or_create_current_material(C, ob); - - int type = get_texture_layer_type(op, "type"); - proj_paint_default_color(op, type, ma); - - if (proj_paint_add_slot(C, op)) { - return OPERATOR_FINISHED; - } - else { - return OPERATOR_CANCELLED; - } + Object *ob = ED_object_active_context(C); + Material *ma = get_or_create_current_material(C, ob); + + int type = get_texture_layer_type(op, "type"); + proj_paint_default_color(op, type, ma); + + if (proj_paint_add_slot(C, op)) { + return OPERATOR_FINISHED; + } + else { + return OPERATOR_CANCELLED; + } } -static void get_default_texture_layer_name_for_object(Object *ob, int texture_type, char *dst, int dst_length) +static void get_default_texture_layer_name_for_object(Object *ob, + int texture_type, + char *dst, + int dst_length) { - Material *ma = give_current_material(ob, ob->actcol); - const char *base_name = ma ? &ma->id.name[2] : &ob->id.name[2]; - BLI_snprintf(dst, dst_length, "%s %s", base_name, layer_type_items[texture_type].name); + Material *ma = give_current_material(ob, ob->actcol); + const char *base_name = ma ? &ma->id.name[2] : &ob->id.name[2]; + BLI_snprintf(dst, dst_length, "%s %s", base_name, layer_type_items[texture_type].name); } -static int texture_paint_add_texture_paint_slot_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +static int texture_paint_add_texture_paint_slot_invoke(bContext *C, + wmOperator *op, + const wmEvent *UNUSED(event)) { - /* Get material and default color to display in the popup. */ - Object *ob = ED_object_active_context(C); - Material *ma = get_or_create_current_material(C, ob); + /* Get material and default color to display in the popup. */ + Object *ob = ED_object_active_context(C); + Material *ma = get_or_create_current_material(C, ob); - int type = get_texture_layer_type(op, "type"); - proj_paint_default_color(op, type, ma); + int type = get_texture_layer_type(op, "type"); + proj_paint_default_color(op, type, ma); - char imagename[MAX_ID_NAME - 2]; - get_default_texture_layer_name_for_object(ob, type, (char *)&imagename, sizeof(imagename)); - RNA_string_set(op->ptr, "name", imagename); + char imagename[MAX_ID_NAME - 2]; + get_default_texture_layer_name_for_object(ob, type, (char *)&imagename, sizeof(imagename)); + RNA_string_set(op->ptr, "name", imagename); - return WM_operator_props_dialog_popup(C, op, 300, 100); + return WM_operator_props_dialog_popup(C, op, 300, 100); } #define IMA_DEF_NAME N_("Untitled") - void PAINT_OT_add_texture_paint_slot(wmOperatorType *ot) { - PropertyRNA *prop; - static float default_color[4] = {0.0f, 0.0f, 0.0f, 1.0f}; - - /* identifiers */ - ot->name = "Add Texture Paint Slot"; - ot->description = "Add a texture paint slot"; - ot->idname = "PAINT_OT_add_texture_paint_slot"; - - /* api callbacks */ - ot->invoke = texture_paint_add_texture_paint_slot_invoke; - ot->exec = texture_paint_add_texture_paint_slot_exec; - ot->poll = ED_operator_object_active; - - /* flags */ - ot->flag = OPTYPE_UNDO; - - /* properties */ - prop = RNA_def_enum(ot->srna, "type", layer_type_items, 0, "Type", "Merge method to use"); - RNA_def_property_flag(prop, PROP_HIDDEN); - RNA_def_string(ot->srna, "name", IMA_DEF_NAME, MAX_ID_NAME - 2, "Name", "Image data-block name"); - prop = RNA_def_int(ot->srna, "width", 1024, 1, INT_MAX, "Width", "Image width", 1, 16384); - RNA_def_property_subtype(prop, PROP_PIXEL); - prop = RNA_def_int(ot->srna, "height", 1024, 1, INT_MAX, "Height", "Image height", 1, 16384); - RNA_def_property_subtype(prop, PROP_PIXEL); - prop = RNA_def_float_color(ot->srna, "color", 4, NULL, 0.0f, FLT_MAX, "Color", "Default fill color", 0.0f, 1.0f); - RNA_def_property_subtype(prop, PROP_COLOR_GAMMA); - RNA_def_property_float_array_default(prop, default_color); - RNA_def_boolean(ot->srna, "alpha", 1, "Alpha", "Create an image with an alpha channel"); - RNA_def_enum(ot->srna, "generated_type", rna_enum_image_generated_type_items, IMA_GENTYPE_BLANK, - "Generated Type", "Fill the image with a grid for UV map testing"); - RNA_def_boolean(ot->srna, "float", 0, "32 bit Float", "Create image with 32 bit floating point bit depth"); + PropertyRNA *prop; + static float default_color[4] = {0.0f, 0.0f, 0.0f, 1.0f}; + + /* identifiers */ + ot->name = "Add Texture Paint Slot"; + ot->description = "Add a texture paint slot"; + ot->idname = "PAINT_OT_add_texture_paint_slot"; + + /* api callbacks */ + ot->invoke = texture_paint_add_texture_paint_slot_invoke; + ot->exec = texture_paint_add_texture_paint_slot_exec; + ot->poll = ED_operator_object_active; + + /* flags */ + ot->flag = OPTYPE_UNDO; + + /* properties */ + prop = RNA_def_enum(ot->srna, "type", layer_type_items, 0, "Type", "Merge method to use"); + RNA_def_property_flag(prop, PROP_HIDDEN); + RNA_def_string(ot->srna, "name", IMA_DEF_NAME, MAX_ID_NAME - 2, "Name", "Image data-block name"); + prop = RNA_def_int(ot->srna, "width", 1024, 1, INT_MAX, "Width", "Image width", 1, 16384); + RNA_def_property_subtype(prop, PROP_PIXEL); + prop = RNA_def_int(ot->srna, "height", 1024, 1, INT_MAX, "Height", "Image height", 1, 16384); + RNA_def_property_subtype(prop, PROP_PIXEL); + prop = RNA_def_float_color( + ot->srna, "color", 4, NULL, 0.0f, FLT_MAX, "Color", "Default fill color", 0.0f, 1.0f); + RNA_def_property_subtype(prop, PROP_COLOR_GAMMA); + RNA_def_property_float_array_default(prop, default_color); + RNA_def_boolean(ot->srna, "alpha", 1, "Alpha", "Create an image with an alpha channel"); + RNA_def_enum(ot->srna, + "generated_type", + rna_enum_image_generated_type_items, + IMA_GENTYPE_BLANK, + "Generated Type", + "Fill the image with a grid for UV map testing"); + RNA_def_boolean( + ot->srna, "float", 0, "32 bit Float", "Create image with 32 bit floating point bit depth"); } static int add_simple_uvs_exec(bContext *C, wmOperator *UNUSED(op)) { - /* no checks here, poll function does them for us */ - Main *bmain = CTX_data_main(C); - Object *ob = CTX_data_active_object(C); - Scene *scene = CTX_data_scene(C); + /* no checks here, poll function does them for us */ + Main *bmain = CTX_data_main(C); + Object *ob = CTX_data_active_object(C); + Scene *scene = CTX_data_scene(C); - ED_uvedit_add_simple_uvs(bmain, scene, ob); + ED_uvedit_add_simple_uvs(bmain, scene, ob); - BKE_paint_proj_mesh_data_check(scene, ob, NULL, NULL, NULL, NULL); + BKE_paint_proj_mesh_data_check(scene, ob, NULL, NULL, NULL, NULL); - DEG_id_tag_update(ob->data, 0); - WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data); - WM_event_add_notifier(C, NC_SCENE | ND_TOOLSETTINGS, scene); - return OPERATOR_FINISHED; + DEG_id_tag_update(ob->data, 0); + WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data); + WM_event_add_notifier(C, NC_SCENE | ND_TOOLSETTINGS, scene); + return OPERATOR_FINISHED; } static bool add_simple_uvs_poll(bContext *C) { - Object *ob = CTX_data_active_object(C); + Object *ob = CTX_data_active_object(C); - if (!ob || ob->type != OB_MESH || ob->mode != OB_MODE_TEXTURE_PAINT) { - return false; - } - return true; + if (!ob || ob->type != OB_MESH || ob->mode != OB_MODE_TEXTURE_PAINT) { + return false; + } + return true; } void PAINT_OT_add_simple_uvs(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Add simple UVs"; - ot->description = "Add cube map uvs on mesh"; - ot->idname = "PAINT_OT_add_simple_uvs"; + /* identifiers */ + ot->name = "Add simple UVs"; + ot->description = "Add cube map uvs on mesh"; + ot->idname = "PAINT_OT_add_simple_uvs"; - /* api callbacks */ - ot->exec = add_simple_uvs_exec; - ot->poll = add_simple_uvs_poll; + /* api callbacks */ + ot->exec = add_simple_uvs_exec; + ot->poll = add_simple_uvs_poll; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } |