diff options
Diffstat (limited to 'source/blender/editors/sculpt_paint')
16 files changed, 1405 insertions, 935 deletions
diff --git a/source/blender/editors/sculpt_paint/paint_cursor.c b/source/blender/editors/sculpt_paint/paint_cursor.c index 886e4e5ef8c..30ab00a72c2 100644 --- a/source/blender/editors/sculpt_paint/paint_cursor.c +++ b/source/blender/editors/sculpt_paint/paint_cursor.c @@ -955,21 +955,30 @@ static void paint_cursor_on_hit(UnifiedPaintSettings *ups, Brush *brush, ViewCon } } +static bool ommit_cursor_drawing(Paint *paint, PaintMode mode, Brush *brush) +{ + if (paint->flags & PAINT_SHOW_BRUSH) { + if (ELEM(mode, PAINT_TEXTURE_2D, PAINT_TEXTURE_PROJECTIVE) && brush->imagepaint_tool == PAINT_TOOL_FILL) + return true; + } + return false; +} + static void paint_draw_cursor(bContext *C, int x, int y, void *UNUSED(unused)) { Scene *scene = CTX_data_scene(C); UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings; Paint *paint = BKE_paint_get_active_from_context(C); Brush *brush = BKE_paint_brush(paint); + PaintMode mode = BKE_paintmode_get_active_from_context(C); ViewContext vc; - PaintMode mode; float final_radius; float translation[2]; float outline_alpha, *outline_col; float zoomx, zoomy; - + /* check that brush drawing is enabled */ - if (!(paint->flags & PAINT_SHOW_BRUSH)) + if (ommit_cursor_drawing(paint, mode, brush)) return; /* can't use stroke vc here because this will be called during @@ -978,7 +987,6 @@ static void paint_draw_cursor(bContext *C, int x, int y, void *UNUSED(unused)) get_imapaint_zoom(C, &zoomx, &zoomy); zoomx = max_ff(zoomx, zoomy); - mode = BKE_paintmode_get_active_from_context(C); /* skip everything and draw brush here */ if (brush->flag & BRUSH_CURVE) { @@ -1018,8 +1026,8 @@ static void paint_draw_cursor(bContext *C, int x, int y, void *UNUSED(unused)) /* check if brush is subtracting, use different color then */ /* TODO: no way currently to know state of pen flip or * invert key modifier without starting a stroke */ - if ((!(ups->draw_inverted) ^ - !(brush->flag & BRUSH_DIR_IN)) && + if (((ups->draw_inverted == 0) ^ + ((brush->flag & BRUSH_DIR_IN) == 0)) && ELEM(brush->sculpt_tool, SCULPT_TOOL_DRAW, SCULPT_TOOL_INFLATE, SCULPT_TOOL_CLAY, SCULPT_TOOL_PINCH, SCULPT_TOOL_CREASE)) diff --git a/source/blender/editors/sculpt_paint/paint_curve.c b/source/blender/editors/sculpt_paint/paint_curve.c index 439c2a639bd..8c754d7adf3 100644 --- a/source/blender/editors/sculpt_paint/paint_curve.c +++ b/source/blender/editors/sculpt_paint/paint_curve.c @@ -222,6 +222,28 @@ static int paintcurve_point_co_index(char sel) return i; } +static char paintcurve_point_side_index(const BezTriple *bezt, const bool is_first, const char fallback) +{ + /* when matching, guess based on endpoint side */ + if (BEZSELECTED(bezt)) { + if ((bezt->f1 & SELECT) == (bezt->f3 & SELECT)) { + return is_first ? SEL_F1 : SEL_F3; + } + else if (bezt->f1 & SELECT) { + return SEL_F1; + } + else if (bezt->f3 & SELECT) { + return SEL_F3; + } + else { + return fallback; + } + } + else { + return 0; + } +} + /******************* Operators *********************************/ static int paintcurve_new_exec(bContext *C, wmOperator *UNUSED(op)) @@ -295,10 +317,17 @@ static void paintcurve_point_add(bContext *C, wmOperator *op, const int loc[2]) for (i = 0; i < pc->tot_points; i++) { pcp[i].bez.f1 = pcp[i].bez.f2 = pcp[i].bez.f3 = 0; } - pcp[add_index].bez.f3 = SELECT; - pcp[add_index].bez.h2 = HD_ALIGN; - pc->add_index = add_index + 1; + BKE_paint_curve_clamp_endpoint_add_index(pc, add_index); + + if (pc->add_index != 0) { + pcp[add_index].bez.f3 = SELECT; + pcp[add_index].bez.h2 = HD_ALIGN; + } + else { + pcp[add_index].bez.f1 = SELECT; + pcp[add_index].bez.h1 = HD_ALIGN; + } WM_paint_cursor_tag_redraw(window, ar); } @@ -384,7 +413,7 @@ static int paintcurve_delete_point_exec(bContext *C, wmOperator *op) points_new[j] = pc->points[i]; if ((i + 1) == pc->add_index) { - pc->add_index = j + 1; + BKE_paint_curve_clamp_endpoint_add_index(pc, j); } j++; } @@ -469,7 +498,7 @@ static bool paintcurve_point_select(bContext *C, wmOperator *op, const int loc[2 pcp = paintcurve_point_get_closest(pc, loc_fl, false, PAINT_CURVE_SELECT_THRESHOLD, &selflag); if (pcp) { - pc->add_index = (pcp - pc->points) + 1; + BKE_paint_curve_clamp_endpoint_add_index(pc, pcp - pc->points); if (selflag == SEL_F2) { if (extend) @@ -599,9 +628,8 @@ static int paintcurve_slide_invoke(bContext *C, wmOperator *op, const wmEvent *e pcp = NULL; /* just find first selected point */ for (i = 0; i < pc->tot_points; i++) { - if (pc->points[i].bez.f1 || pc->points[i].bez.f2 || pc->points[i].bez.f3) { + if ((select = paintcurve_point_side_index(&pc->points[i].bez, i == 0, SEL_F3))) { pcp = &pc->points[i]; - select = SEL_F3; break; } } @@ -631,7 +659,7 @@ static int paintcurve_slide_invoke(bContext *C, wmOperator *op, const wmEvent *e /* only select the active point */ PAINT_CURVE_POINT_SELECT(pcp, psd->select); - pc->add_index = (pcp - pc->points) + 1; + BKE_paint_curve_clamp_endpoint_add_index(pc, pcp - pc->points); WM_event_add_modal_handler(C, op); WM_paint_cursor_tag_redraw(window, ar); diff --git a/source/blender/editors/sculpt_paint/paint_hide.c b/source/blender/editors/sculpt_paint/paint_hide.c index f1c91d0fcb5..52a60347f9f 100644 --- a/source/blender/editors/sculpt_paint/paint_hide.c +++ b/source/blender/editors/sculpt_paint/paint_hide.c @@ -150,7 +150,7 @@ static void partialvis_update_grids(Object *ob, /* get PBVH data */ BKE_pbvh_node_get_grids(pbvh, node, &grid_indices, &totgrid, NULL, NULL, - &grids, NULL); + &grids); grid_hidden = BKE_pbvh_grid_hidden(pbvh); BKE_pbvh_get_grid_key(pbvh, &key); diff --git a/source/blender/editors/sculpt_paint/paint_image.c b/source/blender/editors/sculpt_paint/paint_image.c index 5cfbd164153..346be5b336e 100644 --- a/source/blender/editors/sculpt_paint/paint_image.c +++ b/source/blender/editors/sculpt_paint/paint_image.c @@ -215,7 +215,7 @@ void *image_undo_find_tile(Image *ima, ImBuf *ibuf, int x_tile, int y_tile, unsi return NULL; } -void *image_undo_push_tile(Image *ima, ImBuf *ibuf, ImBuf **tmpibuf, int x_tile, int y_tile, unsigned short **mask, bool **valid, bool proj) +void *image_undo_push_tile(Image *ima, ImBuf *ibuf, ImBuf **tmpibuf, int x_tile, int y_tile, unsigned short **mask, bool **valid, bool proj, bool find_prev) { ListBase *lb = undo_paint_push_get_list(UNDO_PAINT_IMAGE); UndoImageTile *tile; @@ -226,7 +226,7 @@ void *image_undo_push_tile(Image *ima, ImBuf *ibuf, ImBuf **tmpibuf, int x_tile, /* check if tile is already pushed */ /* in projective painting we keep accounting of tiles, so if we need one pushed, just push! */ - if (!proj) { + if (find_prev) { data = image_undo_find_tile(ima, ibuf, x_tile, y_tile, mask, true); if (data) return data; @@ -445,7 +445,7 @@ void imapaint_region_tiles(ImBuf *ibuf, int x, int y, int w, int h, int *tx, int *ty = (y >> IMAPAINT_TILE_BITS); } -void ED_imapaint_dirty_region(Image *ima, ImBuf *ibuf, int x, int y, int w, int h) +void ED_imapaint_dirty_region(Image *ima, ImBuf *ibuf, int x, int y, int w, int h, bool find_old) { ImBuf *tmpibuf = NULL; int tilex, tiley, tilew, tileh, tx, ty; @@ -474,7 +474,7 @@ void ED_imapaint_dirty_region(Image *ima, ImBuf *ibuf, int x, int y, int w, int for (ty = tiley; ty <= tileh; ty++) for (tx = tilex; tx <= tilew; tx++) - image_undo_push_tile(ima, ibuf, &tmpibuf, tx, ty, NULL, NULL, false); + image_undo_push_tile(ima, ibuf, &tmpibuf, tx, ty, NULL, NULL, false, find_old); ibuf->userflags |= IB_BITMAPDIRTY; @@ -499,7 +499,7 @@ void imapaint_image_update(SpaceImage *sima, Image *image, ImBuf *ibuf, short te int w = imapaintpartial.x2 - imapaintpartial.x1; int h = imapaintpartial.y2 - imapaintpartial.y1; /* Testing with partial update in uv editor too */ - GPU_paint_update_image(image, imapaintpartial.x1, imapaintpartial.y1, w, h); //!texpaint); + GPU_paint_update_image(image, (sima ? &sima->iuser : NULL), imapaintpartial.x1, imapaintpartial.y1, w, h); //!texpaint); } } @@ -1393,7 +1393,7 @@ static int texture_paint_toggle_exec(bContext *C, wmOperator *op) if (ma && ma->texpaintslot) ima = ma->texpaintslot[ma->paint_active_slot].ima; } - else if (imapaint->mode == IMAGEPAINT_MODE_MATERIAL) { + else if (imapaint->mode == IMAGEPAINT_MODE_IMAGE) { ima = imapaint->canvas; } diff --git a/source/blender/editors/sculpt_paint/paint_image_2d.c b/source/blender/editors/sculpt_paint/paint_image_2d.c index b0fd6a9fa6b..1ad700b2964 100644 --- a/source/blender/editors/sculpt_paint/paint_image_2d.c +++ b/source/blender/editors/sculpt_paint/paint_image_2d.c @@ -345,8 +345,8 @@ static unsigned short *brush_painter_curve_mask_new(BrushPainter *painter, int d { Brush *brush = painter->brush; - int xoff = -diameter * 0.5f + 0.5f; - int yoff = -diameter * 0.5f + 0.5f; + int xoff = -radius; + int yoff = -radius; unsigned short *mask, *m; int x, y; @@ -889,7 +889,7 @@ static void paint_2d_lift_soften(ImagePaintState *s, ImBuf *ibuf, ImBuf *ibufb, /* 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 */ - outrgb[0] = outrgb[1] = outrgb[2] = rgb_to_grayscale(outrgb); + outrgb[0] = outrgb[1] = outrgb[2] = IMB_colormanagement_get_luminance(outrgb); if (fabsf(outrgb[0]) > threshold) { float mask = BKE_brush_alpha_get(s->scene, s->brush); float alpha = rgba[3]; @@ -996,8 +996,8 @@ static ImBuf *paint_2d_lift_clone(ImBuf *ibuf, ImBuf *ibufb, int *pos) static void paint_2d_convert_brushco(ImBuf *ibufb, const float pos[2], int ipos[2]) { - ipos[0] = (int)floorf((pos[0] - ibufb->x / 2) + 1.0f); - ipos[1] = (int)floorf((pos[1] - ibufb->y / 2) + 1.0f); + ipos[0] = (int)floorf((pos[0] - ibufb->x / 2)); + ipos[1] = (int)floorf((pos[1] - ibufb->y / 2)); } static int paint_2d_op(void *state, ImBuf *ibufb, unsigned short *curveb, unsigned short *texmaskb, const float lastpos[2], const float pos[2]) @@ -1049,7 +1049,7 @@ static int paint_2d_op(void *state, ImBuf *ibufb, unsigned short *curveb, unsign for (a = 0; a < tot; a++) { ED_imapaint_dirty_region(s->image, s->canvas, region[a].destx, region[a].desty, - region[a].width, region[a].height); + region[a].width, region[a].height, true); if (s->do_masking) { /* masking, find original pixels tiles from undo buffer to composite over */ @@ -1109,7 +1109,7 @@ static int paint_2d_canvas_set(ImagePaintState *s, Image *ima) if (ima == NULL) { return 0; } - else if (ima->packedfile && ima->rr) { + else if (BKE_image_has_packedfile(ima) && ima->rr) { s->warnpackedfile = ima->id.name + 2; return 0; } @@ -1306,12 +1306,12 @@ static void paint_2d_fill_add_pixel_byte( const int x_px, const int y_px, ImBuf *ibuf, BLI_Stack *stack, BLI_bitmap *touched, const float color[4], float threshold_sq) { - int coordinate; + size_t coordinate; if (x_px >= ibuf->x || x_px < 0 || y_px >= ibuf->y || y_px < 0) return; - coordinate = y_px * ibuf->x + x_px; + coordinate = ((size_t)y_px) * ibuf->x + x_px; if (!BLI_BITMAP_TEST(touched, coordinate)) { float color_f[4]; @@ -1329,12 +1329,12 @@ static void paint_2d_fill_add_pixel_float( const int x_px, const int y_px, ImBuf *ibuf, BLI_Stack *stack, BLI_bitmap *touched, const float color[4], float threshold_sq) { - int coordinate; + size_t coordinate; if (x_px >= ibuf->x || x_px < 0 || y_px >= ibuf->y || y_px < 0) return; - coordinate = y_px * ibuf->x + x_px; + coordinate = ((size_t)y_px) * ibuf->x + x_px; if (!BLI_BITMAP_TEST(touched, coordinate)) { if (compare_len_squared_v3v3(ibuf->rect_float + 4 * coordinate, color, threshold_sq)) { @@ -1386,21 +1386,21 @@ void paint_2d_bucket_fill( if (!mouse_init || !br) { /* first case, no image UV, fill the whole image */ - ED_imapaint_dirty_region(ima, ibuf, 0, 0, ibuf->x, ibuf->y); + ED_imapaint_dirty_region(ima, ibuf, 0, 0, ibuf->x, ibuf->y, false); if (do_float) { for (x_px = 0; x_px < ibuf->x; x_px++) { for (y_px = 0; y_px < ibuf->y; y_px++) { - blend_color_mix_float(ibuf->rect_float + 4 * (y_px * ibuf->x + x_px), - ibuf->rect_float + 4 * (y_px * ibuf->x + x_px), color_f); + blend_color_mix_float(ibuf->rect_float + 4 * (((size_t)y_px) * ibuf->x + x_px), + ibuf->rect_float + 4 * (((size_t)y_px) * ibuf->x + x_px), color_f); } } } else { for (x_px = 0; x_px < ibuf->x; x_px++) { for (y_px = 0; y_px < ibuf->y; y_px++) { - blend_color_mix_byte((unsigned char *)(ibuf->rect + y_px * ibuf->x + x_px), - (unsigned char *)(ibuf->rect + y_px * ibuf->x + x_px), (unsigned char *)&color_b); + blend_color_mix_byte((unsigned char *)(ibuf->rect + ((size_t)y_px) * ibuf->x + x_px), + (unsigned char *)(ibuf->rect + ((size_t)y_px) * ibuf->x + x_px), (unsigned char *)&color_b); } } } @@ -1410,7 +1410,7 @@ void paint_2d_bucket_fill( * value is within the brush fill threshold from the fill color */ BLI_Stack *stack; BLI_bitmap *touched; - int coordinate; + size_t coordinate; int width = ibuf->x; float image_init[2]; int minx = ibuf->x, miny = ibuf->y, maxx = 0, maxy = 0; @@ -1428,12 +1428,12 @@ void paint_2d_bucket_fill( } /* change image invalidation method later */ - ED_imapaint_dirty_region(ima, ibuf, 0, 0, ibuf->x, ibuf->y); + ED_imapaint_dirty_region(ima, ibuf, 0, 0, ibuf->x, ibuf->y, false); - stack = BLI_stack_new(sizeof(int), __func__); - touched = BLI_BITMAP_NEW(ibuf->x * ibuf->y, "bucket_fill_bitmap"); + stack = BLI_stack_new(sizeof(size_t), __func__); + touched = BLI_BITMAP_NEW(((size_t)ibuf->x) * ibuf->y, "bucket_fill_bitmap"); - coordinate = (y_px * ibuf->x + x_px); + coordinate = (((size_t)y_px) * ibuf->x + x_px); if (do_float) { copy_v4_v4(pixel_color, ibuf->rect_float + 4 * coordinate); @@ -1566,7 +1566,7 @@ void paint_2d_gradient_fill( do_float = (ibuf->rect_float != NULL); /* this will be substituted by something else when selection is available */ - ED_imapaint_dirty_region(ima, ibuf, 0, 0, ibuf->x, ibuf->y); + ED_imapaint_dirty_region(ima, ibuf, 0, 0, ibuf->x, ibuf->y, false); if (do_float) { for (x_px = 0; x_px < ibuf->x; x_px++) { @@ -1590,8 +1590,8 @@ void paint_2d_gradient_fill( /* convert to premultiplied */ mul_v3_fl(color_f, color_f[3]); color_f[3] *= br->alpha; - IMB_blend_color_float(ibuf->rect_float + 4 * (y_px * ibuf->x + x_px), - ibuf->rect_float + 4 * (y_px * ibuf->x + x_px), + IMB_blend_color_float(ibuf->rect_float + 4 * (((size_t)y_px) * ibuf->x + x_px), + ibuf->rect_float + 4 * (((size_t)y_px) * ibuf->x + x_px), color_f, br->blend); } } @@ -1619,8 +1619,8 @@ void paint_2d_gradient_fill( linearrgb_to_srgb_v3_v3(color_f, color_f); rgba_float_to_uchar((unsigned char *)&color_b, color_f); ((unsigned char *)&color_b)[3] *= br->alpha; - IMB_blend_color_byte((unsigned char *)(ibuf->rect + y_px * ibuf->x + x_px), - (unsigned char *)(ibuf->rect + y_px * ibuf->x + x_px), + IMB_blend_color_byte((unsigned char *)(ibuf->rect + ((size_t)y_px) * ibuf->x + x_px), + (unsigned char *)(ibuf->rect + ((size_t)y_px) * ibuf->x + x_px), (unsigned char *)&color_b, br->blend); } } diff --git a/source/blender/editors/sculpt_paint/paint_image_proj.c b/source/blender/editors/sculpt_paint/paint_image_proj.c index 098f0d04d78..58f1ebb9eac 100644 --- a/source/blender/editors/sculpt_paint/paint_image_proj.c +++ b/source/blender/editors/sculpt_paint/paint_image_proj.c @@ -43,6 +43,7 @@ #include "BLI_blenlib.h" #include "BLI_linklist.h" #include "BLI_math.h" +#include "BLI_math_bits.h" #include "BLI_math_color_blend.h" #include "BLI_memarena.h" #include "BLI_threads.h" @@ -134,6 +135,8 @@ BLI_INLINE unsigned char f_to_char(const float val) //#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 */ @@ -151,6 +154,13 @@ BLI_INLINE unsigned char f_to_char(const float val) #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_SRC_VIEW 1 #define PROJ_SRC_IMAGE_CAM 2 #define PROJ_SRC_IMAGE_VIEW 3 @@ -159,12 +169,6 @@ BLI_INLINE unsigned char f_to_char(const float val) #define PROJ_VIEW_DATA_ID "view_data" #define PROJ_VIEW_DATA_SIZE (4 * 4 + 4 * 4 + 3) /* viewmat + winmat + clipsta + clipend + is_ortho */ - -/* a slightly scaled down face is used to get fake 3D location for edge pixels in the seams - * as this number approaches 1.0f the likelihood increases of float precision errors where - * it is occluded by an adjacent face */ -#define PROJ_FACE_SCALE_SEAM 0.99f - #define PROJ_BUCKET_NULL 0 #define PROJ_BUCKET_INIT (1 << 0) // #define PROJ_BUCKET_CLONE_INIT (1<<1) @@ -190,9 +194,32 @@ typedef struct ProjPaintImage { unsigned short **maskRect; /* the mask accumulation must happen on canvas, not on space screen bucket. * Here we store the mask rectangle */ bool **valid; /* store flag to enforce validation of undo rectangle */ - int touch; + bool touch; } ProjPaintImage; +/** + * Handle for stroke (operator customdata) + */ +typedef struct ProjStrokeHandle { + /* Support for painting from multiple views at once, + * currently used to impliment summetry 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; + + bool need_redraw; + + /* trick to bypass regular paint and allow clone picking */ + bool is_clone_cursor_pick; + + /* In ProjPaintState, only here for convenience */ + Scene *scene; + Brush *brush; +} ProjStrokeHandle; + /* Main projection painting struct passed to all projection painting functions */ typedef struct ProjPaintState { View3D *v3d; @@ -208,24 +235,14 @@ typedef struct ProjPaintState { Brush *brush; short tool, blend, mode; - int orig_brush_size; + 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 */ - DerivedMesh *dm; - int dm_totface; - int dm_totedge; - int dm_totvert; - int dm_release; - - MVert *dm_mvert; - MEdge *dm_medge; - MFace *dm_mface; - MTFace **dm_mtface; - MTFace **dm_mtface_clone; /* other UV map, use for cloning between layers */ - MTFace *dm_mtface_stencil; - Image *stencil_ima; Image *canvas_ima; Image *clone_ima; @@ -236,24 +253,16 @@ typedef struct ProjPaintState { LinkNode **bucketRect; /* screen sized 2D array, each pixel has a linked list of ProjPixel's */ LinkNode **bucketFaces; /* bucketRect aligned array linkList of faces overlapping each bucket */ unsigned char *bucketFlags; /* store if the bucks have been initialized */ -#ifndef PROJ_DEBUG_NOSEAMBLEED - char *faceSeamFlags; /* store info about faces, if they are initialized etc*/ - char *faceWindingFlags; /* save the winding of the face in uv space, helps as an extra validation step for seam detection */ - float (*faceSeamUVs)[4][2]; /* expanded UVs for faces to use as seams */ - LinkNode **vertFaces; /* Only needed for when seam_bleed_px is enabled, use to find UV seams */ -#endif + char *vertFlags; /* store options per vert, now only store if the vert is pointing away from the view */ int buckets_x; /* The size of the bucket grid, the grid span's screenMin/screenMax so you can paint outsize the screen or with 2 brushes at once */ int buckets_y; - ProjPaintImage *projImages; - int pixel_sizeof; /* result of project_paint_pixel_sizeof(), constant per stroke */ int image_tot; /* size of projectImages array */ float (*screenCoords)[4]; /* verts projected into floating point screen space */ - float *cavities; /* cavity amount for vertices */ float screenMin[2]; /* 2D bounds for mesh verts on the screen's plane (screenspace) */ float screenMax[2]; float screen_width; /* Calculated from screenMin & screenMax */ @@ -272,12 +281,15 @@ typedef struct ProjPaintState { bool do_mask_normal; /* mask out pixels based on their normals */ bool do_mask_cavity; /* mask out pixels based on cavity */ bool do_new_shading_nodes; /* cache BKE_scene_use_new_shading_nodes value */ - float normal_angle; /* what angle to mask at*/ + float normal_angle; /* what angle to mask at */ + float normal_angle__cos; /* cos(normal_angle), faster to compare */ float normal_angle_inner; + float normal_angle_inner__cos; float normal_angle_range; /* difference between normal_angle and normal_angle_inner, for easy access */ bool do_face_sel; /* quick access to (me->editflag & ME_EDIT_PAINT_FACE_SEL) */ bool is_ortho; + bool is_flip_object; /* the object is negative scaled */ bool do_masking; /* use masking during painting. Some operations such as airbrush may disable */ bool is_texbrush; /* only to avoid running */ bool is_maskbrush; /* mask brush is applied before masking */ @@ -302,13 +314,51 @@ typedef struct ProjPaintState { int bucketMax[2]; int context_bucket_x, context_bucket_y; /* must lock threads while accessing these */ - /* redraw */ - bool need_redraw; - 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_OFS(ps_dst, ps_src, is_shared_user) + +#define PROJ_PAINT_STATE_SHARED_CLEAR(ps) \ + MEMSET_STRUCT_OFS(ps, 0, is_shared_user) + + bool is_shared_user; + + ProjPaintImage *projImages; + float *cavities; /* cavity amount for vertices */ + +#ifndef PROJ_DEBUG_NOSEAMBLEED + char *faceSeamFlags; /* store info about faces, if they are initialized etc*/ + char *faceWindingFlags; /* save the winding of the face in uv space, helps as an extra validation step for seam detection */ + float (*faceSeamUVs)[4][2]; /* expanded UVs for faces to use as seams */ + LinkNode **vertFaces; /* Only needed for when seam_bleed_px is enabled, use to find UV seams */ +#endif + SpinLock *tile_lock; + + DerivedMesh *dm; + int dm_totface; + int dm_totedge; + int dm_totvert; + int dm_release; + + MVert *dm_mvert; + MEdge *dm_medge; + MFace *dm_mface; + MTFace *dm_mtface_stencil; + + MTFace **dm_mtface; + MTFace **dm_mtface_clone; /* other UV map, use for cloning between layers */ } ProjPaintState; typedef union pixelPointer { @@ -326,25 +376,27 @@ typedef union pixelStore { typedef struct ProjPixel { float projCoSS[2]; /* the floating point screen projection of this pixel */ float worldCoSS[3]; + + short x_px, y_px; + + unsigned short image_index; /* if anyone wants to paint onto more than 65535 images they can bite me */ + unsigned char bb_cell_index; + + /* 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; - /* for various reasons we may want to mask out painting onto this pixel */ - unsigned short mask; - - short x_px, y_px; /* 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; - - short image_index; /* if anyone wants to paint onto more than 32768 images they can bite me */ - unsigned char bb_cell_index; } ProjPixel; typedef struct ProjPixelClone { @@ -474,7 +526,9 @@ static float VecZDepthPersp( /* 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], int *side) +static int project_paint_PickFace( + const ProjPaintState *ps, const float pt[2], + float w[3], int *r_side) { LinkNode *node; float w_tmp[3]; @@ -531,7 +585,7 @@ static int project_paint_PickFace(const ProjPaintState *ps, const float pt[2], f } } - *side = best_side; + *r_side = best_side; return best_face_index; /* will be -1 or a valid face */ } @@ -718,7 +772,7 @@ static bool project_bucket_point_occluded( int face_index; int isect_ret; float w[3]; /* not needed when clipping */ - const short do_clip = ps->rv3d ? ps->rv3d->rflag & RV3D_CLIPPING : 0; + 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 */ @@ -1228,22 +1282,22 @@ static void screen_px_from_persp( static void project_face_pixel( const MTFace *tf_other, ImBuf *ibuf_other, const float w[3], - int side, unsigned char rgba_ub[4], float rgba_f[4]) + bool side, unsigned char rgba_ub[4], float rgba_f[4]) { const float *uvCo1, *uvCo2, *uvCo3; float uv_other[2], x, y; - uvCo1 = (float *)tf_other->uv[0]; + uvCo1 = tf_other->uv[0]; if (side == 1) { - uvCo2 = (float *)tf_other->uv[2]; - uvCo3 = (float *)tf_other->uv[3]; + uvCo2 = tf_other->uv[2]; + uvCo3 = tf_other->uv[3]; } else { - uvCo2 = (float *)tf_other->uv[1]; - uvCo3 = (float *)tf_other->uv[2]; + uvCo2 = tf_other->uv[1]; + uvCo3 = tf_other->uv[2]; } - interp_v2_v2v2v2(uv_other, uvCo1, uvCo2, uvCo3, (float *)w); + interp_v2_v2v2v2(uv_other, uvCo1, uvCo2, uvCo3, w); /* use */ uvco_to_wrapped_pxco(uv_other, ibuf_other->x, ibuf_other->y, &x, &y); @@ -1262,7 +1316,7 @@ static void project_face_pixel( static float project_paint_uvpixel_mask( const ProjPaintState *ps, const int face_index, - const int side, + const bool side, const float w[3]) { float mask; @@ -1327,7 +1381,7 @@ static float project_paint_uvpixel_mask( /* calculate mask */ if (ps->do_mask_normal) { MFace *mf = &ps->dm_mface[face_index]; - float no[3], angle; + float no[3], angle_cos; if (mf->flag & ME_SMOOTH) { const short *no1, *no2, *no3; no1 = ps->dm_mvert[mf->v1].no; @@ -1366,9 +1420,13 @@ static float project_paint_uvpixel_mask( #endif } + if (UNLIKELY(ps->is_flip_object)) { + negate_v3(no); + } + /* now we can use the normal as a mask */ if (ps->is_ortho) { - angle = angle_normalized_v3v3(ps->viewDir, no); + angle_cos = dot_v3v3(ps->viewDir, no); } else { /* Annoying but for the perspective view we need to get the pixels location in 3D space :/ */ @@ -1389,15 +1447,18 @@ static float project_paint_uvpixel_mask( 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 = angle_normalized_v3v3(viewDirPersp, no); + angle_cos = dot_v3v3(viewDirPersp, no); } - if (angle >= ps->normal_angle) { + if (angle_cos <= ps->normal_angle__cos) { return 0.0f; /* outsize the normal limit*/ } - else if (angle > ps->normal_angle_inner) { - mask *= (ps->normal_angle - angle) / ps->normal_angle_range; + 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 */ } @@ -1440,10 +1501,10 @@ static int project_paint_undo_subtiles(const TileInfo *tinf, int tx, int ty) if (generate_tile) { volatile void *undorect; if (tinf->masked) { - undorect = image_undo_push_tile(pjIma->ima, pjIma->ibuf, tinf->tmpibuf, tx, ty, &pjIma->maskRect[tile_index], &pjIma->valid[tile_index], true); + undorect = image_undo_push_tile(pjIma->ima, pjIma->ibuf, tinf->tmpibuf, tx, ty, &pjIma->maskRect[tile_index], &pjIma->valid[tile_index], true, false); } else { - undorect = image_undo_push_tile(pjIma->ima, pjIma->ibuf, tinf->tmpibuf, tx, ty, NULL, &pjIma->valid[tile_index], true); + undorect = image_undo_push_tile(pjIma->ima, pjIma->ibuf, tinf->tmpibuf, tx, ty, NULL, &pjIma->valid[tile_index], true, false); } pjIma->ibuf->userflags |= IB_BITMAPDIRTY; @@ -1470,7 +1531,7 @@ static ProjPixel *project_paint_uvpixel_init( const int face_index, const float pixelScreenCo[4], const float world_spaceCo[3], - const int side, + const bool side, const float w[3]) { ProjPixel *projPixel; @@ -1591,7 +1652,7 @@ static ProjPixel *project_paint_uvpixel_init( } else { float co[2]; - sub_v2_v2v2(co, projPixel->projCoSS, (float *)ps->cloneOffset); + 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(...) */ @@ -1619,13 +1680,14 @@ static ProjPixel *project_paint_uvpixel_init( } 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_GEOM_TOLERANCE) { + 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; @@ -1636,7 +1698,7 @@ static bool line_clip_rect2f( } - if (fabsf(l1[0] - l2[0]) < PROJ_GEOM_TOLERANCE) { /* this is a single point (or close to)*/ + if (fabsf(l1[0] - l2[0]) < PROJ_PIXEL_TOLERANCE) { /* this is a single point (or close to)*/ if (BLI_rctf_isect_pt_v(rect, l1)) { copy_v2_v2(l1_clip, l1); copy_v2_v2(l2_clip, l2); @@ -1653,7 +1715,7 @@ static bool line_clip_rect2f( CLAMP(l2_clip[0], rect->xmin, rect->xmax); return 1; } - else if (fabsf(l1[0] - l2[0]) < PROJ_GEOM_TOLERANCE) { + 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; @@ -1664,7 +1726,7 @@ static bool line_clip_rect2f( return 0; } - if (fabsf(l1[1] - l2[1]) < PROJ_GEOM_TOLERANCE) { /* this is a single point (or close to)*/ + if (fabsf(l1[1] - l2[1]) < PROJ_PIXEL_TOLERANCE) { /* this is a single point (or close to)*/ if (BLI_rctf_isect_pt_v(rect, l1)) { copy_v2_v2(l1_clip, l1); copy_v2_v2(l2_clip, l2); @@ -1703,7 +1765,7 @@ static bool line_clip_rect2f( if (ok1 && ok2) return 1; /* top/bottom */ - if (line_isect_y(l1, l2, rect->ymin, &isect) && (isect >= rect->xmin) && (isect <= rect->xmax)) { + 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; @@ -1718,7 +1780,7 @@ static bool line_clip_rect2f( if (ok1 && ok2) return 1; - if (line_isect_y(l1, l2, rect->ymax, &isect) && (isect >= rect->xmin) && (isect <= rect->xmax)) { + 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; @@ -1734,7 +1796,7 @@ static bool line_clip_rect2f( if (ok1 && ok2) return 1; /* left/right */ - if (line_isect_x(l1, l2, rect->xmin, &isect) && (isect >= rect->ymin) && (isect <= rect->ymax)) { + 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; @@ -1749,7 +1811,7 @@ static bool line_clip_rect2f( if (ok1 && ok2) return 1; - if (line_isect_x(l1, l2, rect->xmax, &isect) && (isect >= rect->ymin) && (isect <= rect->ymax)) { + 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; @@ -1964,7 +2026,7 @@ static float angle_2d_clockwise(const float p1[2], const float p2[2], const floa v1[0] = p1[0] - p2[0]; v1[1] = p1[1] - p2[1]; v2[0] = p3[0] - p2[0]; v2[1] = p3[1] - p2[1]; - return -atan2(v1[0] * v2[1] - v1[1] * v2[0], v1[0] * v2[0] + v1[1] * v2[1]); + return -atan2f(v1[0] * v2[1] - v1[1] * v2[0], v1[0] * v2[0] + v1[1] * v2[1]); } #endif @@ -1976,7 +2038,10 @@ static float angle_2d_clockwise(const float p1[2], const float p2[2], const floa #define ISECT_ALL4 ((1 << 4) - 1) /* limit must be a fraction over 1.0f */ -static bool IsectPT2Df_limit(float pt[2], float v1[2], float v2[2], float v3[2], float limit) +static bool IsectPT2Df_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) + @@ -2043,9 +2108,10 @@ static bool line_rect_clip( static void project_bucket_clip_face( - const bool is_ortho, + const bool is_ortho, const bool is_flip_object, + const rctf *cliprect, const rctf *bucket_bounds, - float *v1coSS, float *v2coSS, float *v3coSS, + 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) @@ -2071,7 +2137,8 @@ static void project_bucket_clip_face( inside_bucket_flag |= BLI_rctf_isect_pt_v(bucket_bounds, v3coSS) << 2; if (inside_bucket_flag == ISECT_ALL3) { - flip = ((line_point_side_v2(v1coSS, v2coSS, v3coSS) > 0.0f) != + /* 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, @@ -2193,7 +2260,7 @@ static void project_bucket_clip_face( float cent[2] = {0.0f, 0.0f}; /*float up[2] = {0.0f, 1.0f};*/ int i; - short doubles; + bool doubles; (*tot) = 0; @@ -2207,21 +2274,21 @@ static void project_bucket_clip_face( 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(bucket_bounds, v1coSS, v2coSS, v1_clipSS, v2_clipSS)) { + 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(bucket_bounds, v2coSS, v3coSS, v1_clipSS, v2_clipSS)) { + 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(bucket_bounds, v3coSS, v1coSS, v1_clipSS, v2_clipSS)) { + 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)++; } } @@ -2404,7 +2471,7 @@ static bool IsectPoly2Df(const float pt[2], float uv[][2], const int tot) static bool IsectPoly2Df_twoside(const float pt[2], float uv[][2], const int tot) { int i; - int side = (line_point_side_v2(uv[tot - 1], uv[0], pt) > 0.0f); + 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) @@ -2423,7 +2490,8 @@ static bool IsectPoly2Df_twoside(const float pt[2], float uv[][2], const int tot static void project_paint_face_init( const ProjPaintState *ps, const int thread_index, const int bucket_index, const int face_index, const int image_index, - const rctf *bucket_bounds, ImBuf *ibuf, ImBuf **tmpibuf, const short clamp_u, const short clamp_v) + const rctf *clip_rect, const rctf *bucket_bounds, ImBuf *ibuf, ImBuf **tmpibuf, + const bool clamp_u, const bool clamp_v) { /* Projection vars, to get the 3D locations into screen space */ MemArena *arena = ps->arena_mt[thread_index]; @@ -2448,8 +2516,8 @@ static void project_paint_face_init( float mask; float uv[2]; /* Image floating point UV - same as x, y but from 0.0-1.0 */ - int side; - float *v1coSS, *v2coSS, *v3coSS; /* vert co screen-space, these will be assigned to mf->v1,2,3 or mf->v1,3,4 */ + bool side; + const float *v1coSS, *v2coSS, *v3coSS; /* vert co screen-space, these will be assigned to mf->v1,2,3 or mf->v1,3,4 */ float *vCo[4]; /* vertex screenspace coords */ @@ -2468,11 +2536,10 @@ static void project_paint_face_init( int has_x_isect = 0, has_isect = 0; /* for early loop exit */ - int i1, i2, i3; - float uv_clip[8][2]; int uv_clip_tot; const 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; @@ -2517,6 +2584,8 @@ static void project_paint_face_init( } do { + int i1, i2, i3; + if (side == 1) { i1 = 0; i2 = 2; i3 = 3; } @@ -2534,11 +2603,12 @@ static void project_paint_face_init( /* This funtion gives is a concave polyline in UV space from the clipped quad and tri*/ project_bucket_clip_face( - is_ortho, bucket_bounds, + is_ortho, is_flip_object, + clip_rect, bucket_bounds, v1coSS, v2coSS, v3coSS, uv1co, uv2co, uv3co, uv_clip, &uv_clip_tot, - ps->do_backfacecull || ps->do_occlude); + do_backfacecull || ps->do_occlude); /* sometimes this happens, better just allow for 8 intersectiosn even though there should be max 6 */ #if 0 @@ -2674,7 +2744,7 @@ static void project_paint_face_init( int fidx1, fidx2; /* face edge pairs - loop throuh these ((0,1), (1,2), (2,3), (3,0)) or ((0,1), (1,2), (2,0)) for a tri */ float seam_subsection[4][2]; - float fac1, fac2, ftot; + float fac1, fac2; if (outset_uv[0][0] == FLT_MAX) /* first time initialize */ uv_image_outset(tf_uv_pxoffset, outset_uv, ps->seam_bleed_px, ibuf->x, ibuf->y, mf->v4 != 0, (ps->faceWindingFlags[face_index] & PROJ_FACE_WINDING_CW) == 0); @@ -2706,19 +2776,16 @@ static void project_paint_face_init( else fidx2 = (fidx1 == 2) ? 0 : fidx1 + 1; /* next fidx in the face (0,1,2) -> (1,2,0) */ if ((face_seam_flag & (1 << fidx1)) && /* 1<<fidx1 -> PROJ_FACE_SEAM# */ - line_clip_rect2f(bucket_bounds, vCoSS[fidx1], vCoSS[fidx2], bucket_clip_edges[0], bucket_clip_edges[1])) + line_clip_rect2f(clip_rect, bucket_bounds, vCoSS[fidx1], vCoSS[fidx2], bucket_clip_edges[0], bucket_clip_edges[1])) { - - ftot = len_v2v2(vCoSS[fidx1], vCoSS[fidx2]); /* screenspace edge length */ - - if (ftot > 0.0f) { /* avoid div by zero */ + if (len_squared_v2v2(vCoSS[fidx1], vCoSS[fidx2]) > FLT_EPSILON) { /* avoid div by zero */ if (mf->v4) { - if (fidx1 == 2 || fidx2 == 2) side = 1; + if (fidx1 == 3 || fidx2 == 3) side = 1; else side = 0; } - fac1 = len_v2v2(vCoSS[fidx1], bucket_clip_edges[0]) / ftot; - fac2 = len_v2v2(vCoSS[fidx1], bucket_clip_edges[1]) / ftot; + 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]); interp_v2_v2v2(seam_subsection[0], tf_uv_pxoffset[fidx1], tf_uv_pxoffset[fidx2], fac1); interp_v2_v2v2(seam_subsection[1], tf_uv_pxoffset[fidx1], tf_uv_pxoffset[fidx2], fac2); @@ -2760,11 +2827,8 @@ static void project_paint_face_init( /* Since this is a seam we need to work out where on the line this pixel is */ //fac = line_point_factor_v2(uv, uv_seam_quad[0], uv_seam_quad[1]); - - fac = line_point_factor_v2(uv, seam_subsection[0], seam_subsection[1]); - if (fac < 0.0f) { copy_v3_v3(pixelScreenCo, edge_verts_inset_clip[0]); } - else if (fac > 1.0f) { copy_v3_v3(pixelScreenCo, edge_verts_inset_clip[1]); } - else { interp_v3_v3v3(pixelScreenCo, edge_verts_inset_clip[0], edge_verts_inset_clip[1], fac); } + fac = resolve_quad_u_v2(uv, UNPACK4(seam_subsection)); + interp_v3_v3v3(pixelScreenCo, edge_verts_inset_clip[0], edge_verts_inset_clip[1], fac); if (!is_ortho) { pixelScreenCo[3] = 1.0f; @@ -2779,24 +2843,20 @@ static void project_paint_face_init( { /* Only bother calculating the weights if we intersect */ if (ps->do_mask_normal || ps->dm_mtface_clone) { -#if 1 + const float uv_fac = fac1 + (fac * (fac2 - fac1)); +#if 0 /* get the UV on the line since we want to copy the pixels from there for bleeding */ float uv_close[2]; - float uv_fac = closest_to_line_v2(uv_close, uv, tf_uv_pxoffset[fidx1], tf_uv_pxoffset[fidx2]); - if (uv_fac < 0.0f) copy_v2_v2(uv_close, tf_uv_pxoffset[fidx1]); - else if (uv_fac > 1.0f) copy_v2_v2(uv_close, tf_uv_pxoffset[fidx2]); - + interp_v2_v2v2(uv_close, tf_uv_pxoffset[fidx1], tf_uv_pxoffset[fidx2], uv_fac); if (side) { barycentric_weights_v2(tf_uv_pxoffset[0], tf_uv_pxoffset[2], tf_uv_pxoffset[3], uv_close, w); } else { barycentric_weights_v2(tf_uv_pxoffset[0], tf_uv_pxoffset[1], tf_uv_pxoffset[2], uv_close, w); } -#else /* this is buggy with quads, don't use for now */ +#else /* Cheat, we know where we are along the edge so work out the weights from that */ - uv_fac = fac1 + (uv_fac * (fac2 - fac1)); - w[0] = w[1] = w[2] = 0.0; if (side) { w[fidx1 ? fidx1 - 1 : 0] = 1.0f - uv_fac; @@ -2811,8 +2871,8 @@ static void project_paint_face_init( /* a pity we need to get the worldspace pixel location here */ if (do_clip || do_3d_mapping) { - if (side) interp_v3_v3v3v3(wco, ps->dm_mvert[mf->v1].co, ps->dm_mvert[mf->v3].co, ps->dm_mvert[mf->v4].co, w); - else interp_v3_v3v3v3(wco, ps->dm_mvert[mf->v1].co, ps->dm_mvert[mf->v2].co, ps->dm_mvert[mf->v3].co, w); + if (side) interp_v3_v3v3v3(wco, vCo[0], vCo[2], vCo[3], w); + else interp_v3_v3v3v3(wco, vCo[0], vCo[1], vCo[2], w); if (do_clip && ED_view3d_clipping_test(ps->rv3d, wco, true)) { continue; /* Watch out that no code below this needs to run */ @@ -2851,6 +2911,8 @@ static void project_paint_face_init( } } } +#else + UNUSED_VARS(vCo, threaded); #endif // PROJ_DEBUG_NOSEAMBLEED } @@ -2887,7 +2949,7 @@ static void project_bucket_bounds(const ProjPaintState *ps, const int bucket_x, /* 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 *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 face_index, image_index = 0; @@ -2902,7 +2964,10 @@ static void project_bucket_init(const ProjPaintState *ps, const int thread_index ima = ps->projImages[0].ima; for (node = ps->bucketFaces[bucket_index]; node; node = node->next) { - project_paint_face_init(ps, thread_index, bucket_index, GET_INT_FROM_POINTER(node->link), 0, bucket_bounds, ibuf, &tmpibuf, ima->tpageflag & IMA_CLAMP_U, ima->tpageflag & IMA_CLAMP_V); + project_paint_face_init( + ps, thread_index, bucket_index, GET_INT_FROM_POINTER(node->link), 0, + clip_rect, bucket_bounds, ibuf, &tmpibuf, + (ima->tpageflag & IMA_CLAMP_U) != 0, (ima->tpageflag & IMA_CLAMP_V) != 0); } } else { @@ -2926,7 +2991,10 @@ static void project_bucket_init(const ProjPaintState *ps, const int thread_index } /* context switching done */ - project_paint_face_init(ps, thread_index, bucket_index, face_index, image_index, bucket_bounds, ibuf, &tmpibuf, ima->tpageflag & IMA_CLAMP_U, ima->tpageflag & IMA_CLAMP_V); + project_paint_face_init( + ps, thread_index, bucket_index, face_index, image_index, + clip_rect, bucket_bounds, ibuf, &tmpibuf, + (ima->tpageflag & IMA_CLAMP_U) != 0, (ima->tpageflag & IMA_CLAMP_V) != 0); } } @@ -3078,7 +3146,8 @@ static void proj_paint_state_non_cddm_init(ProjPaintState *ps) } } -static void proj_paint_state_viewport_init(ProjPaintState *ps) +static void proj_paint_state_viewport_init( + ProjPaintState *ps, const char symmetry_flag) { float mat[3][3]; float viewmat[4][4]; @@ -3088,7 +3157,19 @@ static void proj_paint_state_viewport_init(ProjPaintState *ps) ps->viewDir[1] = 0.0f; ps->viewDir[2] = 1.0f; - invert_m4_m4(ps->ob->imat, ps->ob->obmat); + 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 */ @@ -3098,7 +3179,7 @@ static void proj_paint_state_viewport_init(ProjPaintState *ps) copy_m4_m4(viewmat, ps->rv3d->viewmat); copy_m4_m4(viewinv, ps->rv3d->viewinv); - ED_view3d_ob_project_mat_get(ps->rv3d, ps->ob, ps->projectMat); + ED_view3d_ob_project_mat_get_from_obmat(ps->rv3d, ps->obmat, ps->projectMat); ps->is_ortho = ED_view3d_clip_range_get(ps->v3d, ps->rv3d, &ps->clipsta, &ps->clipend, true); } @@ -3151,24 +3232,27 @@ static void proj_paint_state_viewport_init(ProjPaintState *ps) } /* same as #ED_view3d_ob_project_mat_get */ - mul_m4_m4m4(vmat, viewmat, ps->ob->obmat); + mul_m4_m4m4(vmat, viewmat, ps->obmat); mul_m4_m4m4(ps->projectMat, winmat, vmat); } /* viewDir - object relative */ - invert_m4_m4(ps->ob->imat, ps->ob->obmat); copy_m3_m4(mat, viewinv); mul_m3_v3(mat, ps->viewDir); - copy_m3_m4(mat, ps->ob->imat); + 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->ob->imat); + copy_m3_m4(mat, ps->obmat_imat); mul_m3_v3(mat, ps->viewPos); - add_v3_v3(ps->viewPos, ps->ob->imat[3]); + add_v3_v3(ps->viewPos, ps->obmat_imat[3]); } static void proj_paint_state_screen_coords_init(ProjPaintState *ps, const int diameter) @@ -3234,6 +3318,8 @@ static void proj_paint_state_screen_coords_init(ProjPaintState *ps, const int di CLAMP(ps->screenMin[1], (float)(-diameter), (float)(ps->winy + diameter)); CLAMP(ps->screenMax[1], (float)(-diameter), (float)(ps->winy + diameter)); +#else + UNUSED_VARS(diameter); #endif } else if (ps->source != PROJ_SRC_VIEW_FILL) { /* re-projection, use bounds */ @@ -3313,12 +3399,14 @@ static void proj_paint_state_thread_init(ProjPaintState *ps, const bool reset_th if (reset_threads) ps->thread_tot = 1; - 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"); @@ -3337,16 +3425,22 @@ static void proj_paint_state_vert_flags_init(ProjPaintState *ps) for (a = 0, mv = ps->dm_mvert; a < ps->dm_totvert; a++, mv++) { normal_short_to_float_v3(no, mv->no); + if (UNLIKELY(ps->is_flip_object)) { + negate_v3(no); + } if (ps->is_ortho) { - if (angle_normalized_v3v3(ps->viewDir, no) >= ps->normal_angle) { /* 1 vert of this face is towards us */ + 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 (angle_normalized_v3v3(viewDirPersp, no) >= ps->normal_angle) { /* 1 vert of this face is towards us */ + 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; } } @@ -3628,7 +3722,7 @@ static bool project_paint_backface_cull( } } else { - if (line_point_side_v2(coSS->v1, coSS->v2, coSS->v3) < 0.0f) { + if ((line_point_side_v2(coSS->v1, coSS->v2, coSS->v3) < 0.0f) != ps->is_flip_object) { return true; } @@ -3670,7 +3764,8 @@ static void project_paint_prepare_all_faces( ProjPaintState *ps, MemArena *arena, const ProjPaintFaceLookup *face_lookup, ProjPaintLayerClone *layer_clone, - MTFace *tf_base) + MTFace *tf_base, + const bool is_multi_view) { /* Image Vars - keep track of images we have used */ LinkNode *image_LinkList = NULL; @@ -3729,20 +3824,21 @@ static void project_paint_prepare_all_faces( ProjPaintFaceCoSS coSS; proj_paint_face_coSS_init(ps, mf, &coSS); - 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, mf, &coSS)) { - continue; - } + if (project_paint_winclip(ps, mf, &coSS)) { + continue; + } #endif //PROJ_DEBUG_WINCLIP - - if (project_paint_backface_cull(ps, mf, &coSS)) { - continue; + if (project_paint_backface_cull(ps, mf, &coSS)) { + continue; + } } if (tpage_last != tpage) { @@ -3767,18 +3863,22 @@ static void project_paint_prepare_all_faces( } /* build an array of images we use*/ - project_paint_build_proj_ima(ps, arena, image_LinkList); + if (ps->is_shared_user == false) { + project_paint_build_proj_ima(ps, arena, image_LinkList); + } /* we have built the array, discard the linked list */ BLI_linklist_free(image_LinkList, NULL); } /* run once per stroke before projection painting */ -static void project_paint_begin(ProjPaintState *ps) +static void project_paint_begin( + ProjPaintState *ps, + const bool is_multi_view, const char symmetry_flag) { ProjPaintLayerClone layer_clone; ProjPaintFaceLookup face_lookup; - MTFace *tf_base; + MTFace *tf_base = NULL; MemArena *arena; /* at the moment this is just ps->arena_mt[0], but use this to show were not multithreading */ @@ -3792,10 +3892,13 @@ static void project_paint_begin(ProjPaintState *ps) ED_view3d_clipping_local(ps->rv3d, ps->ob->obmat); /* faster clipping lookups */ 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 (!proj_paint_state_dm_init(ps)) { - return; + if (ps->is_shared_user == false) { + if (!proj_paint_state_dm_init(ps)) { + return; + } } proj_paint_face_lookup_init(ps, &face_lookup); @@ -3817,11 +3920,13 @@ static void project_paint_begin(ProjPaintState *ps) } /* when using subsurf or multires, mface arrays are thrown away, we need to keep a copy */ - proj_paint_state_non_cddm_init(ps); + if (ps->is_shared_user == false) { + proj_paint_state_non_cddm_init(ps); - proj_paint_state_cavity_init(ps); + proj_paint_state_cavity_init(ps); + } - proj_paint_state_viewport_init(ps); + 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 */ @@ -3850,7 +3955,9 @@ static void project_paint_begin(ProjPaintState *ps) ps->bucketFlags = MEM_callocN(sizeof(char) * ps->buckets_x * ps->buckets_y, "paint-bucketFaces"); #ifndef PROJ_DEBUG_NOSEAMBLEED - 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); @@ -3858,7 +3965,7 @@ static void project_paint_begin(ProjPaintState *ps) proj_paint_state_vert_flags_init(ps); - project_paint_prepare_all_faces(ps, arena, &face_lookup, &layer_clone, tf_base); + project_paint_prepare_all_faces(ps, arena, &face_lookup, &layer_clone, tf_base, is_multi_view); } static void paint_proj_begin_clone(ProjPaintState *ps, const float mouse[2]) @@ -3867,7 +3974,7 @@ static void paint_proj_begin_clone(ProjPaintState *ps, const float mouse[2]) if (ps->tool == PAINT_TOOL_CLONE) { float projCo[4]; copy_v3_v3(projCo, ED_view3d_cursor3d_get(ps->scene, ps->v3d)); - mul_m4_v3(ps->ob->imat, projCo); + mul_m4_v3(ps->obmat_imat, projCo); projCo[3] = 1.0f; mul_m4_v4(ps->projectMat, projCo); @@ -3879,14 +3986,16 @@ static void paint_proj_begin_clone(ProjPaintState *ps, const float mouse[2]) static void project_paint_end(ProjPaintState *ps) { int a; - ProjPaintImage *projIma; image_undo_remove_masks(); /* dereference used image buffers */ - for (a = 0, projIma = ps->projImages; a < ps->image_tot; a++, projIma++) { - BKE_image_release_ibuf(projIma->ima, projIma->ibuf, NULL); - DAG_id_tag_update(&projIma->ima->id, 0); + 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); + DAG_id_tag_update(&projIma->ima->id, 0); + } } BKE_image_release_ibuf(ps->reproject_image, ps->reproject_ibuf, NULL); @@ -3895,68 +4004,81 @@ static void project_paint_end(ProjPaintState *ps) MEM_freeN(ps->bucketRect); MEM_freeN(ps->bucketFaces); MEM_freeN(ps->bucketFlags); - MEM_freeN(ps->dm_mtface); - if (ps->do_layer_clone) - MEM_freeN(ps->dm_mtface_clone); - if (ps->thread_tot > 1) { - BLI_spin_end(ps->tile_lock); - MEM_freeN((void *)ps->tile_lock); - } - image_undo_end_locks(); + + if (ps->is_shared_user == false) { + + /* must be set for non-shared */ + BLI_assert(ps->dm_mtface || ps->is_shared_user); + if (ps->dm_mtface) + MEM_freeN(ps->dm_mtface); + + if (ps->do_layer_clone) + MEM_freeN(ps->dm_mtface_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->faceSeamUVs); - } + if (ps->seam_bleed_px > 0.0f) { + MEM_freeN(ps->vertFaces); + MEM_freeN(ps->faceSeamFlags); + MEM_freeN(ps->faceWindingFlags); + MEM_freeN(ps->faceSeamUVs); + } +#endif + + if (ps->do_mask_cavity) { + MEM_freeN(ps->cavities); + } + + /* copy for subsurf/multires, so throw away */ + if (ps->dm->type != DM_TYPE_CDDM) { + if (ps->dm_mvert) MEM_freeN(ps->dm_mvert); + if (ps->dm_mface) MEM_freeN(ps->dm_mface); + /* looks like these don't need copying */ +#if 0 + if (ps->dm_mtface) MEM_freeN(ps->dm_mtface); + if (ps->dm_mtface_clone) MEM_freeN(ps->dm_mtface_clone); + if (ps->dm_mtface_stencil) MEM_freeN(ps->dm_mtface_stencil); #endif + } + + if (ps->dm_release) + ps->dm->release(ps->dm); + } if (ps->blurkernel) { paint_delete_blur_kernel(ps->blurkernel); MEM_freeN(ps->blurkernel); } - if (ps->do_mask_cavity) { - MEM_freeN(ps->cavities); - } - if (ps->vertFlags) MEM_freeN(ps->vertFlags); for (a = 0; a < ps->thread_tot; a++) { BLI_memarena_free(ps->arena_mt[a]); } +} - /* copy for subsurf/multires, so throw away */ - if (ps->dm->type != DM_TYPE_CDDM) { - if (ps->dm_mvert) MEM_freeN(ps->dm_mvert); - if (ps->dm_mface) MEM_freeN(ps->dm_mface); - /* looks like these don't need copying */ -#if 0 - if (ps->dm_mtface) MEM_freeN(ps->dm_mtface); - if (ps->dm_mtface_clone) MEM_freeN(ps->dm_mtface_clone); - if (ps->dm_mtface_stencil) MEM_freeN(ps->dm_mtface_stencil); -#endif - } +/* 1 = an undo, -1 is a redo. */ +static void partial_redraw_single_init(ImagePaintPartialRedraw *pr) +{ + pr->x1 = 10000000; + pr->y1 = 10000000; - if (ps->dm_release) - ps->dm->release(ps->dm); + pr->x2 = -1; + pr->y2 = -1; + + pr->enabled = 1; } -/* 1 = an undo, -1 is a redo. */ static void partial_redraw_array_init(ImagePaintPartialRedraw *pr) { int tot = PROJ_BOUNDBOX_SQUARED; while (tot--) { - pr->x1 = 10000000; - pr->y1 = 10000000; - - pr->x2 = -1; - pr->y2 = -1; - - pr->enabled = 1; - + partial_redraw_single_init(pr); pr++; } } @@ -4000,6 +4122,8 @@ static bool project_image_refresh_tagged(ProjPaintState *ps) imapaint_image_update(NULL, projIma->ima, projIma->ibuf, true); redraw = 1; } + + partial_redraw_single_init(pr); } projIma->touch = 0; /* clear for reuse */ @@ -4207,7 +4331,7 @@ static void do_projectpaint_soften_f(ProjPaintState *ps, ProjPixel *projPixel, f /* 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] = rgb_to_grayscale(rgba); + 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; @@ -4268,7 +4392,7 @@ static void do_projectpaint_soften(ProjPaintState *ps, ProjPixel *projPixel, flo 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] = rgb_to_grayscale(rgba); + 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; @@ -4414,7 +4538,8 @@ static void *do_projectpaint_thread(void *ph_v) const float brush_radius = ps->brush_size; const float brush_radius_sq = brush_radius * brush_radius; /* avoid a square root with every dist comparison */ - short lock_alpha = ELEM(brush->blend, IMB_BLEND_ERASE_ALPHA, IMB_BLEND_ADD_ALPHA) ? 0 : brush->flag & BRUSH_LOCK_ALPHA; + 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; @@ -4440,8 +4565,13 @@ static void *do_projectpaint_thread(void *ph_v) /* 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, &bucket_bounds); + project_bucket_init(ps, thread_index, bucket_index, &clip_rect, &bucket_bounds); } if (ps->source != PROJ_SRC_VIEW) { @@ -4531,8 +4661,17 @@ static void *do_projectpaint_thread(void *ph_v) } if (lock_alpha) { - if (is_floatbuf) projPixel->pixel.f_pt[3] = projPixel->origColor.f_pt[3]; - else projPixel->pixel.ch_pt[3] = projPixel->origColor.ch_pt[3]; + 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; @@ -4697,11 +4836,20 @@ static void *do_projectpaint_thread(void *ph_v) else do_projectpaint_draw(ps, projPixel, texrgb, mask, ps->dither, projPixel->x_px, projPixel->y_px); break; } - } - if (lock_alpha) { - if (is_floatbuf) projPixel->pixel.f_pt[3] = projPixel->origColor.f_pt[3]; - else projPixel->pixel.ch_pt[3] = projPixel->origColor.ch_pt[3]; + 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 */ @@ -4818,7 +4966,8 @@ static bool project_paint_op(void *state, const float lastpos[2], const float po /* calculate pivot for rotation around seletion if needed */ if (U.uiflag & USER_ORBIT_SELECTION) { float w[3]; - int side, index; + int index; + int side; index = project_paint_PickFace(ps, pos, w, &side); @@ -4837,7 +4986,7 @@ static bool project_paint_op(void *state, const float lastpos[2], const float po } ups->average_stroke_counter++; - mul_m4_v3(ps->ob->obmat, world); + mul_m4_v3(ps->obmat, world); add_v3_v3(ups->average_stroke_accum, world); ups->last_stroke_valid = true; } @@ -4847,33 +4996,21 @@ static bool project_paint_op(void *state, const float lastpos[2], const float po } -void paint_proj_stroke(const bContext *C, void *pps, const float prev_pos[2], const float pos[2], const bool eraser, float pressure, float distance, float size) +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 + ) { - ProjPaintState *ps = pps; + ProjStrokeHandle *ps_handle = ps_handle_p; Brush *brush = ps->brush; Scene *scene = ps->scene; - int a; ps->brush_size = size; ps->blend = brush->blend; if (eraser) ps->blend = IMB_BLEND_ERASE_ALPHA; - - /* clone gets special treatment here to avoid going through image initialization */ - if (ps->tool == PAINT_TOOL_CLONE && ps->mode == BRUSH_STROKE_INVERT) { - View3D *v3d = ps->v3d; - float *cursor = ED_view3d_cursor3d_get(scene, v3d); - int mval_i[2] = {(int)pos[0], (int)pos[1]}; - - view3d_operator_needs_opengl(C); - - if (!ED_view3d_autodist(scene, ps->ar, v3d, mval_i, cursor, false, NULL)) - return; - - ED_region_tag_redraw(ps->ar); - - return; - } /* handle gradient and inverted stroke color here */ if (ps->tool == PAINT_TOOL_DRAW) { @@ -4894,14 +5031,42 @@ void paint_proj_stroke(const bContext *C, void *pps, const float prev_pos[2], co } } - /* continue adding to existing partial redraw rects until redraw */ - if (!ps->need_redraw) { - for (a = 0; a < ps->image_tot; a++) - partial_redraw_array_init(ps->projImages[a].partRedrawRect); + 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) +{ + 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; + View3D *v3d = CTX_wm_view3d(C); + ARegion *ar = CTX_wm_region(C); + float *cursor = ED_view3d_cursor3d_get(scene, v3d); + int mval_i[2] = {(int)pos[0], (int)pos[1]}; - if (project_paint_op(ps, prev_pos, pos)) - ps->need_redraw = true; + view3d_operator_needs_opengl(C); + + if (!ED_view3d_autodist(scene, ar, v3d, mval_i, cursor, false, NULL)) + return; + + ED_region_tag_redraw(ar); + + 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); + } } @@ -4920,7 +5085,7 @@ static void project_state_init(bContext *C, Object *ob, ProjPaintState *ps, int 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) ? + 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); @@ -5001,6 +5166,9 @@ static void project_state_init(bContext *C, Object *ob, ProjPaintState *ps, int if (ps->normal_angle_range <= 0.0f) ps->do_mask_normal = false; /* no need to do blending */ + ps->normal_angle__cos = cosf(ps->normal_angle); + ps->normal_angle_inner__cos = cosf(ps->normal_angle_inner); + ps->dither = settings->imapaint.dither; return; @@ -5008,51 +5176,116 @@ static void project_state_init(bContext *C, Object *ob, ProjPaintState *ps, int void *paint_proj_new_stroke(bContext *C, Object *ob, const float mouse[2], int mode) { - ProjPaintState *ps = MEM_callocN(sizeof(ProjPaintState), "ProjectionPaintState"); + 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}; - project_state_init(C, ob, ps, mode); + ps_handle = MEM_callocN(sizeof(ProjStrokeHandle), "ProjStrokeHandle"); + ps_handle->scene = scene; + ps_handle->brush = BKE_paint_brush(&settings->imapaint.paint); - if (ps->tool == PAINT_TOOL_CLONE && mode == BRUSH_STROKE_INVERT) { + /* bypass regular stroke logic */ + if ((ps_handle->brush->imagepaint_tool == PAINT_TOOL_CLONE) && + (mode == BRUSH_STROKE_INVERT)) + { view3d_operator_needs_opengl(C); - return ps; + ps_handle->is_clone_cursor_pick = true; + return ps_handle; } - paint_brush_init_tex(ps->brush); + ps_handle->orig_brush_size = BKE_brush_size_get(scene, ps_handle->brush); - ps->source = (ps->tool == PAINT_TOOL_FILL) ? PROJ_SRC_VIEW_FILL : PROJ_SRC_VIEW; + 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); - if (ps->ob == NULL || !(ps->ob->lay & ps->v3d->lay)) { - MEM_freeN(ps); - return NULL; + 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); } - ps->orig_brush_size = BKE_brush_size_get(ps->scene, ps->brush); + 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->ob->lay & ps->v3d->lay)) { + ps_handle->ps_views_tot = i + 1; + goto fail; + } + } /* Don't allow brush size below 2 */ - if (BKE_brush_size_get(ps->scene, ps->brush) < 2) - BKE_brush_size_set(ps->scene, ps->brush, 2 * U.pixelsize); + 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 */ - project_paint_begin(ps); - if (ps->dm == NULL) { - MEM_freeN(ps); - return NULL; + 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(ps, is_multi_view, symmetry_flag_views[i]); + + paint_proj_begin_clone(ps, mouse); + + if (ps->dm == NULL) { + goto fail; + return NULL; + } } - paint_proj_begin_clone(ps, mouse); + paint_brush_init_tex(ps_handle->brush); + + return ps_handle; + - return ps; +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; } -void paint_proj_redraw(const bContext *C, void *pps, bool final) +void paint_proj_redraw(const bContext *C, void *ps_handle_p, bool final) { - ProjPaintState *ps = pps; - - if (ps->need_redraw) { - project_image_refresh_tagged(ps); + ProjStrokeHandle *ps_handle = ps_handle_p; - ps->need_redraw = false; + if (ps_handle->need_redraw) { + ps_handle->need_redraw = false; } else if (!final) { return; @@ -5067,19 +5300,34 @@ void paint_proj_redraw(const bContext *C, void *pps, bool final) } } -void paint_proj_stroke_done(void *pps) +void paint_proj_stroke_done(void *ps_handle_p) { - ProjPaintState *ps = pps; - if (ps->tool == PAINT_TOOL_CLONE && ps->mode == BRUSH_STROKE_INVERT) { - MEM_freeN(ps); + 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; } - BKE_brush_size_set(ps->scene, ps->brush, ps->orig_brush_size); - paint_brush_exit_tex(ps->brush); + 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); + + 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); + + } - project_paint_end(ps); - MEM_freeN(ps); + MEM_freeN(ps_handle); } /* use project paint to re-apply an image */ static int texture_paint_camera_project_exec(bContext *C, wmOperator *op) @@ -5159,7 +5407,7 @@ static int texture_paint_camera_project_exec(bContext *C, wmOperator *op) ED_image_undo_restore, ED_image_undo_free, NULL); /* allocate and initialize spatial data structures */ - project_paint_begin(&ps); + project_paint_begin(&ps, false, 0); if (ps.dm == NULL) { BKE_brush_size_set(scene, ps.brush, orig_brush_size); @@ -5233,7 +5481,7 @@ static int texture_paint_image_from_view_exec(bContext *C, wmOperator *op) if (w > maxsize) w = maxsize; if (h > maxsize) h = maxsize; - ibuf = ED_view3d_draw_offscreen_imbuf(scene, CTX_wm_view3d(C), CTX_wm_region(C), w, h, IB_rect, false, R_ALPHAPREMUL, err_out); + ibuf = ED_view3d_draw_offscreen_imbuf(scene, CTX_wm_view3d(C), CTX_wm_region(C), w, h, IB_rect, false, R_ALPHAPREMUL, 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 */ @@ -5241,7 +5489,7 @@ static int texture_paint_image_from_view_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - image = BKE_image_add_from_imbuf(ibuf); + image = BKE_image_add_from_imbuf(ibuf, "image_view"); /* Drop reference to ibuf so that the image owns it */ IMB_freeImBuf(ibuf); @@ -5271,8 +5519,6 @@ static int texture_paint_image_from_view_exec(bContext *C, wmOperator *op) array[2] = is_ortho ? 1.0f : 0.0f; IDP_AddToGroup(idgroup, view_data); - - rename_id(&image->id, "image_view"); } return OPERATOR_FINISHED; @@ -5444,7 +5690,7 @@ static Image *proj_paint_image_create(wmOperator *op, Main *bmain) RNA_string_get(op->ptr, "name", imagename); } ima = BKE_image_add_generated(bmain, width, height, imagename, alpha ? 32 : 24, use_float, - gen_type, color); + gen_type, color, false); return ima; } @@ -5487,7 +5733,7 @@ static bool proj_paint_add_slot(bContext *C, wmOperator *op) ntreeUpdateTree(CTX_data_main(C), ntree); } else { - MTex *mtex = add_mtex_id(&ma->id, -1); + MTex *mtex = BKE_texture_mtex_add_id(&ma->id, -1); /* successful creation of mtex layer, now create set */ if (mtex) { @@ -5506,7 +5752,7 @@ static bool proj_paint_add_slot(bContext *C, wmOperator *op) } } - mtex->tex = add_texture(bmain, DATA_(layer_type_items[type_id].name)); + mtex->tex = BKE_texture_add(bmain, DATA_(layer_type_items[type_id].name)); mtex->mapto = type; if (mtex->tex) { diff --git a/source/blender/editors/sculpt_paint/paint_intern.h b/source/blender/editors/sculpt_paint/paint_intern.h index 9e558092f73..fd7e053fea3 100644 --- a/source/blender/editors/sculpt_paint/paint_intern.h +++ b/source/blender/editors/sculpt_paint/paint_intern.h @@ -34,14 +34,11 @@ struct ARegion; struct bContext; -struct bglMats; struct Brush; struct ImagePool; struct ColorSpace; struct ColorManagedDisplay; struct ListBase; -struct Material; -struct Mesh; struct MTex; struct Object; struct PaintStroke; @@ -55,7 +52,6 @@ struct ViewContext; struct wmEvent; struct wmOperator; struct wmOperatorType; -struct ImagePaintState; struct wmWindowManager; struct DMCoNo; enum PaintMode; @@ -149,7 +145,7 @@ typedef struct ImagePaintPartialRedraw { int image_texture_paint_poll(struct bContext *C); void *image_undo_find_tile(struct Image *ima, struct ImBuf *ibuf, int x_tile, int y_tile, unsigned short **mask, bool validate); -void *image_undo_push_tile(struct Image *ima, struct ImBuf *ibuf, struct ImBuf **tmpibuf, int x_tile, int y_tile, unsigned short **, bool **valid, bool proj); +void *image_undo_push_tile(struct Image *ima, struct ImBuf *ibuf, struct ImBuf **tmpibuf, int x_tile, int y_tile, unsigned short **, bool **valid, bool proj, bool find_prev); void image_undo_remove_masks(void); void image_undo_init_locks(void); void image_undo_end_locks(void); @@ -164,7 +160,7 @@ void paint_2d_redraw(const bContext *C, void *ps, bool final); void paint_2d_stroke_done(void *ps); void paint_2d_stroke(void *ps, const float prev_mval[2], const float mval[2], const bool eraser, float pressure, float distance, float size); void paint_2d_bucket_fill(const struct bContext *C, const float color[3], struct Brush *br, const float mouse_init[2], void *ps); -void paint_2d_gradient_fill (const struct bContext *C, struct Brush *br, const float mouse_init[2], const float mouse_final[2], void *ps); +void paint_2d_gradient_fill(const struct bContext *C, struct Brush *br, const float mouse_init[2], const float mouse_final[2], void *ps); void *paint_proj_new_stroke(struct bContext *C, struct Object *ob, const float mouse[2], int mode); void paint_proj_stroke(const struct bContext *C, void *ps, const float prevmval_i[2], const float mval_i[2], const bool eraser, float pressure, float distance, float size); void paint_proj_redraw(const struct bContext *C, void *pps, bool final); diff --git a/source/blender/editors/sculpt_paint/paint_ops.c b/source/blender/editors/sculpt_paint/paint_ops.c index 121b0b83a4b..b95951a4d7a 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.c +++ b/source/blender/editors/sculpt_paint/paint_ops.c @@ -44,6 +44,7 @@ #include "ED_screen.h" #include "ED_image.h" #include "UI_resources.h" +#include "UI_interface.h" #include "WM_api.h" #include "WM_types.h" @@ -60,19 +61,39 @@ #include <stddef.h> /* Brush operators */ + static int brush_add_exec(bContext *C, wmOperator *UNUSED(op)) { /*int type = RNA_enum_get(op->ptr, "type");*/ - Paint *paint = BKE_paint_get_active_from_context(C); - Brush *br = BKE_paint_brush(paint); Main *bmain = CTX_data_main(C); - + Scene *scene = CTX_data_scene(C); + Object *ob = CTX_data_active_object(C); + Paint *paint = NULL; + HairEditSettings *hair_edit = NULL; + Brush *br = NULL; + + /* get active brush context */ + if (ob->mode == OB_MODE_HAIR_EDIT) { + hair_edit = &scene->toolsettings->hair_edit; + br = hair_edit->brush; + } + else { + paint = BKE_paint_get_active_from_context(C); + br = BKE_paint_brush(paint); + } + if (br) br = BKE_brush_copy(br); else br = BKE_brush_add(bmain, "Brush"); - BKE_paint_brush_set(paint, br); + /* set new brush pointer in the context */ + if (ob->mode == OB_MODE_HAIR_EDIT) { + hair_edit->brush = br; + } + else { + BKE_paint_brush_set(paint, br); + } return OPERATOR_FINISHED; } @@ -95,11 +116,20 @@ static void BRUSH_OT_add(wmOperatorType *ot) static int brush_scale_size_exec(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); - Paint *paint = BKE_paint_get_active_from_context(C); - Brush *brush = BKE_paint_brush(paint); - // Object *ob = CTX_data_active_object(C); + Object *ob = CTX_data_active_object(C); + Brush *brush; float scalar = RNA_float_get(op->ptr, "scalar"); + /* get active brush context */ + if (ob->mode == OB_MODE_HAIR_EDIT) { + HairEditSettings *hair_edit = &scene->toolsettings->hair_edit; + brush = hair_edit->brush; + } + else { + Paint *paint = BKE_paint_get_active_from_context(C); + brush = BKE_paint_brush(paint); + } + if (brush) { // pixel radius { @@ -196,7 +226,10 @@ static int palette_color_add_exec(bContext *C, wmOperator *UNUSED(op)) Brush *brush = paint->brush; PaintMode mode = BKE_paintmode_get_active_from_context(C); Palette *palette = paint->palette; - PaletteColor *color = BKE_palette_color_add(palette); + PaletteColor *color; + + color = BKE_palette_color_add(palette); + palette->active_color = BLI_listbase_count(&palette->colors) - 1; if (ELEM(mode, PAINT_TEXTURE_PROJECTIVE, PAINT_TEXTURE_2D, PAINT_VERTEX)) { copy_v3_v3(color->rgb, BKE_brush_color_get(scene, brush)); @@ -231,7 +264,9 @@ static int palette_color_delete_exec(bContext *C, wmOperator *UNUSED(op)) Palette *palette = paint->palette; PaletteColor *color = BLI_findlink(&palette->colors, palette->active_color); - BKE_palette_color_remove(palette, color); + if (color) { + BKE_palette_color_remove(palette, color); + } return OPERATOR_FINISHED; } @@ -462,7 +497,7 @@ static int brush_select_exec(bContext *C, wmOperator *op) Object *ob = CTX_data_active_object(C); if (ob) { /* select current paint mode */ - paint_mode = ob->mode & OB_MODE_ALL_PAINT; + paint_mode = ob->mode & OB_MODE_ALL_BRUSH; } else { return OPERATOR_CANCELLED; diff --git a/source/blender/editors/sculpt_paint/paint_stroke.c b/source/blender/editors/sculpt_paint/paint_stroke.c index bfd429d0924..21beb97ffae 100644 --- a/source/blender/editors/sculpt_paint/paint_stroke.c +++ b/source/blender/editors/sculpt_paint/paint_stroke.c @@ -68,7 +68,7 @@ #include <float.h> #include <math.h> -// #define DEBUG_TIME +#define DEBUG_TIME #ifdef DEBUG_TIME # include "PIL_time_utildefines.h" @@ -347,7 +347,8 @@ static bool paint_brush_update(bContext *C, if (!stroke->brush_init) { copy_v2_v2(ups->last_rake, mouse_init); } - else { + /* curve strokes do their own rake calculation */ + else if (!(brush->flag & BRUSH_CURVE)) { paint_calculate_rake_rotation(ups, brush, mouse_init); } } @@ -671,9 +672,8 @@ PaintStroke *paint_stroke_new(bContext *C, get_imapaint_zoom(C, &zoomx, &zoomy); stroke->zoom_2d = max_ff(zoomx, zoomy); - if (stroke->stroke_mode == BRUSH_STROKE_INVERT) - { - if (br->flag & (BRUSH_CURVE | BRUSH_LINE)) { + if (stroke->stroke_mode == BRUSH_STROKE_INVERT) { + if (br->flag & (BRUSH_CURVE)) { RNA_enum_set(op->ptr, "mode", BRUSH_STROKE_NORMAL); } } @@ -955,6 +955,7 @@ static bool paint_stroke_curve_end(bContext *C, wmOperator *op, PaintStroke *str Brush *br = stroke->brush; if (br->flag & BRUSH_CURVE) { + UnifiedPaintSettings *ups = &CTX_data_tool_settings(C)->unified_paint_settings; const Scene *scene = CTX_data_scene(C); const float spacing = paint_space_stroke_spacing(scene, stroke, 1.0f, 1.0f); PaintCurve *pc = br->paint_curve; @@ -975,18 +976,39 @@ static bool paint_stroke_curve_end(bContext *C, wmOperator *op, PaintStroke *str for (i = 0; i < pc->tot_points - 1; i++, pcp++) { int j; float data[(PAINT_CURVE_NUM_SEGMENTS + 1) * 2]; + float tangents[(PAINT_CURVE_NUM_SEGMENTS + 1) * 2]; PaintCurvePoint *pcp_next = pcp + 1; + bool do_rake = false; - for (j = 0; j < 2; j++) + for (j = 0; j < 2; j++) { BKE_curve_forward_diff_bezier( pcp->bez.vec[1][j], pcp->bez.vec[2][j], pcp_next->bez.vec[0][j], pcp_next->bez.vec[1][j], data + j, PAINT_CURVE_NUM_SEGMENTS, sizeof(float[2])); + } + if ((br->mtex.brush_angle_mode & MTEX_ANGLE_RAKE) || + (br->mask_mtex.brush_angle_mode & MTEX_ANGLE_RAKE)) + { + do_rake = true; + for (j = 0; j < 2; j++) { + BKE_curve_forward_diff_tangent_bezier( + pcp->bez.vec[1][j], + pcp->bez.vec[2][j], + pcp_next->bez.vec[0][j], + pcp_next->bez.vec[1][j], + tangents + j, PAINT_CURVE_NUM_SEGMENTS, sizeof(float[2])); + } + } for (j = 0; j < PAINT_CURVE_NUM_SEGMENTS; j++) { + if (do_rake) { + float rotation = atan2f(tangents[2 * j], tangents[2 * j + 1]); + paint_update_brush_rake_rotation(ups, br, rotation); + } + if (!stroke->stroke_started) { stroke->last_pressure = 1.0; copy_v2_v2(stroke->last_mouse_position, data + 2 * j); @@ -1015,14 +1037,14 @@ static bool paint_stroke_curve_end(bContext *C, wmOperator *op, PaintStroke *str return false; } -static void paint_stroke_line_constrain (PaintStroke *stroke, float mouse[2]) +static void paint_stroke_line_constrain(PaintStroke *stroke, float mouse[2]) { if (stroke->constrain_line) { float line[2]; float angle, len, res; sub_v2_v2v2(line, mouse, stroke->last_mouse_position); - angle = atan2(line[1], line[0]); + angle = atan2f(line[1], line[0]); len = len_v2(line); /* divide angle by PI/4 */ @@ -1129,7 +1151,7 @@ int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event) return OPERATOR_FINISHED; } else if (br->flag & BRUSH_LINE) { - if (event->ctrl) + if (event->alt) stroke->constrain_line = true; else stroke->constrain_line = false; @@ -1137,8 +1159,7 @@ int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event) copy_v2_fl2(mouse, event->mval[0], event->mval[1]); paint_stroke_line_constrain(stroke, mouse); - if (stroke->stroke_started && (first_modal || (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)))) - { + if (stroke->stroke_started && (first_modal || (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)))) { if ((br->mtex.brush_angle_mode & MTEX_ANGLE_RAKE) || (br->mtex.brush_angle_mode & MTEX_ANGLE_RAKE)) { copy_v2_v2(stroke->ups->last_rake, stroke->last_mouse_position); } diff --git a/source/blender/editors/sculpt_paint/paint_undo.c b/source/blender/editors/sculpt_paint/paint_undo.c index 0293a0bfc00..42f0aaab173 100644 --- a/source/blender/editors/sculpt_paint/paint_undo.c +++ b/source/blender/editors/sculpt_paint/paint_undo.c @@ -331,32 +331,33 @@ void ED_undo_paint_step_num(bContext *C, int type, int step) undo_step_num(C, &MeshUndoStack, step); } -static char *undo_stack_get_name(UndoStack *stack, int nr, int *active) +static char *undo_stack_get_name(UndoStack *stack, int nr, bool *r_active) { UndoElem *uel; - if (active) *active = 0; + if (r_active) *r_active = false; uel = BLI_findlink(&stack->elems, nr); if (uel) { - if (active && uel == stack->current) - *active = 1; + if (r_active && (uel == stack->current)) { + *r_active = true; + } return uel->name; } return NULL; } -const char *ED_undo_paint_get_name(bContext *C, int type, int nr, int *active) +const char *ED_undo_paint_get_name(bContext *C, int type, int nr, bool *r_active) { if (type == UNDO_PAINT_IMAGE) { undo_stack_cleanup(&ImageUndoStack, C); - return undo_stack_get_name(&ImageUndoStack, nr, active); + return undo_stack_get_name(&ImageUndoStack, nr, r_active); } else if (type == UNDO_PAINT_MESH) { undo_stack_cleanup(&MeshUndoStack, C); - return undo_stack_get_name(&MeshUndoStack, nr, active); + return undo_stack_get_name(&MeshUndoStack, nr, r_active); } return NULL; } @@ -379,7 +380,7 @@ bool ED_undo_paint_empty(int type) return false; } -int ED_undo_paint_valid(int type, const char *name) +bool ED_undo_paint_is_valid(int type, const char *name) { UndoStack *stack; diff --git a/source/blender/editors/sculpt_paint/paint_utils.c b/source/blender/editors/sculpt_paint/paint_utils.c index c0ed5005397..bdf11de0f87 100644 --- a/source/blender/editors/sculpt_paint/paint_utils.c +++ b/source/blender/editors/sculpt_paint/paint_utils.c @@ -379,7 +379,7 @@ static int imapaint_pick_face(ViewContext *vc, const int mval[2], unsigned int * return 0; /* sample only on the exact position */ - *r_index = view3d_sample_backbuf(vc, mval[0], mval[1]); + *r_index = ED_view3d_backbuf_sample(vc, mval[0], mval[1]); if ((*r_index) == 0 || (*r_index) > (unsigned int)totpoly) { return 0; @@ -439,6 +439,7 @@ void paint_sample_color(bContext *C, ARegion *ar, int x, int y, bool texpaint_pr } color = BKE_palette_color_add(palette); + palette->active_color = BLI_listbase_count(&palette->colors) - 1; } @@ -486,12 +487,15 @@ void paint_sample_color(bContext *C, ARegion *ar, int x, int y, bool texpaint_pr if (u < 0.0f) u += 1.0f; if (v < 0.0f) v += 1.0f; - u = u * ibuf->x - 0.5f; - v = v * ibuf->y - 0.5f; + u = u * ibuf->x; + v = v * ibuf->y; if (ibuf->rect_float) { float rgba_f[4]; - bilinear_interpolation_color_wrap(ibuf, NULL, rgba_f, u, v); + if (U.gameflags & USER_DISABLE_MIPMAP) + nearest_interpolation_color_wrap(ibuf, NULL, rgba_f, u, v); + else + bilinear_interpolation_color_wrap(ibuf, NULL, rgba_f, u, v); straight_to_premul_v4(rgba_f); if (use_palette) { linearrgb_to_srgb_v3_v3(color->rgb, rgba_f); @@ -503,7 +507,10 @@ void paint_sample_color(bContext *C, ARegion *ar, int x, int y, bool texpaint_pr } else { unsigned char rgba[4]; - bilinear_interpolation_color_wrap(ibuf, rgba, NULL, u, v); + if (U.gameflags & USER_DISABLE_MIPMAP) + nearest_interpolation_color_wrap(ibuf, rgba, NULL, u, v); + else + bilinear_interpolation_color_wrap(ibuf, rgba, NULL, u, v); if (use_palette) { rgb_uchar_to_float(color->rgb, rgba); } diff --git a/source/blender/editors/sculpt_paint/paint_vertex.c b/source/blender/editors/sculpt_paint/paint_vertex.c index e87dd6c2810..1a31442faf4 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex.c +++ b/source/blender/editors/sculpt_paint/paint_vertex.c @@ -37,6 +37,7 @@ #include "IMB_imbuf.h" #include "IMB_imbuf_types.h" +#include "IMB_colormanagement.h" #include "DNA_armature_types.h" #include "DNA_mesh_types.h" @@ -208,7 +209,7 @@ static void do_shared_vertex_tesscol(Mesh *me, bool *mfacetag) { /* if no mcol: do not do */ /* if tface: only the involved faces, otherwise all */ - const int use_face_sel = (me->editflag & ME_EDIT_PAINT_FACE_SEL); + const bool use_face_sel = (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0; MFace *mface; int a; short *scolmain, *scol; @@ -279,7 +280,7 @@ static void do_shared_vertex_tesscol(Mesh *me, bool *mfacetag) static void do_shared_vertexcol(Mesh *me, bool *mlooptag, bool *mfacetag, const bool do_tessface) { - const int use_face_sel = (me->editflag & ME_EDIT_PAINT_FACE_SEL); + const bool use_face_sel = (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0; MPoly *mp; int (*scol)[4]; int i, j; @@ -768,7 +769,7 @@ BLI_INLINE unsigned int mcol_lighten(unsigned int col1, unsigned int col2, int f /* See if are lighter, if so mix, else don't do anything. * if the paint col is darker then the original, then ignore */ - if (rgb_to_grayscale_byte(cp1) > rgb_to_grayscale_byte(cp2)) { + if (IMB_colormanagement_get_luminance_byte(cp1) > IMB_colormanagement_get_luminance_byte(cp2)) { return col1; } @@ -801,7 +802,7 @@ BLI_INLINE unsigned int mcol_darken(unsigned int col1, unsigned int col2, int fa /* See if were darker, if so mix, else don't do anything. * if the paint col is brighter then the original, then ignore */ - if (rgb_to_grayscale_byte(cp1) < rgb_to_grayscale_byte(cp2)) { + if (IMB_colormanagement_get_luminance_byte(cp1) < IMB_colormanagement_get_luminance_byte(cp2)) { return col1; } @@ -877,7 +878,7 @@ static int sample_backbuf_area(ViewContext *vc, int *indexar, int totface, int x * brushes with size > 64, why is this here? */ /*if (size > 64.0) size = 64.0;*/ - ibuf = view3d_read_backbuf(vc, x - size, y - size, x + size, y + size); + ibuf = ED_view3d_backbuf_read(vc, x - size, y - size, x + size, y + size); if (ibuf) { unsigned int *rt = ibuf->rect; @@ -1090,7 +1091,7 @@ static int weight_sample_invoke(bContext *C, wmOperator *op, const wmEvent *even me = BKE_mesh_from_object(vc.obact); if (me && me->dvert && vc.v3d && vc.rv3d) { - const int use_vert_sel = (me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0; + const bool use_vert_sel = (me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0; int v_idx_best = -1; unsigned int index; @@ -1176,7 +1177,7 @@ static EnumPropertyItem *weight_paint_sample_enum_itemf(bContext *C, PointerRNA if (me && me->dvert && vc.v3d && vc.rv3d && vc.obact->defbase.first) { const int defbase_tot = BLI_listbase_count(&vc.obact->defbase); - const int use_vert_sel = (me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0; + const bool use_vert_sel = (me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0; int *groups = MEM_callocN(defbase_tot * sizeof(int), "groups"); bool found = false; unsigned int index; diff --git a/source/blender/editors/sculpt_paint/paint_vertex_proj.c b/source/blender/editors/sculpt_paint/paint_vertex_proj.c index ae729248f7e..c939eb6df35 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex_proj.c +++ b/source/blender/editors/sculpt_paint/paint_vertex_proj.c @@ -186,7 +186,7 @@ static void vpaint_proj_dm_map_cosnos_update(struct VertProjHandle *vp_handle, /* highly unlikely this will become unavailable once painting starts (perhaps with animated modifiers) */ if (LIKELY(dm->foreachMappedVert)) { - fill_vn_fl(vp_handle->dists_sq, me->totvert, FLT_MAX); + copy_vn_fl(vp_handle->dists_sq, me->totvert, FLT_MAX); dm->foreachMappedVert(dm, vpaint_proj_dm_map_cosnos_update__map_cb, &vp_update, DM_FOREACH_USE_NORMAL); } diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index c0925eeca39..ede90b6e1f1 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -56,6 +56,7 @@ #include "BKE_ccg.h" #include "BKE_context.h" #include "BKE_depsgraph.h" +#include "BKE_global.h" #include "BKE_image.h" #include "BKE_key.h" #include "BKE_library.h" @@ -115,6 +116,13 @@ static int system_physical_thread_count(void) } #endif /* __APPLE__ */ +/** \name Tool Capabilities + * + * Avoid duplicate checks, internal logic only, + * share logic with #rna_def_sculpt_capabilities where possible. + * + * \{ */ + /* Check if there are any active modifiers in stack (used for flushing updates at enter/exit sculpt mode) */ static bool sculpt_has_active_modifiers(Scene *scene, Object *ob) { @@ -132,6 +140,43 @@ static bool sculpt_has_active_modifiers(Scene *scene, Object *ob) return 0; } +static bool sculpt_tool_needs_original(const char sculpt_tool) +{ + return ELEM(sculpt_tool, + SCULPT_TOOL_GRAB, + SCULPT_TOOL_ROTATE, + SCULPT_TOOL_THUMB, + SCULPT_TOOL_LAYER); +} + +static bool sculpt_tool_is_proxy_used(const char sculpt_tool) +{ + return ELEM(sculpt_tool, + SCULPT_TOOL_SMOOTH, + SCULPT_TOOL_LAYER); +} + +/** + * Test whether the #StrokeCache.sculpt_normal needs update in #do_brush_action + */ +static int sculpt_brush_needs_normal(const Brush *brush) +{ + return ((SCULPT_TOOL_HAS_NORMAL_WEIGHT(brush->sculpt_tool) && + (brush->normal_weight > 0)) || + + ELEM(brush->sculpt_tool, + SCULPT_TOOL_BLOB, + SCULPT_TOOL_CREASE, + SCULPT_TOOL_DRAW, + SCULPT_TOOL_LAYER, + SCULPT_TOOL_NUDGE, + SCULPT_TOOL_ROTATE, + SCULPT_TOOL_THUMB) || + + (brush->mtex.brush_map_mode == MTEX_MAP_MODE_AREA)); +} + +/** \} */ typedef enum StrokeFlags { @@ -181,8 +226,6 @@ typedef struct StrokeCache { ViewContext *vc; Brush *brush; - float (*face_norms)[3]; /* Copy of the mesh faces' normals */ - float special_rotation; float grab_delta[3], grab_delta_symmetry[3]; float old_grab_location[3], orig_grab_location[3]; @@ -284,30 +327,70 @@ static void sculpt_orig_vert_data_update(SculptOrigVertData *orig_data, PBVHVertexIter *iter) { if (orig_data->unode->type == SCULPT_UNDO_COORDS) { - if (orig_data->coords) { - orig_data->co = orig_data->coords[iter->i]; + if (orig_data->bm_log) { + BM_log_original_vert_data( + orig_data->bm_log, iter->bm_vert, + &orig_data->co, &orig_data->no); } else { - orig_data->co = BM_log_original_vert_co(orig_data->bm_log, iter->bm_vert); - } - - if (orig_data->normals) { + orig_data->co = orig_data->coords[iter->i]; orig_data->no = orig_data->normals[iter->i]; } - else { - orig_data->no = BM_log_original_vert_no(orig_data->bm_log, iter->bm_vert); - } } else if (orig_data->unode->type == SCULPT_UNDO_MASK) { - if (orig_data->vmasks) { - orig_data->mask = orig_data->vmasks[iter->i]; + if (orig_data->bm_log) { + orig_data->mask = BM_log_original_mask(orig_data->bm_log, iter->bm_vert); } else { - orig_data->mask = BM_log_original_mask(orig_data->bm_log, iter->bm_vert); + orig_data->mask = orig_data->vmasks[iter->i]; } } } +/** \name SculptProjectVector + * + * Fast-path for #project_plane_v3_v3v3 + * + * \{ */ + +typedef struct SculptProjectVector { + float plane[3]; + float len_sq; + float len_sq_inv_neg; + bool is_valid; + +} SculptProjectVector; + +/** + * \param plane Direction, can be any length. + */ +static void sculpt_project_v3_cache_init( + SculptProjectVector *spvc, const float plane[3]) +{ + copy_v3_v3(spvc->plane, plane); + spvc->len_sq = len_squared_v3(spvc->plane); + spvc->is_valid = (spvc->len_sq > FLT_EPSILON); + spvc->len_sq_inv_neg = (spvc->is_valid) ? -1.0f / spvc->len_sq : 0.0f; +} + +/** + * Calculate the projection. + */ +static void sculpt_project_v3( + const SculptProjectVector *spvc, const float vec[3], + float r_vec[3]) +{ +#if 0 + project_plane_v3_v3v3(r_vec, vec, spvc->plane); +#else + /* inline the projection, cache `-1.0 / dot_v3_v3(v_proj, v_proj)` */ + madd_v3_v3fl(r_vec, spvc->plane, dot_v3v3(vec, spvc->plane) * spvc->len_sq_inv_neg); +#endif +} + +/** \} */ + + /**********************************************************************/ /* Returns true if the stroke will use dynamic topology, false @@ -316,8 +399,8 @@ static void sculpt_orig_vert_data_update(SculptOrigVertData *orig_data, * Factors: some brushes like grab cannot do dynamic topology. * Others, like smooth, are better without. Same goes for alt- * key smoothing. */ -static int sculpt_stroke_dynamic_topology(const SculptSession *ss, - const Brush *brush) +static bool sculpt_stroke_is_dynamic_topology( + const SculptSession *ss, const Brush *brush) { return ((BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) && @@ -327,20 +410,8 @@ static int sculpt_stroke_dynamic_topology(const SculptSession *ss, * dynamic-topology */ !(brush->flag & BRUSH_ANCHORED) && !(brush->flag & BRUSH_DRAG_DOT) && - - (!ELEM(brush->sculpt_tool, - /* These brushes, as currently coded, cannot - * support dynamic topology */ - SCULPT_TOOL_GRAB, - SCULPT_TOOL_ROTATE, - SCULPT_TOOL_THUMB, - SCULPT_TOOL_LAYER, - - /* These brushes could handle dynamic topology, - * but user feedback indicates it's better not - * to */ - SCULPT_TOOL_SMOOTH, - SCULPT_TOOL_MASK))); + + SCULPT_TOOL_HAS_DYNTOPO(brush->sculpt_tool)); } /*** paint mesh ***/ @@ -348,17 +419,11 @@ static int sculpt_stroke_dynamic_topology(const SculptSession *ss, static void paint_mesh_restore_co(Sculpt *sd, Object *ob) { SculptSession *ss = ob->sculpt; - StrokeCache *cache = ss->cache; const Brush *brush = BKE_paint_brush(&sd->paint); - int i; PBVHNode **nodes; int n, totnode; -#ifndef _OPENMP - (void)sd; /* quied unused warning */ -#endif - BKE_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, &totnode); /* Disable OpenMP when dynamic-topology is enabled. Otherwise, new @@ -403,12 +468,6 @@ static void paint_mesh_restore_co(Sculpt *sd, Object *ob) } } - if (ss->face_normals) { - for (i = 0; i < ss->totpoly; i++) { - copy_v3_v3(ss->face_normals[i], cache->face_norms[i]); - } - } - if (nodes) MEM_freeN(nodes); } @@ -508,7 +567,7 @@ static void sculpt_brush_test_init(SculptSession *ss, SculptBrushTest *test) } } -BLI_INLINE bool sculpt_brush_test_clipping(SculptBrushTest *test, const float co[3]) +BLI_INLINE bool sculpt_brush_test_clipping(const SculptBrushTest *test, const float co[3]) { RegionView3D *rv3d = test->clip_rv3d; return (rv3d && (ED_view3d_clipping_test(rv3d, co, true))); @@ -546,7 +605,7 @@ static bool sculpt_brush_test_sq(SculptBrushTest *test, const float co[3]) } } -static bool sculpt_brush_test_fast(SculptBrushTest *test, float co[3]) +static bool sculpt_brush_test_fast(const SculptBrushTest *test, const float co[3]) { if (sculpt_brush_test_clipping(test, co)) { return 0; @@ -554,7 +613,7 @@ static bool sculpt_brush_test_fast(SculptBrushTest *test, float co[3]) return len_squared_v3v3(co, test->location) <= test->radius_squared; } -static bool sculpt_brush_test_cube(SculptBrushTest *test, float co[3], float local[4][4]) +static bool sculpt_brush_test_cube(SculptBrushTest *test, const float co[3], float local[4][4]) { float side = M_SQRT1_2; float local_co[3]; @@ -607,7 +666,7 @@ static float frontface(Brush *br, const float sculpt_normal[3], #if 0 -static bool sculpt_brush_test_cyl(SculptBrushTest *test, float co[3], float location[3], float an[3]) +static bool sculpt_brush_test_cyl(SculptBrushTest *test, float co[3], float location[3], const float area_no[3]) { if (sculpt_brush_test_fast(test, co)) { float t1[3], t2[3], t3[3], dist; @@ -615,7 +674,7 @@ static bool sculpt_brush_test_cyl(SculptBrushTest *test, float co[3], float loca sub_v3_v3v3(t1, location, co); sub_v3_v3v3(t2, x2, location); - cross_v3_v3v3(t3, an, t1); + cross_v3_v3v3(t3, area_no, t1); dist = len_v3(t3) / len_v3(t2); @@ -701,18 +760,447 @@ static float calc_symmetry_feather(Sculpt *sd, StrokeCache *cache) } } +/** \name Calculate Normal and Center + * + * Calculate geometry surrounding the brush center. + * (optionally using original coordinates). + * + * Functions are: + * - #calc_area_center + * - #calc_area_normal + * - #calc_area_normal_and_center + * + * \note These are all _very_ similar, when changing one, check others. + * \{ */ + +static void calc_area_center( + Sculpt *sd, Object *ob, + PBVHNode **nodes, int totnode, + float r_area_co[3]) +{ + const Brush *brush = BKE_paint_brush(&sd->paint); + SculptSession *ss = ob->sculpt; + const bool has_bm_orco = ss->bm && sculpt_stroke_is_dynamic_topology(ss, brush); + int n; + + /* 0=towards view, 1=flipped */ + float area_co[2][3] = {{0.0f}}; + + int count[2] = {0}; + +#pragma omp parallel for schedule(guided) if ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_OMP_LIMIT) + for (n = 0; n < totnode; n++) { + PBVHVertexIter vd; + SculptBrushTest test; + SculptUndoNode *unode; + float private_co[2][3] = {{0.0f}}; + int private_count[2] = {0}; + bool use_original; + + unode = sculpt_undo_push_node(ob, nodes[n], SCULPT_UNDO_COORDS); + sculpt_brush_test_init(ss, &test); + + use_original = (ss->cache->original && (unode->co || unode->bm_entry)); + + /* when the mesh is edited we can't rely on original coords + * (original mesh may not even have verts in brush radius) */ + if (use_original && has_bm_orco) { + float (*orco_coords)[3]; + int (*orco_tris)[3]; + int orco_tris_num; + int i; + + BKE_pbvh_node_get_bm_orco_data( + nodes[n], + &orco_tris, &orco_tris_num, &orco_coords); + + for (i = 0; i < orco_tris_num; i++) { + const float *co_tri[3] = { + orco_coords[orco_tris[i][0]], + orco_coords[orco_tris[i][1]], + orco_coords[orco_tris[i][2]], + }; + float co[3]; + + closest_on_tri_to_point_v3(co, test.location, UNPACK3(co_tri)); + + if (sculpt_brush_test_fast(&test, co)) { + float no[3]; + int flip_index; + + cross_tri_v3(no, UNPACK3(co_tri)); + + flip_index = (dot_v3v3(ss->cache->view_normal, no) <= 0.0f); + add_v3_v3(private_co[flip_index], co); + private_count[flip_index] += 1; + } + } + } + else { + BKE_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) + { + const float *co; + const short *no_s; /* bm_vert only */ + + if (use_original) { + if (unode->bm_entry) { + BM_log_original_vert_data(ss->bm_log, vd.bm_vert, &co, &no_s); + } + else { + co = unode->co[vd.i]; + no_s = unode->no[vd.i]; + } + } + else { + co = vd.co; + } + + if (sculpt_brush_test_fast(&test, co)) { + float no_buf[3]; + const float *no; + int flip_index; + + if (use_original) { + normal_short_to_float_v3(no_buf, no_s); + no = no_buf; + } + else { + if (vd.no) { + normal_short_to_float_v3(no_buf, vd.no); + no = no_buf; + } + else { + no = vd.fno; + } + } + + flip_index = (dot_v3v3(ss->cache->view_normal, no) <= 0.0f); + add_v3_v3(private_co[flip_index], co); + private_count[flip_index] += 1; + } + } + BKE_pbvh_vertex_iter_end; + } + +#pragma omp critical + { + /* for flatten center */ + add_v3_v3(area_co[0], private_co[0]); + add_v3_v3(area_co[1], private_co[1]); + + /* weights */ + count[0] += private_count[0]; + count[1] += private_count[1]; + } + } + + /* for flatten center */ + for (n = 0; n < ARRAY_SIZE(area_co); n++) { + if (count[n] != 0) { + mul_v3_v3fl(r_area_co, area_co[n], 1.0f / count[n]); + break; + } + } + if (n == 2) { + zero_v3(r_area_co); + } +} + + +static void calc_area_normal( + Sculpt *sd, Object *ob, + PBVHNode **nodes, int totnode, + float r_area_no[3]) +{ + const Brush *brush = BKE_paint_brush(&sd->paint); + SculptSession *ss = ob->sculpt; + const bool has_bm_orco = ss->bm && sculpt_stroke_is_dynamic_topology(ss, brush); + int n; + + /* 0=towards view, 1=flipped */ + float area_no[2][3] = {{0.0f}}; + + int count[2] = {0}; + +#pragma omp parallel for schedule(guided) if ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_OMP_LIMIT) + for (n = 0; n < totnode; n++) { + PBVHVertexIter vd; + SculptBrushTest test; + SculptUndoNode *unode; + float private_no[2][3] = {{0.0f}}; + int private_count[2] = {0}; + bool use_original; + + unode = sculpt_undo_push_node(ob, nodes[n], SCULPT_UNDO_COORDS); + sculpt_brush_test_init(ss, &test); + + use_original = (ss->cache->original && (unode->co || unode->bm_entry)); + + /* when the mesh is edited we can't rely on original coords + * (original mesh may not even have verts in brush radius) */ + if (use_original && has_bm_orco) { + float (*orco_coords)[3]; + int (*orco_tris)[3]; + int orco_tris_num; + int i; + + BKE_pbvh_node_get_bm_orco_data( + nodes[n], + &orco_tris, &orco_tris_num, &orco_coords); + + for (i = 0; i < orco_tris_num; i++) { + const float *co_tri[3] = { + orco_coords[orco_tris[i][0]], + orco_coords[orco_tris[i][1]], + orco_coords[orco_tris[i][2]], + }; + float co[3]; + + closest_on_tri_to_point_v3(co, test.location, UNPACK3(co_tri)); + + if (sculpt_brush_test_fast(&test, co)) { + float no[3]; + int flip_index; + + normal_tri_v3(no, UNPACK3(co_tri)); + + flip_index = (dot_v3v3(ss->cache->view_normal, no) <= 0.0f); + add_v3_v3(private_no[flip_index], no); + private_count[flip_index] += 1; + } + } + } + else { + BKE_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) + { + const float *co; + const short *no_s; /* bm_vert only */ + + if (use_original) { + if (unode->bm_entry) { + BM_log_original_vert_data(ss->bm_log, vd.bm_vert, &co, &no_s); + } + else { + co = unode->co[vd.i]; + no_s = unode->no[vd.i]; + } + } + else { + co = vd.co; + } + + if (sculpt_brush_test_fast(&test, co)) { + float no_buf[3]; + const float *no; + int flip_index; + + if (use_original) { + normal_short_to_float_v3(no_buf, no_s); + no = no_buf; + } + else { + if (vd.no) { + normal_short_to_float_v3(no_buf, vd.no); + no = no_buf; + } + else { + no = vd.fno; + } + } + + flip_index = (dot_v3v3(ss->cache->view_normal, no) <= 0.0f); + add_v3_v3(private_no[flip_index], no); + private_count[flip_index] += 1; + } + } + BKE_pbvh_vertex_iter_end; + } + +#pragma omp critical + { + /* for area normal */ + add_v3_v3(area_no[0], private_no[0]); + add_v3_v3(area_no[1], private_no[1]); + + /* weights */ + count[0] += private_count[0]; + count[1] += private_count[1]; + } + } + + /* for area normal */ + for (n = 0; n < ARRAY_SIZE(area_no); n++) { + if (normalize_v3_v3(r_area_no, area_no[n]) != 0.0f) { + break; + } + } +} + +/* this calculates flatten center and area normal together, + * amortizing the memory bandwidth and loop overhead to calculate both at the same time */ +static void calc_area_normal_and_center( + Sculpt *sd, Object *ob, + PBVHNode **nodes, int totnode, + float r_area_no[3], float r_area_co[3]) +{ + const Brush *brush = BKE_paint_brush(&sd->paint); + SculptSession *ss = ob->sculpt; + const bool has_bm_orco = ss->bm && sculpt_stroke_is_dynamic_topology(ss, brush); + int n; + + /* 0=towards view, 1=flipped */ + float area_co[2][3] = {{0.0f}}; + float area_no[2][3] = {{0.0f}}; + + int count[2] = {0}; + +#pragma omp parallel for schedule(guided) if ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_OMP_LIMIT) + for (n = 0; n < totnode; n++) { + PBVHVertexIter vd; + SculptBrushTest test; + SculptUndoNode *unode; + float private_co[2][3] = {{0.0f}}; + float private_no[2][3] = {{0.0f}}; + int private_count[2] = {0}; + bool use_original; + + unode = sculpt_undo_push_node(ob, nodes[n], SCULPT_UNDO_COORDS); + sculpt_brush_test_init(ss, &test); + + use_original = (ss->cache->original && (unode->co || unode->bm_entry)); + + /* when the mesh is edited we can't rely on original coords + * (original mesh may not even have verts in brush radius) */ + if (use_original && has_bm_orco) { + float (*orco_coords)[3]; + int (*orco_tris)[3]; + int orco_tris_num; + int i; + + BKE_pbvh_node_get_bm_orco_data( + nodes[n], + &orco_tris, &orco_tris_num, &orco_coords); + + for (i = 0; i < orco_tris_num; i++) { + const float *co_tri[3] = { + orco_coords[orco_tris[i][0]], + orco_coords[orco_tris[i][1]], + orco_coords[orco_tris[i][2]], + }; + float co[3]; + + closest_on_tri_to_point_v3(co, test.location, UNPACK3(co_tri)); + + if (sculpt_brush_test_fast(&test, co)) { + float no[3]; + int flip_index; + + normal_tri_v3(no, UNPACK3(co_tri)); + + flip_index = (dot_v3v3(ss->cache->view_normal, no) <= 0.0f); + add_v3_v3(private_co[flip_index], co); + add_v3_v3(private_no[flip_index], no); + private_count[flip_index] += 1; + } + } + } + else { + BKE_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) + { + const float *co; + const short *no_s; /* bm_vert only */ + + if (use_original) { + if (unode->bm_entry) { + BM_log_original_vert_data(ss->bm_log, vd.bm_vert, &co, &no_s); + } + else { + co = unode->co[vd.i]; + no_s = unode->no[vd.i]; + } + } + else { + co = vd.co; + } + + if (sculpt_brush_test_fast(&test, co)) { + float no_buf[3]; + const float *no; + int flip_index; + + if (use_original) { + normal_short_to_float_v3(no_buf, no_s); + no = no_buf; + } + else { + if (vd.no) { + normal_short_to_float_v3(no_buf, vd.no); + no = no_buf; + } + else { + no = vd.fno; + } + } + + flip_index = (dot_v3v3(ss->cache->view_normal, no) <= 0.0f); + add_v3_v3(private_co[flip_index], co); + add_v3_v3(private_no[flip_index], no); + private_count[flip_index] += 1; + } + } + BKE_pbvh_vertex_iter_end; + } + +#pragma omp critical + { + /* for flatten center */ + add_v3_v3(area_co[0], private_co[0]); + add_v3_v3(area_co[1], private_co[1]); + + /* for area normal */ + add_v3_v3(area_no[0], private_no[0]); + add_v3_v3(area_no[1], private_no[1]); + + /* weights */ + count[0] += private_count[0]; + count[1] += private_count[1]; + } + } + + /* for flatten center */ + for (n = 0; n < ARRAY_SIZE(area_co); n++) { + if (count[n] != 0) { + mul_v3_v3fl(r_area_co, area_co[n], 1.0f / count[n]); + break; + } + } + if (n == 2) { + zero_v3(r_area_co); + } + + /* for area normal */ + for (n = 0; n < ARRAY_SIZE(area_no); n++) { + if (normalize_v3_v3(r_area_no, area_no[n]) != 0.0f) { + break; + } + } +} + +/** \} */ + + /* Return modified brush strength. Includes the direction of the brush, positive * values pull vertices, negative values push. Uses tablet pressure and a * special multiplier found experimentally to scale the strength factor. */ -static float brush_strength(Sculpt *sd, StrokeCache *cache, float feather, UnifiedPaintSettings *ups) +static float brush_strength(const Sculpt *sd, const StrokeCache *cache, const float feather, const UnifiedPaintSettings *ups) { const Scene *scene = cache->vc->scene; - Brush *brush = BKE_paint_brush(&sd->paint); + const Brush *brush = BKE_paint_brush((Paint *)&sd->paint); /* Primary strength input; square it to make lower values more sensitive */ const float root_alpha = BKE_brush_alpha_get(scene, brush); float alpha = root_alpha * root_alpha; - float dir = brush->flag & BRUSH_DIR_IN ? -1 : 1; + float dir = (brush->flag & BRUSH_DIR_IN) ? -1 : 1; float pressure = BKE_brush_use_alpha_pressure(scene, brush) ? cache->pressure : 1; float pen_flip = cache->pen_flip ? -1 : 1; float invert = cache->invert ? -1 : 1; @@ -926,132 +1414,34 @@ static void sculpt_clip(Sculpt *sd, SculptSession *ss, float co[3], const float } } -static void add_norm_if(float view_vec[3], float out[3], float out_flip[3], float fno[3]) -{ - if ((dot_v3v3(view_vec, fno)) > 0) { - add_v3_v3(out, fno); - } - else { - add_v3_v3(out_flip, fno); /* out_flip is used when out is {0,0,0} */ - } -} - -static void calc_area_normal(Sculpt *sd, Object *ob, float an[3], PBVHNode **nodes, int totnode) -{ - float out_flip[3] = {0.0f, 0.0f, 0.0f}; - - SculptSession *ss = ob->sculpt; - const Brush *brush = BKE_paint_brush(&sd->paint); - int n; - bool original; - - /* Grab brush requires to test on original data (see r33888 and - * bug #25371) */ - original = (BKE_paint_brush(&sd->paint)->sculpt_tool == SCULPT_TOOL_GRAB ? - true : ss->cache->original); - - /* In general the original coords are not available with dynamic - * topology - * - * Mask tool could not use undo nodes to get coordinates from - * since the coordinates are not stored in those odes. - * And mask tool is not gonna to modify vertex coordinates, - * so we don't actually need to use modified coords. - */ - if (ss->bm || brush->sculpt_tool == SCULPT_TOOL_MASK) - original = false; - - (void)sd; /* unused w/o openmp */ - - zero_v3(an); - -#pragma omp parallel for schedule(guided) if ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_OMP_LIMIT) - for (n = 0; n < totnode; n++) { - PBVHVertexIter vd; - SculptBrushTest test; - SculptUndoNode *unode; - float private_an[3] = {0.0f, 0.0f, 0.0f}; - float private_out_flip[3] = {0.0f, 0.0f, 0.0f}; - - unode = sculpt_undo_push_node(ob, nodes[n], SCULPT_UNDO_COORDS); - sculpt_brush_test_init(ss, &test); - - if (original) { - BKE_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) - { - if (sculpt_brush_test_fast(&test, unode->co[vd.i])) { - float fno[3]; - - normal_short_to_float_v3(fno, unode->no[vd.i]); - add_norm_if(ss->cache->view_normal, private_an, private_out_flip, fno); - } - } - BKE_pbvh_vertex_iter_end; - } - else { - BKE_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) - { - if (sculpt_brush_test_fast(&test, vd.co)) { - if (vd.no) { - float fno[3]; - - normal_short_to_float_v3(fno, vd.no); - add_norm_if(ss->cache->view_normal, private_an, private_out_flip, fno); - } - else { - add_norm_if(ss->cache->view_normal, private_an, private_out_flip, vd.fno); - } - } - } - BKE_pbvh_vertex_iter_end; - } - -#pragma omp critical - { - add_v3_v3(an, private_an); - add_v3_v3(out_flip, private_out_flip); - } - } - - if (is_zero_v3(an)) - copy_v3_v3(an, out_flip); - - normalize_v3(an); -} - /* Calculate primary direction of movement for many brushes */ -static void calc_sculpt_normal(Sculpt *sd, Object *ob, - PBVHNode **nodes, int totnode, - float an[3]) +static void calc_sculpt_normal( + Sculpt *sd, Object *ob, + PBVHNode **nodes, int totnode, + float r_area_no[3]) { const Brush *brush = BKE_paint_brush(&sd->paint); const SculptSession *ss = ob->sculpt; switch (brush->sculpt_plane) { case SCULPT_DISP_DIR_VIEW: - copy_v3_v3(an, ss->cache->true_view_normal); + copy_v3_v3(r_area_no, ss->cache->true_view_normal); break; case SCULPT_DISP_DIR_X: - an[1] = 0.0; - an[2] = 0.0; - an[0] = 1.0; + ARRAY_SET_ITEMS(r_area_no, 1, 0, 0); break; case SCULPT_DISP_DIR_Y: - an[0] = 0.0; - an[2] = 0.0; - an[1] = 1.0; + ARRAY_SET_ITEMS(r_area_no, 0, 1, 0); break; case SCULPT_DISP_DIR_Z: - an[0] = 0.0; - an[1] = 0.0; - an[2] = 1.0; + ARRAY_SET_ITEMS(r_area_no, 0, 0, 1); break; case SCULPT_DISP_DIR_AREA: - calc_area_normal(sd, ob, an, nodes, totnode); + calc_area_normal(sd, ob, nodes, totnode, r_area_no); break; default: @@ -1152,27 +1542,6 @@ static void update_brush_local_mat(Sculpt *sd, Object *ob) } } -/* Test whether the StrokeCache.sculpt_normal needs update in - * do_brush_action() */ -static int brush_needs_sculpt_normal(const Brush *brush) -{ - return ((ELEM(brush->sculpt_tool, - SCULPT_TOOL_GRAB, - SCULPT_TOOL_SNAKE_HOOK) && - (brush->normal_weight > 0)) || - - ELEM(brush->sculpt_tool, - SCULPT_TOOL_BLOB, - SCULPT_TOOL_CREASE, - SCULPT_TOOL_DRAW, - SCULPT_TOOL_LAYER, - SCULPT_TOOL_NUDGE, - SCULPT_TOOL_ROTATE, - SCULPT_TOOL_THUMB) || - - (brush->mtex.brush_map_mode == MTEX_MAP_MODE_AREA)); -} - /* For the smooth brush, uses the neighboring vertices around vert to calculate * a smoothed location for vert. Skips corner vertices (used by only one * polygon.) */ @@ -1182,20 +1551,19 @@ static void neighbor_average(SculptSession *ss, float avg[3], unsigned vert) const MVert *mvert = ss->mvert; float (*deform_co)[3] = ss->deform_cos; - zero_v3(avg); - /* Don't modify corner vertices */ if (vert_map->count > 1) { int i, total = 0; + zero_v3(avg); + for (i = 0; i < vert_map->count; i++) { const MPoly *p = &ss->mpoly[vert_map->indices[i]]; - unsigned f_adj_v[3]; + unsigned f_adj_v[2]; if (poly_get_adj_loops_from_vert(f_adj_v, p, ss->mloop, vert) != -1) { int j; - - for (j = 0; j < 3; j++) { + for (j = 0; j < ARRAY_SIZE(f_adj_v); j += 1) { if (vert_map->count != 2 || ss->pmap[f_adj_v[j]].count <= 2) { add_v3_v3(avg, deform_co ? deform_co[f_adj_v[j]] : mvert[f_adj_v[j]].co); @@ -1226,12 +1594,11 @@ static float neighbor_average_mask(SculptSession *ss, unsigned vert) for (i = 0; i < ss->pmap[vert].count; i++) { const MPoly *p = &ss->mpoly[ss->pmap[vert].indices[i]]; - unsigned f_adj_v[3]; + unsigned f_adj_v[2]; if (poly_get_adj_loops_from_vert(f_adj_v, p, ss->mloop, vert) != -1) { int j; - - for (j = 0; j < 3; j++) { + for (j = 0; j < ARRAY_SIZE(f_adj_v); j += 1) { avg += vmask[f_adj_v[j]]; total++; } @@ -1247,22 +1614,24 @@ static float neighbor_average_mask(SculptSession *ss, unsigned vert) /* Same logic as neighbor_average(), but for bmesh rather than mesh */ static void bmesh_neighbor_average(float avg[3], BMVert *v) { - const int vfcount = BM_vert_face_count(v); + /* logic for 3 or more is identical */ + const int vfcount = BM_vert_face_count_ex(v, 3); - zero_v3(avg); - /* Don't modify corner vertices */ if (vfcount > 1) { BMIter liter; BMLoop *l; int i, total = 0; + zero_v3(avg); + BM_ITER_ELEM (l, &liter, v, BM_LOOPS_OF_VERT) { - BMVert *adj_v[3] = {l->prev->v, v, l->next->v}; + const BMVert *adj_v[2] = {l->prev->v, l->next->v}; - for (i = 0; i < 3; i++) { - if (vfcount != 2 || BM_vert_face_count(adj_v[i]) <= 2) { - add_v3_v3(avg, adj_v[i]->co); + for (i = 0; i < ARRAY_SIZE(adj_v); i++) { + const BMVert *v_other = adj_v[i]; + if (vfcount != 2 || BM_vert_face_count_ex(v_other, 2) <= 2) { + add_v3_v3(avg, v_other->co); total++; } } @@ -1278,7 +1647,7 @@ static void bmesh_neighbor_average(float avg[3], BMVert *v) } /* Same logic as neighbor_average_mask(), but for bmesh rather than mesh */ -static float bmesh_neighbor_average_mask(BMesh *bm, BMVert *v) +static float bmesh_neighbor_average_mask(BMVert *v, const int cd_vert_mask_offset) { BMIter liter; BMLoop *l; @@ -1286,13 +1655,12 @@ static float bmesh_neighbor_average_mask(BMesh *bm, BMVert *v) int i, total = 0; BM_ITER_ELEM (l, &liter, v, BM_LOOPS_OF_VERT) { - BMVert *adj_v[3] = {l->prev->v, v, l->next->v}; + /* skip this vertex */ + const BMVert *adj_v[2] = {l->prev->v, l->next->v}; - for (i = 0; i < 3; i++) { - BMVert *v2 = adj_v[i]; - float *vmask = CustomData_bmesh_get(&bm->vdata, - v2->head.data, - CD_PAINT_MASK); + for (i = 0; i < ARRAY_SIZE(adj_v); i++) { + const BMVert *v_other = adj_v[i]; + const float *vmask = BM_ELEM_CD_GET_VOID_P(v_other, cd_vert_mask_offset); avg += (*vmask); total++; } @@ -1302,9 +1670,7 @@ static float bmesh_neighbor_average_mask(BMesh *bm, BMVert *v) return avg / (float)total; } else { - float *vmask = CustomData_bmesh_get(&bm->vdata, - v->head.data, - CD_PAINT_MASK); + const float *vmask = BM_ELEM_CD_GET_VOID_P(v, cd_vert_mask_offset); return (*vmask); } } @@ -1367,7 +1733,7 @@ static void do_bmesh_smooth_brush(Sculpt *sd, SculptSession *ss, PBVHNode *node, vd.no, vd.fno, smooth_mask ? 0 : *vd.mask); if (smooth_mask) { - float val = bmesh_neighbor_average_mask(ss->bm, vd.bm_vert) - *vd.mask; + float val = bmesh_neighbor_average_mask(vd.bm_vert, vd.cd_vert_mask_offset) - *vd.mask; val *= fade * bstrength; *vd.mask += val; CLAMP(*vd.mask, 0, 1); @@ -1398,12 +1764,11 @@ static void do_multires_smooth_brush(Sculpt *sd, SculptSession *ss, PBVHNode *no SculptBrushTest test; CCGElem **griddata, *data; CCGKey key; - DMGridAdjacency *gridadj, *adj; float (*tmpgrid_co)[3], (*tmprow_co)[3]; float *tmpgrid_mask, *tmprow_mask; int v1, v2, v3, v4; int thread_num; - BLI_bitmap **grid_hidden; + BLI_bitmap * const *grid_hidden; int *grid_indices, totgrid, gridsize, i, x, y; sculpt_brush_test_init(ss, &test); @@ -1411,7 +1776,7 @@ static void do_multires_smooth_brush(Sculpt *sd, SculptSession *ss, PBVHNode *no CLAMP(bstrength, 0.0f, 1.0f); BKE_pbvh_node_get_grids(ss->pbvh, node, &grid_indices, &totgrid, - NULL, &gridsize, &griddata, &gridadj); + NULL, &gridsize, &griddata); BKE_pbvh_get_grid_key(ss->pbvh, &key); grid_hidden = BKE_pbvh_grid_hidden(ss->pbvh); @@ -1428,9 +1793,8 @@ static void do_multires_smooth_brush(Sculpt *sd, SculptSession *ss, PBVHNode *no for (i = 0; i < totgrid; ++i) { int gi = grid_indices[i]; - BLI_bitmap *gh = grid_hidden[gi]; + const BLI_bitmap *gh = grid_hidden[gi]; data = griddata[gi]; - adj = &gridadj[gi]; if (smooth_mask) memset(tmpgrid_mask, 0, sizeof(float) * gridsize * gridsize); @@ -1496,18 +1860,6 @@ static void do_multires_smooth_brush(Sculpt *sd, SculptSession *ss, PBVHNode *no continue; } - if (x == 0 && adj->index[0] == -1) - continue; - - if (x == gridsize - 1 && adj->index[2] == -1) - continue; - - if (y == 0 && adj->index[3] == -1) - continue; - - if (y == gridsize - 1 && adj->index[1] == -1) - continue; - index = x + y * gridsize; co = CCG_elem_offset_co(&key, data, index); fno = CCG_elem_offset_no(&key, data, index); @@ -1698,6 +2050,8 @@ static void do_crease_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnod float brush_alpha; int n; + SculptProjectVector spvc; + /* offset with as much as possible factored in already */ mul_v3_v3fl(offset, ss->cache->sculpt_normal_symm, ss->cache->radius); mul_v3_v3(offset, ss->cache->scale); @@ -1714,6 +2068,10 @@ static void do_crease_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnod if (brush->sculpt_tool == SCULPT_TOOL_BLOB) flippedbstrength *= -1.0f; + /* Use surface normal for 'spvc', so the vertices are pinched towards a line instead of a single point. + * Without this we get a 'flat' surface surrounding the pinch */ + sculpt_project_v3_cache_init(&spvc, ss->cache->sculpt_normal_symm); + /* threaded loop over nodes */ #pragma omp parallel for schedule(guided) if ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_OMP_LIMIT) for (n = 0; n < totnode; n++) { @@ -1738,6 +2096,8 @@ static void do_crease_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnod sub_v3_v3v3(val1, test.location, vd.co); mul_v3_fl(val1, fade * flippedbstrength); + sculpt_project_v3(&spvc, val1, val1); + /* then we draw */ mul_v3_v3fl(val2, offset, fade); @@ -2131,233 +2491,10 @@ static void do_inflate_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totno } } -static void calc_flatten_center(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, float fc[3]) -{ - SculptSession *ss = ob->sculpt; - int n; - - int count = 0; - int count_flip = 0; - - float fc_flip[3] = {0.0, 0.0, 0.0}; - - (void)sd; /* unused w/o openmp */ - - zero_v3(fc); - -#pragma omp parallel for schedule(guided) if ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_OMP_LIMIT) - for (n = 0; n < totnode; n++) { - PBVHVertexIter vd; - SculptBrushTest test; - SculptUndoNode *unode; - float private_fc[3] = {0.0f, 0.0f, 0.0f}; - float private_fc_flip[3] = {0.0f, 0.0f, 0.0f}; - int private_count = 0; - int private_count_flip = 0; - - unode = sculpt_undo_push_node(ob, nodes[n], SCULPT_UNDO_COORDS); - sculpt_brush_test_init(ss, &test); - - if (ss->cache->original && unode->co) { - BKE_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) - { - if (sculpt_brush_test_fast(&test, unode->co[vd.i])) { - float fno[3]; - - normal_short_to_float_v3(fno, unode->no[vd.i]); - if (dot_v3v3(ss->cache->view_normal, fno) > 0) { - add_v3_v3(private_fc, unode->co[vd.i]); - private_count++; - } - else { - add_v3_v3(private_fc_flip, unode->co[vd.i]); - private_count_flip++; - } - } - } - BKE_pbvh_vertex_iter_end; - } - else { - BKE_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) - { - if (sculpt_brush_test_fast(&test, vd.co)) { - /* for area normal */ - if (vd.no) { - float fno[3]; - - normal_short_to_float_v3(fno, vd.no); - - if (dot_v3v3(ss->cache->view_normal, fno) > 0) { - add_v3_v3(private_fc, vd.co); - private_count++; - } - else { - add_v3_v3(private_fc_flip, vd.co); - private_count_flip++; - } - } - else { - if (dot_v3v3(ss->cache->view_normal, vd.fno) > 0) { - add_v3_v3(private_fc, vd.co); - private_count++; - } - else { - add_v3_v3(private_fc_flip, vd.co); - private_count_flip++; - } - } - } - } - BKE_pbvh_vertex_iter_end; - } - -#pragma omp critical - { - add_v3_v3(fc, private_fc); - add_v3_v3(fc_flip, private_fc_flip); - count += private_count; - count_flip += private_count_flip; - } - } - if (count != 0) - mul_v3_fl(fc, 1.0f / count); - else if (count_flip != 0) - mul_v3_v3fl(fc, fc_flip, 1.0f / count_flip); - else - zero_v3(fc); -} - -/* this calculates flatten center and area normal together, - * amortizing the memory bandwidth and loop overhead to calculate both at the same time */ -static void calc_area_normal_and_flatten_center(Sculpt *sd, Object *ob, - PBVHNode **nodes, int totnode, - float an[3], float fc[3]) -{ - SculptSession *ss = ob->sculpt; - int n; - - /* for area normal */ - float out_flip[3] = {0.0f, 0.0f, 0.0f}; - float fc_flip[3] = {0.0f, 0.0f, 0.0f}; - - /* for flatten center */ - int count = 0; - int count_flipped = 0; - - (void)sd; /* unused w/o openmp */ - - /* for area normal */ - zero_v3(an); - - /* for flatten center */ - zero_v3(fc); - -#pragma omp parallel for schedule(guided) if ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_OMP_LIMIT) - for (n = 0; n < totnode; n++) { - PBVHVertexIter vd; - SculptBrushTest test; - SculptUndoNode *unode; - float private_an[3] = {0.0f, 0.0f, 0.0f}; - float private_out_flip[3] = {0.0f, 0.0f, 0.0f}; - float private_fc[3] = {0.0f, 0.0f, 0.0f}; - float private_fc_flip[3] = {0.0f, 0.0f, 0.0f}; - int private_count = 0; - int private_count_flip = 0; - - unode = sculpt_undo_push_node(ob, nodes[n], SCULPT_UNDO_COORDS); - sculpt_brush_test_init(ss, &test); - - if (ss->cache->original && unode->co) { - BKE_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) - { - if (sculpt_brush_test_fast(&test, unode->co[vd.i])) { - /* for area normal */ - float fno[3]; - - normal_short_to_float_v3(fno, unode->no[vd.i]); - - if (dot_v3v3(ss->cache->view_normal, fno) > 0) { - add_v3_v3(private_an, fno); - add_v3_v3(private_fc, unode->co[vd.i]); - private_count++; - } - else { - add_v3_v3(private_out_flip, fno); - add_v3_v3(private_fc_flip, unode->co[vd.i]); - private_count_flip++; - } - } - } - BKE_pbvh_vertex_iter_end; - } - else { - BKE_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) - { - if (sculpt_brush_test_fast(&test, vd.co)) { - /* for area normal */ - if (vd.no) { - float fno[3]; - - normal_short_to_float_v3(fno, vd.no); - - if (dot_v3v3(ss->cache->view_normal, fno) > 0) { - add_v3_v3(private_an, fno); - add_v3_v3(private_fc, vd.co); - private_count++; - } - else { - add_v3_v3(private_out_flip, fno); - add_v3_v3(private_fc_flip, vd.co); - private_count_flip++; - } - } - else { - if (dot_v3v3(ss->cache->view_normal, vd.fno) > 0) { - add_v3_v3(private_an, vd.fno); - add_v3_v3(private_fc, vd.co); - private_count++; - } - else { - add_v3_v3(private_out_flip, vd.fno); - add_v3_v3(private_fc_flip, vd.co); - private_count_flip++; - } - } - } - } - BKE_pbvh_vertex_iter_end; - } - -#pragma omp critical - { - /* for area normal */ - add_v3_v3(an, private_an); - add_v3_v3(out_flip, private_out_flip); - - /* for flatten center */ - add_v3_v3(fc, private_fc); - add_v3_v3(fc_flip, private_fc_flip); - count += private_count; - count_flipped += private_count_flip; - } - } - - /* for area normal */ - if (is_zero_v3(an)) - copy_v3_v3(an, out_flip); - - normalize_v3(an); - - /* for flatten center */ - if (count != 0) - mul_v3_fl(fc, 1.0f / count); - else if (count_flipped != 0) - mul_v3_v3fl(fc, fc_flip, 1.0f / count_flipped); - else - zero_v3(fc); -} - -static void calc_sculpt_plane(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, float an[3], float fc[3]) +static void calc_sculpt_plane( + Sculpt *sd, Object *ob, + PBVHNode **nodes, int totnode, + float r_area_no[3], float r_area_co[3]) { SculptSession *ss = ob->sculpt; Brush *brush = BKE_paint_brush(&sd->paint); @@ -2368,29 +2505,23 @@ static void calc_sculpt_plane(Sculpt *sd, Object *ob, PBVHNode **nodes, int totn { switch (brush->sculpt_plane) { case SCULPT_DISP_DIR_VIEW: - copy_v3_v3(an, ss->cache->true_view_normal); + copy_v3_v3(r_area_no, ss->cache->true_view_normal); break; case SCULPT_DISP_DIR_X: - an[1] = 0.0; - an[2] = 0.0; - an[0] = 1.0; + ARRAY_SET_ITEMS(r_area_no, 1, 0, 0); break; case SCULPT_DISP_DIR_Y: - an[0] = 0.0; - an[2] = 0.0; - an[1] = 1.0; + ARRAY_SET_ITEMS(r_area_no, 0, 1, 0); break; case SCULPT_DISP_DIR_Z: - an[0] = 0.0; - an[1] = 0.0; - an[2] = 1.0; + ARRAY_SET_ITEMS(r_area_no, 0, 0, 1); break; case SCULPT_DISP_DIR_AREA: - calc_area_normal_and_flatten_center(sd, ob, nodes, totnode, an, fc); + calc_area_normal_and_center(sd, ob, nodes, totnode, r_area_no, r_area_co); break; default: @@ -2400,50 +2531,54 @@ static void calc_sculpt_plane(Sculpt *sd, Object *ob, PBVHNode **nodes, int totn /* for flatten center */ /* flatten center has not been calculated yet if we are not using the area normal */ if (brush->sculpt_plane != SCULPT_DISP_DIR_AREA) - calc_flatten_center(sd, ob, nodes, totnode, fc); + calc_area_center(sd, ob, nodes, totnode, r_area_co); /* for area normal */ - copy_v3_v3(ss->cache->sculpt_normal, an); + copy_v3_v3(ss->cache->sculpt_normal, r_area_no); /* for flatten center */ - copy_v3_v3(ss->cache->last_center, fc); + copy_v3_v3(ss->cache->last_center, r_area_co); } else { /* for area normal */ - copy_v3_v3(an, ss->cache->sculpt_normal); + copy_v3_v3(r_area_no, ss->cache->sculpt_normal); /* for flatten center */ - copy_v3_v3(fc, ss->cache->last_center); + copy_v3_v3(r_area_co, ss->cache->last_center); /* for area normal */ - flip_v3(an, ss->cache->mirror_symmetry_pass); + flip_v3(r_area_no, ss->cache->mirror_symmetry_pass); /* for flatten center */ - flip_v3(fc, ss->cache->mirror_symmetry_pass); + flip_v3(r_area_co, ss->cache->mirror_symmetry_pass); /* for area normal */ - mul_m4_v3(ss->cache->symm_rot_mat, an); + mul_m4_v3(ss->cache->symm_rot_mat, r_area_no); /* for flatten center */ - mul_m4_v3(ss->cache->symm_rot_mat, fc); + mul_m4_v3(ss->cache->symm_rot_mat, r_area_co); } } /* Projects a point onto a plane along the plane's normal */ -static void point_plane_project(float intr[3], float co[3], float plane_normal[3], float plane_center[3]) +static void point_plane_project( + float intr[3], + const float co[3], const float plane_normal[3], const float plane_center[3]) { sub_v3_v3v3(intr, co, plane_center); mul_v3_v3fl(intr, plane_normal, dot_v3v3(plane_normal, intr)); sub_v3_v3v3(intr, co, intr); } -static int plane_trim(StrokeCache *cache, Brush *brush, float val[3]) +static int plane_trim(const StrokeCache *cache, const Brush *brush, const float val[3]) { return (!(brush->flag & BRUSH_PLANE_TRIM) || ((dot_v3v3(val, val) <= cache->radius_squared * cache->plane_trim_squared))); } -static int plane_point_side_flip(float co[3], float plane_normal[3], float plane_center[3], int flip) +static bool plane_point_side_flip( + const float co[3], const float plane_normal[3], const float plane_center[3], + const bool flip) { float delta[3]; float d; @@ -2456,7 +2591,7 @@ static int plane_point_side_flip(float co[3], float plane_normal[3], float plane return d <= 0.0f; } -static int plane_point_side(float co[3], float plane_normal[3], float plane_center[3]) +static int plane_point_side(const float co[3], const float plane_normal[3], const float plane_center[3]) { float delta[3]; @@ -2485,8 +2620,8 @@ static void do_flatten_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totno float bstrength = ss->cache->bstrength; const float radius = ss->cache->radius; - float an[3]; - float fc[3]; + float area_no[3]; + float area_co[3]; float offset = get_offset(sd, ss); @@ -2496,13 +2631,13 @@ static void do_flatten_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totno float temp[3]; - calc_sculpt_plane(sd, ob, nodes, totnode, an, fc); + calc_sculpt_plane(sd, ob, nodes, totnode, area_no, area_co); displace = radius * offset; - mul_v3_v3v3(temp, an, ss->cache->scale); + mul_v3_v3v3(temp, area_no, ss->cache->scale); mul_v3_fl(temp, displace); - add_v3_v3(fc, temp); + add_v3_v3(area_co, temp); #pragma omp parallel for schedule(guided) if ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_OMP_LIMIT) for (n = 0; n < totnode; n++) { @@ -2520,7 +2655,7 @@ static void do_flatten_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totno float intr[3]; float val[3]; - point_plane_project(intr, vd.co, an, fc); + point_plane_project(intr, vd.co, area_no, area_co); sub_v3_v3v3(val, intr, vd.co); @@ -2550,16 +2685,16 @@ static void do_clay_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) float displace; - float an[3]; - float fc[3]; + float area_no[3]; + float area_co[3]; int n; float temp[3]; - int flip; + bool flip; - calc_sculpt_plane(sd, ob, nodes, totnode, an, fc); + calc_sculpt_plane(sd, ob, nodes, totnode, area_no, area_co); flip = bstrength < 0; @@ -2570,11 +2705,11 @@ static void do_clay_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) displace = radius * (0.25f + offset); - mul_v3_v3v3(temp, an, ss->cache->scale); + mul_v3_v3v3(temp, area_no, ss->cache->scale); mul_v3_fl(temp, displace); - add_v3_v3(fc, temp); + add_v3_v3(area_co, temp); - /* add_v3_v3v3(p, ss->cache->location, an); */ + /* add_v3_v3v3(p, ss->cache->location, area_no); */ #pragma omp parallel for schedule(guided) if ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_OMP_LIMIT) for (n = 0; n < totnode; n++) { @@ -2589,15 +2724,17 @@ static void do_clay_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) BKE_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { if (sculpt_brush_test_sq(&test, vd.co)) { - if (plane_point_side_flip(vd.co, an, fc, flip)) { + if (plane_point_side_flip(vd.co, area_no, area_co, flip)) { float intr[3]; float val[3]; - point_plane_project(intr, vd.co, an, fc); + point_plane_project(intr, vd.co, area_no, area_co); sub_v3_v3v3(val, intr, vd.co); if (plane_trim(ss->cache, brush, val)) { + /* note, the normal from the vertices is ignored, + * causes glitch with planes, see: T44390 */ const float fade = bstrength * tex_strength(ss, brush, vd.co, sqrtf(test.dist), vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f); @@ -2624,9 +2761,9 @@ static void do_clay_strips_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int t float displace; - float sn[3]; - float an[3]; - float fc[3]; + float area_no_sp[3]; /* the sculpt-plane normal (whatever its set to) */ + float area_no[3]; /* geometry normal */ + float area_co[3]; int n; @@ -2635,14 +2772,14 @@ static void do_clay_strips_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int t float scale[4][4]; float tmat[4][4]; - int flip; + bool flip; - calc_sculpt_plane(sd, ob, nodes, totnode, sn, fc); + calc_sculpt_plane(sd, ob, nodes, totnode, area_no_sp, area_co); if (brush->sculpt_plane != SCULPT_DISP_DIR_AREA || (brush->flag & BRUSH_ORIGINAL_NORMAL)) - calc_area_normal(sd, ob, an, nodes, totnode); + calc_area_normal(sd, ob, nodes, totnode, area_no); else - copy_v3_v3(an, sn); + copy_v3_v3(area_no, area_no_sp); /* delay the first daub because grab delta is not setup */ if (ss->cache->first_time) @@ -2657,16 +2794,16 @@ static void do_clay_strips_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int t displace = radius * (0.25f + offset); - mul_v3_v3v3(temp, sn, ss->cache->scale); + mul_v3_v3v3(temp, area_no_sp, ss->cache->scale); mul_v3_fl(temp, displace); - add_v3_v3(fc, temp); + add_v3_v3(area_co, temp); /* init mat */ - cross_v3_v3v3(mat[0], an, ss->cache->grab_delta_symmetry); + cross_v3_v3v3(mat[0], area_no, ss->cache->grab_delta_symmetry); mat[0][3] = 0; - cross_v3_v3v3(mat[1], an, mat[0]); + cross_v3_v3v3(mat[1], area_no, mat[0]); mat[1][3] = 0; - copy_v3_v3(mat[2], an); + copy_v3_v3(mat[2], area_no); mat[2][3] = 0; copy_v3_v3(mat[3], ss->cache->location); mat[3][3] = 1; @@ -2690,15 +2827,17 @@ static void do_clay_strips_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int t BKE_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { if (sculpt_brush_test_cube(&test, vd.co, mat)) { - if (plane_point_side_flip(vd.co, sn, fc, flip)) { + if (plane_point_side_flip(vd.co, area_no_sp, area_co, flip)) { float intr[3]; float val[3]; - point_plane_project(intr, vd.co, sn, fc); + point_plane_project(intr, vd.co, area_no_sp, area_co); sub_v3_v3v3(val, intr, vd.co); if (plane_trim(ss->cache, brush, val)) { + /* note, the normal from the vertices is ignored, + * causes glitch with planes, see: T44390 */ const float fade = bstrength * tex_strength(ss, brush, vd.co, ss->cache->radius * test.dist, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f); @@ -2723,8 +2862,8 @@ static void do_fill_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) float bstrength = ss->cache->bstrength; const float radius = ss->cache->radius; - float an[3]; - float fc[3]; + float area_no[3]; + float area_co[3]; float offset = get_offset(sd, ss); float displace; @@ -2733,13 +2872,13 @@ static void do_fill_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) float temp[3]; - calc_sculpt_plane(sd, ob, nodes, totnode, an, fc); + calc_sculpt_plane(sd, ob, nodes, totnode, area_no, area_co); displace = radius * offset; - mul_v3_v3v3(temp, an, ss->cache->scale); + mul_v3_v3v3(temp, area_no, ss->cache->scale); mul_v3_fl(temp, displace); - add_v3_v3(fc, temp); + add_v3_v3(area_co, temp); #pragma omp parallel for schedule(guided) if ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_OMP_LIMIT) for (n = 0; n < totnode; n++) { @@ -2754,11 +2893,11 @@ static void do_fill_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) BKE_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { if (sculpt_brush_test_sq(&test, vd.co)) { - if (plane_point_side(vd.co, an, fc)) { + if (plane_point_side(vd.co, area_no, area_co)) { float intr[3]; float val[3]; - point_plane_project(intr, vd.co, an, fc); + point_plane_project(intr, vd.co, area_no, area_co); sub_v3_v3v3(val, intr, vd.co); @@ -2787,8 +2926,8 @@ static void do_scrape_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnod float bstrength = ss->cache->bstrength; const float radius = ss->cache->radius; - float an[3]; - float fc[3]; + float area_no[3]; + float area_co[3]; float offset = get_offset(sd, ss); float displace; @@ -2797,13 +2936,13 @@ static void do_scrape_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnod float temp[3]; - calc_sculpt_plane(sd, ob, nodes, totnode, an, fc); + calc_sculpt_plane(sd, ob, nodes, totnode, area_no, area_co); displace = -radius * offset; - mul_v3_v3v3(temp, an, ss->cache->scale); + mul_v3_v3v3(temp, area_no, ss->cache->scale); mul_v3_fl(temp, displace); - add_v3_v3(fc, temp); + add_v3_v3(area_co, temp); #pragma omp parallel for schedule(guided) if ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_OMP_LIMIT) for (n = 0; n < totnode; n++) { @@ -2818,11 +2957,11 @@ static void do_scrape_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnod BKE_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { if (sculpt_brush_test_sq(&test, vd.co)) { - if (!plane_point_side(vd.co, an, fc)) { + if (!plane_point_side(vd.co, area_no, area_co)) { float intr[3]; float val[3]; - point_plane_project(intr, vd.co, an, fc); + point_plane_project(intr, vd.co, area_no, area_co); sub_v3_v3v3(val, intr, vd.co); @@ -2848,7 +2987,7 @@ static void do_gravity(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, fl SculptSession *ss = ob->sculpt; Brush *brush = BKE_paint_brush(&sd->paint); - float offset[3]/*, an[3]*/; + float offset[3]/*, area_no[3]*/; int n; float gravity_vector[3]; @@ -2945,11 +3084,7 @@ static void sculpt_topology_update(Sculpt *sd, Object *ob, Brush *brush, Unified radius = ss->cache->radius * 1.25f; data.radius_squared = radius * radius; - data.original = ELEM(brush->sculpt_tool, - SCULPT_TOOL_GRAB, - SCULPT_TOOL_ROTATE, - SCULPT_TOOL_THUMB, - SCULPT_TOOL_LAYER) ? true : ss->cache->original; + data.original = sculpt_tool_needs_original(brush->sculpt_tool) ? true : ss->cache->original; BKE_pbvh_search_gather(ss->pbvh, sculpt_search_sphere_cb, &data, &nodes, &totnode); @@ -2980,9 +3115,11 @@ static void sculpt_topology_update(Sculpt *sd, Object *ob, Brush *brush, Unified } if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) { - BKE_pbvh_bmesh_update_topology(ss->pbvh, mode, - ss->cache->location, - ss->cache->radius); + BKE_pbvh_bmesh_update_topology( + ss->pbvh, mode, + ss->cache->location, + (brush->flag & BRUSH_FRONTFACE) ? ss->cache->view_normal : NULL, + ss->cache->radius); } MEM_freeN(nodes); @@ -3004,11 +3141,7 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe data.ss = ss; data.sd = sd; data.radius_squared = ss->cache->radius_squared; - data.original = ELEM(brush->sculpt_tool, - SCULPT_TOOL_GRAB, - SCULPT_TOOL_ROTATE, - SCULPT_TOOL_THUMB, - SCULPT_TOOL_LAYER) ? true : ss->cache->original; + data.original = sculpt_tool_needs_original(brush->sculpt_tool) ? true : ss->cache->original; BKE_pbvh_search_gather(ss->pbvh, sculpt_search_sphere_cb, &data, &nodes, &totnode); /* Only act if some verts are inside the brush area */ @@ -3023,7 +3156,7 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe BKE_pbvh_node_mark_update(nodes[n]); } - if (brush_needs_sculpt_normal(brush)) + if (sculpt_brush_needs_normal(brush)) update_sculpt_normal(sd, ob, nodes, totnode); if (brush->mtex.brush_map_mode == MTEX_MAP_MODE_AREA) @@ -3143,8 +3276,8 @@ static void sculpt_combine_proxies(Sculpt *sd, Object *ob) BKE_pbvh_gather_proxies(ss->pbvh, &nodes, &totnode); /* first line is tools that don't support proxies */ - if (!ELEM(brush->sculpt_tool, SCULPT_TOOL_SMOOTH, SCULPT_TOOL_LAYER) || - ss->cache->supports_gravity) + if (ss->cache->supports_gravity || + (sculpt_tool_is_proxy_used(brush->sculpt_tool) == false)) { /* these brushes start from original coordinates */ const bool use_orco = ELEM(brush->sculpt_tool, SCULPT_TOOL_GRAB, @@ -3222,7 +3355,7 @@ static void sculpt_flush_stroke_deform(Sculpt *sd, Object *ob) SculptSession *ss = ob->sculpt; Brush *brush = BKE_paint_brush(&sd->paint); - if (ELEM(brush->sculpt_tool, SCULPT_TOOL_SMOOTH, SCULPT_TOOL_LAYER)) { + if (sculpt_tool_is_proxy_used(brush->sculpt_tool)) { /* this brushes aren't using proxies, so sculpt_combine_proxies() wouldn't * propagate needed deformation to original base */ @@ -3483,8 +3616,6 @@ static const char *sculpt_tool_name(Sculpt *sd) static void sculpt_cache_free(StrokeCache *cache) { - if (cache->face_norms) - MEM_freeN(cache->face_norms); if (cache->dial) MEM_freeN(cache->dial); MEM_freeN(cache); @@ -3556,7 +3687,7 @@ static void sculpt_omp_start(Sculpt *sd, SculptSession *ss) if (ss->multires) { int i, gridsize, array_mem_size; BKE_pbvh_node_get_grids(ss->pbvh, NULL, NULL, NULL, NULL, - &gridsize, NULL, NULL); + &gridsize, NULL); array_mem_size = cache->num_threads * sizeof(void *); @@ -3744,21 +3875,10 @@ static void sculpt_update_cache_invariants(bContext *C, Sculpt *sd, SculptSessio /* Make copies of the mesh vertex locations and normals for some tools */ if (brush->flag & BRUSH_ANCHORED) { - if (ss->face_normals) { - cache->face_norms = MEM_mallocN(sizeof(float) * 3 * ss->totpoly, "Sculpt face norms"); - for (i = 0; i < ss->totpoly; ++i) { - copy_v3_v3(cache->face_norms[i], ss->face_normals[i]); - } - } - cache->original = 1; } - if (ELEM(brush->sculpt_tool, - SCULPT_TOOL_DRAW, SCULPT_TOOL_CREASE, SCULPT_TOOL_BLOB, - SCULPT_TOOL_LAYER, SCULPT_TOOL_INFLATE, SCULPT_TOOL_CLAY, - SCULPT_TOOL_CLAY_STRIPS, SCULPT_TOOL_ROTATE, SCULPT_TOOL_FLATTEN)) - { + if (SCULPT_TOOL_HAS_ACCUMULATE(brush->sculpt_tool)) { if (!(brush->flag & BRUSH_ACCUMULATE)) { cache->original = 1; } @@ -4250,6 +4370,9 @@ static void sculpt_stroke_update_step(bContext *C, struct PaintStroke *UNUSED(st if (sd->flags & SCULPT_DYNTOPO_DETAIL_CONSTANT) { BKE_pbvh_bmesh_detail_size_set(ss->pbvh, sd->constant_detail / 100.0f); } + else if (sd->flags & SCULPT_DYNTOPO_DETAIL_BRUSH) { + BKE_pbvh_bmesh_detail_size_set(ss->pbvh, ss->cache->radius * sd->detail_percent / 100.0f); + } else { BKE_pbvh_bmesh_detail_size_set( ss->pbvh, @@ -4258,7 +4381,7 @@ static void sculpt_stroke_update_step(bContext *C, struct PaintStroke *UNUSED(st (float)(sd->detail_size * U.pixelsize) / 0.4f); } - if (sculpt_stroke_dynamic_topology(ss, brush)) { + if (sculpt_stroke_is_dynamic_topology(ss, brush)) { do_symmetrical_brush_actions(sd, ob, sculpt_topology_update, ups); } @@ -4650,7 +4773,7 @@ static int sculpt_dynamic_topology_toggle_exec(bContext *C, wmOperator *UNUSED(o Object *ob = CTX_data_active_object(C); SculptSession *ss = ob->sculpt; - if (!GPU_vertex_buffer_support()) { + if (!G.background && !GPU_vertex_buffer_support()) { return OPERATOR_CANCELLED; } @@ -4745,7 +4868,7 @@ static int sculpt_dynamic_topology_toggle_invoke(bContext *C, wmOperator *op, co /* exception for shape keys because we can edit those */ for (; md; md = md->next) { - ModifierTypeInfo *mti = modifierType_getInfo(md->type); + const ModifierTypeInfo *mti = modifierType_getInfo(md->type); if (!modifier_isEnabled(scene, md, eModifierMode_Realtime)) continue; if (mti->type == eModifierTypeType_Constructive) { @@ -4837,6 +4960,9 @@ static int sculpt_symmetrize_exec(bContext *C, wmOperator *UNUSED(op)) sd->symmetrize_direction, 0.00001f); sculpt_dynamic_topology_triangulate(ss->bm); + /* bisect operator flags edges (keep tags clean for edge queue) */ + BM_mesh_elem_hflag_disable_all(ss->bm, BM_EDGE, BM_ELEM_TAG, false); + /* Finish undo */ BM_log_all_added(ss->bm, ss->bm_log); sculpt_undo_push_end(); @@ -4911,7 +5037,7 @@ static int sculpt_mode_toggle_exec(bContext *C, wmOperator *op) /* Leave sculptmode */ ob->mode &= ~mode_flag; - BKE_free_sculptsession(ob); + BKE_sculptsession_free(ob); paint_cursor_delete_textures(); } @@ -4940,16 +5066,16 @@ static int sculpt_mode_toggle_exec(bContext *C, wmOperator *op) ts->sculpt->flags |= SCULPT_DYNTOPO_SUBDIVIDE | SCULPT_DYNTOPO_COLLAPSE; } - if (!ts->sculpt->detail_size) { + if (!ts->sculpt->detail_size) ts->sculpt->detail_size = 12; - } - + if (!ts->sculpt->detail_percent) + ts->sculpt->detail_percent = 25; if (ts->sculpt->constant_detail == 0.0f) ts->sculpt->constant_detail = 30.0f; /* Create sculpt mode session data */ if (ob->sculpt) - BKE_free_sculptsession(ob); + BKE_sculptsession_free(ob); sculpt_init_session(scene, ob); @@ -5032,7 +5158,10 @@ static int sculpt_detail_flood_fill_exec(bContext *C, wmOperator *UNUSED(op)) sculpt_undo_push_begin("Dynamic topology flood fill"); sculpt_undo_push_node(ob, NULL, SCULPT_UNDO_COORDS); - while (BKE_pbvh_bmesh_update_topology(ss->pbvh, PBVH_Collapse | PBVH_Subdivide, bb_min, size)) { + while (BKE_pbvh_bmesh_update_topology( + ss->pbvh, PBVH_Collapse | PBVH_Subdivide, + bb_min, NULL, size)) + { for (i = 0; i < totnodes; i++) BKE_pbvh_node_mark_topology_update(nodes[i]); } @@ -5176,6 +5305,10 @@ static int sculpt_set_detail_size_exec(bContext *C, wmOperator *UNUSED(op)) set_brush_rc_props(&props_ptr, "sculpt", "constant_detail", NULL, 0); RNA_string_set(&props_ptr, "data_path_primary", "tool_settings.sculpt.constant_detail"); } + else if (sd->flags & SCULPT_DYNTOPO_DETAIL_BRUSH) { + set_brush_rc_props(&props_ptr, "sculpt", "constant_detail", NULL, 0); + RNA_string_set(&props_ptr, "data_path_primary", "tool_settings.sculpt.detail_percent"); + } else { set_brush_rc_props(&props_ptr, "sculpt", "detail_size", NULL, 0); RNA_string_set(&props_ptr, "data_path_primary", "tool_settings.sculpt.detail_size"); diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h index a61f571fdf6..8f1a4655c37 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.h +++ b/source/blender/editors/sculpt_paint/sculpt_intern.h @@ -41,14 +41,8 @@ #include "BKE_pbvh.h" struct bContext; -struct Brush; struct KeyBlock; -struct Mesh; -struct MultiresModifierData; struct Object; -struct Scene; -struct Sculpt; -struct SculptStroke; struct SculptUndoNode; int sculpt_mode_poll(struct bContext *C); diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.c b/source/blender/editors/sculpt_paint/sculpt_undo.c index a4adbc6bca8..2f0957c3b60 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.c +++ b/source/blender/editors/sculpt_paint/sculpt_undo.c @@ -484,7 +484,7 @@ static void sculpt_undo_restore(bContext *C, ListBase *lb) BKE_mesh_calc_normals_tessface(mesh->mvert, mesh->totvert, mesh->mface, mesh->totface, NULL); - BKE_free_sculptsession_deformMats(ss); + BKE_sculptsession_free_deformMats(ss); tag_update |= true; } @@ -581,7 +581,7 @@ static void sculpt_undo_alloc_and_store_hidden(PBVH *pbvh, grid_hidden = BKE_pbvh_grid_hidden(pbvh); BKE_pbvh_node_get_grids(pbvh, node, &grid_indices, &totgrid, - NULL, NULL, NULL, NULL); + NULL, NULL, NULL); unode->grid_hidden = MEM_mapallocN(sizeof(*unode->grid_hidden) * totgrid, "unode->grid_hidden"); @@ -610,7 +610,7 @@ static SculptUndoNode *sculpt_undo_alloc_node(Object *ob, PBVHNode *node, if (node) { BKE_pbvh_node_num_verts(ss->pbvh, node, &totvert, &allvert); BKE_pbvh_node_get_grids(ss->pbvh, node, &grids, &totgrid, - &maxgrid, &gridsize, NULL, NULL); + &maxgrid, &gridsize, NULL); unode->totvert = totvert; } @@ -842,7 +842,7 @@ SculptUndoNode *sculpt_undo_push_node(Object *ob, PBVHNode *node, if (unode->grids) { int totgrid, *grids; BKE_pbvh_node_get_grids(ss->pbvh, node, &grids, &totgrid, - NULL, NULL, NULL, NULL); + NULL, NULL, NULL); memcpy(unode->grids, grids, sizeof(int) * totgrid); } else { |