diff options
Diffstat (limited to 'source/blender/editors/sculpt_paint')
-rw-r--r-- | source/blender/editors/sculpt_paint/SConscript | 10 | ||||
-rw-r--r-- | source/blender/editors/sculpt_paint/paint_image.c | 78 | ||||
-rw-r--r-- | source/blender/editors/sculpt_paint/paint_intern.h | 7 | ||||
-rw-r--r-- | source/blender/editors/sculpt_paint/paint_ops.c | 93 | ||||
-rw-r--r-- | source/blender/editors/sculpt_paint/paint_stroke.c | 816 | ||||
-rw-r--r-- | source/blender/editors/sculpt_paint/paint_utils.c | 6 | ||||
-rw-r--r-- | source/blender/editors/sculpt_paint/sculpt.c | 3015 | ||||
-rw-r--r-- | source/blender/editors/sculpt_paint/sculpt_intern.h | 44 | ||||
-rw-r--r-- | source/blender/editors/sculpt_paint/sculpt_undo.c | 302 |
9 files changed, 3418 insertions, 953 deletions
diff --git a/source/blender/editors/sculpt_paint/SConscript b/source/blender/editors/sculpt_paint/SConscript index 472ba361059..3d9a5144c93 100644 --- a/source/blender/editors/sculpt_paint/SConscript +++ b/source/blender/editors/sculpt_paint/SConscript @@ -3,6 +3,8 @@ Import ('env') sources = env.Glob('*.c') +defs = [] + incs = '../include ../../blenlib ../../blenkernel ../../makesdna ../../imbuf' incs += ' ../../windowmanager #/intern/guardedalloc #/extern/glew/include' incs += ' ../../render/extern/include' @@ -12,7 +14,11 @@ if env['OURPLATFORM'] == 'linux2': cflags='-pthread' incs += ' ../../../extern/binreloc/include' +if env['OURPLATFORM'] == 'linuxcross': + if env['WITH_BF_OPENMP']: + incs += ' ' + env['BF_OPENMP_INC'] + if env['OURPLATFORM'] in ('win32-vc', 'win32-mingw', 'linuxcross', 'win64-vc'): - incs += ' ' + env['BF_PTHREADS_INC'] + incs += ' ' + env['BF_PTHREADS_INC'] -env.BlenderLib ( 'bf_editors_sculpt_paint', sources, Split(incs), [], libtype=['core'], priority=[40] ) +env.BlenderLib ( 'bf_editors_sculpt_paint', sources, Split(incs), defines=defs, libtype=['core'], priority=[40] ) diff --git a/source/blender/editors/sculpt_paint/paint_image.c b/source/blender/editors/sculpt_paint/paint_image.c index 41908bbe388..45c0396855d 100644 --- a/source/blender/editors/sculpt_paint/paint_image.c +++ b/source/blender/editors/sculpt_paint/paint_image.c @@ -641,8 +641,8 @@ static int project_paint_PickColor(const ProjPaintState *ps, float pt[2], float } } else { - xi = (uv[0]*ibuf->x) + 0.5f; - yi = (uv[1]*ibuf->y) + 0.5f; + xi = (int)((uv[0]*ibuf->x) + 0.5f); + yi = (int)((uv[1]*ibuf->y) + 0.5f); //if (xi<0 || xi>=ibuf->x || yi<0 || yi>=ibuf->y) return 0; @@ -1053,15 +1053,15 @@ static void uv_image_outset(float (*orig_uv)[2], float (*outset_uv)[2], const fl * This is incorrect. Its already given radians but without it wont work. * need to look into a fix - campbell */ if (is_quad) { - a1 = shell_angle_to_dist(angle_normalized_v2v2(dir4, dir1) * (M_PI/180.0f)); - a2 = shell_angle_to_dist(angle_normalized_v2v2(dir1, dir2) * (M_PI/180.0f)); - a3 = shell_angle_to_dist(angle_normalized_v2v2(dir2, dir3) * (M_PI/180.0f)); - a4 = shell_angle_to_dist(angle_normalized_v2v2(dir3, dir4) * (M_PI/180.0f)); + a1 = shell_angle_to_dist(angle_normalized_v2v2(dir4, dir1) * ((float)M_PI/180.0f)); + a2 = shell_angle_to_dist(angle_normalized_v2v2(dir1, dir2) * ((float)M_PI/180.0f)); + a3 = shell_angle_to_dist(angle_normalized_v2v2(dir2, dir3) * ((float)M_PI/180.0f)); + a4 = shell_angle_to_dist(angle_normalized_v2v2(dir3, dir4) * ((float)M_PI/180.0f)); } else { - a1 = shell_angle_to_dist(angle_normalized_v2v2(dir3, dir1) * (M_PI/180.0f)); - a2 = shell_angle_to_dist(angle_normalized_v2v2(dir1, dir2) * (M_PI/180.0f)); - a3 = shell_angle_to_dist(angle_normalized_v2v2(dir2, dir3) * (M_PI/180.0f)); + a1 = shell_angle_to_dist(angle_normalized_v2v2(dir3, dir1) * ((float)M_PI/180.0f)); + a2 = shell_angle_to_dist(angle_normalized_v2v2(dir1, dir2) * ((float)M_PI/180.0f)); + a3 = shell_angle_to_dist(angle_normalized_v2v2(dir2, dir3) * ((float)M_PI/180.0f)); } if (is_quad) { @@ -1197,7 +1197,7 @@ static void screen_px_from_persp( w[2] *= wtot_inv; } else { - w[0] = w[1] = w[2] = 1.0/3.0; /* dummy values for zero area face */ + w[0] = w[1] = w[2] = 1.0f/3.0f; /* dummy values for zero area face */ } /* done re-weighting */ @@ -2513,11 +2513,11 @@ static void project_paint_face_init(const ProjPaintState *ps, const int thread_i w[0]=w[1]=w[2]= 0.0; if (side) { w[fidx1?fidx1-1:0] = fac; - w[fidx2?fidx2-1:0] = 1.0-fac; + w[fidx2?fidx2-1:0] = 1.0f-fac; } else { w[fidx1] = fac; - w[fidx2] = 1.0-fac; + w[fidx2] = 1.0f-fac; } #endif } @@ -2571,11 +2571,12 @@ static void project_paint_face_init(const ProjPaintState *ps, const int thread_i static void project_paint_bucket_bounds(const ProjPaintState *ps, const float min[2], const float max[2], int bucketMin[2], int bucketMax[2]) { /* divide by bucketWidth & bucketHeight so the bounds are offset in bucket grid units */ - bucketMin[0] = (int)(((float)(min[0] - ps->screenMin[0]) / ps->screen_width) * ps->buckets_x) + 0.5f; /* these offsets of 0.5 and 1.5 seem odd but they are correct */ - bucketMin[1] = (int)(((float)(min[1] - ps->screenMin[1]) / ps->screen_height) * ps->buckets_y) + 0.5f; + /* XXX: the offset of 0.5 is always truncated to zero and the offset of 1.5f is always truncated to 1, is this really correct?? - jwilkins */ + bucketMin[0] = (int)((int)(((float)(min[0] - ps->screenMin[0]) / ps->screen_width) * ps->buckets_x) + 0.5f); /* these offsets of 0.5 and 1.5 seem odd but they are correct */ + bucketMin[1] = (int)((int)(((float)(min[1] - ps->screenMin[1]) / ps->screen_height) * ps->buckets_y) + 0.5f); - bucketMax[0] = (int)(((float)(max[0] - ps->screenMin[0]) / ps->screen_width) * ps->buckets_x) + 1.5f; - bucketMax[1] = (int)(((float)(max[1] - ps->screenMin[1]) / ps->screen_height) * ps->buckets_y) + 1.5f; + bucketMax[0] = (int)((int)(((float)(max[0] - ps->screenMin[0]) / ps->screen_width) * ps->buckets_x) + 1.5f); + bucketMax[1] = (int)((int)(((float)(max[1] - ps->screenMin[1]) / ps->screen_height) * ps->buckets_y) + 1.5f); /* incase the rect is outside the mesh 2d bounds */ CLAMP(bucketMin[0], 0, ps->buckets_x); @@ -3029,19 +3030,19 @@ static void project_paint_begin(ProjPaintState *ps) if(ps->source==PROJ_SRC_VIEW) { #ifdef PROJ_DEBUG_WINCLIP - CLAMP(ps->screenMin[0], -ps->brush->size, ps->winx + ps->brush->size); - CLAMP(ps->screenMax[0], -ps->brush->size, ps->winx + ps->brush->size); + CLAMP(ps->screenMin[0], (float)(-ps->brush->size), (float)(ps->winx + ps->brush->size)); + CLAMP(ps->screenMax[0], (float)(-ps->brush->size), (float)(ps->winx + ps->brush->size)); - CLAMP(ps->screenMin[1], -ps->brush->size, ps->winy + ps->brush->size); - CLAMP(ps->screenMax[1], -ps->brush->size, ps->winy + ps->brush->size); + CLAMP(ps->screenMin[1], (float)(-ps->brush->size), (float)(ps->winy + ps->brush->size)); + CLAMP(ps->screenMax[1], (float)(-ps->brush->size), (float)(ps->winy + ps->brush->size)); #endif } else { /* reprojection, use bounds */ ps->screenMin[0]= 0; - ps->screenMax[0]= ps->winx; + ps->screenMax[0]= (float)(ps->winx); ps->screenMin[1]= 0; - ps->screenMax[1]= ps->winy; + ps->screenMax[1]= (float)(ps->winy); } /* only for convenience */ @@ -3497,7 +3498,7 @@ static int project_bucket_iter_next(ProjPaintState *ps, int *bucket_index, rctf project_bucket_bounds(ps, ps->context_bucket_x, ps->context_bucket_y, bucket_bounds); if ( (ps->source != PROJ_SRC_VIEW) || - project_bucket_isect_circle(ps->context_bucket_x, ps->context_bucket_y, mval, ps->brush->size * ps->brush->size, bucket_bounds) + project_bucket_isect_circle(ps->context_bucket_x, ps->context_bucket_y, mval, (float)(ps->brush->size * ps->brush->size), bucket_bounds) ) { *bucket_index = ps->context_bucket_x + (ps->context_bucket_y * ps->buckets_x); ps->context_bucket_x++; @@ -3545,7 +3546,7 @@ static void blend_color_mix(unsigned char *cp, const unsigned char *cp1, const u static void blend_color_mix_float(float *cp, const float *cp1, const float *cp2, const float fac) { - const float mfac= 1.0-fac; + const float mfac= 1.0f-fac; cp[0]= mfac*cp1[0] + fac*cp2[0]; cp[1]= mfac*cp1[1] + fac*cp2[1]; cp[2]= mfac*cp1[2] + fac*cp2[2]; @@ -3712,7 +3713,7 @@ static void *do_projectpaint_thread(void *ph_v) } /* avoid a square root with every dist comparison */ - brush_size_sqared = ps->brush->size * ps->brush->size; + brush_size_sqared = (float)(ps->brush->size * ps->brush->size); /* printf("brush bounds %d %d %d %d\n", bucketMin[0], bucketMin[1], bucketMax[0], bucketMax[1]); */ @@ -3771,7 +3772,7 @@ static void *do_projectpaint_thread(void *ph_v) falloff = 1.0f - falloff; falloff = 1.0f - (falloff * falloff); - mask_short = projPixel->mask * (ps->brush->alpha * falloff); + mask_short = (unsigned short)(projPixel->mask * (ps->brush->alpha * falloff)); if (mask_short > projPixel->mask_max) { mask = ((float)mask_short)/65535.0f; projPixel->mask_max = mask_short; @@ -3932,8 +3933,8 @@ static int project_paint_sub_stroke(ProjPaintState *ps, BrushPainter *painter, i /* Use mouse coords as floats for projection painting */ float pos[2]; - pos[0] = mval_i[0]; - pos[1] = mval_i[1]; + pos[0] = (float)(mval_i[0]); + pos[1] = (float)(mval_i[1]); // we may want to use this later // brush_painter_require_imbuf(painter, ((ibuf->rect_float)? 1: 0), 0, 0); @@ -4057,7 +4058,8 @@ static int imapaint_ibuf_add_if(ImBuf *ibuf, unsigned int x, unsigned int y, flo { float inrgb[3]; - if ((x >= ibuf->x) || (y >= ibuf->y)) { + // XXX: signed unsigned mismatch + if ((x >= (unsigned int)(ibuf->x)) || (y >= (unsigned int)(ibuf->y))) { if (torus) imapaint_ibuf_get_set_rgb(ibuf, x, y, 1, 0, inrgb); else return 0; } @@ -4611,8 +4613,8 @@ static void project_state_init(bContext *C, Object *ob, ProjPaintState *ps) ps->normal_angle_inner= ps->normal_angle= settings->imapaint.normal_angle; } - ps->normal_angle_inner *= M_PI_2 / 90; - ps->normal_angle *= M_PI_2 / 90; + ps->normal_angle_inner *= (float)(M_PI_2 / 90); + ps->normal_angle *= (float)(M_PI_2 / 90); ps->normal_angle_range = ps->normal_angle - ps->normal_angle_inner; if(ps->normal_angle_range <= 0.0f) @@ -4711,8 +4713,8 @@ static void paint_apply(bContext *C, wmOperator *op, PointerRNA *itemptr) int mouse[2], redraw; RNA_float_get_array(itemptr, "mouse", mousef); - mouse[0] = mousef[0]; - mouse[1] = mousef[1]; + mouse[0] = (int)(mousef[0]); + mouse[1] = (int)(mousef[1]); time= RNA_float_get(itemptr, "time"); pressure= RNA_float_get(itemptr, "pressure"); @@ -4832,8 +4834,8 @@ static void paint_apply_event(bContext *C, wmOperator *op, wmEvent *event) /* fill in stroke */ RNA_collection_add(op->ptr, "stroke", &itemptr); - mousef[0] = mouse[0]; - mousef[1] = mouse[1]; + mousef[0] = (float)(mouse[0]); + mousef[1] = (float)(mouse[1]); RNA_float_set_array(&itemptr, "mouse", mousef); RNA_float_set(&itemptr, "time", (float)(time - pop->starttime)); RNA_float_set(&itemptr, "pressure", pressure); @@ -4950,7 +4952,7 @@ static void brush_drawcursor(bContext *C, int x, int y, void *customdata) glColor4ub(255, 255, 255, 128); glEnable( GL_LINE_SMOOTH ); glEnable(GL_BLEND); - glutil_draw_lined_arc(0.0, M_PI*2.0, brush->size*0.5f, 40); + glutil_draw_lined_arc(0, (float)(M_PI*2.0), brush->size*0.5f, 40); glDisable(GL_BLEND); glDisable( GL_LINE_SMOOTH ); @@ -4977,7 +4979,7 @@ static int paint_radial_control_invoke(bContext *C, wmOperator *op, wmEvent *eve ToolSettings *ts = CTX_data_scene(C)->toolsettings; get_imapaint_zoom(C, &zoom, &zoom); toggle_paint_cursor(C, !ts->imapaint.paintcursor); - brush_radial_control_invoke(op, paint_brush(&ts->imapaint.paint), 0.5 * zoom); + brush_radial_control_invoke(op, paint_brush(&ts->imapaint.paint), 0.5f * zoom); return WM_radial_control_invoke(C, op, event); } @@ -4997,7 +4999,7 @@ static int paint_radial_control_exec(bContext *C, wmOperator *op) int ret; char str[256]; get_imapaint_zoom(C, &zoom, &zoom); - ret = brush_radial_control_exec(op, brush, 2.0 / zoom); + ret = brush_radial_control_exec(op, brush, 2.0f / zoom); WM_radial_control_string(op, str, 256); WM_event_add_notifier(C, NC_BRUSH|NA_EDITED, brush); diff --git a/source/blender/editors/sculpt_paint/paint_intern.h b/source/blender/editors/sculpt_paint/paint_intern.h index b25eec70311..0ae7c7fac19 100644 --- a/source/blender/editors/sculpt_paint/paint_intern.h +++ b/source/blender/editors/sculpt_paint/paint_intern.h @@ -110,6 +110,13 @@ void PAINT_OT_face_select_all(struct wmOperatorType *ot); int facemask_paint_poll(struct bContext *C); +/* stroke operator */ +typedef enum wmBrushStrokeMode { + WM_BRUSHSTROKE_NORMAL, + WM_BRUSHSTROKE_INVERT, + WM_BRUSHSTROKE_SMOOTH, +} wmBrushStrokeMode; + /* paint_undo.c */ typedef void (*UndoRestoreCb)(struct bContext *C, struct ListBase *lb); typedef void (*UndoFreeCb)(struct ListBase *lb); diff --git a/source/blender/editors/sculpt_paint/paint_ops.c b/source/blender/editors/sculpt_paint/paint_ops.c index 5470ac83370..5d52b0a3272 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.c +++ b/source/blender/editors/sculpt_paint/paint_ops.c @@ -39,28 +39,25 @@ #include "sculpt_intern.h" #include <string.h> +//#include <stdio.h> /* Brush operators */ static int brush_add_exec(bContext *C, wmOperator *op) { /*int type = RNA_enum_get(op->ptr, "type");*/ - Brush *br = NULL; + Paint *paint = paint_get_active(CTX_data_scene(C)); + Brush *br = paint_brush(paint); - br = add_brush("Brush"); + if (br) + br = copy_brush(br); + else + br = add_brush("Brush"); - if(br) - paint_brush_set(paint_get_active(CTX_data_scene(C)), br); + paint_brush_set(paint_get_active(CTX_data_scene(C)), br); return OPERATOR_FINISHED; } -static EnumPropertyItem brush_type_items[] = { - {OB_MODE_SCULPT, "SCULPT", ICON_SCULPTMODE_HLT, "Sculpt", ""}, - {OB_MODE_VERTEX_PAINT, "VERTEX_PAINT", ICON_VPAINT_HLT, "Vertex Paint", ""}, - {OB_MODE_WEIGHT_PAINT, "WEIGHT_PAINT", ICON_WPAINT_HLT, "Weight Paint", ""}, - {OB_MODE_TEXTURE_PAINT, "TEXTURE_PAINT", ICON_TPAINT_HLT, "Texture Paint", ""}, - {0, NULL, 0, NULL, NULL}}; - void BRUSH_OT_add(wmOperatorType *ot) { /* identifiers */ @@ -72,9 +69,43 @@ void BRUSH_OT_add(wmOperatorType *ot) ot->exec= brush_add_exec; /* flags */ - ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; + ot->flag= OPTYPE_UNDO; +} + + +static int brush_scale_size_exec(bContext *C, wmOperator *op) +{ + /*int type = RNA_enum_get(op->ptr, "type");*/ + Paint *paint = paint_get_active(CTX_data_scene(C)); + Brush *br = paint_brush(paint); + float factor = RNA_float_get(op->ptr, "scalar"); + + if (br) { + if (U.sculpt_paint_settings & SCULPT_PAINT_USE_UNIFIED_SIZE) { + U.sculpt_paint_unified_size *= factor; + } + else { + br->size *= factor; + } + } + + return OPERATOR_FINISHED; +} + +void BRUSH_OT_scale_size(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Scale Sculpt/Paint Brush Size"; + ot->description= "Change brush size by a scalar"; + ot->idname= "BRUSH_OT_scale_size"; + + /* api callbacks */ + ot->exec= brush_scale_size_exec; + + /* flags */ + ot->flag= OPTYPE_UNDO; - RNA_def_enum(ot->srna, "type", brush_type_items, OB_MODE_VERTEX_PAINT, "Type", "Which paint mode to create the brush for."); + RNA_def_float(ot->srna, "scalar", 1, 0, 2, "Scalar", "Factor to scale brush size by", 0, 2); } static int vertex_color_set_exec(bContext *C, wmOperator *op) @@ -108,8 +139,10 @@ void ED_operatortypes_paint(void) { /* brush */ WM_operatortype_append(BRUSH_OT_add); + WM_operatortype_append(BRUSH_OT_scale_size); WM_operatortype_append(BRUSH_OT_curve_preset); + /* image */ WM_operatortype_append(PAINT_OT_texture_paint_toggle); WM_operatortype_append(PAINT_OT_texture_paint_radial_control); @@ -141,6 +174,7 @@ void ED_operatortypes_paint(void) WM_operatortype_append(PAINT_OT_face_select_all); } + static void ed_keymap_paint_brush_switch(wmKeyMap *keymap, const char *path) { wmKeyMapItem *kmi; @@ -180,15 +214,13 @@ static void ed_keymap_paint_brush_switch(wmKeyMap *keymap, const char *path) static void ed_keymap_paint_brush_size(wmKeyMap *keymap, const char *path) { wmKeyMapItem *kmi; - - kmi= WM_keymap_add_item(keymap, "WM_OT_context_scale_int", LEFTBRACKETKEY, KM_PRESS, 0, 0); - RNA_string_set(kmi->ptr, "data_path", path); - RNA_float_set(kmi->ptr, "value", 0.9); - - kmi= WM_keymap_add_item(keymap, "WM_OT_context_scale_int", RIGHTBRACKETKEY, KM_PRESS, 0, 0); - RNA_string_set(kmi->ptr, "data_path", path); - RNA_float_set(kmi->ptr, "value", 10.0/9.0); // 1.1111.... -} + + kmi= WM_keymap_add_item(keymap, "BRUSH_OT_scale_size", LEFTBRACKETKEY, KM_PRESS, 0, 0); + RNA_float_set(kmi->ptr, "scalar", 0.9); + + kmi= WM_keymap_add_item(keymap, "BRUSH_OT_scale_size", RIGHTBRACKETKEY, KM_PRESS, 0, 0); + RNA_float_set(kmi->ptr, "scalar", 10.0/9.0); // 1.1111.... +} void ED_keymap_paint(wmKeyConfig *keyconf) { @@ -200,12 +232,15 @@ void ED_keymap_paint(wmKeyConfig *keyconf) keymap= WM_keymap_find(keyconf, "Sculpt", 0, 0); keymap->poll= sculpt_poll; - RNA_enum_set(WM_keymap_add_item(keymap, "SCULPT_OT_radial_control", FKEY, KM_PRESS, 0, 0)->ptr, "mode", WM_RADIALCONTROL_SIZE); + RNA_enum_set(WM_keymap_add_item(keymap, "SCULPT_OT_radial_control", FKEY, KM_PRESS, 0, 0)->ptr, "mode", WM_RADIALCONTROL_SIZE); RNA_enum_set(WM_keymap_add_item(keymap, "SCULPT_OT_radial_control", FKEY, KM_PRESS, KM_SHIFT, 0)->ptr, "mode", WM_RADIALCONTROL_STRENGTH); - RNA_enum_set(WM_keymap_add_item(keymap, "SCULPT_OT_radial_control", FKEY, KM_PRESS, KM_CTRL, 0)->ptr, "mode", WM_RADIALCONTROL_ANGLE); + RNA_enum_set(WM_keymap_add_item(keymap, "SCULPT_OT_radial_control", FKEY, KM_PRESS, KM_CTRL, 0)->ptr, "mode", WM_RADIALCONTROL_ANGLE); - WM_keymap_add_item(keymap, "SCULPT_OT_brush_stroke", LEFTMOUSE, KM_PRESS, 0, 0); - WM_keymap_add_item(keymap, "SCULPT_OT_brush_stroke", LEFTMOUSE, KM_PRESS, KM_SHIFT, 0); + RNA_enum_set(WM_keymap_add_item(keymap, "SCULPT_OT_brush_stroke", LEFTMOUSE, KM_PRESS, 0, 0)->ptr, "mode", WM_BRUSHSTROKE_NORMAL); + RNA_enum_set(WM_keymap_add_item(keymap, "SCULPT_OT_brush_stroke", LEFTMOUSE, KM_PRESS, KM_CTRL, 0)->ptr, "mode", WM_BRUSHSTROKE_INVERT); + RNA_enum_set(WM_keymap_add_item(keymap, "SCULPT_OT_brush_stroke", LEFTMOUSE, KM_PRESS, KM_SHIFT, 0)->ptr, "mode", WM_BRUSHSTROKE_SMOOTH); + + //stroke_mode_modal_keymap(keyconf); for(i=0; i<=5; i++) RNA_int_set(WM_keymap_add_item(keymap, "OBJECT_OT_subdivision_set", ZEROKEY+i, KM_PRESS, KM_CTRL, 0)->ptr, "level", i); @@ -221,7 +256,8 @@ void ED_keymap_paint(wmKeyConfig *keyconf) ed_keymap_paint_brush_switch(keymap, "tool_settings.sculpt.active_brush_index"); ed_keymap_paint_brush_size(keymap, "tool_settings.sculpt.brush.size"); - + + /* */ kmi = WM_keymap_add_item(keymap, "WM_OT_context_toggle", AKEY, KM_PRESS, 0, 0); RNA_string_set(kmi->ptr, "data_path", "tool_settings.sculpt.brush.use_anchor"); @@ -254,11 +290,10 @@ void ED_keymap_paint(wmKeyConfig *keyconf) kmi = WM_keymap_add_item(keymap, "WM_OT_context_set_enum", LKEY, KM_PRESS, 0, 0); RNA_string_set(kmi->ptr, "data_path", "tool_settings.sculpt.active_brush_name"); RNA_string_set(kmi->ptr, "value", "Layer"); - + kmi = WM_keymap_add_item(keymap, "WM_OT_context_set_enum", TKEY, KM_PRESS, KM_SHIFT, 0); // was just T in 2.4x RNA_string_set(kmi->ptr, "data_path", "tool_settings.sculpt.active_brush_name"); RNA_string_set(kmi->ptr, "value", "Flatten"); - /* Vertex Paint mode */ keymap= WM_keymap_find(keyconf, "Vertex Paint", 0, 0); diff --git a/source/blender/editors/sculpt_paint/paint_stroke.c b/source/blender/editors/sculpt_paint/paint_stroke.c index 441464c5743..e373a254c3e 100644 --- a/source/blender/editors/sculpt_paint/paint_stroke.c +++ b/source/blender/editors/sculpt_paint/paint_stroke.c @@ -20,7 +20,7 @@ * * The Original Code is: all of this file. * - * Contributor(s): none yet. + * Contributor(s): Jason Wilkins, Tom Musgrove. * * ***** END GPL LICENSE BLOCK ***** * @@ -35,6 +35,7 @@ #include "BKE_context.h" #include "BKE_paint.h" +#include "BKE_brush.h" #include "WM_api.h" #include "WM_types.h" @@ -49,6 +50,9 @@ #include "ED_view3d.h" #include "paint_intern.h" +#include "sculpt_intern.h" // XXX, for expedience in getting this working, refactor later (or this just shows that this needs unification) + +#include "BKE_image.h" #include <float.h> #include <math.h> @@ -96,54 +100,748 @@ static void paint_draw_smooth_stroke(bContext *C, int x, int y, void *customdata glDisable(GL_LINE_SMOOTH); } -static void paint_draw_cursor(bContext *C, int x, int y, void *customdata) +#if 0 + +// grid texture for testing + +#define GRID_WIDTH 8 +#define GRID_LENGTH 8 + +#define W (0xFFFFFFFF) +#define G (0x00888888) +#define E (0xE1E1E1E1) +#define C (0xC3C3C3C3) +#define O (0xB4B4B4B4) +#define Q (0xA9A9A9A9) + +static unsigned grid_texture0[256] = +{ + W,W,W,W,W,W,W,W,W,W,W,W,W,W,W,W, + W,G,G,G,G,G,G,G,G,G,G,G,G,G,G,W, + W,G,G,G,G,G,G,G,G,G,G,G,G,G,G,W, + W,G,G,G,G,G,G,G,G,G,G,G,G,G,G,W, + W,G,G,G,G,G,G,G,G,G,G,G,G,G,G,W, + W,G,G,G,G,G,G,G,G,G,G,G,G,G,G,W, + W,G,G,G,G,G,G,G,G,G,G,G,G,G,G,W, + W,G,G,G,G,G,G,G,G,G,G,G,G,G,G,W, + W,G,G,G,G,G,G,G,G,G,G,G,G,G,G,W, + W,G,G,G,G,G,G,G,G,G,G,G,G,G,G,W, + W,G,G,G,G,G,G,G,G,G,G,G,G,G,G,W, + W,G,G,G,G,G,G,G,G,G,G,G,G,G,G,W, + W,G,G,G,G,G,G,G,G,G,G,G,G,G,G,W, + W,G,G,G,G,G,G,G,G,G,G,G,G,G,G,W, + W,G,G,G,G,G,G,G,G,G,G,G,G,G,G,W, + W,W,W,W,W,W,W,W,W,W,W,W,W,W,W,W, +}; + +static unsigned grid_texture1[64] = { - Paint *paint = paint_get_active(CTX_data_scene(C)); - Brush *brush = paint_brush(paint); + C,C,C,C,C,C,C,C, + C,G,G,G,G,G,G,C, + C,G,G,G,G,G,G,C, + C,G,G,G,G,G,G,C, + C,G,G,G,G,G,G,C, + C,G,G,G,G,G,G,C, + C,G,G,G,G,G,G,C, + C,C,C,C,C,C,C,C, +}; + +static unsigned grid_texture2[16] = +{ + O,O,O,O, + O,G,G,O, + O,G,G,O, + O,O,O,O, +}; - if(!(paint->flags & PAINT_SHOW_BRUSH)) - return; +static unsigned grid_texture3[4] = +{ + Q,Q, + Q,Q, +}; - glColor4ubv(paint_get_active(CTX_data_scene(C))->paint_cursor_col); - glEnable(GL_LINE_SMOOTH); - glEnable(GL_BLEND); +static unsigned grid_texture4[1] = +{ + Q, +}; - glTranslatef((float)x, (float)y, 0.0f); - glutil_draw_lined_arc(0.0, M_PI*2.0, brush->size, 40); - glTranslatef((float)-x, (float)-y, 0.0f); +#undef W +#undef G +#undef E +#undef C +#undef O +#undef Q - glDisable(GL_BLEND); - glDisable(GL_LINE_SMOOTH); +static void load_grid() +{ + static GLuint overlay_texture; + + if (!overlay_texture) { + //GLfloat largest_supported_anisotropy; + + glGenTextures(1, &overlay_texture); + glBindTexture(GL_TEXTURE_2D, overlay_texture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, grid_texture0); + glTexImage2D(GL_TEXTURE_2D, 1, GL_RGB, 8, 8, 0, GL_RGBA, GL_UNSIGNED_BYTE, grid_texture1); + glTexImage2D(GL_TEXTURE_2D, 2, GL_RGB, 4, 4, 0, GL_RGBA, GL_UNSIGNED_BYTE, grid_texture2); + glTexImage2D(GL_TEXTURE_2D, 3, GL_RGB, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, grid_texture3); + glTexImage2D(GL_TEXTURE_2D, 4, GL_RGB, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, grid_texture4); + glEnable(GL_TEXTURE_2D); + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_LINEAR); + + //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); + //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); + + //glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &largest_supported_anisotropy); + //glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, largest_supported_anisotropy); + } +} + +#endif + +extern float get_tex_pixel(Brush* br, float u, float v); + +typedef struct Snapshot { + float size[3]; + float ofs[3]; + float rot; + int brush_size; + int winx; + int winy; + int brush_map_mode; + int curve_changed_timestamp; +} Snapshot; + +static int same_snap(Snapshot* snap, Brush* brush, ViewContext* vc) +{ + MTex* mtex = &brush->mtex; + + return + (mtex->tex && + mtex->ofs[0] == snap->ofs[0] && + mtex->ofs[1] == snap->ofs[1] && + mtex->ofs[2] == snap->ofs[2] && + mtex->size[0] == snap->size[0] && + mtex->size[1] == snap->size[1] && + mtex->size[2] == snap->size[2] && + mtex->rot == snap->rot) && + ((mtex->brush_map_mode == MTEX_MAP_MODE_FIXED && sculpt_get_brush_size(brush) <= snap->brush_size) || (sculpt_get_brush_size(brush) == snap->brush_size)) && // make brush smaller shouldn't cause a resample + mtex->brush_map_mode == snap->brush_map_mode && + vc->ar->winx == snap->winx && + vc->ar->winy == snap->winy; +} + +static void make_snap(Snapshot* snap, Brush* brush, ViewContext* vc) +{ + if (brush->mtex.tex) { + snap->brush_map_mode = brush->mtex.brush_map_mode; + copy_v3_v3(snap->ofs, brush->mtex.ofs); + copy_v3_v3(snap->size, brush->mtex.size); + snap->rot = brush->mtex.rot; + } + else { + snap->brush_map_mode = -1; + snap->ofs[0]= snap->ofs[0]= snap->ofs[0]= -1; + snap->size[0]= snap->size[0]= snap->size[0]= -1; + snap->rot = -1; + } + + snap->brush_size = sculpt_get_brush_size(brush); + snap->winx = vc->ar->winx; + snap->winy = vc->ar->winy; +} + +int load_tex(Sculpt *sd, Brush* br, ViewContext* vc) +{ + static GLuint overlay_texture = 0; + static int init = 0; + static int tex_changed_timestamp = -1; + static int curve_changed_timestamp = -1; + static Snapshot snap; + static int old_size = -1; + + GLubyte* buffer = 0; + + int size; + int j; + int refresh; + + if (br->mtex.brush_map_mode == MTEX_MAP_MODE_TILED && !br->mtex.tex) return 0; + + refresh = + !overlay_texture || + (br->mtex.tex && + (!br->mtex.tex->preview || + br->mtex.tex->preview->changed_timestamp[0] != tex_changed_timestamp)) || + !br->curve || + br->curve->changed_timestamp != curve_changed_timestamp || + !same_snap(&snap, br, vc); + + if (refresh) { + if (br->mtex.tex && br->mtex.tex->preview) + tex_changed_timestamp = br->mtex.tex->preview->changed_timestamp[0]; + + if (br->curve) + curve_changed_timestamp = br->curve->changed_timestamp; + + make_snap(&snap, br, vc); + + if (br->mtex.brush_map_mode == MTEX_MAP_MODE_FIXED) { + int s = sculpt_get_brush_size(br); + int r = 1; + + for (s >>= 1; s > 0; s >>= 1) + r++; + + size = (1<<r); + + if (size < 256) + size = 256; + + if (size < old_size) + size = old_size; + } + else + size = 512; + + if (old_size != size) { + if (overlay_texture) { + glDeleteTextures(1, &overlay_texture); + overlay_texture = 0; + } + + init = 0; + + old_size = size; + } + + buffer = MEM_mallocN(sizeof(GLubyte)*size*size, "load_tex"); + + #pragma omp parallel for schedule(static) if (sd->flags & SCULPT_USE_OPENMP) + for (j= 0; j < size; j++) { + int i; + float y; + float len; + + for (i= 0; i < size; i++) { + + // largely duplicated from tex_strength + + const float rotation = -br->mtex.rot; + float diameter = sculpt_get_brush_size(br); + int index = j*size + i; + float x; + float avg; + + x = (float)i/size; + y = (float)j/size; + + x -= 0.5f; + y -= 0.5f; + + if (br->mtex.brush_map_mode == MTEX_MAP_MODE_TILED) { + x *= vc->ar->winx / diameter; + y *= vc->ar->winy / diameter; + } + else { + x *= 2; + y *= 2; + } + + len = sqrtf(x*x + y*y); + + if ((br->mtex.brush_map_mode == MTEX_MAP_MODE_TILED) || len <= 1) { + /* it is probably worth optimizing for those cases where + the texture is not rotated by skipping the calls to + atan2, sqrtf, sin, and cos. */ + if (br->mtex.tex && (rotation > 0.001 || rotation < -0.001)) { + const float angle = atan2(y, x) + rotation; + + x = len * cos(angle); + y = len * sin(angle); + } + + x *= br->mtex.size[0]; + y *= br->mtex.size[1]; + + x += br->mtex.ofs[0]; + y += br->mtex.ofs[1]; + + avg = br->mtex.tex ? get_tex_pixel(br, x, y) : 1; + + avg += br->texture_sample_bias; + + if (br->mtex.brush_map_mode == MTEX_MAP_MODE_FIXED) + avg *= brush_curve_strength(br, len, 1); /* Falloff curve */ + + buffer[index] = (GLubyte)(255*avg); + } + else { + buffer[index] = 0; + } + } + } + + if (!overlay_texture) + glGenTextures(1, &overlay_texture); + } + else { + size= old_size; + } + + glBindTexture(GL_TEXTURE_2D, overlay_texture); + + if (refresh) { + if (!init) { + glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, size, size, 0, GL_ALPHA, GL_UNSIGNED_BYTE, buffer); + init = 1; + } + else { + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, size, size, GL_ALPHA, GL_UNSIGNED_BYTE, buffer); + } + + if (buffer) + MEM_freeN(buffer); + } + + glEnable(GL_TEXTURE_2D); + + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + if (br->mtex.brush_map_mode == MTEX_MAP_MODE_FIXED) { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); + } + + return 1; +} + +/* Convert a point in model coordinates to 2D screen coordinates. */ +// XXX duplicated from sculpt.c, deal with this later. +static void projectf(bglMats *mats, const float v[3], float p[2]) +{ + double ux, uy, uz; + + gluProject(v[0],v[1],v[2], mats->modelview, mats->projection, + (GLint *)mats->viewport, &ux, &uy, &uz); + p[0]= ux; + p[1]= uy; +} + +static int project_brush_radius(RegionView3D* rv3d, float radius, float location[3], bglMats* mats) +{ + float view[3], nonortho[3], ortho[3], offset[3], p1[2], p2[2]; + + viewvector(rv3d, location, view); + + // create a vector that is not orthogonal to view + + if (fabsf(view[0]) < 0.1) { + nonortho[0] = view[0] + 1; + nonortho[1] = view[1]; + nonortho[2] = view[2]; + } + else if (fabsf(view[1]) < 0.1) { + nonortho[0] = view[0]; + nonortho[1] = view[1] + 1; + nonortho[2] = view[2]; + } + else { + nonortho[0] = view[0]; + nonortho[1] = view[1]; + nonortho[2] = view[2] + 1; + } + + // get a vector in the plane of the view + cross_v3_v3v3(ortho, nonortho, view); + normalize_v3(ortho); + + // make a point on the surface of the brush tagent to the view + mul_v3_fl(ortho, radius); + add_v3_v3v3(offset, location, ortho); + + // project the center of the brush, and the tagent point to the view onto the screen + projectf(mats, location, p1); + projectf(mats, offset, p2); + + // the distance between these points is the size of the projected brush in pixels + return len_v2v2(p1, p2); +} + +int sculpt_get_brush_geometry(bContext* C, int x, int y, int* pixel_radius, float location[3], float modelview[16], float projection[16], int viewport[4]) +{ + struct PaintStroke *stroke; + float window[2]; + int hit; + + stroke = paint_stroke_new(C, NULL, NULL, NULL, NULL); + + window[0] = x + stroke->vc.ar->winrct.xmin; + window[1] = y + stroke->vc.ar->winrct.ymin; + + memcpy(modelview, stroke->vc.rv3d->viewmat, sizeof(float[16])); + memcpy(projection, stroke->vc.rv3d->winmat, sizeof(float[16])); + memcpy(viewport, stroke->mats.viewport, sizeof(int[4])); + + if (stroke->vc.obact->sculpt && stroke->vc.obact->sculpt->pbvh && sculpt_stroke_get_location(C, stroke, location, window)) { + *pixel_radius = project_brush_radius(stroke->vc.rv3d, sculpt_get_brush_unprojected_radius(stroke->brush), location, &stroke->mats); + + if (*pixel_radius == 0) + *pixel_radius = sculpt_get_brush_size(stroke->brush); + + mul_m4_v3(stroke->vc.obact->sculpt->ob->obmat, location); + + hit = 1; + } + else { + Sculpt* sd = CTX_data_tool_settings(C)->sculpt; + Brush* brush = paint_brush(&sd->paint); + + *pixel_radius = sculpt_get_brush_size(brush); + hit = 0; + } + + paint_stroke_free(stroke); + + return hit; +} + +// XXX duplicated from sculpt.c +float unproject_brush_radius(Object *ob, ViewContext *vc, float center[3], float offset) +{ + float delta[3], scale, loc[3]; + + mul_v3_m4v3(loc, ob->obmat, center); + + initgrabz(vc->rv3d, loc[0], loc[1], loc[2]); + window_to_3d_delta(vc->ar, delta, offset, 0); + + scale= fabsf(mat4_to_scale(ob->obmat)); + scale= (scale == 0.0f)? 1.0f: scale; + + return len_v3(delta)/scale; +} + +// XXX paint cursor now does a lot of the same work that is needed during a sculpt stroke +// problem: all this stuff was not intended to be used at this point, so things feel a +// bit hacked. I've put lots of stuff in Brush that probably better goes in Paint +// Functions should be refactored so that they can be used between sculpt.c and +// paint_stroke.c clearly and optimally and the lines of communication between the +// two modules should be more clearly defined. +static void paint_draw_cursor(bContext *C, int x, int y, void *unused) +{ + ViewContext vc; + + (void)unused; + + view3d_set_viewcontext(C, &vc); + + if (vc.obact->sculpt) { + Paint *paint = paint_get_active(CTX_data_scene(C)); + Sculpt *sd = CTX_data_tool_settings(C)->sculpt; + Brush *brush = paint_brush(paint); + + int pixel_radius, viewport[4]; + float location[3], modelview[16], projection[16]; + + int hit; + + int flip; + int sign; + + float* col; + float alpha; + + float visual_strength = sculpt_get_brush_alpha(brush)*sculpt_get_brush_alpha(brush); + + const float min_alpha = 0.20f; + const float max_alpha = 0.80f; + + { + const float u = 0.5f; + const float v = 1 - u; + const float r = 20; + + const float dx = sd->last_x - x; + const float dy = sd->last_y - y; + + if (dx*dx + dy*dy >= r*r) { + sd->last_angle = atan2(dx, dy); + + sd->last_x = u*sd->last_x + v*x; + sd->last_y = u*sd->last_y + v*y; + } + } + + if(!sculpt_get_lock_brush_size(brush) && !(paint->flags & PAINT_SHOW_BRUSH)) + return; + + hit = sculpt_get_brush_geometry(C, x, y, &pixel_radius, location, modelview, projection, viewport); + + if (sculpt_get_lock_brush_size(brush)) + sculpt_set_brush_size(brush, pixel_radius); + + // XXX: no way currently to know state of pen flip or invert key modifier without starting a stroke + flip = 1; + + sign = flip * ((brush->flag & BRUSH_DIR_IN)? -1 : 1); + + if (sign < 0 && ELEM4(brush->sculpt_tool, SCULPT_TOOL_DRAW, SCULPT_TOOL_INFLATE, SCULPT_TOOL_CLAY, SCULPT_TOOL_PINCH)) + col = brush->sub_col; + else + col = brush->add_col; + + alpha = (paint->flags & PAINT_SHOW_BRUSH_ON_SURFACE) ? min_alpha + (visual_strength*(max_alpha-min_alpha)) : 0.50f; + + if (ELEM(brush->mtex.brush_map_mode, MTEX_MAP_MODE_FIXED, MTEX_MAP_MODE_TILED) && brush->flag & BRUSH_TEXTURE_OVERLAY) { + glPushAttrib( + GL_COLOR_BUFFER_BIT| + GL_CURRENT_BIT| + GL_DEPTH_BUFFER_BIT| + GL_ENABLE_BIT| + GL_LINE_BIT| + GL_POLYGON_BIT| + GL_STENCIL_BUFFER_BIT| + GL_TRANSFORM_BIT| + GL_VIEWPORT_BIT| + GL_TEXTURE_BIT); + + if (load_tex(sd, brush, &vc)) { + glEnable(GL_BLEND); + + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + glDepthMask(GL_FALSE); + glDepthFunc(GL_ALWAYS); + + glMatrixMode(GL_TEXTURE); + glPushMatrix(); + glLoadIdentity(); + + if (brush->mtex.brush_map_mode == MTEX_MAP_MODE_FIXED) { + glTranslatef(0.5f, 0.5f, 0); + + if (brush->flag & BRUSH_RAKE) { + glRotatef(sd->last_angle*(float)(180.0/M_PI), 0, 0, 1); + } + else { + glRotatef(sd->special_rotation*(float)(180.0/M_PI), 0, 0, 1); + } + + glTranslatef(-0.5f, -0.5f, 0); + + if (sd->draw_pressure && (brush->flag & BRUSH_SIZE_PRESSURE)) { + glTranslatef(0.5f, 0.5f, 0); + glScalef(1.0f/sd->pressure_value, 1.0f/sd->pressure_value, 1); + glTranslatef(-0.5f, -0.5f, 0); + } + } + + glColor4f( + U.sculpt_paint_overlay_col[0], + U.sculpt_paint_overlay_col[1], + U.sculpt_paint_overlay_col[2], + brush->texture_overlay_alpha / 100.0f); + + glBegin(GL_QUADS); + if (brush->mtex.brush_map_mode == MTEX_MAP_MODE_FIXED) { + if (sd->draw_anchored) { + glTexCoord2f(0, 0); + glVertex2f(sd->anchored_initial_mouse[0]-sd->anchored_size - vc.ar->winrct.xmin, sd->anchored_initial_mouse[1]-sd->anchored_size - vc.ar->winrct.ymin); + + glTexCoord2f(1, 0); + glVertex2f(sd->anchored_initial_mouse[0]+sd->anchored_size - vc.ar->winrct.xmin, sd->anchored_initial_mouse[1]-sd->anchored_size - vc.ar->winrct.ymin); + + glTexCoord2f(1, 1); + glVertex2f(sd->anchored_initial_mouse[0]+sd->anchored_size - vc.ar->winrct.xmin, sd->anchored_initial_mouse[1]+sd->anchored_size - vc.ar->winrct.ymin); + + glTexCoord2f(0, 1); + glVertex2f(sd->anchored_initial_mouse[0]-sd->anchored_size - vc.ar->winrct.xmin, sd->anchored_initial_mouse[1]+sd->anchored_size - vc.ar->winrct.ymin); + } + else { + glTexCoord2f(0, 0); + glVertex2f((float)x-sculpt_get_brush_size(brush), (float)y-sculpt_get_brush_size(brush)); + + glTexCoord2f(1, 0); + glVertex2f((float)x+sculpt_get_brush_size(brush), (float)y-sculpt_get_brush_size(brush)); + + glTexCoord2f(1, 1); + glVertex2f((float)x+sculpt_get_brush_size(brush), (float)y+sculpt_get_brush_size(brush)); + + glTexCoord2f(0, 1); + glVertex2f((float)x-sculpt_get_brush_size(brush), (float)y+sculpt_get_brush_size(brush)); + } + } + else { + glTexCoord2f(0, 0); + glVertex2f(0, 0); + + glTexCoord2f(1, 0); + glVertex2f(viewport[2], 0); + + glTexCoord2f(1, 1); + glVertex2f(viewport[2], viewport[3]); + + glTexCoord2f(0, 1); + glVertex2f(0, viewport[3]); + } + glEnd(); + + glPopMatrix(); + } + + glPopAttrib(); + } + + if (hit) { + float unprojected_radius; + + // XXX duplicated from brush_strength & paint_stroke_add_step, refactor later + //wmEvent* event = CTX_wm_window(C)->eventstate; + + if (sd->draw_pressure && (brush->flag & BRUSH_ALPHA_PRESSURE)) + visual_strength *= sd->pressure_value; + + // don't show effect of strength past the soft limit + if (visual_strength > 1) visual_strength = 1; + + if (sd->draw_anchored) { + unprojected_radius = unproject_brush_radius(CTX_data_active_object(C), &vc, location, sd->anchored_size); + } + else { + if (brush->flag & BRUSH_ANCHORED) + unprojected_radius = unproject_brush_radius(CTX_data_active_object(C), &vc, location, 8); + else + unprojected_radius = unproject_brush_radius(CTX_data_active_object(C), &vc, location, sculpt_get_brush_size(brush)); + } + + if (sd->draw_pressure && (brush->flag & BRUSH_SIZE_PRESSURE)) + unprojected_radius *= sd->pressure_value; + + if (!sculpt_get_lock_brush_size(brush)) + sculpt_set_brush_unprojected_radius(brush, unprojected_radius); + + if(!(paint->flags & PAINT_SHOW_BRUSH)) + return; + + } + + glPushAttrib( + GL_COLOR_BUFFER_BIT| + GL_CURRENT_BIT| + GL_DEPTH_BUFFER_BIT| + GL_ENABLE_BIT| + GL_LINE_BIT| + GL_POLYGON_BIT| + GL_STENCIL_BUFFER_BIT| + GL_TRANSFORM_BIT| + GL_VIEWPORT_BIT| + GL_TEXTURE_BIT); + + glColor4f(col[0], col[1], col[2], alpha); + + glEnable(GL_BLEND); + + glEnable(GL_LINE_SMOOTH); + + if (sd->draw_anchored) { + glTranslatef(sd->anchored_initial_mouse[0] - vc.ar->winrct.xmin, sd->anchored_initial_mouse[1] - vc.ar->winrct.ymin, 0.0f); + glutil_draw_lined_arc(0.0, M_PI*2.0, sd->anchored_size, 40); + glTranslatef(-sd->anchored_initial_mouse[0] + vc.ar->winrct.xmin, -sd->anchored_initial_mouse[1] + vc.ar->winrct.xmin, 0.0f); + } + else { + glTranslatef((float)x, (float)y, 0.0f); + glutil_draw_lined_arc(0.0, M_PI*2.0, sculpt_get_brush_size(brush), 40); + glTranslatef(-(float)x, -(float)y, 0.0f); + } + + glPopAttrib(); + } + else { + Paint *paint = paint_get_active(CTX_data_scene(C)); + Brush *brush = paint_brush(paint); + + if(!(paint->flags & PAINT_SHOW_BRUSH)) + return; + + glColor4ubv(paint_get_active(CTX_data_scene(C))->paint_cursor_col); + glEnable(GL_LINE_SMOOTH); + glEnable(GL_BLEND); + + glTranslatef((float)x, (float)y, 0.0f); + glutil_draw_lined_arc(0.0, M_PI*2.0, brush->size, 40); // XXX: for now use the brushes size instead of potentially using the unified size because the feature has been enabled for sculpt + glTranslatef((float)-x, (float)-y, 0.0f); + + glDisable(GL_BLEND); + glDisable(GL_LINE_SMOOTH); + } } /* Put the location of the next stroke dot into the stroke RNA and apply it to the mesh */ -static void paint_brush_stroke_add_step(bContext *C, wmOperator *op, wmEvent *event, float mouse[2]) +static void paint_brush_stroke_add_step(bContext *C, wmOperator *op, wmEvent *event, float mouse_in[2]) { + Paint *paint = paint_get_active(CTX_data_scene(C)); // XXX + Brush *brush = paint_brush(paint); // XXX + + float mouse[2]; + PointerRNA itemptr; - float pressure = 1; - float center[3] = {0, 0, 0}; - int flip= event->shift?1:0; + + float location[3]; + + float pressure; + int pen_flip; + + ViewContext vc; // XXX + PaintStroke *stroke = op->customdata; - /* XXX: can remove the if statement once all modes have this */ - if(stroke->get_location) - stroke->get_location(C, stroke, center, mouse); + view3d_set_viewcontext(C, &vc); // XXX /* Tablet */ if(event->custom == EVT_DATA_TABLET) { wmTabletData *wmtab= event->customdata; - if(wmtab->Active != EVT_TABLET_NONE) - pressure= wmtab->Pressure; - if(wmtab->Active == EVT_TABLET_ERASER) - flip = 1; + + pressure = (wmtab->Active != EVT_TABLET_NONE) ? wmtab->Pressure : 1; + pen_flip = (wmtab->Active == EVT_TABLET_ERASER); + } + else { + pressure = 1; + pen_flip = 0; } - + + // XXX: temporary check for sculpt mode until things are more unified + if (vc.obact->sculpt) { + float delta[3]; + + brush_jitter_pos(brush, mouse_in, mouse); + + // XXX: meh, this is round about because brush_jitter_pos isn't written in the best way to be reused here + if (brush->flag & BRUSH_JITTER_PRESSURE) { + sub_v3_v3v3(delta, mouse, mouse_in); + mul_v3_fl(delta, pressure); + add_v3_v3v3(mouse, mouse_in, delta); + } + } + else + copy_v3_v3(mouse, mouse_in); + + /* XXX: can remove the if statement once all modes have this */ + if(stroke->get_location) + stroke->get_location(C, stroke, location, mouse); + else + zero_v3(location); + /* Add to stroke */ RNA_collection_add(op->ptr, "stroke", &itemptr); - RNA_float_set_array(&itemptr, "location", center); - RNA_float_set_array(&itemptr, "mouse", mouse); - RNA_boolean_set(&itemptr, "flip", flip); - RNA_float_set(&itemptr, "pressure", pressure); + + RNA_float_set_array(&itemptr, "location", location); + RNA_float_set_array(&itemptr, "mouse", mouse); + RNA_boolean_set (&itemptr, "pen_flip", pen_flip); + RNA_float_set (&itemptr, "pressure", pressure); stroke->last_mouse_position[0] = mouse[0]; stroke->last_mouse_position[1] = mouse[1]; @@ -154,10 +852,14 @@ static void paint_brush_stroke_add_step(bContext *C, wmOperator *op, wmEvent *ev /* Returns zero if no sculpt changes should be made, non-zero otherwise */ static int paint_smooth_stroke(PaintStroke *stroke, float output[2], wmEvent *event) { - output[0] = event->x; + output[0] = event->x; output[1] = event->y; - if(stroke->brush->flag & BRUSH_SMOOTH_STROKE && stroke->brush->sculpt_tool != SCULPT_TOOL_GRAB) { + if ((stroke->brush->flag & BRUSH_SMOOTH_STROKE) && + !ELEM4(stroke->brush->sculpt_tool, SCULPT_TOOL_GRAB, SCULPT_TOOL_THUMB, SCULPT_TOOL_ROTATE, SCULPT_TOOL_SNAKE_HOOK) && + !(stroke->brush->flag & BRUSH_ANCHORED) && + !(stroke->brush->flag & BRUSH_RESTORE_MESH)) + { float u = stroke->brush->smooth_stroke_factor, v = 1.0 - u; float dx = stroke->last_mouse_position[0] - event->x, dy = stroke->last_mouse_position[1] - event->y; @@ -176,7 +878,9 @@ static int paint_smooth_stroke(PaintStroke *stroke, float output[2], wmEvent *ev /* Returns zero if the stroke dots should not be spaced, non-zero otherwise */ static int paint_space_stroke_enabled(Brush *br) { - return (br->flag & BRUSH_SPACE) && !(br->flag & BRUSH_ANCHORED) && (br->sculpt_tool != SCULPT_TOOL_GRAB); + return (br->flag & BRUSH_SPACE) && + !(br->flag & BRUSH_ANCHORED) && + !ELEM4(br->sculpt_tool, SCULPT_TOOL_GRAB, SCULPT_TOOL_THUMB, SCULPT_TOOL_ROTATE, SCULPT_TOOL_SNAKE_HOOK); } /* For brushes with stroke spacing enabled, moves mouse in steps @@ -187,23 +891,34 @@ static int paint_space_stroke(bContext *C, wmOperator *op, wmEvent *event, const int cnt = 0; if(paint_space_stroke_enabled(stroke->brush)) { - float mouse[2] = {stroke->last_mouse_position[0], stroke->last_mouse_position[1]}; - float vec[2] = {final_mouse[0] - mouse[0], final_mouse[1] - mouse[1]}; + float mouse[2]; + float vec[2]; float length, scale; - int steps = 0, i; - /* Normalize the vector between the last stroke dot and the goal */ - length = sqrt(vec[0]*vec[0] + vec[1]*vec[1]); + copy_v2_v2(mouse, stroke->last_mouse_position); + sub_v2_v2v2(vec, final_mouse, mouse); + + length = len_v2(vec); if(length > FLT_EPSILON) { - scale = stroke->brush->spacing / length; - vec[0] *= scale; - vec[1] *= scale; + int steps; + int i; + float pressure = 1; + + // XXX duplicate code + if(event->custom == EVT_DATA_TABLET) { + wmTabletData *wmtab= event->customdata; + if(wmtab->Active != EVT_TABLET_NONE) + pressure = stroke->brush->flag & BRUSH_SIZE_PRESSURE ? wmtab->Pressure : 1; + } + + scale = (sculpt_get_brush_size(stroke->brush)*pressure*stroke->brush->spacing/50.0f) / length; + mul_v2_fl(vec, scale); + + steps = (int)(1.0f / scale); - steps = (int)(length / stroke->brush->spacing); for(i = 0; i < steps; ++i, ++cnt) { - mouse[0] += vec[0]; - mouse[1] += vec[1]; + add_v2_v2(mouse, vec); paint_brush_stroke_add_step(C, op, event, mouse); } } @@ -283,14 +998,25 @@ int paint_stroke_modal(bContext *C, wmOperator *op, wmEvent *event) //ED_region_tag_redraw(ar); } } - else + else { paint_brush_stroke_add_step(C, op, event, mouse); + } } else ;//ED_region_tag_redraw(ar); } } + /* we want the stroke to have the first daub at the start location instead of waiting till we have moved the space distance */ + if(first && + stroke->stroke_started && + paint_space_stroke_enabled(stroke->brush) && + !(stroke->brush->flag & BRUSH_ANCHORED) && + !(stroke->brush->flag & BRUSH_SMOOTH_STROKE)) + { + paint_brush_stroke_add_step(C, op, event, mouse); + } + return OPERATOR_RUNNING_MODAL; } diff --git a/source/blender/editors/sculpt_paint/paint_utils.c b/source/blender/editors/sculpt_paint/paint_utils.c index 85fbd5954e8..21ea6cb7f75 100644 --- a/source/blender/editors/sculpt_paint/paint_utils.c +++ b/source/blender/editors/sculpt_paint/paint_utils.c @@ -216,6 +216,10 @@ void BRUSH_OT_curve_preset(wmOperatorType *ot) {CURVE_PRESET_SHARP, "SHARP", 0, "Sharp", ""}, {CURVE_PRESET_SMOOTH, "SMOOTH", 0, "Smooth", ""}, {CURVE_PRESET_MAX, "MAX", 0, "Max", ""}, + {CURVE_PRESET_MID9, "MID9", 0, "Mid9", ""}, + {CURVE_PRESET_LINE, "LINE", 0, "Line", ""}, + {CURVE_PRESET_ROUND, "ROUND", 0, "Round", ""}, + {CURVE_PRESET_ROOT, "ROOT", 0, "Root", ""}, {0, NULL, 0, NULL, NULL}}; ot->name= "Preset"; @@ -225,7 +229,7 @@ void BRUSH_OT_curve_preset(wmOperatorType *ot) ot->exec= brush_curve_preset_exec; ot->poll= brush_curve_preset_poll; - ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; + ot->flag= OPTYPE_UNDO; RNA_def_enum(ot->srna, "shape", prop_shape_items, CURVE_PRESET_SMOOTH, "Mode", ""); } diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index 53e07a50020..eee0d520747 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -22,7 +22,7 @@ * * The Original Code is: all of this file. * - * Contributor(s): none yet. + * Contributor(s): Jason Wilkins, Tom Musgrove. * * ***** END GPL LICENSE BLOCK ***** * @@ -38,6 +38,8 @@ #include "BLI_ghash.h" #include "BLI_pbvh.h" #include "BLI_threads.h" +#include "BLI_editVert.h" +#include "BLI_rand.h" #include "DNA_key_types.h" #include "DNA_mesh_types.h" @@ -73,6 +75,7 @@ #include "ED_screen.h" #include "ED_sculpt.h" #include "ED_view3d.h" +#include "ED_mesh.h" #include "paint_intern.h" #include "sculpt_intern.h" @@ -81,20 +84,78 @@ #include "RE_render_ext.h" +#include "RE_shader_ext.h" -#include "gpu_buffers.h" +#include "GPU_buffers.h" #include <math.h> #include <stdlib.h> #include <string.h> -/* Number of vertices to average in order to determine the flatten distance */ -#define FLATTEN_SAMPLE_SIZE 10 +#ifdef _OPENMP +#include <omp.h> +#endif /* ==== FORWARD DEFINITIONS ===== * */ -static void sculpt_vertcos_to_key(Object *ob, KeyBlock *kb, float (*vertCos)[3]); + +void ED_sculpt_force_update(bContext *C) +{ + Object *ob= CTX_data_active_object(C); + + if(ob && (ob->mode & OB_MODE_SCULPT)) + multires_force_update(ob); +} + +/* Sculpt mode handles multires differently from regular meshes, but only if + it's the last modifier on the stack and it is not on the first level */ +struct MultiresModifierData *sculpt_multires_active(Scene *scene, Object *ob) +{ + ModifierData *md, *nmd; + + for(md= modifiers_getVirtualModifierList(ob); md; md= md->next) { + if(md->type == eModifierType_Multires) { + MultiresModifierData *mmd= (MultiresModifierData*)md; + + /* Check if any of the modifiers after multires are active + * if not it can use the multires struct */ + for(nmd= md->next; nmd; nmd= nmd->next) + if(modifier_isEnabled(scene, nmd, eModifierMode_Realtime)) + break; + + if(!nmd && mmd->sculptlvl > 0) + return mmd; + } + } + + return NULL; +} + +/* Checks whether full update mode (slower) needs to be used to work with modifiers */ +int sculpt_modifiers_active(Scene *scene, Object *ob) +{ + ModifierData *md; + MultiresModifierData *mmd= sculpt_multires_active(scene, ob); + + /* check if there are any modifiers after what we are sculpting, + for a multires modifier with a deform modifier in front, we + do no need to recalculate the modifier stack. note that this + needs to be in sync with ccgDM_use_grid_pbvh! */ + if(mmd) + md= mmd->modifier.next; + else + md= modifiers_getVirtualModifierList(ob); + + /* exception for shape keys because we can edit those */ + for(; md; md= md->next) { + if(modifier_isEnabled(scene, md, eModifierMode_Realtime)) + if(md->type != eModifierType_ShapeKey) + return 1; + } + + return 0; +} /* ===== STRUCTS ===== * @@ -121,10 +182,13 @@ typedef struct StrokeCache { /* Variants */ float radius; + float radius_squared; + //float traced_location[3]; float true_location[3]; float location[3]; - float flip; + float pen_flip; + float invert; float pressure; float mouse[2]; float bstrength; @@ -141,15 +205,32 @@ typedef struct StrokeCache { Brush *brush; float (*face_norms)[3]; /* Copy of the mesh faces' normals */ - float rotation; /* Texture rotation (radians) for anchored and rake modes */ + float special_rotation; /* Texture rotation (radians) for anchored and rake modes */ int pixel_radius, previous_pixel_radius; - float grab_active_location[8][3]; float grab_delta[3], grab_delta_symmetry[3]; float old_grab_location[3], orig_grab_location[3]; - int symmetry; /* Symmetry index between 0 and 7 */ - float view_normal[3], view_normal_symmetry[3]; - int last_rake[2]; /* Last location of updating rake rotation */ + + int symmetry; /* Symmetry index between 0 and 7 bit combo 0 is Brush only; + 1 is X mirror; 2 is Y mirror; 3 is XY; 4 is Z; 5 is XZ; 6 is YZ; 7 is XYZ */ + int mirror_symmetry_pass; /* the symmetry pass we are currently on between 0 and 7*/ + float true_view_normal[3]; + float view_normal[3]; + float last_area_normal[3]; + float last_center[3]; + int radial_symmetry_pass; + float symm_rot_mat[4][4]; + float symm_rot_mat_inv[4][4]; + float last_rake[2]; /* Last location of updating rake rotation */ int original; + + float vertex_rotation; + + char saved_active_brush_name[24]; + int alt_smooth; + + float plane_trim_squared; + + float autosmooth_overlap; } StrokeCache; /* ===== OPENGL ===== @@ -224,10 +305,12 @@ void sculpt_get_redraw_planes(float planes[4][4], ARegion *ar, RegionView3D *rv3d, Object *ob) { PBVH *pbvh= ob->sculpt->pbvh; - BoundBox *bb = MEM_callocN(sizeof(BoundBox), "sculpt boundbox"); + BoundBox bb; bglMats mats; rcti rect; + memset(&bb, 0, sizeof(BoundBox)); + view3d_get_transformation(ar, rv3d, ob, &mats); sculpt_get_redraw_rect(ar, rv3d,ob, &rect); @@ -246,405 +329,351 @@ void sculpt_get_redraw_planes(float planes[4][4], ARegion *ar, rect.ymax -= 2; #endif - view3d_calculate_clipping(bb, planes, &mats, &rect); + view3d_calculate_clipping(&bb, planes, &mats, &rect); mul_m4_fl(planes, -1.0f); - MEM_freeN(bb); - /* clear redraw flag from nodes */ if(pbvh) BLI_pbvh_update(pbvh, PBVH_UpdateRedraw, NULL); } -/************************** Undo *************************/ - -typedef struct SculptUndoNode { - struct SculptUndoNode *next, *prev; - - char idname[MAX_ID_NAME]; /* name instead of pointer*/ - void *node; /* only during push, not valid afterwards! */ - - float (*co)[3]; - short (*no)[3]; - int totvert; - - /* non-multires */ - int maxvert; /* to verify if totvert it still the same */ - int *index; /* to restore into right location */ - - /* multires */ - int maxgrid; /* same for grid */ - int gridsize; /* same for grid */ - int totgrid; /* to restore into right location */ - int *grids; /* to restore into right location */ - - /* layer brush */ - float *layer_disp; +/************************ Brush Testing *******************/ - /* shape keys */ - char *shapeName[32]; /* keep size in sync with keyblock dna */ -} SculptUndoNode; +typedef struct SculptBrushTest { + float radius_squared; + float location[3]; + float dist; +} SculptBrushTest; -static void update_cb(PBVHNode *node, void *data) +static void sculpt_brush_test_init(SculptSession *ss, SculptBrushTest *test) { - BLI_pbvh_node_mark_update(node); + test->radius_squared= ss->cache->radius_squared; + copy_v3_v3(test->location, ss->cache->location); } -/* Checks whether full update mode (slower) needs to be used to work with modifiers */ -static int sculpt_modifiers_active(Scene *scene, Object *ob) +static int sculpt_brush_test(SculptBrushTest *test, float co[3]) { - ModifierData *md; - MultiresModifierData *mmd = sculpt_multires_active(scene, ob); + float distsq = len_squared_v3v3(co, test->location); - /* check if there are any modifiers after what we are sculpting, - for a multires modifier with a deform modifier in front, we - do no need to recalculate the modifier stack. note that this - needs to be in sync with ccgDM_use_grid_pbvh! */ - if(mmd) - md= mmd->modifier.next; - else - md= modifiers_getVirtualModifierList(ob); - - /* exception for shape keys because we can edit those */ - for(; md; md= md->next) { - if(modifier_isEnabled(scene, md, eModifierMode_Realtime)) - if(md->type != eModifierType_ShapeKey) - return 1; + if(distsq <= test->radius_squared) { + test->dist = sqrt(distsq); + return 1; + } + else { + return 0; } - - return 0; } -static void sculpt_undo_restore(bContext *C, ListBase *lb) +static int sculpt_brush_test_sq(SculptBrushTest *test, float co[3]) { - Scene *scene = CTX_data_scene(C); - Object *ob = CTX_data_active_object(C); - DerivedMesh *dm = mesh_get_derived_final(scene, ob, 0); - SculptSession *ss = ob->sculpt; - SculptUndoNode *unode; - MVert *mvert; - MultiresModifierData *mmd; - int *index; - int i, j, update= 0; - - sculpt_update_mesh_elements(scene, ob, 0); - - for(unode=lb->first; unode; unode=unode->next) { - if(!(strcmp(unode->idname, ob->id.name)==0)) - continue; + float distsq = len_squared_v3v3(co, test->location); - if(unode->maxvert) { - char *shapeName= (char*)unode->shapeName; - - /* regular mesh restore */ - if(ss->totvert != unode->maxvert) - continue; + if(distsq <= test->radius_squared) { + test->dist = distsq; + return 1; + } + else { + return 0; + } +} - if (ss->kb && strcmp(ss->kb->name, shapeName)) { - /* shape key has been changed before calling undo operator */ +static int sculpt_brush_test_fast(SculptBrushTest *test, float co[3]) +{ + return len_squared_v3v3(co, test->location) <= test->radius_squared; +} - Key *key= ob_get_key(ob); - KeyBlock *kb= key_get_named_keyblock(key, shapeName); +static int sculpt_brush_test_cube(SculptBrushTest *test, float co[3], float local[4][4]) +{ + const static float side = 0.70710678118654752440084436210485; // sqrt(.5); - if (kb) { - ob->shapenr= BLI_findindex(&key->block, kb) + 1; - ob->shapeflag|= OB_SHAPE_LOCK; + float local_co[3]; - sculpt_update_mesh_elements(scene, ob, 0); - WM_event_add_notifier(C, NC_OBJECT|ND_DATA, ob); - } else { - /* key has been removed -- skip this undo node */ - continue; - } - } + mul_v3_m4v3(local_co, local, co); - index= unode->index; - mvert= ss->mvert; + local_co[0] = fabs(local_co[0]); + local_co[1] = fabs(local_co[1]); + local_co[2] = fabs(local_co[2]); - if (ss->kb) { - float (*vertCos)[3]; - vertCos= key_to_vertcos(ob, ss->kb); + if (local_co[0] <= side && local_co[1] <= side && local_co[2] <= side) { + test->dist = MAX3(local_co[0], local_co[1], local_co[2]) / side; - for(i=0; i<unode->totvert; i++) - swap_v3_v3(vertCos[index[i]], unode->co[i]); + return 1; + } + else { + return 0; + } +} - /* propagate new coords to keyblock */ - sculpt_vertcos_to_key(ob, ss->kb, vertCos); +static float frontface(Brush *brush, float sculpt_normal[3], short no[3], float fno[3]) +{ + if (brush->flag & BRUSH_FRONTFACE) { + float dot; - /* pbvh uses it's own mvert array, so coords should be */ - /* propagated to pbvh here */ - BLI_pbvh_apply_vertCos(ss->pbvh, vertCos); + if (no) { + float tmp[3]; - MEM_freeN(vertCos); - } else { - for(i=0; i<unode->totvert; i++) { - swap_v3_v3(mvert[index[i]].co, unode->co[i]); - mvert[index[i]].flag |= ME_VERT_PBVH_UPDATE; - } - } + normal_short_to_float_v3(tmp, no); + dot= dot_v3v3(tmp, sculpt_normal); } - else if(unode->maxgrid && dm->getGridData) { - /* multires restore */ - DMGridData **grids, *grid; - float (*co)[3]; - int gridsize; - - if(dm->getNumGrids(dm) != unode->maxgrid) - continue; - if(dm->getGridSize(dm) != unode->gridsize) - continue; - - grids= dm->getGridData(dm); - gridsize= dm->getGridSize(dm); - - co = unode->co; - for(j=0; j<unode->totgrid; j++) { - grid= grids[unode->grids[j]]; - - for(i=0; i<gridsize*gridsize; i++, co++) - swap_v3_v3(grid[i].co, co[0]); - } + else { + dot= dot_v3v3(fno, sculpt_normal); } - update= 1; + return dot > 0 ? dot : 0; } - - if(update) { - /* we update all nodes still, should be more clever, but also - needs to work correct when exiting/entering sculpt mode and - the nodes get recreated, though in that case it could do all */ - BLI_pbvh_search_callback(ss->pbvh, NULL, NULL, update_cb, NULL); - BLI_pbvh_update(ss->pbvh, PBVH_UpdateBB|PBVH_UpdateOriginalBB|PBVH_UpdateRedraw, NULL); - - if((mmd=sculpt_multires_active(scene, ob))) - multires_mark_as_modified(ob); - - if(ss->modifiers_active || ((Mesh*)ob->data)->id.us > 1) - DAG_id_flush_update(&ob->id, OB_RECALC_DATA); + else { + return 1; } } -static void sculpt_undo_free(ListBase *lb) +#if 0 + +static int sculpt_brush_test_cyl(SculptBrushTest *test, float co[3], float location[3], float an[3]) { - SculptUndoNode *unode; + if (sculpt_brush_test_fast(test, co)) { + float t1[3], t2[3], t3[3], dist; - for(unode=lb->first; unode; unode=unode->next) { - if(unode->co) - MEM_freeN(unode->co); - if(unode->no) - MEM_freeN(unode->no); - if(unode->index) - MEM_freeN(unode->index); - if(unode->grids) - MEM_freeN(unode->grids); - if(unode->layer_disp) - MEM_freeN(unode->layer_disp); - } -} + sub_v3_v3v3(t1, location, co); + sub_v3_v3v3(t2, x2, location); -static SculptUndoNode *sculpt_undo_get_node(SculptSession *ss, PBVHNode *node) -{ - ListBase *lb= undo_paint_push_get_list(UNDO_PAINT_MESH); - SculptUndoNode *unode; + cross_v3_v3v3(t3, an, t1); - if(!lb) - return NULL; + dist = len_v3(t3)/len_v3(t2); - for(unode=lb->first; unode; unode=unode->next) - if(unode->node == node) - return unode; + test->dist = dist; - return NULL; + return 1; + } + + return 0; } -static SculptUndoNode *sculpt_undo_push_node(SculptSession *ss, PBVHNode *node) -{ - ListBase *lb= undo_paint_push_get_list(UNDO_PAINT_MESH); - Object *ob= ss->ob; - SculptUndoNode *unode; - int totvert, allvert, totgrid, maxgrid, gridsize, *grids; +#endif - /* list is manipulated by multiple threads, so we lock */ - BLI_lock_thread(LOCK_CUSTOM1); +/* ===== Sculpting ===== + * + */ + - if((unode= sculpt_undo_get_node(ss, node))) { - BLI_unlock_thread(LOCK_CUSTOM1); - return unode; - } +static float overlapped_curve(Brush* br, float x) +{ + int i; + const int n = 100 / br->spacing; + const float h = br->spacing / 50.0f; + const float x0 = x-1; - unode= MEM_callocN(sizeof(SculptUndoNode), "SculptUndoNode"); - strcpy(unode->idname, ob->id.name); - unode->node= node; + float sum; - BLI_pbvh_node_num_verts(ss->pbvh, node, &totvert, &allvert); - BLI_pbvh_node_get_grids(ss->pbvh, node, &grids, &totgrid, - &maxgrid, &gridsize, NULL, NULL); + sum = 0; + for (i= 0; i < n; i++) { + float xx; - unode->totvert= totvert; - /* we will use this while sculpting, is mapalloc slow to access then? */ - unode->co= MEM_mapallocN(sizeof(float)*3*allvert, "SculptUndoNode.co"); - unode->no= MEM_mapallocN(sizeof(short)*3*allvert, "SculptUndoNode.no"); - undo_paint_push_count_alloc(UNDO_PAINT_MESH, (sizeof(float)*3 + sizeof(short)*3 + sizeof(int))*allvert); - BLI_addtail(lb, unode); + xx = fabs(x0 + i*h); - if(maxgrid) { - /* multires */ - unode->maxgrid= maxgrid; - unode->totgrid= totgrid; - unode->gridsize= gridsize; - unode->grids= MEM_mapallocN(sizeof(int)*totgrid, "SculptUndoNode.grids"); + if (xx < 1.0f) + sum += brush_curve_strength(br, xx, 1); } - else { - /* regular mesh */ - unode->maxvert= ss->totvert; - unode->index= MEM_mapallocN(sizeof(int)*allvert, "SculptUndoNode.index"); - } - - BLI_unlock_thread(LOCK_CUSTOM1); - /* copy threaded, hopefully this is the performance critical part */ - { - PBVHVertexIter vd; + return sum; +} - BLI_pbvh_vertex_iter_begin(ss->pbvh, node, vd, PBVH_ITER_ALL) { - copy_v3_v3(unode->co[vd.i], vd.co); - if(vd.no) VECCOPY(unode->no[vd.i], vd.no) - else normal_float_to_short_v3(unode->no[vd.i], vd.fno); - if(vd.vert_indices) unode->index[vd.i]= vd.vert_indices[vd.i]; - } - BLI_pbvh_vertex_iter_end; - } +static float integrate_overlap(Brush* br) +{ + int i; + int m= 10; + float g = 1.0f/m; + float overlap; + float max; - if(unode->grids) - memcpy(unode->grids, grids, sizeof(int)*totgrid); + overlap= 0; + max= 0; + for(i= 0; i < m; i++) { + overlap = overlapped_curve(br, i*g); - /* store active shape key */ - if(ss->kb) BLI_strncpy((char*)unode->shapeName, ss->kb->name, sizeof(ss->kb->name)); - else unode->shapeName[0]= '\0'; + if (overlap > max) + max = overlap; + } - return unode; + return max; } -static void sculpt_undo_push_begin(SculptSession *ss, char *name) +/* Uses symm to selectively flip any axis of a coordinate. */ +static void flip_coord(float out[3], float in[3], const char symm) { - undo_paint_push_begin(UNDO_PAINT_MESH, name, - sculpt_undo_restore, sculpt_undo_free); + if(symm & SCULPT_SYMM_X) + out[0]= -in[0]; + else + out[0]= in[0]; + if(symm & SCULPT_SYMM_Y) + out[1]= -in[1]; + else + out[1]= in[1]; + if(symm & SCULPT_SYMM_Z) + out[2]= -in[2]; + else + out[2]= in[2]; } -static void sculpt_undo_push_end(SculptSession *ss) +float calc_overlap(StrokeCache *cache, const char symm, const char axis, const float angle) { - ListBase *lb= undo_paint_push_get_list(UNDO_PAINT_MESH); - SculptUndoNode *unode; - - /* we don't need normals in the undo stack */ - for(unode=lb->first; unode; unode=unode->next) { - if(unode->no) { - MEM_freeN(unode->no); - unode->no= NULL; - } + float mirror[3]; + float distsq; + float mat[4][4]; + + //flip_coord(mirror, cache->traced_location, symm); + flip_coord(mirror, cache->true_location, symm); - if(unode->layer_disp) { - MEM_freeN(unode->layer_disp); - unode->layer_disp= NULL; - } - } + unit_m4(mat); + rotate_m4(mat, axis, angle); - undo_paint_push_end(UNDO_PAINT_MESH); -} + mul_m4_v3(mat, mirror); -void ED_sculpt_force_update(bContext *C) -{ - Object *ob= CTX_data_active_object(C); + //distsq = len_squared_v3v3(mirror, cache->traced_location); + distsq = len_squared_v3v3(mirror, cache->true_location); - if(ob && (ob->mode & OB_MODE_SCULPT)) - multires_force_update(ob); + if (distsq <= 4*(cache->radius_squared)) + return (2*(cache->radius) - sqrt(distsq)) / (2*(cache->radius)); + else + return 0; } -/************************ Brush Testing *******************/ - -typedef struct SculptBrushTest { - float radius_squared; - float location[3]; +static float calc_radial_symmetry_feather(Sculpt *sd, StrokeCache *cache, const char symm, const char axis) +{ + int i; + float overlap; - float dist; -} SculptBrushTest; + overlap = 0; + for(i = 1; i < sd->radial_symm[axis-'X']; ++i) { + const float angle = 2*M_PI*i/sd->radial_symm[axis-'X']; + overlap += calc_overlap(cache, symm, axis, angle); + } -static void sculpt_brush_test_init(SculptSession *ss, SculptBrushTest *test) -{ - test->radius_squared= ss->cache->radius*ss->cache->radius; - copy_v3_v3(test->location, ss->cache->location); + return overlap; } -static int sculpt_brush_test(SculptBrushTest *test, float co[3]) +static float calc_symmetry_feather(Sculpt *sd, StrokeCache* cache) { - float distsq, delta[3]; + if (sd->flags & SCULPT_SYMMETRY_FEATHER) { + float overlap; + int symm = cache->symmetry; + int i; - sub_v3_v3v3(delta, co, test->location); - distsq = INPR(delta, delta); + overlap = 0; + for (i = 0; i <= symm; i++) { + if(i == 0 || (symm & i && (symm != 5 || i != 3) && (symm != 6 || (i != 3 && i != 5)))) { - if(distsq < test->radius_squared) { - test->dist = sqrt(distsq); + overlap += calc_overlap(cache, i, 0, 0); + + overlap += calc_radial_symmetry_feather(sd, cache, i, 'X'); + overlap += calc_radial_symmetry_feather(sd, cache, i, 'Y'); + overlap += calc_radial_symmetry_feather(sd, cache, i, 'Z'); + } + } + + return 1/overlap; + } + else { return 1; } - - return 0; } -/* ===== Sculpting ===== - * - */ - /* 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) +static float brush_strength(Sculpt *sd, StrokeCache *cache, float feather, float overlap) { Brush *brush = paint_brush(&sd->paint); - /* Primary strength input; square it to make lower values more sensitive */ - float alpha = brush->alpha * brush->alpha; - float dir= brush->flag & BRUSH_DIR_IN ? -1 : 1; - float pressure= 1; - float flip= cache->flip ? -1:1; + /* Primary strength input; square it to make lower values more sensitive */ + float alpha = sculpt_get_brush_alpha(brush)*sculpt_get_brush_alpha(brush); + float dir = brush->flag & BRUSH_DIR_IN ? -1 : 1; + float pressure = brush->flag & BRUSH_ALPHA_PRESSURE ? cache->pressure : 1; + float pen_flip = cache->pen_flip ? -1 : 1; + float invert = cache->invert ? -1 : 1; + float flip = dir * invert * pen_flip; - if(brush->flag & BRUSH_ALPHA_PRESSURE) - pressure *= cache->pressure; - switch(brush->sculpt_tool){ - case SCULPT_TOOL_DRAW: - case SCULPT_TOOL_INFLATE: - case SCULPT_TOOL_CLAY: - case SCULPT_TOOL_FLATTEN: - case SCULPT_TOOL_LAYER: - return alpha * dir * pressure * flip; /*XXX: not sure why? was multiplied by G.vd->grid */; - case SCULPT_TOOL_SMOOTH: - return alpha * 4 * pressure; - case SCULPT_TOOL_PINCH: - return alpha / 2 * dir * pressure * flip; - case SCULPT_TOOL_GRAB: - return 1; - default: - return 0; + case SCULPT_TOOL_CLAY: + case SCULPT_TOOL_CLAY_TUBES: + case SCULPT_TOOL_DRAW: + case SCULPT_TOOL_LAYER: + return alpha * flip * pressure * overlap * feather; + + case SCULPT_TOOL_CREASE: + case SCULPT_TOOL_BLOB: + return alpha * flip * pressure * overlap * feather; + + case SCULPT_TOOL_INFLATE: + if (flip > 0) { + return 0.250f * alpha * flip * pressure * overlap * feather; + } + else { + return 0.125f * alpha * flip * pressure * overlap * feather; + } + + case SCULPT_TOOL_FILL: + case SCULPT_TOOL_SCRAPE: + case SCULPT_TOOL_FLATTEN: + if (flip > 0) { + overlap = (1+overlap) / 2; + return alpha * flip * pressure * overlap * feather; + } + else { + /* reduce strength for DEEPEN, PEAKS, and CONTRAST */ + return 0.5f * alpha * flip * pressure * overlap * feather; + } + + case SCULPT_TOOL_SMOOTH: + return alpha * pressure * feather; + + case SCULPT_TOOL_PINCH: + if (flip > 0) { + return alpha * flip * pressure * overlap * feather; + } + else { + return 0.25f * alpha * flip * pressure * overlap * feather; + } + + case SCULPT_TOOL_NUDGE: + overlap = (1+overlap) / 2; + return alpha * pressure * overlap * feather; + + case SCULPT_TOOL_THUMB: + return alpha*pressure*feather; + + case SCULPT_TOOL_SNAKE_HOOK: + return feather; + + case SCULPT_TOOL_GRAB: + case SCULPT_TOOL_ROTATE: + return feather; + + default: + return 0; } } -/* Uses symm to selectively flip any axis of a coordinate. */ -static void flip_coord(float out[3], float in[3], const char symm) +float get_tex_pixel(Brush* br, float u, float v) { - if(symm & SCULPT_SYMM_X) - out[0]= -in[0]; - else - out[0]= in[0]; - if(symm & SCULPT_SYMM_Y) - out[1]= -in[1]; - else - out[1]= in[1]; - if(symm & SCULPT_SYMM_Z) - out[2]= -in[2]; - else - out[2]= in[2]; + TexResult texres; + float co[3]; + int hasrgb; + + co[0] = u; + co[1] = v; + co[2] = 0; + + memset(&texres, 0, sizeof(TexResult)); + hasrgb = multitex_ext(br->mtex.tex, co, NULL, NULL, 1, &texres); + + if (hasrgb & TEX_RGB) + texres.tin = (0.35*texres.tr + 0.45*texres.tg + 0.2*texres.tb)*texres.ta; + + return texres.tin; } +#if 0 + /* Get a pixel from the texcache at (px, py) */ static unsigned char get_texcache_pixel(const SculptSession *ss, int px, int py) { @@ -655,7 +684,7 @@ static unsigned char get_texcache_pixel(const SculptSession *ss, int px, int py) static float get_texcache_pixel_bilinear(const SculptSession *ss, float u, float v) { - int x, y, x2, y2; + unsigned x, y, x2, y2; const int tc_max = ss->texcache_side - 1; float urat, vrat, uopp; @@ -682,76 +711,97 @@ static float get_texcache_pixel_bilinear(const SculptSession *ss, float u, float get_texcache_pixel(ss, x2, y2) * urat) * vrat) / 255.0; } +#endif + /* Return a multiplier for brush strength on a particular vertex. */ static float tex_strength(SculptSession *ss, Brush *br, float *point, const float len) { - MTex *tex = &br->mtex; + MTex *mtex = &br->mtex; float avg= 1; - if(!tex) { + if(!mtex->tex) { avg= 1; } - else if(tex->brush_map_mode == MTEX_MAP_MODE_3D) { + else if(mtex->brush_map_mode == MTEX_MAP_MODE_3D) { float jnk; /* Get strength by feeding the vertex location directly into a texture */ - externtex(tex, point, &avg, + externtex(mtex, point, &avg, &jnk, &jnk, &jnk, &jnk); } else if(ss->texcache) { - const float bsize= ss->cache->pixel_radius * 2; - const float rot= tex->rot + ss->cache->rotation; - int px, py; - float flip[3], point_2d[2]; - - /* If the active area is being applied for symmetry, flip it - across the symmetry axis in order to project it. This insures - that the brush texture will be oriented correctly. */ - copy_v3_v3(flip, point); - flip_coord(flip, flip, ss->cache->symmetry); - projectf(ss->cache->mats, flip, point_2d); - - /* For Tile and Drag modes, get the 2D screen coordinates of the - and scale them up or down to the texture size. */ - if(tex->brush_map_mode == MTEX_MAP_MODE_TILED) { - const int sx= (const int)tex->size[0]; - const int sy= (const int)tex->size[1]; - - float fx= point_2d[0]; - float fy= point_2d[1]; - - float angle= atan2(fy, fx) - rot; - float flen= sqrtf(fx*fx + fy*fy); - - if(rot<0.001 && rot>-0.001) { - px= point_2d[0]; - py= point_2d[1]; - } else { - px= flen * cos(angle) + 2000; - py= flen * sin(angle) + 2000; - } - if(sx != 1) - px %= sx-1; - if(sy != 1) - py %= sy-1; - avg= get_texcache_pixel_bilinear(ss, ss->texcache_side*px/sx, ss->texcache_side*py/sy); - } - else if(tex->brush_map_mode == MTEX_MAP_MODE_FIXED) { - float fx= (point_2d[0] - ss->cache->tex_mouse[0]) / bsize; - float fy= (point_2d[1] - ss->cache->tex_mouse[1]) / bsize; - - float angle= atan2(fy, fx) - rot; - float flen= sqrtf(fx*fx + fy*fy); - - fx = flen * cos(angle) + 0.5; - fy = flen * sin(angle) + 0.5; + float rotation = -mtex->rot; + float x, y, point_2d[3]; + float diameter; + + /* if the active area is being applied for symmetry, flip it + across the symmetry axis and rotate it back to the orignal + position in order to project it. This insures that the + brush texture will be oriented correctly. */ + + flip_coord(point_2d, point, ss->cache->mirror_symmetry_pass); + + if (ss->cache->radial_symmetry_pass) + mul_m4_v3(ss->cache->symm_rot_mat_inv, point_2d); + + projectf(ss->cache->mats, point_2d, point_2d); + + /* if fixed mode, keep coordinates relative to mouse */ + if(mtex->brush_map_mode == MTEX_MAP_MODE_FIXED) { + rotation += ss->cache->special_rotation; + + point_2d[0] -= ss->cache->tex_mouse[0]; + point_2d[1] -= ss->cache->tex_mouse[1]; + + diameter = ss->cache->pixel_radius; // use pressure adjusted size for fixed mode + + x = point_2d[0]; + y = point_2d[1]; + } + else /* else (mtex->brush_map_mode == MTEX_MAP_MODE_TILED), + leave the coordinates relative to the screen */ + { + diameter = sculpt_get_brush_size(br); // use unadjusted size for tiled mode + + x = point_2d[0] - ss->cache->vc->ar->winrct.xmin; + y = point_2d[1] - ss->cache->vc->ar->winrct.ymin; + } + + x /= ss->cache->vc->ar->winx; + y /= ss->cache->vc->ar->winy; - avg= get_texcache_pixel_bilinear(ss, fx * ss->texcache_side, fy * ss->texcache_side); + if (mtex->brush_map_mode == MTEX_MAP_MODE_TILED) { + x -= 0.5f; + y -= 0.5f; } + + x *= ss->cache->vc->ar->winx / diameter; + y *= ss->cache->vc->ar->winy / diameter; + + /* it is probably worth optimizing for those cases where + the texture is not rotated by skipping the calls to + atan2, sqrtf, sin, and cos. */ + if (rotation > 0.001 || rotation < -0.001) { + const float angle = atan2(y, x) + rotation; + const float flen = sqrtf(x*x + y*y); + + x = flen * cos(angle); + y = flen * sin(angle); + } + + x *= br->mtex.size[0]; + y *= br->mtex.size[1]; + + x += br->mtex.ofs[0]; + y += br->mtex.ofs[1]; + + avg = get_tex_pixel(br, x, y); } - avg*= brush_curve_strength(br, len, ss->cache->radius); /* Falloff curve */ + avg += br->texture_sample_bias; + + avg *= brush_curve_strength(br, len, ss->cache->radius); /* Falloff curve */ return avg; } @@ -787,7 +837,7 @@ static int sculpt_search_sphere_cb(PBVHNode *node, void *data_v) sub_v3_v3v3(t, center, nearest); - return t[0] * t[0] + t[1] * t[1] + t[2] * t[2] < data->radius_squared; + return dot_v3v3(t, t) < data->radius_squared; } /* Handles clipping against a mirror modifier and SCULPT_LOCK axis flags */ @@ -803,7 +853,7 @@ static void sculpt_clip(Sculpt *sd, SculptSession *ss, float *co, const float va co[i]= 0.0f; else co[i]= val[i]; - } + } } static void add_norm_if(float view_vec[3], float out[3], float out_flip[3], float fno[3]) @@ -815,52 +865,48 @@ static void add_norm_if(float view_vec[3], float out[3], float out_flip[3], floa } } -/* For draw/layer/flatten; finds average normal for all active vertices */ -static void calc_area_normal(Sculpt *sd, SculptSession *ss, float area_normal[3], PBVHNode **nodes, int totnode) +static void calc_area_normal(Sculpt *sd, SculptSession *ss, float an[3], PBVHNode **nodes, int totnode) { - PBVH *bvh= ss->pbvh; - StrokeCache *cache = ss->cache; - const int view = 0; /* XXX: should probably be a flag, not number: brush_type==SCULPT_TOOL_DRAW ? sculptmode_brush()->view : 0; */ - float out[3] = {0.0f, 0.0f, 0.0f}; - float out_flip[3] = {0.0f, 0.0f, 0.0f}; - float out_dir[3]; int n; - copy_v3_v3(out_dir, cache->view_normal_symmetry); + float out_flip[3] = {0.0f, 0.0f, 0.0f}; - /* threaded loop over nodes */ - //#pragma omp parallel for private(n) schedule(static) + zero_v3(an); + + #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) for(n=0; n<totnode; n++) { PBVHVertexIter vd; SculptBrushTest test; SculptUndoNode *unode; - float fno[3]; - float nout[3] = {0.0f, 0.0f, 0.0f}; - float nout_flip[3] = {0.0f, 0.0f, 0.0f}; - - // XXX push instead of get for thread safety in draw - // brush .. lame, but also not harmful really - unode= sculpt_undo_push_node(ss, nodes[n]); + 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(ss, nodes[n]); sculpt_brush_test_init(ss, &test); if(ss->cache->original) { - BLI_pbvh_vertex_iter_begin(bvh, nodes[n], vd, PBVH_ITER_UNIQUE) { - if(sculpt_brush_test(&test, unode->co[vd.i])) { + BLI_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(out_dir, nout, nout_flip, fno); + add_norm_if(ss->cache->view_normal, private_an, private_out_flip, fno); } } BLI_pbvh_vertex_iter_end; } else { - BLI_pbvh_vertex_iter_begin(bvh, nodes[n], vd, PBVH_ITER_UNIQUE) { - if(sculpt_brush_test(&test, vd.co)) { + BLI_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(out_dir, nout, nout_flip, fno); + 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); } - else - add_norm_if(out_dir, nout, nout_flip, vd.fno); } } BLI_pbvh_vertex_iter_end; @@ -868,72 +914,70 @@ static void calc_area_normal(Sculpt *sd, SculptSession *ss, float area_normal[3] //#pragma omp critical { - /* we sum per node and add together later for threads */ - add_v3_v3(out, nout); - add_v3_v3(out_flip, nout_flip); + add_v3_v3(an, private_an); + add_v3_v3(out_flip, private_out_flip); } } - if (out[0]==0.0 && out[1]==0.0 && out[2]==0.0) { - copy_v3_v3(out, out_flip); - } - - normalize_v3(out); + if (is_zero_v3(an)) + copy_v3_v3(an, out_flip); - out[0] = out_dir[0] * view + out[0] * (10-view); - out[1] = out_dir[1] * view + out[1] * (10-view); - out[2] = out_dir[2] * view + out[2] * (10-view); - - normalize_v3(out); - copy_v3_v3(area_normal, out); + normalize_v3(an); } -static void do_draw_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode) +/* This initializes the faces to be moved for this sculpt for draw/layer/flatten; then it + finds average normal for all active vertices - note that this is called once for each mirroring direction */ +static void calc_sculpt_normal(Sculpt *sd, SculptSession *ss, float an[3], PBVHNode **nodes, int totnode) { Brush *brush = paint_brush(&sd->paint); - float offset[3], area_normal[3]; - float bstrength= ss->cache->bstrength; - int n; - /* area normal */ - calc_area_normal(sd, ss, area_normal, nodes, totnode); + if (ss->cache->mirror_symmetry_pass == 0 && + ss->cache->radial_symmetry_pass == 0 && + (ss->cache->first_time || !(brush->flag & BRUSH_ORIGINAL_NORMAL))) + { + switch (brush->sculpt_plane) { + case SCULPT_DISP_DIR_VIEW: + viewvector(ss->cache->vc->rv3d, ss->cache->vc->rv3d->twmat[3], an); + break; - /* offset with as much as possible factored in already */ - offset[0]= area_normal[0]*ss->cache->radius*ss->cache->scale[0]*bstrength; - offset[1]= area_normal[1]*ss->cache->radius*ss->cache->scale[1]*bstrength; - offset[2]= area_normal[2]*ss->cache->radius*ss->cache->scale[2]*bstrength; + case SCULPT_DISP_DIR_X: + an[1] = 0.0; + an[2] = 0.0; + an[0] = 1.0; + break; - /* threaded loop over nodes */ - //#pragma omp parallel for private(n) schedule(static) - for(n=0; n<totnode; n++) { - PBVHVertexIter vd; - SculptBrushTest test; - - sculpt_undo_push_node(ss, nodes[n]); - sculpt_brush_test_init(ss, &test); + case SCULPT_DISP_DIR_Y: + an[0] = 0.0; + an[2] = 0.0; + an[1] = 1.0; + break; - BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { - if(sculpt_brush_test(&test, vd.co)) { - /* offset vertex */ - float fade = tex_strength(ss, brush, vd.co, test.dist); - float val[3]= {vd.co[0] + offset[0]*fade, - vd.co[1] + offset[1]*fade, - vd.co[2] + offset[2]*fade}; + case SCULPT_DISP_DIR_Z: + an[0] = 0.0; + an[1] = 0.0; + an[2] = 1.0; + break; - sculpt_clip(sd, ss, vd.co, val); - if(vd.mvert) vd.mvert->flag |= ME_VERT_PBVH_UPDATE; - } + case SCULPT_DISP_DIR_AREA: + calc_area_normal(sd, ss, an, nodes, totnode); + + default: + break; } - BLI_pbvh_vertex_iter_end; - BLI_pbvh_node_mark_update(nodes[n]); + copy_v3_v3(ss->cache->last_area_normal, an); + } + else { + copy_v3_v3(an, ss->cache->last_area_normal); + flip_coord(an, an, ss->cache->mirror_symmetry_pass); + mul_m4_v3(ss->cache->symm_rot_mat, an); } } /* 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.) */ -static void neighbor_average(SculptSession *ss, float avg[3], const int vert) +static void neighbor_average(SculptSession *ss, float avg[3], const unsigned vert) { int i, skip= -1, total=0; IndexNode *node= ss->fmap[vert].first; @@ -974,52 +1018,58 @@ static void neighbor_average(SculptSession *ss, float avg[3], const int vert) copy_v3_v3(avg, ss->mvert[vert].co); } -static void do_mesh_smooth_brush(Sculpt *sd, SculptSession *ss, PBVHNode *node) +static void do_mesh_smooth_brush(Sculpt *sd, SculptSession *ss, PBVHNode *node, float bstrength) { Brush *brush = paint_brush(&sd->paint); - float bstrength= ss->cache->bstrength; PBVHVertexIter vd; SculptBrushTest test; + CLAMP(bstrength, 0.0f, 1.0f); + sculpt_brush_test_init(ss, &test); BLI_pbvh_vertex_iter_begin(ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { if(sculpt_brush_test(&test, vd.co)) { - float fade = tex_strength(ss, brush, vd.co, test.dist)*bstrength; + const float fade = bstrength*tex_strength(ss, brush, vd.co, test.dist)*frontface(brush, ss->cache->view_normal, vd.no, vd.fno); float avg[3], val[3]; - CLAMP(fade, 0.0f, 1.0f); - neighbor_average(ss, avg, vd.vert_indices[vd.i]); - val[0] = vd.co[0]+(avg[0]-vd.co[0])*fade; - val[1] = vd.co[1]+(avg[1]-vd.co[1])*fade; - val[2] = vd.co[2]+(avg[2]-vd.co[2])*fade; - - sculpt_clip(sd, ss, vd.co, val); - if(vd.mvert) vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + sub_v3_v3v3(val, avg, vd.co); + mul_v3_fl(val, fade); + + add_v3_v3(val, vd.co); + + sculpt_clip(sd, ss, vd.co, val); + + if(vd.mvert) + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; } } BLI_pbvh_vertex_iter_end; } -static void do_multires_smooth_brush(Sculpt *sd, SculptSession *ss, PBVHNode *node) +static void do_multires_smooth_brush(Sculpt *sd, SculptSession *ss, PBVHNode *node, float bstrength) { Brush *brush = paint_brush(&sd->paint); SculptBrushTest test; DMGridData **griddata, *data; DMGridAdjacency *gridadj, *adj; - float bstrength= ss->cache->bstrength; - float co[3], (*tmpgrid)[3]; + float (*tmpgrid)[3], (*tmprow)[3]; int v1, v2, v3, v4; int *grid_indices, totgrid, gridsize, i, x, y; - + sculpt_brush_test_init(ss, &test); + CLAMP(bstrength, 0.0f, 1.0f); + BLI_pbvh_node_get_grids(ss->pbvh, node, &grid_indices, &totgrid, NULL, &gridsize, &griddata, &gridadj); //#pragma omp critical - tmpgrid= MEM_mallocN(sizeof(float)*3*gridsize*gridsize, "tmpgrid"); + { + tmpgrid= MEM_mallocN(sizeof(float)*3*gridsize*gridsize, "tmpgrid"); + tmprow= MEM_mallocN(sizeof(float)*3*gridsize, "tmprow"); + } for(i = 0; i < totgrid; ++i) { data = griddata[grid_indices[i]]; @@ -1027,75 +1077,106 @@ static void do_multires_smooth_brush(Sculpt *sd, SculptSession *ss, PBVHNode *no memset(tmpgrid, 0, sizeof(float)*3*gridsize*gridsize); - /* average grid values */ - for(y = 0; y < gridsize-1; ++y) { - for(x = 0; x < gridsize-1; ++x) { + for (y= 0; y < gridsize-1; y++) { + float tmp[3]; + + v1 = y*gridsize; + add_v3_v3v3(tmprow[0], data[v1].co, data[v1+gridsize].co); + + for (x= 0; x < gridsize-1; x++) { v1 = x + y*gridsize; - v2 = (x + 1) + y*gridsize; - v3 = (x + 1) + (y + 1)*gridsize; - v4 = x + (y + 1)*gridsize; + v2 = v1 + 1; + v3 = v1 + gridsize; + v4 = v3 + 1; - cent_quad_v3(co, data[v1].co, data[v2].co, data[v3].co, data[v4].co); - mul_v3_fl(co, 0.25f); + add_v3_v3v3(tmprow[x+1], data[v2].co, data[v4].co); + add_v3_v3v3(tmp, tmprow[x+1], tmprow[x]); - add_v3_v3(tmpgrid[v1], co); - add_v3_v3(tmpgrid[v2], co); - add_v3_v3(tmpgrid[v3], co); - add_v3_v3(tmpgrid[v4], co); + add_v3_v3(tmpgrid[v1], tmp); + add_v3_v3(tmpgrid[v2], tmp); + add_v3_v3(tmpgrid[v3], tmp); + add_v3_v3(tmpgrid[v4], tmp); } } /* blend with existing coordinates */ for(y = 0; y < gridsize; ++y) { for(x = 0; x < gridsize; ++x) { - 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; + float *co; + float *fno; + int index; - copy_v3_v3(co, data[x + y*gridsize].co); + 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= data[index].co; + fno= data[index].no; if(sculpt_brush_test(&test, co)) { - float fade = tex_strength(ss, brush, co, test.dist)*bstrength; - float avg[3], val[3]; + const float fade = bstrength*tex_strength(ss, brush, co, test.dist)*frontface(brush, ss->cache->view_normal, NULL, fno); + float *avg, val[3]; + float n; + + avg = tmpgrid[x + y*gridsize]; + + n = 1/16.0f; - copy_v3_v3(avg, tmpgrid[x + y*gridsize]); if(x == 0 || x == gridsize - 1) - mul_v3_fl(avg, 2.0f); + n *= 2; + if(y == 0 || y == gridsize - 1) - mul_v3_fl(avg, 2.0f); + n *= 2; + + mul_v3_fl(avg, n); - CLAMP(fade, 0.0f, 1.0f); + sub_v3_v3v3(val, avg, co); + mul_v3_fl(val, fade); - val[0] = co[0]+(avg[0]-co[0])*fade; - val[1] = co[1]+(avg[1]-co[1])*fade; - val[2] = co[2]+(avg[2]-co[2])*fade; - - sculpt_clip(sd, ss, data[x + y*gridsize].co, val); + add_v3_v3(val, co); + + sculpt_clip(sd, ss, co, val); } } } } //#pragma omp critical - MEM_freeN(tmpgrid); + { + MEM_freeN(tmpgrid); + MEM_freeN(tmprow); + } } -static void do_smooth_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode) +static void smooth(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode, float bstrength) { - int iteration, n; + const int max_iterations = 4; + const float fract = 1.0f/max_iterations; + int iteration, n, count; + float last; - for(iteration = 0; iteration < 2; ++iteration) { - //#pragma omp parallel for private(n) schedule(static) - for(n=0; n<totnode; n++) { - sculpt_undo_push_node(ss, nodes[n]); + CLAMP(bstrength, 0, 1); - if(ss->multires) - do_multires_smooth_brush(sd, ss, nodes[n]); - else if(ss->fmap) - do_mesh_smooth_brush(sd, ss, nodes[n]); + count = (int)(bstrength*max_iterations); + last = max_iterations*(bstrength - count*fract); - BLI_pbvh_node_mark_update(nodes[n]); + for(iteration = 1; iteration <= count; ++iteration) { + #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) + for(n=0; n<totnode; n++) { + if(ss->multires) { + do_multires_smooth_brush(sd, ss, nodes[n], iteration != count ? 1.0f : last); + } + else if(ss->fmap) + do_mesh_smooth_brush(sd, ss, nodes[n], iteration != count ? 1.0f : last); } if(ss->multires) @@ -1103,69 +1184,378 @@ static void do_smooth_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int } } -static void do_pinch_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode) +static void do_smooth_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode) +{ + smooth(sd, ss, nodes, totnode, ss->cache->bstrength); +} + +static void do_draw_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode) { Brush *brush = paint_brush(&sd->paint); + float offset[3], area_normal[3]; float bstrength= ss->cache->bstrength; int n; - //#pragma omp parallel for private(n) schedule(static) + calc_sculpt_normal(sd, ss, area_normal, nodes, totnode); + + /* offset with as much as possible factored in already */ + mul_v3_v3fl(offset, area_normal, ss->cache->radius); + mul_v3_v3(offset, ss->cache->scale); + mul_v3_fl(offset, bstrength); + + /* threaded loop over nodes */ + #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) for(n=0; n<totnode; n++) { PBVHVertexIter vd; SculptBrushTest test; - - sculpt_undo_push_node(ss, nodes[n]); + float (*proxy)[3]; + + proxy= BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co; + + sculpt_brush_test_init(ss, &test); + + BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { + if (sculpt_brush_test(&test, vd.co)) { + //if(sculpt_brush_test_cyl(&test, vd.co, ss->cache->location, area_normal)) { + /* offset vertex */ + float fade = tex_strength(ss, brush, vd.co, test.dist)*frontface(brush, area_normal, vd.no, vd.fno); + + mul_v3_v3fl(proxy[vd.i], offset, fade); + + if(vd.mvert) + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + } + BLI_pbvh_vertex_iter_end; + } +} + +static void do_crease_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode) +{ + Brush *brush = paint_brush(&sd->paint); + float offset[3], area_normal[3]; + float bstrength= ss->cache->bstrength; + float flippedbstrength, crease_correction; + int n; + + calc_sculpt_normal(sd, ss, area_normal, nodes, totnode); + + /* offset with as much as possible factored in already */ + mul_v3_v3fl(offset, area_normal, ss->cache->radius); + mul_v3_v3(offset, ss->cache->scale); + mul_v3_fl(offset, bstrength); + + /* we divide out the squared alpha and multiply by the squared crease to give us the pinch strength */ + + if(sculpt_get_brush_alpha(brush) > 0.0f) + crease_correction = brush->crease_pinch_factor*brush->crease_pinch_factor/(sculpt_get_brush_alpha(brush)*sculpt_get_brush_alpha(brush)); + else + crease_correction = brush->crease_pinch_factor*brush->crease_pinch_factor; + + /* we always want crease to pinch or blob to relax even when draw is negative */ + flippedbstrength = (bstrength < 0) ? -crease_correction*bstrength : crease_correction*bstrength; + + if(brush->sculpt_tool == SCULPT_TOOL_BLOB) flippedbstrength *= -1.0f; + + /* threaded loop over nodes */ + #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) + for(n=0; n<totnode; n++) { + PBVHVertexIter vd; + SculptBrushTest test; + float (*proxy)[3]; + + proxy= BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co; + sculpt_brush_test_init(ss, &test); BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { if(sculpt_brush_test(&test, vd.co)) { - float fade = tex_strength(ss, brush, vd.co, test.dist)*bstrength; - float val[3]= {vd.co[0]+(test.location[0]-vd.co[0])*fade, - vd.co[1]+(test.location[1]-vd.co[1])*fade, - vd.co[2]+(test.location[2]-vd.co[2])*fade}; + /* offset vertex */ + const float fade = tex_strength(ss, brush, vd.co, test.dist)*frontface(brush, area_normal, vd.no, vd.fno); + float val1[3]; + float val2[3]; + + /* first we pinch */ + sub_v3_v3v3(val1, test.location, vd.co); + //mul_v3_v3(val1, ss->cache->scale); + mul_v3_fl(val1, fade*flippedbstrength); - sculpt_clip(sd, ss, vd.co, val); - if(vd.mvert) vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + /* then we draw */ + mul_v3_v3fl(val2, offset, fade); + + add_v3_v3v3(proxy[vd.i], val1, val2); + + if(vd.mvert) + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; } } BLI_pbvh_vertex_iter_end; + } +} + +static void do_pinch_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode) +{ + Brush *brush = paint_brush(&sd->paint); + float bstrength= ss->cache->bstrength; + int n; + + #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) + for(n=0; n<totnode; n++) { + PBVHVertexIter vd; + SculptBrushTest test; + float (*proxy)[3]; + + proxy= BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co; + + sculpt_brush_test_init(ss, &test); + + BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { + if(sculpt_brush_test(&test, vd.co)) { + float fade = bstrength*tex_strength(ss, brush, vd.co, test.dist)*frontface(brush, ss->cache->view_normal, vd.no, vd.fno); + float val[3]; + + sub_v3_v3v3(val, test.location, vd.co); + mul_v3_v3fl(proxy[vd.i], val, fade); - BLI_pbvh_node_mark_update(nodes[n]); + if(vd.mvert) + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + } + BLI_pbvh_vertex_iter_end; } } static void do_grab_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode) { - Brush *brush = paint_brush(&sd->paint); + Brush *brush= paint_brush(&sd->paint); float bstrength= ss->cache->bstrength; - float grab_delta[3]; + float grab_delta[3], an[3]; int n; - + float len; + + if (brush->normal_weight > 0) + calc_sculpt_normal(sd, ss, an, nodes, totnode); + copy_v3_v3(grab_delta, ss->cache->grab_delta_symmetry); - //#pragma omp parallel for private(n) schedule(static) + len = len_v3(grab_delta); + + if (brush->normal_weight > 0) { + mul_v3_fl(an, len*brush->normal_weight); + mul_v3_fl(grab_delta, 1.0f - brush->normal_weight); + add_v3_v3(grab_delta, an); + } + + #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) for(n=0; n<totnode; n++) { PBVHVertexIter vd; + SculptUndoNode* unode; SculptBrushTest test; float (*origco)[3]; - - origco= sculpt_undo_push_node(ss, nodes[n])->co; + short (*origno)[3]; + float (*proxy)[3]; + + unode= sculpt_undo_push_node(ss, nodes[n]); + origco= unode->co; + origno= unode->no; + + proxy= BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co; + + sculpt_brush_test_init(ss, &test); + + BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { + if(sculpt_brush_test(&test, origco[vd.i])) { + const float fade = bstrength*tex_strength(ss, brush, origco[vd.i], test.dist)*frontface(brush, an, origno[vd.i], NULL); + + mul_v3_v3fl(proxy[vd.i], grab_delta, fade); + + if(vd.mvert) + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + } + BLI_pbvh_vertex_iter_end; + } +} + +static void do_nudge_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode) +{ + Brush *brush = paint_brush(&sd->paint); + float bstrength = ss->cache->bstrength; + float grab_delta[3]; + int n; + float an[3]; + float tmp[3], cono[3]; + + copy_v3_v3(grab_delta, ss->cache->grab_delta_symmetry); + + calc_sculpt_normal(sd, ss, an, nodes, totnode); + + cross_v3_v3v3(tmp, an, grab_delta); + cross_v3_v3v3(cono, tmp, an); + + #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) + for(n = 0; n < totnode; n++) { + PBVHVertexIter vd; + SculptBrushTest test; + float (*proxy)[3]; + + proxy= BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co; + + sculpt_brush_test_init(ss, &test); + + BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { + if(sculpt_brush_test(&test, vd.co)) { + const float fade = bstrength*tex_strength(ss, brush, vd.co, test.dist)*frontface(brush, an, vd.no, vd.fno); + + mul_v3_v3fl(proxy[vd.i], cono, fade); + + if(vd.mvert) + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + } + BLI_pbvh_vertex_iter_end; + } +} + +static void do_snake_hook_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode) +{ + Brush *brush = paint_brush(&sd->paint); + float bstrength = ss->cache->bstrength; + float grab_delta[3], an[3]; + int n; + float len; + + if (brush->normal_weight > 0) + calc_sculpt_normal(sd, ss, an, nodes, totnode); + + copy_v3_v3(grab_delta, ss->cache->grab_delta_symmetry); + + len = len_v3(grab_delta); + + if (bstrength < 0) + negate_v3(grab_delta); + + if (brush->normal_weight > 0) { + mul_v3_fl(an, len*brush->normal_weight); + mul_v3_fl(grab_delta, 1.0f - brush->normal_weight); + add_v3_v3(grab_delta, an); + } + + #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) + for(n = 0; n < totnode; n++) { + PBVHVertexIter vd; + SculptBrushTest test; + float (*proxy)[3]; + + proxy= BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co; + + sculpt_brush_test_init(ss, &test); + + BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { + if(sculpt_brush_test(&test, vd.co)) { + const float fade = bstrength*tex_strength(ss, brush, vd.co, test.dist)*frontface(brush, an, vd.no, vd.fno); + + mul_v3_v3fl(proxy[vd.i], grab_delta, fade); + + if(vd.mvert) + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + } + BLI_pbvh_vertex_iter_end; + } +} + +static void do_thumb_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode) +{ + Brush *brush = paint_brush(&sd->paint); + float bstrength = ss->cache->bstrength; + float grab_delta[3]; + int n; + float an[3]; + float tmp[3], cono[3]; + + copy_v3_v3(grab_delta, ss->cache->grab_delta_symmetry); + + calc_sculpt_normal(sd, ss, an, nodes, totnode); + + cross_v3_v3v3(tmp, an, grab_delta); + cross_v3_v3v3(cono, tmp, an); + + #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) + for(n = 0; n < totnode; n++) { + PBVHVertexIter vd; + SculptUndoNode* unode; + SculptBrushTest test; + float (*origco)[3]; + short (*origno)[3]; + float (*proxy)[3]; + + unode= sculpt_undo_push_node(ss, nodes[n]); + origco= unode->co; + origno= unode->no; + + proxy= BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co; + sculpt_brush_test_init(ss, &test); BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { if(sculpt_brush_test(&test, origco[vd.i])) { - float fade = tex_strength(ss, brush, origco[vd.i], test.dist)*bstrength; - float add[3]= {vd.co[0]+fade*grab_delta[0], - vd.co[1]+fade*grab_delta[1], - vd.co[2]+fade*grab_delta[2]}; + const float fade = bstrength*tex_strength(ss, brush, origco[vd.i], test.dist)*frontface(brush, an, origno[vd.i], NULL); + + mul_v3_v3fl(proxy[vd.i], cono, fade); - sculpt_clip(sd, ss, vd.co, add); - if(vd.mvert) vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + if(vd.mvert) + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; } } BLI_pbvh_vertex_iter_end; + } +} + +static void do_rotate_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode) +{ + Brush *brush= paint_brush(&sd->paint); + float bstrength= ss->cache->bstrength; + float an[3]; + int n; + float m[3][3]; + static const int flip[8] = { 1, -1, -1, 1, -1, 1, 1, -1 }; + float angle = ss->cache->vertex_rotation * flip[ss->cache->mirror_symmetry_pass]; + + calc_sculpt_normal(sd, ss, an, nodes, totnode); + + axis_angle_to_mat3(m, an, angle); + + #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) + for(n=0; n<totnode; n++) { + PBVHVertexIter vd; + SculptUndoNode* unode; + SculptBrushTest test; + float (*origco)[3]; + short (*origno)[3]; + float (*proxy)[3]; + + unode= sculpt_undo_push_node(ss, nodes[n]); + origco= unode->co; + origno= unode->no; + + proxy= BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co; + + sculpt_brush_test_init(ss, &test); + + BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { + if(sculpt_brush_test(&test, origco[vd.i])) { + const float fade = bstrength*tex_strength(ss, brush, origco[vd.i], test.dist)*frontface(brush, an, origno[vd.i], NULL); + + mul_v3_m3v3(proxy[vd.i], m, origco[vd.i]); + sub_v3_v3(proxy[vd.i], origco[vd.i]); + mul_v3_fl(proxy[vd.i], fade); - BLI_pbvh_node_mark_update(nodes[n]); + if(vd.mvert) + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + } + BLI_pbvh_vertex_iter_end; } } @@ -1177,63 +1567,66 @@ static void do_layer_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int float lim= ss->cache->radius / 4; int n; - if(ss->cache->flip) + if(bstrength < 0) lim = -lim; - calc_area_normal(sd, ss, area_normal, nodes, totnode); + calc_sculpt_normal(sd, ss, area_normal, nodes, totnode); - offset[0]= ss->cache->scale[0]*area_normal[0]; - offset[1]= ss->cache->scale[1]*area_normal[1]; - offset[2]= ss->cache->scale[2]*area_normal[2]; + mul_v3_v3v3(offset, ss->cache->scale, area_normal); - //#pragma omp parallel for private(n) schedule(static) + #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) for(n=0; n<totnode; n++) { PBVHVertexIter vd; SculptBrushTest test; SculptUndoNode *unode; float (*origco)[3], *layer_disp; + //float (*proxy)[3]; // XXX layer brush needs conversion to proxy but its more complicated + + //proxy= BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co; unode= sculpt_undo_push_node(ss, nodes[n]); origco=unode->co; if(!unode->layer_disp) - unode->layer_disp= MEM_callocN(sizeof(float)*unode->totvert, "layer disp"); + { + #pragma omp critical + unode->layer_disp= MEM_callocN(sizeof(float)*unode->totvert, "layer disp"); + } + layer_disp= unode->layer_disp; sculpt_brush_test_init(ss, &test); BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { if(sculpt_brush_test(&test, vd.co)) { - float fade = tex_strength(ss, brush, vd.co, test.dist)*bstrength; + const float fade = bstrength*ss->cache->radius*tex_strength(ss, brush, vd.co, test.dist)*frontface(brush, area_normal, vd.no, vd.fno); float *disp= &layer_disp[vd.i]; float val[3]; - + *disp+= fade; - + /* Don't let the displacement go past the limit */ if((lim < 0 && *disp < lim) || (lim > 0 && *disp > lim)) *disp = lim; - + + mul_v3_v3fl(val, offset, *disp); + if(ss->layer_co && (brush->flag & BRUSH_PERSISTENT)) { int index= vd.vert_indices[vd.i]; /* persistent base */ - val[0] = ss->layer_co[index][0] + (*disp)*offset[0]; - val[1] = ss->layer_co[index][1] + (*disp)*offset[1]; - val[2] = ss->layer_co[index][2] + (*disp)*offset[2]; + add_v3_v3(val, ss->layer_co[index]); } else { - val[0] = origco[vd.i][0] + (*disp)*offset[0]; - val[1] = origco[vd.i][1] + (*disp)*offset[1]; - val[2] = origco[vd.i][2] + (*disp)*offset[2]; + add_v3_v3(val, origco[vd.i]); } sculpt_clip(sd, ss, vd.co, val); - if(vd.mvert) vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + + if(vd.mvert) + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; } } BLI_pbvh_vertex_iter_end; - - BLI_pbvh_node_mark_update(nodes[n]); } } @@ -1243,177 +1636,634 @@ static void do_inflate_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, in float bstrength= ss->cache->bstrength; int n; - //#pragma omp parallel for private(n) schedule(static) + #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) for(n=0; n<totnode; n++) { PBVHVertexIter vd; SculptBrushTest test; - - sculpt_undo_push_node(ss, nodes[n]); + float (*proxy)[3]; + + proxy= BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co; + sculpt_brush_test_init(ss, &test); BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { if(sculpt_brush_test(&test, vd.co)) { - float fade = tex_strength(ss, brush, vd.co, test.dist)*bstrength; - float add[3]; + const float fade = bstrength*tex_strength(ss, brush, vd.co, test.dist)*frontface(brush, ss->cache->view_normal, vd.no, vd.fno); + float val[3]; - if(vd.fno) copy_v3_v3(add, vd.fno); - else normal_short_to_float_v3(add, vd.no); + if(vd.fno) copy_v3_v3(val, vd.fno); + else normal_short_to_float_v3(val, vd.no); - mul_v3_fl(add, fade * ss->cache->radius); - add[0]*= ss->cache->scale[0]; - add[1]*= ss->cache->scale[1]; - add[2]*= ss->cache->scale[2]; - add_v3_v3(add, vd.co); - - sculpt_clip(sd, ss, vd.co, add); - if(vd.mvert) vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + mul_v3_fl(val, fade * ss->cache->radius); + mul_v3_v3v3(proxy[vd.i], val, ss->cache->scale); + + if(vd.mvert) + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; } } BLI_pbvh_vertex_iter_end; - - BLI_pbvh_node_mark_update(nodes[n]); } } -static void calc_flatten_center(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode, float co[3]) +static void calc_flatten_center(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode, float fc[3]) { - float outer_dist[FLATTEN_SAMPLE_SIZE]; - float outer_co[FLATTEN_SAMPLE_SIZE][3]; - int i, n; - - for(i = 0; i < FLATTEN_SAMPLE_SIZE; ++i) { - zero_v3(outer_co[i]); - outer_dist[i]= -1.0f; + int n; + + float count = 0; + + zero_v3(fc); + + #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) + for(n=0; n<totnode; n++) { + PBVHVertexIter vd; + SculptBrushTest test; + SculptUndoNode *unode; + float private_fc[3] = {0.0f, 0.0f, 0.0f}; + int private_count = 0; + + unode = sculpt_undo_push_node(ss, nodes[n]); + sculpt_brush_test_init(ss, &test); + + if(ss->cache->original) { + BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { + if(sculpt_brush_test_fast(&test, unode->co[vd.i])) { + add_v3_v3(private_fc, vd.co); + private_count++; + } + } + BLI_pbvh_vertex_iter_end; + } + else { + BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { + if(sculpt_brush_test_fast(&test, vd.co)) { + add_v3_v3(private_fc, vd.co); + private_count++; + } + } + BLI_pbvh_vertex_iter_end; + } + + #pragma omp critical + { + add_v3_v3(fc, private_fc); + count += private_count; + } } - - //#pragma omp parallel for private(n) schedule(static) + + mul_v3_fl(fc, 1.0f / count); +} + +/* 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, SculptSession *ss, PBVHNode **nodes, int totnode, float an[3], float fc[3]) +{ + int n; + + // an + float out_flip[3] = {0.0f, 0.0f, 0.0f}; + + // fc + float count = 0; + + // an + zero_v3(an); + + // fc + zero_v3(fc); + + #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) for(n=0; n<totnode; n++) { PBVHVertexIter vd; SculptBrushTest test; - int j; - - sculpt_undo_push_node(ss, nodes[n]); + 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}; + int private_count = 0; + + unode = sculpt_undo_push_node(ss, nodes[n]); sculpt_brush_test_init(ss, &test); - BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { - if(sculpt_brush_test(&test, vd.co)) { - for(j = 0; j < FLATTEN_SAMPLE_SIZE; ++j) { - if(test.dist > outer_dist[j]) { - copy_v3_v3(outer_co[j], vd.co); - outer_dist[j] = test.dist; - break; + if(ss->cache->original) { + BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { + if(sculpt_brush_test_fast(&test, unode->co[vd.i])) { + // an + 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); + + // fc + add_v3_v3(private_fc, vd.co); + private_count++; + } + } + BLI_pbvh_vertex_iter_end; + } + else { + BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { + if(sculpt_brush_test_fast(&test, vd.co)) { + // an + 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); + } + + // fc + add_v3_v3(private_fc, vd.co); + private_count++; } } + BLI_pbvh_vertex_iter_end; } - BLI_pbvh_vertex_iter_end; - BLI_pbvh_node_mark_update(nodes[n]); + #pragma omp critical + { + // an + add_v3_v3(an, private_an); + add_v3_v3(out_flip, private_out_flip); + + // fc + add_v3_v3(fc, private_fc); + count += private_count; + } + } + + // an + if (is_zero_v3(an)) + copy_v3_v3(an, out_flip); + + normalize_v3(an); + + // fc + mul_v3_fl(fc, 1.0f / count); +} + +static void calc_sculpt_plane(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode, float an[3], float fc[3]) +{ + Brush *brush = paint_brush(&sd->paint); + + if (ss->cache->mirror_symmetry_pass == 0 && + ss->cache->radial_symmetry_pass == 0 && + (ss->cache->first_time || !(brush->flag & BRUSH_ORIGINAL_NORMAL))) + { + switch (brush->sculpt_plane) { + case SCULPT_DISP_DIR_VIEW: + viewvector(ss->cache->vc->rv3d, ss->cache->vc->rv3d->twmat[3], an); + break; + + case SCULPT_DISP_DIR_X: + an[1] = 0.0; + an[2] = 0.0; + an[0] = 1.0; + break; + + case SCULPT_DISP_DIR_Y: + an[0] = 0.0; + an[2] = 0.0; + an[1] = 1.0; + break; + + case SCULPT_DISP_DIR_Z: + an[0] = 0.0; + an[1] = 0.0; + an[2] = 1.0; + break; + + case SCULPT_DISP_DIR_AREA: + calc_area_normal_and_flatten_center(sd, ss, nodes, totnode, an, fc); + + default: + break; + } + + // fc + /* 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, ss, nodes, totnode, fc); + + // an + copy_v3_v3(ss->cache->last_area_normal, an); + + // fc + copy_v3_v3(ss->cache->last_center, fc); + } + else { + // an + copy_v3_v3(an, ss->cache->last_area_normal); + + // fc + copy_v3_v3(fc, ss->cache->last_center); + + // an + flip_coord(an, an, ss->cache->mirror_symmetry_pass); + + // fc + flip_coord(fc, fc, ss->cache->mirror_symmetry_pass); + + // an + mul_m4_v3(ss->cache->symm_rot_mat, an); + + // fc + mul_m4_v3(ss->cache->symm_rot_mat, fc); } - - co[0] = co[1] = co[2] = 0.0f; - for(i = 0; i < FLATTEN_SAMPLE_SIZE; ++i) - if(outer_dist[i] >= 0.0f) - add_v3_v3(co, outer_co[i]); - mul_v3_fl(co, 1.0f / FLATTEN_SAMPLE_SIZE); } /* 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]) { - float p1[3], sub1[3], sub2[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); +} - /* Find the intersection between squash-plane and vertex (along the area normal) */ - sub_v3_v3v3(p1, co, plane_normal); - sub_v3_v3v3(sub1, plane_center, p1); - sub_v3_v3v3(sub2, co, p1); - sub_v3_v3v3(intr, co, p1); - mul_v3_fl(intr, dot_v3v3(plane_normal, sub1) / dot_v3v3(plane_normal, sub2)); - add_v3_v3(intr, p1); +static int plane_trim(StrokeCache *cache, Brush *brush, 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(float co[3], float plane_normal[3], float plane_center[3], int flip) +static int plane_point_side_flip(float co[3], float plane_normal[3], float plane_center[3], int flip) { - float delta[3]; - float d; + float delta[3]; + float d; - sub_v3_v3v3(delta, co, plane_center); - d = dot_v3v3(plane_normal, delta); + sub_v3_v3v3(delta, co, plane_center); + d = dot_v3v3(plane_normal, delta); - if(flip) - d = -d; + if (flip) d = -d; + + return d <= 0.0f; +} + +static int plane_point_side(float co[3], float plane_normal[3], float plane_center[3]) +{ + float delta[3]; + + sub_v3_v3v3(delta, co, plane_center); + return dot_v3v3(plane_normal, delta) <= 0.0f; +} + +static float get_offset(Sculpt *sd, SculptSession *ss) +{ + Brush* brush = paint_brush(&sd->paint); + + float rv = brush->plane_offset; + + if (brush->flag & BRUSH_OFFSET_PRESSURE) { + rv *= ss->cache->pressure; + } - return d <= 0.0f; + return rv; } -static void do_flatten_clay_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode, int clay) +static void do_flatten_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode) { - /* area_normal and cntr define the plane towards which vertices are squashed */ Brush *brush = paint_brush(&sd->paint); - float bstrength= ss->cache->bstrength; - float area_normal[3]; - float cntr[3], cntr2[3] = {0}, bstr = 0; - int n, flip = 0; - calc_area_normal(sd, ss, area_normal, nodes, totnode); - calc_flatten_center(sd, ss, nodes, totnode, cntr); + float bstrength = ss->cache->bstrength; + const float radius = ss->cache->radius; + + float an[3]; + float fc[3]; + + float offset = get_offset(sd, ss); + + float displace; + + int n; + + float temp[3]; + + calc_sculpt_plane(sd, ss, nodes, totnode, an, fc); + + displace = radius*offset; + + mul_v3_v3v3(temp, an, ss->cache->scale); + mul_v3_fl(temp, displace); + add_v3_v3(fc, temp); + + #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) + for(n = 0; n < totnode; n++) { + PBVHVertexIter vd; + SculptBrushTest test; + float (*proxy)[3]; + + proxy= BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co; + + sculpt_brush_test_init(ss, &test); + + BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { + if (sculpt_brush_test_sq(&test, vd.co)) { + float intr[3]; + float val[3]; + + point_plane_project(intr, vd.co, an, fc); - if(clay) { - bstr= brush_strength(sd, ss->cache); - /* Limit clay application to here */ - cntr2[0]=cntr[0]+area_normal[0]*bstr*ss->cache->scale[0]; - cntr2[1]=cntr[1]+area_normal[1]*bstr*ss->cache->scale[1]; - cntr2[2]=cntr[2]+area_normal[2]*bstr*ss->cache->scale[2]; - flip = bstr < 0; + sub_v3_v3v3(val, intr, vd.co); + + if (plane_trim(ss->cache, brush, val)) { + const float fade = bstrength*tex_strength(ss, brush, vd.co, sqrt(test.dist))*frontface(brush, an, vd.no, vd.fno); + + mul_v3_v3fl(proxy[vd.i], val, fade); + + if(vd.mvert) + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + } + } + BLI_pbvh_vertex_iter_end; } +} - //#pragma omp parallel for private(n) schedule(static) - for(n=0; n<totnode; n++) { +static void do_clay_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode) +{ + Brush *brush = paint_brush(&sd->paint); + + float bstrength = ss->cache->bstrength; + float radius = ss->cache->radius; + float offset = get_offset(sd, ss); + + float displace; + + float an[3]; // area normal + float fc[3]; // flatten center + + int n; + + float temp[3]; + //float p[3]; + + int flip; + + calc_sculpt_plane(sd, ss, nodes, totnode, an, fc); + + flip = bstrength < 0; + + if (flip) { + bstrength = -bstrength; + radius = -radius; + } + + displace = radius * (0.25f+offset); + + mul_v3_v3v3(temp, an, ss->cache->scale); + mul_v3_fl(temp, displace); + add_v3_v3(fc, temp); + + //add_v3_v3v3(p, ss->cache->location, an); + + #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) + for (n = 0; n < totnode; n++) { PBVHVertexIter vd; SculptBrushTest test; - - sculpt_undo_push_node(ss, nodes[n]); + float (*proxy)[3]; + + proxy= BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co; + sculpt_brush_test_init(ss, &test); BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { - if(sculpt_brush_test(&test, vd.co)) { - float intr[3], val[3]; - - if(!clay || plane_point_side(vd.co, area_normal, cntr2, flip)) { - const float fade = tex_strength(ss, brush, vd.co, test.dist)*bstrength; + if (sculpt_brush_test_sq(&test, vd.co)) { + if (plane_point_side_flip(vd.co, an, fc, flip)) { + //if (sculpt_brush_test_cyl(&test, vd.co, ss->cache->location, p)) { + float intr[3]; + float val[3]; + + point_plane_project(intr, vd.co, an, fc); + + sub_v3_v3v3(val, intr, vd.co); + + if (plane_trim(ss->cache, brush, val)) { + const float fade = bstrength*tex_strength(ss, brush, vd.co, sqrt(test.dist))*frontface(brush, an, vd.no, vd.fno); + + mul_v3_v3fl(proxy[vd.i], val, fade); + + if(vd.mvert) + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + } + } + } + BLI_pbvh_vertex_iter_end; + } +} + +static void do_clay_tubes_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode) +{ + Brush *brush = paint_brush(&sd->paint); + + float bstrength = ss->cache->bstrength; + float radius = ss->cache->radius; + float offset = get_offset(sd, ss); + + float displace; + + float sn[3]; // sculpt normal + float an[3]; // area normal + float fc[3]; // flatten center + + int n; + + float temp[3]; + float mat[4][4]; + float scale[4][4]; + float tmat[4][4]; + + int flip; + + calc_sculpt_plane(sd, ss, nodes, totnode, sn, fc); + + if (brush->sculpt_plane != SCULPT_DISP_DIR_AREA || (brush->flag & BRUSH_ORIGINAL_NORMAL)) + calc_area_normal(sd, ss, an, nodes, totnode); + else + copy_v3_v3(an, sn); - /* Find the intersection between squash-plane and vertex (along the area normal) */ - point_plane_project(intr, vd.co, area_normal, cntr); + if (ss->cache->first_time) + return; // delay the first daub because grab delta is not setup + + flip = bstrength < 0; + + if (flip) { + bstrength = -bstrength; + radius = -radius; + } + + displace = radius * (0.25f+offset); + + mul_v3_v3v3(temp, sn, ss->cache->scale); + mul_v3_fl(temp, displace); + add_v3_v3(fc, temp); + + cross_v3_v3v3(mat[0], an, ss->cache->grab_delta_symmetry); mat[0][3] = 0; + cross_v3_v3v3(mat[1], an, mat[0]); mat[1][3] = 0; + copy_v3_v3(mat[2], an); mat[2][3] = 0; + copy_v3_v3(mat[3], ss->cache->location); mat[3][3] = 1; + normalize_m4(mat); + scale_m4_fl(scale, ss->cache->radius); + mul_m4_m4m4(tmat, scale, mat); + invert_m4_m4(mat, tmat); + + #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) + for (n = 0; n < totnode; n++) { + PBVHVertexIter vd; + SculptBrushTest test; + float (*proxy)[3]; + + proxy= BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co; + + sculpt_brush_test_init(ss, &test); + + BLI_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)) { + float intr[3]; + float val[3]; + + point_plane_project(intr, vd.co, sn, fc); sub_v3_v3v3(val, intr, vd.co); - if(clay) { - if(bstr > FLT_EPSILON) - mul_v3_fl(val, fade / bstr); - else - mul_v3_fl(val, fade); - /* Clay displacement */ - val[0]+=area_normal[0] * ss->cache->scale[0]*fade; - val[1]+=area_normal[1] * ss->cache->scale[1]*fade; - val[2]+=area_normal[2] * ss->cache->scale[2]*fade; + if (plane_trim(ss->cache, brush, val)) { + const float fade = bstrength*tex_strength(ss, brush, vd.co, ss->cache->radius*test.dist)*frontface(brush, an, vd.no, vd.fno); + + mul_v3_v3fl(proxy[vd.i], val, fade); + + if(vd.mvert) + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; } - else - mul_v3_fl(val, fabs(fade)); + } + } + } + BLI_pbvh_vertex_iter_end; + } +} + +static void do_fill_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode) +{ + Brush *brush = paint_brush(&sd->paint); - add_v3_v3(val, vd.co); + float bstrength = ss->cache->bstrength; + const float radius = ss->cache->radius; - sculpt_clip(sd, ss, vd.co, val); - if(vd.mvert) vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + float an[3]; + float fc[3]; + float offset = get_offset(sd, ss); + + float displace; + + int n; + + float temp[3]; + + calc_sculpt_plane(sd, ss, nodes, totnode, an, fc); + + displace = radius*offset; + + mul_v3_v3v3(temp, an, ss->cache->scale); + mul_v3_fl(temp, displace); + add_v3_v3(fc, temp); + + #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) + for (n = 0; n < totnode; n++) { + PBVHVertexIter vd; + SculptBrushTest test; + float (*proxy)[3]; + + proxy= BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co; + + sculpt_brush_test_init(ss, &test); + + BLI_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)) { + float intr[3]; + float val[3]; + + point_plane_project(intr, vd.co, an, fc); + + sub_v3_v3v3(val, intr, vd.co); + + if (plane_trim(ss->cache, brush, val)) { + const float fade = bstrength*tex_strength(ss, brush, vd.co, sqrt(test.dist))*frontface(brush, an, vd.no, vd.fno); + + mul_v3_v3fl(proxy[vd.i], val, fade); + + if(vd.mvert) + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } } } } BLI_pbvh_vertex_iter_end; + } +} + +static void do_scrape_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode) +{ + Brush *brush = paint_brush(&sd->paint); + + float bstrength = ss->cache->bstrength; + const float radius = ss->cache->radius; + + float an[3]; + float fc[3]; + float offset = get_offset(sd, ss); - BLI_pbvh_node_mark_update(nodes[n]); + float displace; + + int n; + + float temp[3]; + + calc_sculpt_plane(sd, ss, nodes, totnode, an, fc); + + displace = -radius*offset; + + mul_v3_v3v3(temp, an, ss->cache->scale); + mul_v3_fl(temp, displace); + add_v3_v3(fc, temp); + + #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) + for (n = 0; n < totnode; n++) { + PBVHVertexIter vd; + SculptBrushTest test; + float (*proxy)[3]; + + proxy= BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co; + + sculpt_brush_test_init(ss, &test); + + BLI_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)) { + float intr[3]; + float val[3]; + + point_plane_project(intr, vd.co, an, fc); + + sub_v3_v3v3(val, intr, vd.co); + + if (plane_trim(ss->cache, brush, val)) { + const float fade = bstrength*tex_strength(ss, brush, vd.co, sqrt(test.dist))*frontface(brush, an, vd.no, vd.fno); + + mul_v3_v3fl(proxy[vd.i], val, fade); + + if(vd.mvert) + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + } + } + } + BLI_pbvh_vertex_iter_end; } } -static void sculpt_vertcos_to_key(Object *ob, KeyBlock *kb, float (*vertCos)[3]) +void sculpt_vertcos_to_key(Object *ob, KeyBlock *kb, float (*vertCos)[3]) { Mesh *me= (Mesh*)ob->data; float (*ofs)[3]= NULL; @@ -1475,36 +2325,27 @@ static void sculpt_update_keyblock(SculptSession *ss) } } -static void do_brush_action(Sculpt *sd, SculptSession *ss, StrokeCache *cache) +static void do_brush_action(Sculpt *sd, SculptSession *ss, Brush *brush) { SculptSearchSphereData data; - Brush *brush = paint_brush(&sd->paint); - PBVHNode **nodes= NULL; - int totnode; + PBVHNode **nodes = NULL; + int n, totnode; + /* Build a list of all nodes that are potentially within the brush's area of influence */ data.ss = ss; data.sd = sd; - data.radius_squared = ss->cache->radius * ss->cache->radius; - - /* Build a list of all nodes that are potentially within the brush's - area of influence */ - if(brush->sculpt_tool == SCULPT_TOOL_GRAB) { - data.original= 1; - BLI_pbvh_search_gather(ss->pbvh, sculpt_search_sphere_cb, &data, - &nodes, &totnode); - - if(cache->first_time) - copy_v3_v3(ss->cache->grab_active_location[ss->cache->symmetry], ss->cache->location); - else - copy_v3_v3(ss->cache->location, ss->cache->grab_active_location[ss->cache->symmetry]); - } - else { - BLI_pbvh_search_gather(ss->pbvh, sculpt_search_sphere_cb, &data, - &nodes, &totnode); - } + data.radius_squared = ss->cache->radius_squared; + data.original = ELEM4(brush->sculpt_tool, SCULPT_TOOL_GRAB, SCULPT_TOOL_ROTATE, SCULPT_TOOL_THUMB, SCULPT_TOOL_LAYER); + BLI_pbvh_search_gather(ss->pbvh, sculpt_search_sphere_cb, &data, &nodes, &totnode); /* Only act if some verts are inside the brush area */ - if(totnode) { + if (totnode) { + #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) + for (n= 0; n < totnode; n++) { + sculpt_undo_push_node(ss, nodes[n]); + BLI_pbvh_node_mark_update(nodes[n]); + } + /* Apply one type of brush action */ switch(brush->sculpt_tool){ case SCULPT_TOOL_DRAW: @@ -1513,6 +2354,12 @@ static void do_brush_action(Sculpt *sd, SculptSession *ss, StrokeCache *cache) case SCULPT_TOOL_SMOOTH: do_smooth_brush(sd, ss, nodes, totnode); break; + case SCULPT_TOOL_CREASE: + do_crease_brush(sd, ss, nodes, totnode); + break; + case SCULPT_TOOL_BLOB: + do_crease_brush(sd, ss, nodes, totnode); + break; case SCULPT_TOOL_PINCH: do_pinch_brush(sd, ss, nodes, totnode); break; @@ -1522,57 +2369,244 @@ static void do_brush_action(Sculpt *sd, SculptSession *ss, StrokeCache *cache) case SCULPT_TOOL_GRAB: do_grab_brush(sd, ss, nodes, totnode); break; + case SCULPT_TOOL_ROTATE: + do_rotate_brush(sd, ss, nodes, totnode); + break; + case SCULPT_TOOL_SNAKE_HOOK: + do_snake_hook_brush(sd, ss, nodes, totnode); + break; + case SCULPT_TOOL_NUDGE: + do_nudge_brush(sd, ss, nodes, totnode); + break; + case SCULPT_TOOL_THUMB: + do_thumb_brush(sd, ss, nodes, totnode); + break; case SCULPT_TOOL_LAYER: do_layer_brush(sd, ss, nodes, totnode); break; case SCULPT_TOOL_FLATTEN: - do_flatten_clay_brush(sd, ss, nodes, totnode, 0); + do_flatten_brush(sd, ss, nodes, totnode); break; case SCULPT_TOOL_CLAY: - do_flatten_clay_brush(sd, ss, nodes, totnode, 1); + do_clay_brush(sd, ss, nodes, totnode); + break; + case SCULPT_TOOL_CLAY_TUBES: + do_clay_tubes_brush(sd, ss, nodes, totnode); + break; + case SCULPT_TOOL_FILL: + do_fill_brush(sd, ss, nodes, totnode); + break; + case SCULPT_TOOL_SCRAPE: + do_scrape_brush(sd, ss, nodes, totnode); break; } + if (brush->sculpt_tool != SCULPT_TOOL_SMOOTH && brush->autosmooth_factor > 0) { + if (brush->flag & BRUSH_INVERSE_SMOOTH_PRESSURE) { + smooth(sd, ss, nodes, totnode, brush->autosmooth_factor*(1-ss->cache->pressure)*ss->cache->autosmooth_overlap); + } + else { + smooth(sd, ss, nodes, totnode, brush->autosmooth_factor*ss->cache->autosmooth_overlap); + } + } + + /* copy the modified vertices from mesh to the active key */ + if(ss->kb) + mesh_to_key(ss->ob->data, ss->kb); + /* optimization: we could avoid copying new coords to keyblock at each */ /* stroke step if there are no modifiers due to pbvh is used for displaying */ /* so to increase speed we'll copy new coords to keyblock when stroke is done */ if(ss->kb && ss->modifiers_active) sculpt_update_keyblock(ss); - if(nodes) - MEM_freeN(nodes); + MEM_freeN(nodes); + } +} + +static void sculpt_combine_proxies(Sculpt *sd, SculptSession *ss) +{ + Brush *brush= paint_brush(&sd->paint); + PBVHNode** nodes; + int totnode; + int n; + + BLI_pbvh_gather_proxies(ss->pbvh, &nodes, &totnode); + + switch (brush->sculpt_tool) { + case SCULPT_TOOL_GRAB: + case SCULPT_TOOL_ROTATE: + case SCULPT_TOOL_THUMB: + #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) + for (n= 0; n < totnode; n++) { + PBVHVertexIter vd; + PBVHProxyNode* proxies; + int proxy_count; + float (*origco)[3]; + + origco= sculpt_undo_push_node(ss, nodes[n])->co; + + BLI_pbvh_node_get_proxies(nodes[n], &proxies, &proxy_count); + + BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { + float val[3]; + int p; + + copy_v3_v3(val, origco[vd.i]); + + for (p= 0; p < proxy_count; p++) + add_v3_v3(val, proxies[p].co[vd.i]); + + sculpt_clip(sd, ss, vd.co, val); + } + BLI_pbvh_vertex_iter_end; + + BLI_pbvh_node_free_proxies(nodes[n]); + } + + break; + + case SCULPT_TOOL_DRAW: + case SCULPT_TOOL_CLAY: + case SCULPT_TOOL_CLAY_TUBES: + case SCULPT_TOOL_CREASE: + case SCULPT_TOOL_BLOB: + case SCULPT_TOOL_FILL: + case SCULPT_TOOL_FLATTEN: + case SCULPT_TOOL_INFLATE: + case SCULPT_TOOL_NUDGE: + case SCULPT_TOOL_PINCH: + case SCULPT_TOOL_SCRAPE: + case SCULPT_TOOL_SNAKE_HOOK: + #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) + for (n= 0; n < totnode; n++) { + PBVHVertexIter vd; + PBVHProxyNode* proxies; + int proxy_count; + + BLI_pbvh_node_get_proxies(nodes[n], &proxies, &proxy_count); + + BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { + float val[3]; + int p; + + copy_v3_v3(val, vd.co); + + for (p= 0; p < proxy_count; p++) + add_v3_v3(val, proxies[p].co[vd.i]); + + sculpt_clip(sd, ss, vd.co, val); + } + BLI_pbvh_vertex_iter_end; + + BLI_pbvh_node_free_proxies(nodes[n]); + + } + + break; + + case SCULPT_TOOL_SMOOTH: + case SCULPT_TOOL_LAYER: + default: + break; } + + if (nodes) + MEM_freeN(nodes); } +//static int max_overlap_count(Sculpt *sd) +//{ +// int count[3]; +// int i, j; +// +// for (i= 0; i < 3; i++) { +// count[i] = sd->radial_symm[i]; +// +// for (j= 0; j < 3; j++) { +// if (i != j && sd->flags & (SCULPT_SYMM_X<<i)) +// count[i] *= 2; +// } +// } +// +// return MAX3(count[0], count[1], count[2]); +//} + /* Flip all the editdata across the axis/axes specified by symm. Used to calculate multiple modifications to the mesh when symmetry is enabled. */ -static void calc_brushdata_symm(StrokeCache *cache, const char symm) +static void calc_brushdata_symm(Sculpt *sd, StrokeCache *cache, const char symm, const char axis, const float angle, const float feather) { flip_coord(cache->location, cache->true_location, symm); - flip_coord(cache->view_normal_symmetry, cache->view_normal, symm); flip_coord(cache->grab_delta_symmetry, cache->grab_delta, symm); - cache->symmetry= symm; + flip_coord(cache->view_normal, cache->true_view_normal, symm); + + // XXX This reduces the length of the grab delta if it approaches the line of symmetry + // XXX However, a different approach appears to be needed + //if (sd->flags & SCULPT_SYMMETRY_FEATHER) { + // float frac = 1.0f/max_overlap_count(sd); + // float reduce = (feather-frac)/(1-frac); + + // printf("feather: %f frac: %f reduce: %f\n", feather, frac, reduce); + + // if (frac < 1) + // mul_v3_fl(cache->grab_delta_symmetry, reduce); + //} + + unit_m4(cache->symm_rot_mat); + unit_m4(cache->symm_rot_mat_inv); + rotate_m4(cache->symm_rot_mat, axis, angle); + rotate_m4(cache->symm_rot_mat_inv, axis, -angle); + + mul_m4_v3(cache->symm_rot_mat, cache->location); + mul_m4_v3(cache->symm_rot_mat, cache->grab_delta_symmetry); +} + +static void do_radial_symmetry(Sculpt *sd, SculptSession *ss, Brush *brush, const char symm, const int axis, const float feather) +{ + int i; + + for(i = 1; i < sd->radial_symm[axis-'X']; ++i) { + const float angle = 2*M_PI*i/sd->radial_symm[axis-'X']; + ss->cache->radial_symmetry_pass= i; + calc_brushdata_symm(sd, ss->cache, symm, axis, angle, feather); + do_brush_action(sd, ss, brush); + } } static void do_symmetrical_brush_actions(Sculpt *sd, SculptSession *ss) { + Brush *brush = paint_brush(&sd->paint); StrokeCache *cache = ss->cache; const char symm = sd->flags & 7; int i; - copy_v3_v3(cache->location, cache->true_location); - copy_v3_v3(cache->grab_delta_symmetry, cache->grab_delta); - cache->symmetry = 0; - cache->bstrength = brush_strength(sd, cache); - do_brush_action(sd, ss, cache); + float feather = calc_symmetry_feather(sd, ss->cache); + float accum = integrate_overlap(brush); + float overlap = (brush->flag & BRUSH_SPACE_ATTEN && brush->flag & BRUSH_SPACE && !(brush->flag & BRUSH_ANCHORED)) && (brush->spacing < 100) ? 1.0f/accum : 1; // spacing is integer percentage of radius, divide by 50 to get normalized diameter + + ss->cache->autosmooth_overlap = overlap; + + cache->bstrength= brush_strength(sd, cache, feather, overlap); + + cache->symmetry= symm; + + /* symm is a bit combination of XYZ - 1 is mirror X; 2 is Y; 3 is XY; 4 is Z; 5 is XZ; 6 is YZ; 7 is XYZ */ + for(i = 0; i <= symm; ++i) { + if(i == 0 || (symm & i && (symm != 5 || i != 3) && (symm != 6 || (i != 3 && i != 5)))) { + cache->mirror_symmetry_pass= i; + cache->radial_symmetry_pass= 0; - for(i = 1; i <= symm; ++i) { - if(symm & i && (symm != 5 || i != 3) && (symm != 6 || (i != 3 && i != 5))) { - calc_brushdata_symm(cache, i); - do_brush_action(sd, ss, cache); + calc_brushdata_symm(sd, cache, i, 0, 0, feather); + do_brush_action(sd, ss, brush); + + do_radial_symmetry(sd, ss, brush, i, 'X', feather); + do_radial_symmetry(sd, ss, brush, i, 'Y', feather); + do_radial_symmetry(sd, ss, brush, i, 'Z', feather); } } - cache->first_time = 0; + sculpt_combine_proxies(sd, ss); + + cache->first_time= 0; } static void sculpt_update_tex(Sculpt *sd, SculptSession *ss) @@ -1585,37 +2619,13 @@ static void sculpt_update_tex(Sculpt *sd, SculptSession *ss) } /* Need to allocate a bigger buffer for bigger brush size */ - ss->texcache_side = brush->size * 2; + ss->texcache_side = 2*sculpt_get_brush_size(brush); if(!ss->texcache || ss->texcache_side > ss->texcache_actual) { - ss->texcache = brush_gen_texture_cache(brush, brush->size); + ss->texcache = brush_gen_texture_cache(brush, sculpt_get_brush_size(brush)); ss->texcache_actual = ss->texcache_side; } } -/* Sculpt mode handles multires differently from regular meshes, but only if - it's the last modifier on the stack and it is not on the first level */ -struct MultiresModifierData *sculpt_multires_active(Scene *scene, Object *ob) -{ - ModifierData *md, *nmd; - - for(md= modifiers_getVirtualModifierList(ob); md; md= md->next) { - if(md->type == eModifierType_Multires) { - MultiresModifierData *mmd= (MultiresModifierData*)md; - - /* Check if any of the modifiers after multires are active - * if not it can use the multires struct */ - for(nmd= md->next; nmd; nmd= nmd->next) - if(modifier_isEnabled(scene, nmd, eModifierMode_Realtime)) - break; - - if(!nmd && mmd->sculptlvl > 0) - return mmd; - } - } - - return NULL; -} - void sculpt_update_mesh_elements(Scene *scene, Object *ob, int need_fmap) { DerivedMesh *dm = mesh_get_derived_final(scene, ob, CD_MASK_BAREMESH); @@ -1682,16 +2692,32 @@ static char *sculpt_tool_name(Sculpt *sd) return "Draw Brush"; break; case SCULPT_TOOL_SMOOTH: return "Smooth Brush"; break; + case SCULPT_TOOL_CREASE: + return "Crease Brush"; break; + case SCULPT_TOOL_BLOB: + return "Blob Brush"; break; case SCULPT_TOOL_PINCH: return "Pinch Brush"; break; case SCULPT_TOOL_INFLATE: return "Inflate Brush"; break; case SCULPT_TOOL_GRAB: return "Grab Brush"; break; + case SCULPT_TOOL_NUDGE: + return "Nudge Brush"; break; + case SCULPT_TOOL_THUMB: + return "Thumb Brush"; break; case SCULPT_TOOL_LAYER: return "Layer Brush"; break; case SCULPT_TOOL_FLATTEN: - return "Flatten Brush"; break; + return "Flatten Brush"; break; + case SCULPT_TOOL_CLAY: + return "Clay Brush"; break; + case SCULPT_TOOL_CLAY_TUBES: + return "Clay Tubes Brush"; break; + case SCULPT_TOOL_FILL: + return "Fill Brush"; break; + case SCULPT_TOOL_SCRAPE: + return "Scrape Brush"; break; default: return "Sculpting"; break; } @@ -1769,20 +2795,119 @@ static void sculpt_cache_free(StrokeCache *cache) MEM_freeN(cache); } +int sculpt_get_brush_size(Brush *brush) +{ + return (U.sculpt_paint_settings & SCULPT_PAINT_USE_UNIFIED_SIZE) ? U.sculpt_paint_unified_size : brush->size; +} + +void sculpt_set_brush_size(Brush *brush, int size) +{ + if (U.sculpt_paint_settings & SCULPT_PAINT_USE_UNIFIED_SIZE) + U.sculpt_paint_unified_size = size; + else + brush->size = size; +} + +int sculpt_get_lock_brush_size(Brush *brush) +{ + return (U.sculpt_paint_settings & SCULPT_PAINT_USE_UNIFIED_SIZE) ? (U.sculpt_paint_settings & SCULPT_PAINT_UNIFIED_LOCK_BRUSH_SIZE) : (brush->flag & BRUSH_LOCK_SIZE); +} + +float sculpt_get_brush_unprojected_radius(Brush *brush) +{ + return (U.sculpt_paint_settings & SCULPT_PAINT_USE_UNIFIED_SIZE) ? U.sculpt_paint_unified_unprojected_radius : brush->unprojected_radius; +} + +void sculpt_set_brush_unprojected_radius(Brush *brush, float unprojected_radius) +{ + if (U.sculpt_paint_settings & SCULPT_PAINT_USE_UNIFIED_SIZE) + U.sculpt_paint_unified_unprojected_radius = unprojected_radius; + else + brush->unprojected_radius = unprojected_radius; +} + +float sculpt_get_brush_alpha(Brush *brush) +{ + return (U.sculpt_paint_settings & SCULPT_PAINT_USE_UNIFIED_ALPHA) ? U.sculpt_paint_unified_alpha : brush->alpha; +} + +void sculpt_set_brush_alpha(Brush *brush, float alpha) +{ + if (U.sculpt_paint_settings & SCULPT_PAINT_USE_UNIFIED_ALPHA) + U.sculpt_paint_unified_alpha = alpha; + else + brush->alpha = alpha; +} + /* Initialize the stroke cache invariants from operator properties */ -static void sculpt_update_cache_invariants(Sculpt *sd, SculptSession *ss, bContext *C, wmOperator *op) +static void sculpt_update_cache_invariants(bContext* C, Sculpt *sd, SculptSession *ss, wmOperator *op, wmEvent *event) { StrokeCache *cache = MEM_callocN(sizeof(StrokeCache), "stroke cache"); Brush *brush = paint_brush(&sd->paint); ViewContext *vc = paint_stroke_view_context(op->customdata); + Object *ob= CTX_data_active_object(C); + ModifierData *md; int i; + int mode; ss->cache = cache; - RNA_float_get_array(op->ptr, "scale", cache->scale); - cache->flag = RNA_int_get(op->ptr, "flag"); - RNA_float_get_array(op->ptr, "clip_tolerance", cache->clip_tolerance); - RNA_float_get_array(op->ptr, "initial_mouse", cache->initial_mouse); + /* Set scaling adjustment */ + ss->cache->scale[0] = 1.0f / ob->size[0]; + ss->cache->scale[1] = 1.0f / ob->size[1]; + ss->cache->scale[2] = 1.0f / ob->size[2]; + + ss->cache->plane_trim_squared = brush->plane_trim * brush->plane_trim; + + /* Initialize mirror modifier clipping */ + + ss->cache->flag = 0; + + for(md= ob->modifiers.first; md; md= md->next) { + if(md->type==eModifierType_Mirror && (md->mode & eModifierMode_Realtime)) { + const MirrorModifierData *mmd = (MirrorModifierData*) md; + + /* Mark each axis that needs clipping along with its tolerance */ + if(mmd->flag & MOD_MIR_CLIPPING) { + ss->cache->flag |= CLIP_X << mmd->axis; + if(mmd->tolerance > ss->cache->clip_tolerance[mmd->axis]) + ss->cache->clip_tolerance[mmd->axis] = mmd->tolerance; + } + } + } + + /* Initial mouse location */ + if (event) { + ss->cache->initial_mouse[0] = event->x; + ss->cache->initial_mouse[1] = event->y; + } + else { + ss->cache->initial_mouse[0] = 0; + ss->cache->initial_mouse[1] = 0; + } + + mode = RNA_int_get(op->ptr, "mode"); + cache->invert = mode == WM_BRUSHSTROKE_INVERT; + cache->alt_smooth = mode == WM_BRUSHSTROKE_SMOOTH; + + /* Alt-Smooth */ + if (ss->cache->alt_smooth) { + Paint *p= &sd->paint; + Brush *br; + int i; + + BLI_strncpy(cache->saved_active_brush_name, brush->id.name+2, sizeof(cache->saved_active_brush_name)); + + for(i = 0; i < p->brush_count; ++i) { + br = p->brushes[i]; + + if (strcmp(br->id.name+2, "Smooth")==0) { + paint_brush_set(p, br); + brush = br; + break; + } + } + } copy_v2_v2(cache->mouse, cache->initial_mouse); copy_v2_v2(cache->tex_mouse, cache->initial_mouse); @@ -1790,11 +2915,13 @@ static void sculpt_update_cache_invariants(Sculpt *sd, SculptSession *ss, bConte /* Truly temporary data that isn't stored in properties */ cache->vc = vc; + cache->brush = brush; cache->mats = MEM_callocN(sizeof(bglMats), "sculpt bglMats"); view3d_get_transformation(vc->ar, vc->rv3d, vc->obact, cache->mats); + viewvector(cache->vc->rv3d, cache->vc->rv3d->twmat[3], cache->true_view_normal); /* Initialize layer brush displacements and persistent coords */ if(brush->sculpt_tool == SCULPT_TOOL_LAYER) { /* not supported yet for multires */ @@ -1820,69 +2947,143 @@ static void sculpt_update_cache_invariants(Sculpt *sd, SculptSession *ss, bConte cache->original = 1; } - if(ELEM3(brush->sculpt_tool, SCULPT_TOOL_DRAW, SCULPT_TOOL_LAYER, SCULPT_TOOL_INFLATE)) + if(ELEM7(brush->sculpt_tool, SCULPT_TOOL_DRAW, SCULPT_TOOL_CREASE, SCULPT_TOOL_BLOB, SCULPT_TOOL_LAYER, SCULPT_TOOL_INFLATE, SCULPT_TOOL_CLAY, SCULPT_TOOL_CLAY_TUBES)) if(!(brush->flag & BRUSH_ACCUMULATE)) cache->original = 1; - cache->rotation = 0; - cache->first_time = 1; + cache->special_rotation = (brush->flag & BRUSH_RAKE) ? sd->last_angle : 0; + //cache->last_rake[0] = sd->last_x; + //cache->last_rake[1] = sd->last_y; + + cache->first_time= 1; + + cache->vertex_rotation= 0; } /* Initialize the stroke cache variants from operator properties */ -static void sculpt_update_cache_variants(Sculpt *sd, SculptSession *ss, struct PaintStroke *stroke, PointerRNA *ptr) +static void sculpt_update_cache_variants(bContext *C, Sculpt *sd, SculptSession *ss, struct PaintStroke *stroke, PointerRNA *ptr) { StrokeCache *cache = ss->cache; Brush *brush = paint_brush(&sd->paint); - + int dx, dy; - if(!(brush->flag & BRUSH_ANCHORED) || cache->first_time) + //RNA_float_get_array(ptr, "location", cache->traced_location); + + if (cache->first_time || + !((brush->flag & BRUSH_ANCHORED)|| + (brush->sculpt_tool == SCULPT_TOOL_SNAKE_HOOK)|| + (brush->sculpt_tool == SCULPT_TOOL_ROTATE)) + ) + { RNA_float_get_array(ptr, "location", cache->true_location); - cache->flip = RNA_boolean_get(ptr, "flip"); + } + + cache->pen_flip = RNA_boolean_get(ptr, "pen_flip"); RNA_float_get_array(ptr, "mouse", cache->mouse); + cache->pressure = RNA_float_get(ptr, "pressure"); - + /* Truly temporary data that isn't stored in properties */ + sd->draw_pressure= 1; + sd->pressure_value= cache->pressure; + cache->previous_pixel_radius = cache->pixel_radius; - cache->pixel_radius = brush->size; + cache->pixel_radius = sculpt_get_brush_size(brush); - if(cache->first_time) - cache->initial_radius = unproject_brush_radius(ss->ob, cache->vc, cache->true_location, brush->size); + if(cache->first_time) { + if (!sculpt_get_lock_brush_size(brush)) { + cache->initial_radius= unproject_brush_radius(ss->ob, cache->vc, cache->true_location, sculpt_get_brush_size(brush)); + sculpt_set_brush_unprojected_radius(brush, cache->initial_radius); + } + else { + cache->initial_radius= sculpt_get_brush_unprojected_radius(brush); + } + + if (ELEM(brush->sculpt_tool, SCULPT_TOOL_GRAB, SCULPT_TOOL_SNAKE_HOOK)) + cache->initial_radius *= 2.0f; + } - if(brush->flag & BRUSH_SIZE_PRESSURE && brush->sculpt_tool != SCULPT_TOOL_GRAB) { + if(brush->flag & BRUSH_SIZE_PRESSURE) { cache->pixel_radius *= cache->pressure; - cache->radius = cache->initial_radius * cache->pressure; + cache->radius= cache->initial_radius * cache->pressure; } else - cache->radius = cache->initial_radius; + cache->radius= cache->initial_radius; - if(!(brush->flag & BRUSH_ANCHORED)) + cache->radius_squared = cache->radius*cache->radius; + + if(!(brush->flag & BRUSH_ANCHORED || ELEM4(brush->sculpt_tool, SCULPT_TOOL_GRAB, SCULPT_TOOL_SNAKE_HOOK, SCULPT_TOOL_THUMB, SCULPT_TOOL_ROTATE))) { copy_v2_v2(cache->tex_mouse, cache->mouse); + if ( (brush->mtex.brush_map_mode == MTEX_MAP_MODE_FIXED) && + (brush->flag & BRUSH_RANDOM_ROTATION) && + !(brush->flag & BRUSH_RAKE)) + { + cache->special_rotation = 2*M_PI*BLI_frand(); + } + } + if(brush->flag & BRUSH_ANCHORED) { + int hit = 0; + dx = cache->mouse[0] - cache->initial_mouse[0]; dy = cache->mouse[1] - cache->initial_mouse[1]; - cache->pixel_radius = sqrt(dx*dx + dy*dy); - cache->radius = unproject_brush_radius(ss->ob, paint_stroke_view_context(stroke), - cache->true_location, cache->pixel_radius); - cache->rotation = atan2(dy, dx); + + sd->anchored_size = cache->pixel_radius = sqrt(dx*dx + dy*dy); + + cache->special_rotation = atan2(dx, dy) + M_PI; + + if (brush->flag & BRUSH_EDGE_TO_EDGE) { + float d[3]; + float halfway[3]; + float out[3]; + + d[0] = dx; + d[1] = dy; + d[2] = 0; + + mul_v3_v3fl(halfway, d, 0.5f); + add_v3_v3(halfway, cache->initial_mouse); + + if (sculpt_stroke_get_location(C, stroke, out, halfway)) { + copy_v3_v3(sd->anchored_location, out); + copy_v3_v3(sd->anchored_initial_mouse, halfway); + copy_v2_v2(cache->tex_mouse, halfway); + copy_v3_v3(cache->true_location, sd->anchored_location); + sd->anchored_size /= 2.0f; + cache->pixel_radius /= 2.0f; + hit = 1; + } + } + + if (!hit) + copy_v2_v2(sd->anchored_initial_mouse, cache->initial_mouse); + + cache->radius= unproject_brush_radius(ss->ob, paint_stroke_view_context(stroke), cache->true_location, cache->pixel_radius); + cache->radius_squared = cache->radius*cache->radius; + + copy_v3_v3(sd->anchored_location, cache->true_location); + + sd->draw_anchored = 1; } else if(brush->flag & BRUSH_RAKE) { - int update; - - dx = cache->last_rake[0] - cache->mouse[0]; - dy = cache->last_rake[1] - cache->mouse[1]; + const float u = 0.5f; + const float v = 1 - u; + const float r = 20; - update = dx*dx + dy*dy > 100; + const float dx = cache->last_rake[0] - cache->mouse[0]; + const float dy = cache->last_rake[1] - cache->mouse[1]; - /* To prevent jitter, only update the angle if the mouse has moved over 10 pixels */ - if(update && !cache->first_time) - cache->rotation = M_PI_2 + atan2(dy, dx); + if (cache->first_time) { + copy_v3_v3(cache->last_rake, cache->mouse); + } + else if (dx*dx + dy*dy >= r*r) { + cache->special_rotation = atan2(dx, dy); - if(update || cache->first_time) { - cache->last_rake[0] = cache->mouse[0]; - cache->last_rake[1] = cache->mouse[1]; + cache->last_rake[0] = u*cache->last_rake[0] + v*cache->mouse[0]; + cache->last_rake[1] = u*cache->last_rake[1] + v*cache->mouse[1]; } } @@ -1900,16 +3101,123 @@ static void sculpt_update_cache_variants(Sculpt *sd, SculptSession *ss, struct P /* compute delta to move verts by */ if(!cache->first_time) { + float delta[3]; + sub_v3_v3v3(delta, grab_location, cache->old_grab_location); + invert_m4_m4(imat, ss->ob->obmat); + mul_mat3_m4_v3(imat, delta); + add_v3_v3(cache->grab_delta, delta); + } + else { + zero_v3(cache->grab_delta); + } + + copy_v3_v3(cache->old_grab_location, grab_location); + + /* location stays the same for finding vertices in brush radius */ + copy_v3_v3(cache->true_location, cache->orig_grab_location); + + sd->draw_anchored = 1; + copy_v3_v3(sd->anchored_location, cache->true_location); + copy_v3_v3(sd->anchored_initial_mouse, cache->initial_mouse); + sd->anchored_size = cache->pixel_radius; + } + /* Find the nudge/clay tubes delta */ + else if(brush->sculpt_tool == SCULPT_TOOL_NUDGE || brush->sculpt_tool == SCULPT_TOOL_CLAY_TUBES) { + float grab_location[3], imat[4][4]; + + if(cache->first_time) + copy_v3_v3(cache->orig_grab_location, cache->true_location); + + /* compute 3d coordinate at same z from original location + mouse */ + initgrabz(cache->vc->rv3d, cache->orig_grab_location[0], + cache->orig_grab_location[1], cache->orig_grab_location[2]); + window_to_3d_delta(cache->vc->ar, grab_location, cache->mouse[0], cache->mouse[1]); + + /* compute delta to move verts by */ + if (!cache->first_time) { sub_v3_v3v3(cache->grab_delta, grab_location, cache->old_grab_location); invert_m4_m4(imat, ss->ob->obmat); mul_mat3_m4_v3(imat, cache->grab_delta); } + else { + zero_v3(cache->grab_delta); + } + + copy_v3_v3(cache->old_grab_location, grab_location); + } + /* Find the snake hook delta */ + else if(brush->sculpt_tool == SCULPT_TOOL_SNAKE_HOOK) { + float grab_location[3], imat[4][4]; + + if(cache->first_time) + copy_v3_v3(cache->orig_grab_location, cache->true_location); + else + add_v3_v3(cache->true_location, cache->grab_delta); + + /* compute 3d coordinate at same z from original location + mouse */ + initgrabz(cache->vc->rv3d, cache->orig_grab_location[0], + cache->orig_grab_location[1], cache->orig_grab_location[2]); + window_to_3d_delta(cache->vc->ar, grab_location, cache->mouse[0], cache->mouse[1]); + + /* compute delta to move verts by */ + if (!cache->first_time) { + sub_v3_v3v3(cache->grab_delta, grab_location, cache->old_grab_location); + invert_m4_m4(imat, ss->ob->obmat); + mul_mat3_m4_v3(imat, cache->grab_delta); + } + else { + zero_v3(cache->grab_delta); + } + + copy_v3_v3(cache->old_grab_location, grab_location); + } + /* Find the thumb delta */ + else if(brush->sculpt_tool == SCULPT_TOOL_THUMB) { + float grab_location[3], imat[4][4]; + + if(cache->first_time) + copy_v3_v3(cache->orig_grab_location, cache->true_location); + + /* compute 3d coordinate at same z from original location + mouse */ + initgrabz(cache->vc->rv3d, cache->orig_grab_location[0], + cache->orig_grab_location[1], cache->orig_grab_location[2]); + window_to_3d_delta(cache->vc->ar, grab_location, cache->mouse[0], cache->mouse[1]); + + /* compute delta to move verts by */ + if (!cache->first_time) { + float delta[3]; + sub_v3_v3v3(delta, grab_location, cache->old_grab_location); + invert_m4_m4(imat, ss->ob->obmat); + mul_mat3_m4_v3(imat, delta); + add_v3_v3(cache->grab_delta, delta); + } + else { + zero_v3(cache->grab_delta); + } copy_v3_v3(cache->old_grab_location, grab_location); /* location stays the same for finding vertices in brush radius */ copy_v3_v3(cache->true_location, cache->orig_grab_location); + + sd->draw_anchored = 1; + copy_v3_v3(sd->anchored_location, cache->orig_grab_location); + copy_v3_v3(sd->anchored_initial_mouse, cache->initial_mouse); + sd->anchored_size = cache->pixel_radius; + } + else if(brush->sculpt_tool == SCULPT_TOOL_ROTATE) { + dx = cache->mouse[0] - cache->initial_mouse[0]; + dy = cache->mouse[1] - cache->initial_mouse[1]; + + cache->vertex_rotation = -atan2(dx, dy) / 4.0f; + + sd->draw_anchored = 1; + copy_v2_v2(sd->anchored_initial_mouse, cache->initial_mouse); + copy_v3_v3(sd->anchored_location, cache->true_location); + sd->anchored_size = cache->pixel_radius; } + + sd->special_rotation = cache->special_rotation; } static void sculpt_stroke_modifiers_check(bContext *C, SculptSession *ss) @@ -1930,19 +3238,23 @@ typedef struct { int original; } SculptRaycastData; -void sculpt_raycast_cb(PBVHNode *node, void *data_v) +void sculpt_raycast_cb(PBVHNode *node, void *data_v, float* tmin) { - SculptRaycastData *srd = data_v; - float (*origco)[3]= NULL; + if (BLI_pbvh_node_get_tmin(node) < *tmin) { + SculptRaycastData *srd = data_v; + float (*origco)[3]= NULL; - if(srd->original && srd->ss->cache) { - /* intersect with coordinates from before we started stroke */ - SculptUndoNode *unode= sculpt_undo_get_node(srd->ss, node); - origco= (unode)? unode->co: NULL; - } + if(srd->original && srd->ss->cache) { + /* intersect with coordinates from before we started stroke */ + SculptUndoNode *unode= sculpt_undo_get_node(node); + origco= (unode)? unode->co: NULL; + } - srd->hit |= BLI_pbvh_node_raycast(srd->ss->pbvh, node, origco, - srd->ray_start, srd->ray_normal, &srd->dist); + if (BLI_pbvh_node_raycast(srd->ss->pbvh, node, origco, srd->ray_start, srd->ray_normal, &srd->dist)) { + srd->hit = 1; + *tmin = srd->dist; + } + } } /* Do a raycast in the tree to find the 3d brush location @@ -1956,10 +3268,12 @@ int sculpt_stroke_get_location(bContext *C, struct PaintStroke *stroke, float ou StrokeCache *cache= ss->cache; float ray_start[3], ray_end[3], ray_normal[3], dist; float obimat[4][4]; - float mval[2] = {mouse[0] - vc->ar->winrct.xmin, - mouse[1] - vc->ar->winrct.ymin}; + float mval[2]; SculptRaycastData srd; + mval[0] = mouse[0] - vc->ar->winrct.xmin; + mval[1] = mouse[1] - vc->ar->winrct.ymin; + sculpt_stroke_modifiers_check(C, ss); viewline(vc->ar, vc->v3d, mval, ray_start, ray_end); @@ -1987,43 +3301,6 @@ int sculpt_stroke_get_location(bContext *C, struct PaintStroke *stroke, float ou return srd.hit; } -/* Initialize stroke operator properties */ -static void sculpt_brush_stroke_init_properties(bContext *C, wmOperator *op, wmEvent *event, SculptSession *ss) -{ - Object *ob= CTX_data_active_object(C); - ModifierData *md; - float scale[3], clip_tolerance[3] = {0,0,0}; - float mouse[2]; - int flag = 0; - - /* Set scaling adjustment */ - scale[0] = 1.0f / ob->size[0]; - scale[1] = 1.0f / ob->size[1]; - scale[2] = 1.0f / ob->size[2]; - RNA_float_set_array(op->ptr, "scale", scale); - - /* Initialize mirror modifier clipping */ - for(md= ob->modifiers.first; md; md= md->next) { - if(md->type==eModifierType_Mirror && (md->mode & eModifierMode_Realtime)) { - const MirrorModifierData *mmd = (MirrorModifierData*) md; - - /* Mark each axis that needs clipping along with its tolerance */ - if(mmd->flag & MOD_MIR_CLIPPING) { - flag |= CLIP_X << mmd->axis; - if(mmd->tolerance > clip_tolerance[mmd->axis]) - clip_tolerance[mmd->axis] = mmd->tolerance; - } - } - } - RNA_int_set(op->ptr, "flag", flag); - RNA_float_set_array(op->ptr, "clip_tolerance", clip_tolerance); - - /* Initial mouse location */ - mouse[0] = event->x; - mouse[1] = event->y; - RNA_float_set_array(op->ptr, "initial_mouse", mouse); -} - static int sculpt_brush_stroke_init(bContext *C, ReportList *reports) { Scene *scene= CTX_data_scene(C); @@ -2051,22 +3328,26 @@ static int sculpt_brush_stroke_init(bContext *C, ReportList *reports) static void sculpt_restore_mesh(Sculpt *sd, SculptSession *ss) { - StrokeCache *cache = ss->cache; Brush *brush = paint_brush(&sd->paint); - int i; /* Restore the mesh before continuing with anchored stroke */ - if(brush->flag & BRUSH_ANCHORED) { + if((brush->flag & BRUSH_ANCHORED) || + (brush->sculpt_tool == SCULPT_TOOL_GRAB && (brush->flag & BRUSH_SIZE_PRESSURE)) || + (brush->flag & BRUSH_RESTORE_MESH)) + { + StrokeCache *cache = ss->cache; + int i; + PBVHNode **nodes; int n, totnode; BLI_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, &totnode); - //#pragma omp parallel for private(n) schedule(static) + #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) for(n=0; n<totnode; n++) { SculptUndoNode *unode; - unode= sculpt_undo_get_node(ss, nodes[n]); + unode= sculpt_undo_get_node(nodes[n]); if(unode) { PBVHVertexIter vd; @@ -2074,8 +3355,12 @@ static void sculpt_restore_mesh(Sculpt *sd, SculptSession *ss) copy_v3_v3(vd.co, unode->co[vd.i]); if(vd.no) VECCOPY(vd.no, unode->no[vd.i]) else normal_short_to_float_v3(vd.fno, unode->no[vd.i]); + + if(vd.mvert) vd.mvert->flag |= ME_VERT_PBVH_UPDATE; } BLI_pbvh_vertex_iter_end; + + BLI_pbvh_node_mark_update(nodes[n]); } } @@ -2096,7 +3381,6 @@ static void sculpt_flush_update(bContext *C) SculptSession *ss = ob->sculpt; ARegion *ar = CTX_wm_region(C); MultiresModifierData *mmd = ss->multires; - int redraw = 0; if(mmd) multires_mark_as_modified(ob); @@ -2109,14 +3393,22 @@ static void sculpt_flush_update(bContext *C) rcti r; BLI_pbvh_update(ss->pbvh, PBVH_UpdateBB, NULL); - redraw = sculpt_get_redraw_rect(ar, CTX_wm_region_view3d(C), ob, &r); - if(redraw) { + if (sculpt_get_redraw_rect(ar, CTX_wm_region_view3d(C), ob, &r)) { + //rcti tmp; + r.xmin += ar->winrct.xmin + 1; r.xmax += ar->winrct.xmin - 1; r.ymin += ar->winrct.ymin + 1; r.ymax += ar->winrct.ymin - 1; - + + //tmp = r; + + //if (!BLI_rcti_is_empty(&ss->previous_r)) + // BLI_union_rcti(&r, &ss->previous_r); + + //ss->previous_r= tmp; + ss->partial_redraw = 1; ED_region_tag_redraw_partial(ar, &r); } @@ -2127,9 +3419,12 @@ static void sculpt_flush_update(bContext *C) or over the background (0) */ static int over_mesh(bContext *C, struct wmOperator *op, float x, float y) { - float mouse[2] = {x, y}, co[3]; - - return (int)sculpt_stroke_get_location(C, op->customdata, co, mouse); + float mouse[2], co[3]; + + mouse[0] = x; + mouse[1] = y; + + return sculpt_stroke_get_location(C, op->customdata, co, mouse); } static int sculpt_stroke_test_start(bContext *C, struct wmOperator *op, @@ -2143,11 +3438,22 @@ static int sculpt_stroke_test_start(bContext *C, struct wmOperator *op, ED_view3d_init_mats_rv3d(ob, CTX_wm_region_view3d(C)); - sculpt_brush_stroke_init_properties(C, op, event, ss); + sculpt_update_cache_invariants(C, sd, ss, op, event); - sculpt_update_cache_invariants(sd, ss, C, op); + sculpt_undo_push_begin(sculpt_tool_name(sd)); - sculpt_undo_push_begin(ss, sculpt_tool_name(sd)); +#ifdef _OPENMP + /* If using OpenMP then create a number of threads two times the + number of processor cores. + Justification: Empirically I've found that two threads per + processor gives higher throughput. */ + if (sd->flags & SCULPT_USE_OPENMP) { + int num_procs; + + num_procs = omp_get_num_procs(); + omp_set_num_threads(2*num_procs); + } +#endif return 1; } @@ -2161,7 +3467,7 @@ static void sculpt_stroke_update_step(bContext *C, struct PaintStroke *stroke, P SculptSession *ss = CTX_data_active_object(C)->sculpt; sculpt_stroke_modifiers_check(C, ss); - sculpt_update_cache_variants(sd, ss, stroke, itemptr); + sculpt_update_cache_variants(C, sd, ss, stroke, itemptr); sculpt_restore_mesh(sd, ss); do_symmetrical_brush_actions(sd, ss); @@ -2169,19 +3475,43 @@ static void sculpt_stroke_update_step(bContext *C, struct PaintStroke *stroke, P sculpt_flush_update(C); } -static void sculpt_stroke_done(bContext *C, struct PaintStroke *stroke) +static void sculpt_stroke_done(bContext *C, struct PaintStroke *unused) { Object *ob= CTX_data_active_object(C); SculptSession *ss = ob->sculpt; + Sculpt *sd = CTX_data_tool_settings(C)->sculpt; + + (void)unused; + + // reset values used to draw brush after completing the stroke + sd->draw_anchored= 0; + sd->draw_pressure= 0; + sd->special_rotation= 0; /* Finished */ if(ss->cache) { sculpt_stroke_modifiers_check(C, ss); + /* Alt-Smooth */ + if (ss->cache->alt_smooth) { + Paint *p= &sd->paint; + Brush *br; + int i; + + for(i = 0; i < p->brush_count; ++i) { + br = p->brushes[i]; + + if (strcmp(br->id.name+2, ss->cache->saved_active_brush_name)==0) { + paint_brush_set(p, br); + break; + } + } + } + sculpt_cache_free(ss->cache); ss->cache = NULL; - sculpt_undo_push_end(ss); + sculpt_undo_push_end(); BLI_pbvh_update(ss->pbvh, PBVH_UpdateOriginalBB, NULL); @@ -2218,6 +3548,7 @@ static int sculpt_brush_stroke_invoke(bContext *C, wmOperator *op, wmEvent *even /* For tablet rotation */ ignore_background_click = RNA_boolean_get(op->ptr, "ignore_background_click"); + if(ignore_background_click && !over_mesh(C, op, event->x, event->y)) { paint_stroke_free(stroke); return OPERATOR_PASS_THROUGH; @@ -2242,7 +3573,7 @@ static int sculpt_brush_stroke_exec(bContext *C, wmOperator *op) op->customdata = paint_stroke_new(C, sculpt_stroke_get_location, sculpt_stroke_test_start, sculpt_stroke_update_step, sculpt_stroke_done); - sculpt_update_cache_invariants(sd, ss, C, op); + sculpt_update_cache_invariants(C, sd, ss, op, NULL); paint_stroke_exec(C, op); @@ -2254,7 +3585,12 @@ static int sculpt_brush_stroke_exec(bContext *C, wmOperator *op) static void SCULPT_OT_brush_stroke(wmOperatorType *ot) { - ot->flag |= OPTYPE_REGISTER; + static EnumPropertyItem stroke_mode_items[] = { + { WM_BRUSHSTROKE_NORMAL, "NORMAL", 0, "Normal", "Apply brush normally" }, + { WM_BRUSHSTROKE_INVERT, "INVERT", 0, "Invert", "Invert action of brush for duration of stroke" }, + { WM_BRUSHSTROKE_SMOOTH, "SMOOTH", 0, "Smooth", "Switch brush to smooth mode for duration of stroke" }, + { 0 } + }; /* identifiers */ ot->name= "Sculpt Mode"; @@ -2265,36 +3601,32 @@ static void SCULPT_OT_brush_stroke(wmOperatorType *ot) ot->modal= paint_stroke_modal; ot->exec= sculpt_brush_stroke_exec; ot->poll= sculpt_poll; - + /* flags (sculpt does own undo? (ton) */ - ot->flag= OPTYPE_REGISTER|OPTYPE_BLOCKING; + ot->flag= OPTYPE_BLOCKING; /* properties */ - RNA_def_collection_runtime(ot->srna, "stroke", &RNA_OperatorStrokeElement, "Stroke", ""); - - /* If the object has a scaling factor, brushes also need to be scaled - to work as expected. */ - RNA_def_float_vector(ot->srna, "scale", 3, NULL, 0.0f, FLT_MAX, "Scale", "", 0.0f, 1000.0f); - RNA_def_int(ot->srna, "flag", 0, 0, INT_MAX, "flag", "", 0, INT_MAX); + RNA_def_collection_runtime(ot->srna, "stroke", &RNA_OperatorStrokeElement, + "Stroke", ""); - /* For mirror modifiers */ - RNA_def_float_vector(ot->srna, "clip_tolerance", 3, NULL, 0.0f, FLT_MAX, "clip_tolerance", "", 0.0f, 1000.0f); - - /* The initial 2D location of the mouse */ - RNA_def_float_vector(ot->srna, "initial_mouse", 2, NULL, INT_MIN, INT_MAX, "initial_mouse", "", INT_MIN, INT_MAX); + RNA_def_enum(ot->srna, "mode", stroke_mode_items, WM_BRUSHSTROKE_NORMAL, + "Sculpt Stroke Mode", + "Action taken when a sculpt stroke is made"); RNA_def_boolean(ot->srna, "ignore_background_click", 0, "Ignore Background Click", - "Clicks on the background don't start the stroke"); + "Clicks on the background do not start the stroke"); } /**** Reset the copy of the mesh that is being sculpted on (currently just for the layer brush) ****/ -static int sculpt_set_persistent_base(bContext *C, wmOperator *op) +static int sculpt_set_persistent_base(bContext *C, wmOperator *unused) { SculptSession *ss = CTX_data_active_object(C)->sculpt; + (void)unused; + if(ss) { if(ss->layer_co) MEM_freeN(ss->layer_co); @@ -2314,7 +3646,7 @@ static void SCULPT_OT_set_persistent_base(wmOperatorType *ot) ot->exec= sculpt_set_persistent_base; ot->poll= sculpt_mode_poll; - ot->flag= OPTYPE_REGISTER; + ot->flag= 0;//OPTYPE_REGISTER; } /**** Toggle operator for turning sculpt mode on or off ****/ @@ -2322,18 +3654,21 @@ static void SCULPT_OT_set_persistent_base(wmOperatorType *ot) static void sculpt_init_session(Scene *scene, Object *ob) { ob->sculpt = MEM_callocN(sizeof(SculptSession), "sculpt session"); + ob->sculpt->ob = ob; sculpt_update_mesh_elements(scene, ob, 0); } -static int sculpt_toggle_mode(bContext *C, wmOperator *op) +static int sculpt_toggle_mode(bContext *C, wmOperator *unused) { Scene *scene = CTX_data_scene(C); ToolSettings *ts = CTX_data_tool_settings(C); Object *ob = CTX_data_active_object(C); - MultiresModifierData *mmd = sculpt_multires_active(scene, ob); + MultiresModifierData *mmd= sculpt_multires_active(scene, ob); int flush_recalc= 0; + (void)unused; + /* multires in sculpt mode could have different from object mode subdivision level */ flush_recalc |= mmd && mmd->sculptlvl != mmd->lvl; /* if object has got active modifiers, it's dm could be different in sculpt mode */ @@ -2359,9 +3694,13 @@ static int sculpt_toggle_mode(bContext *C, wmOperator *op) DAG_id_flush_update(&ob->id, OB_RECALC_DATA); /* Create persistent sculpt mode data */ - if(!ts->sculpt) + if(!ts->sculpt) { ts->sculpt = MEM_callocN(sizeof(Sculpt), "sculpt mode data"); + /* Turn on X plane mirror symmetry by default */ + ts->sculpt->flags |= SCULPT_SYMM_X; + } + /* Create sculpt mode session data */ if(ob->sculpt) free_sculptsession(ob); @@ -2388,7 +3727,7 @@ static void SCULPT_OT_sculptmode_toggle(wmOperatorType *ot) ot->exec= sculpt_toggle_mode; ot->poll= ED_operator_object_active; - ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; + ot->flag= 0; } void ED_operatortypes_sculpt() diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h index c7c6833b695..a46823a0b28 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.h +++ b/source/blender/editors/sculpt_paint/sculpt_intern.h @@ -32,6 +32,9 @@ #include "DNA_listBase.h" #include "DNA_vec_types.h" +#include "DNA_key_types.h" + +#include "BLI_pbvh.h" struct bContext; struct Brush; @@ -65,8 +68,49 @@ void sculpt_stroke_free(struct SculptStroke *); void sculpt_stroke_add_point(struct SculptStroke *, const short x, const short y); void sculpt_stroke_apply(struct Sculpt *sd, struct SculptStroke *); void sculpt_stroke_apply_all(struct Sculpt *sd, struct SculptStroke *); +int sculpt_stroke_get_location(bContext *C, struct PaintStroke *stroke, float out[3], float mouse[2]); /* Partial Mesh Visibility */ void sculptmode_pmv(int mode); +/* Undo */ + +typedef struct SculptUndoNode { + struct SculptUndoNode *next, *prev; + + char idname[MAX_ID_NAME]; /* name instead of pointer*/ + void *node; /* only during push, not valid afterwards! */ + + float (*co)[3]; + short (*no)[3]; + int totvert; + + /* non-multires */ + int maxvert; /* to verify if totvert it still the same */ + int *index; /* to restore into right location */ + + /* multires */ + int maxgrid; /* same for grid */ + int gridsize; /* same for grid */ + int totgrid; /* to restore into right location */ + int *grids; /* to restore into right location */ + + /* layer brush */ + float *layer_disp; + + /* shape keys */ + char *shapeName[32]; /* keep size in sync with keyblock dna */ +} SculptUndoNode; + +SculptUndoNode *sculpt_undo_push_node(SculptSession *ss, PBVHNode *node); +SculptUndoNode *sculpt_undo_get_node(PBVHNode *node); +void sculpt_undo_push_begin(char *name); +void sculpt_undo_push_end(void); + +struct MultiresModifierData *sculpt_multires_active(struct Scene *scene, struct Object *ob); +int sculpt_modifiers_active(Scene *scene, Object *ob); +void sculpt_vertcos_to_key(Object *ob, KeyBlock *kb, float (*vertCos)[3]); + +void brush_jitter_pos(Brush *brush, float *pos, float *jitterpos); + #endif diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.c b/source/blender/editors/sculpt_paint/sculpt_undo.c new file mode 100644 index 00000000000..e4121c9c76a --- /dev/null +++ b/source/blender/editors/sculpt_paint/sculpt_undo.c @@ -0,0 +1,302 @@ +/* + * $Id$ + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2006 by Nicholas Bishop + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + * + * Implements the Sculpt Mode tools + * + */ + +#include "BLI_math.h" +#include "BLI_ghash.h" +#include "BLI_threads.h" + +#include "DNA_meshdata_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" +#include "DNA_mesh_types.h" +#include "DNA_key_types.h" + +#include "BKE_cdderivedmesh.h" +#include "BKE_context.h" +#include "BKE_depsgraph.h" +#include "BKE_modifier.h" +#include "BKE_multires.h" +#include "BKE_paint.h" +#include "BKE_mesh.h" +#include "BKE_key.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "ED_sculpt.h" +#include "paint_intern.h" +#include "sculpt_intern.h" + +/************************** Undo *************************/ + +static void update_cb(PBVHNode *node, void *unused) +{ + (void)unused; + BLI_pbvh_node_mark_update(node); +} + +static void sculpt_undo_restore(bContext *C, ListBase *lb) +{ + Scene *scene = CTX_data_scene(C); + Object *ob = CTX_data_active_object(C); + DerivedMesh *dm = mesh_get_derived_final(scene, ob, 0); + SculptSession *ss = ob->sculpt; + SculptUndoNode *unode; + MVert *mvert; + MultiresModifierData *mmd; + int *index; + int i, j, update= 0; + + sculpt_update_mesh_elements(scene, ob, 0); + + for(unode=lb->first; unode; unode=unode->next) { + if(!(strcmp(unode->idname, ob->id.name)==0)) + continue; + + if(unode->maxvert) { + char *shapeName= (char*)unode->shapeName; + + /* regular mesh restore */ + if(ss->totvert != unode->maxvert) + continue; + + if (ss->kb && strcmp(ss->kb->name, shapeName)) { + /* shape key has been changed before calling undo operator */ + + Key *key= ob_get_key(ob); + KeyBlock *kb= key_get_named_keyblock(key, shapeName); + + if (kb) { + ob->shapenr= BLI_findindex(&key->block, kb) + 1; + ob->shapeflag|= OB_SHAPE_LOCK; + + sculpt_update_mesh_elements(scene, ob, 0); + WM_event_add_notifier(C, NC_OBJECT|ND_DATA, ob); + } else { + /* key has been removed -- skip this undo node */ + continue; + } + } + + index= unode->index; + mvert= ss->mvert; + + if (ss->kb) { + float (*vertCos)[3]; + vertCos= key_to_vertcos(ob, ss->kb); + + for(i=0; i<unode->totvert; i++) + swap_v3_v3(vertCos[index[i]], unode->co[i]); + + /* propagate new coords to keyblock */ + sculpt_vertcos_to_key(ob, ss->kb, vertCos); + + /* pbvh uses it's own mvert array, so coords should be */ + /* propagated to pbvh here */ + BLI_pbvh_apply_vertCos(ss->pbvh, vertCos); + + MEM_freeN(vertCos); + } else { + for(i=0; i<unode->totvert; i++) { + swap_v3_v3(mvert[index[i]].co, unode->co[i]); + mvert[index[i]].flag |= ME_VERT_PBVH_UPDATE; + } + } + } + else if(unode->maxgrid && dm->getGridData) { + /* multires restore */ + DMGridData **grids, *grid; + float (*co)[3]; + int gridsize; + + if(dm->getNumGrids(dm) != unode->maxgrid) + continue; + if(dm->getGridSize(dm) != unode->gridsize) + continue; + + grids= dm->getGridData(dm); + gridsize= dm->getGridSize(dm); + + co = unode->co; + for(j=0; j<unode->totgrid; j++) { + grid= grids[unode->grids[j]]; + + for(i=0; i<gridsize*gridsize; i++, co++) + swap_v3_v3(grid[i].co, co[0]); + } + } + + update= 1; + } + + if(update) { + /* we update all nodes still, should be more clever, but also + needs to work correct when exiting/entering sculpt mode and + the nodes get recreated, though in that case it could do all */ + BLI_pbvh_search_callback(ss->pbvh, NULL, NULL, update_cb, NULL); + BLI_pbvh_update(ss->pbvh, PBVH_UpdateBB|PBVH_UpdateOriginalBB|PBVH_UpdateRedraw, NULL); + + if((mmd=sculpt_multires_active(scene, ob))) + multires_mark_as_modified(ob); + + if(ss->modifiers_active || ((Mesh*)ob->data)->id.us > 1) + DAG_id_flush_update(&ob->id, OB_RECALC_DATA); + } +} + +static void sculpt_undo_free(ListBase *lb) +{ + SculptUndoNode *unode; + + for(unode=lb->first; unode; unode=unode->next) { + if(unode->co) + MEM_freeN(unode->co); + if(unode->no) + MEM_freeN(unode->no); + if(unode->index) + MEM_freeN(unode->index); + if(unode->grids) + MEM_freeN(unode->grids); + if(unode->layer_disp) + MEM_freeN(unode->layer_disp); + } +} + +SculptUndoNode *sculpt_undo_get_node(PBVHNode *node) +{ + ListBase *lb= undo_paint_push_get_list(UNDO_PAINT_MESH); + SculptUndoNode *unode; + + if(!lb) + return NULL; + + for(unode=lb->first; unode; unode=unode->next) + if(unode->node == node) + return unode; + + return NULL; +} + +SculptUndoNode *sculpt_undo_push_node(SculptSession *ss, PBVHNode *node) +{ + ListBase *lb= undo_paint_push_get_list(UNDO_PAINT_MESH); + Object *ob= ss->ob; + SculptUndoNode *unode; + int totvert, allvert, totgrid, maxgrid, gridsize, *grids; + + /* list is manipulated by multiple threads, so we lock */ + BLI_lock_thread(LOCK_CUSTOM1); + + if((unode= sculpt_undo_get_node(node))) { + BLI_unlock_thread(LOCK_CUSTOM1); + return unode; + } + + unode= MEM_callocN(sizeof(SculptUndoNode), "SculptUndoNode"); + strcpy(unode->idname, ob->id.name); + unode->node= node; + + BLI_pbvh_node_num_verts(ss->pbvh, node, &totvert, &allvert); + BLI_pbvh_node_get_grids(ss->pbvh, node, &grids, &totgrid, + &maxgrid, &gridsize, NULL, NULL); + + unode->totvert= totvert; + /* we will use this while sculpting, is mapalloc slow to access then? */ + unode->co= MEM_mapallocN(sizeof(float)*3*allvert, "SculptUndoNode.co"); + unode->no= MEM_mapallocN(sizeof(short)*3*allvert, "SculptUndoNode.no"); + undo_paint_push_count_alloc(UNDO_PAINT_MESH, (sizeof(float)*3 + sizeof(short)*3 + sizeof(int))*allvert); + BLI_addtail(lb, unode); + + if(maxgrid) { + /* multires */ + unode->maxgrid= maxgrid; + unode->totgrid= totgrid; + unode->gridsize= gridsize; + unode->grids= MEM_mapallocN(sizeof(int)*totgrid, "SculptUndoNode.grids"); + } + else { + /* regular mesh */ + unode->maxvert= ss->totvert; + unode->index= MEM_mapallocN(sizeof(int)*allvert, "SculptUndoNode.index"); + } + + BLI_unlock_thread(LOCK_CUSTOM1); + + /* copy threaded, hopefully this is the performance critical part */ + { + PBVHVertexIter vd; + + BLI_pbvh_vertex_iter_begin(ss->pbvh, node, vd, PBVH_ITER_ALL) { + copy_v3_v3(unode->co[vd.i], vd.co); + if(vd.no) VECCOPY(unode->no[vd.i], vd.no) + else normal_float_to_short_v3(unode->no[vd.i], vd.fno); + if(vd.vert_indices) unode->index[vd.i]= vd.vert_indices[vd.i]; + } + BLI_pbvh_vertex_iter_end; + } + + if(unode->grids) + memcpy(unode->grids, grids, sizeof(int)*totgrid); + + /* store active shape key */ + if(ss->kb) BLI_strncpy((char*)unode->shapeName, ss->kb->name, sizeof(ss->kb->name)); + else unode->shapeName[0]= '\0'; + + return unode; +} + +void sculpt_undo_push_begin(char *name) +{ + undo_paint_push_begin(UNDO_PAINT_MESH, name, + sculpt_undo_restore, sculpt_undo_free); +} + +void sculpt_undo_push_end(void) +{ + ListBase *lb= undo_paint_push_get_list(UNDO_PAINT_MESH); + SculptUndoNode *unode; + + /* we don't need normals in the undo stack */ + for(unode=lb->first; unode; unode=unode->next) { + if(unode->no) { + MEM_freeN(unode->no); + unode->no= NULL; + } + + if(unode->layer_disp) { + MEM_freeN(unode->layer_disp); + unode->layer_disp= NULL; + } + } + + undo_paint_push_end(UNDO_PAINT_MESH); +} |