diff options
Diffstat (limited to 'source/blender/editors/sculpt_paint')
-rw-r--r-- | source/blender/editors/sculpt_paint/CMakeLists.txt | 41 | ||||
-rw-r--r-- | source/blender/editors/sculpt_paint/Makefile | 2 | ||||
-rw-r--r-- | source/blender/editors/sculpt_paint/SConscript | 10 | ||||
-rw-r--r-- | source/blender/editors/sculpt_paint/paint_image.c | 207 | ||||
-rw-r--r-- | source/blender/editors/sculpt_paint/paint_intern.h | 9 | ||||
-rw-r--r-- | source/blender/editors/sculpt_paint/paint_ops.c | 244 | ||||
-rw-r--r-- | source/blender/editors/sculpt_paint/paint_stroke.c | 831 | ||||
-rw-r--r-- | source/blender/editors/sculpt_paint/paint_undo.c | 36 | ||||
-rw-r--r-- | source/blender/editors/sculpt_paint/paint_utils.c | 7 | ||||
-rw-r--r-- | source/blender/editors/sculpt_paint/paint_vertex.c | 94 | ||||
-rw-r--r-- | source/blender/editors/sculpt_paint/sculpt.c | 3102 | ||||
-rw-r--r-- | source/blender/editors/sculpt_paint/sculpt_intern.h | 48 | ||||
-rw-r--r-- | source/blender/editors/sculpt_paint/sculpt_undo.c | 302 |
13 files changed, 3791 insertions, 1142 deletions
diff --git a/source/blender/editors/sculpt_paint/CMakeLists.txt b/source/blender/editors/sculpt_paint/CMakeLists.txt new file mode 100644 index 00000000000..f0493d8e2d8 --- /dev/null +++ b/source/blender/editors/sculpt_paint/CMakeLists.txt @@ -0,0 +1,41 @@ +# $Id: CMakeLists.txt 12931 2007-12-17 18:20:48Z theeth $ +# ***** 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. +# +# Contributor(s): Jacques Beaurain. +# +# ***** END GPL LICENSE BLOCK ***** + +FILE(GLOB SRC *.c) + +SET(INC + ../../blenkernel + ../../imbuf + ../../gpu + ../../blenlib + ../include + ../../../../intern/guardedalloc + ../../makesdna + ../../makesrna + ../../windowmanager + ../../render/extern/include +) + +IF(WIN32) + SET(INC ${INC} ${PTHREADS_INC}) +ENDIF(WIN32) + +BLENDERLIB(bf_editor_sculpt_paint "${SRC}" "${INC}") diff --git a/source/blender/editors/sculpt_paint/Makefile b/source/blender/editors/sculpt_paint/Makefile index 012a39b8d25..f9f39fea7eb 100644 --- a/source/blender/editors/sculpt_paint/Makefile +++ b/source/blender/editors/sculpt_paint/Makefile @@ -15,7 +15,7 @@ # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software Foundation, -# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # # The Original Code is Copyright (C) 2007 Blender Foundation # All rights reserved. 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 df2e4032257..418bac69974 100644 --- a/source/blender/editors/sculpt_paint/paint_image.c +++ b/source/blender/editors/sculpt_paint/paint_image.c @@ -54,18 +54,16 @@ #include "DNA_meshdata_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" +#include "DNA_brush_types.h" #include "BKE_context.h" #include "BKE_idprop.h" #include "BKE_object.h" #include "BKE_brush.h" -#include "BKE_global.h" #include "BKE_image.h" #include "BKE_main.h" #include "BKE_mesh.h" -#include "BKE_node.h" #include "BKE_paint.h" -#include "BKE_utildefines.h" #include "BKE_DerivedMesh.h" #include "BKE_report.h" #include "BKE_depsgraph.h" @@ -641,8 +639,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 +1051,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 +1195,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 */ @@ -1334,7 +1332,7 @@ float project_paint_uvpixel_mask( // This only works when the opacity dosnt change while painting, stylus pressure messes with this // so dont use it. - // if (ps->is_airbrush==0) mask *= ps->brush->alpha; + // if (ps->is_airbrush==0) mask *= brush_alpha(ps->brush); return mask; } @@ -1444,7 +1442,7 @@ static ProjPixel *project_paint_uvpixel_init( sub_v2_v2v2(co, projPixel->projCoSS, (float *)ps->cloneOffset); /* no need to initialize the bucket, we're only checking buckets faces and for this - * the faces are alredy initialized in project_paint_delayed_face_init(...) */ + * the faces are already initialized in project_paint_delayed_face_init(...) */ if (ibuf->rect_float) { if (!project_paint_PickColor(ps, co, ((ProjPixelClone *)projPixel)->clonepx.f, NULL, 1)) { ((ProjPixelClone *)projPixel)->clonepx.f[3] = 0; /* zero alpha - ignore */ @@ -1642,10 +1640,10 @@ static void scale_quad(float insetCos[4][3], float *origCos[4], const float inse mul_v3_fl(insetCos[2], inset); mul_v3_fl(insetCos[3], inset); - add_v3_v3v3(insetCos[0], insetCos[0], cent); - add_v3_v3v3(insetCos[1], insetCos[1], cent); - add_v3_v3v3(insetCos[2], insetCos[2], cent); - add_v3_v3v3(insetCos[3], insetCos[3], cent); + add_v3_v3(insetCos[0], cent); + add_v3_v3(insetCos[1], cent); + add_v3_v3(insetCos[2], cent); + add_v3_v3(insetCos[3], cent); } @@ -1664,9 +1662,9 @@ static void scale_tri(float insetCos[4][3], float *origCos[4], const float inset mul_v3_fl(insetCos[1], inset); mul_v3_fl(insetCos[2], inset); - add_v3_v3v3(insetCos[0], insetCos[0], cent); - add_v3_v3v3(insetCos[1], insetCos[1], cent); - add_v3_v3v3(insetCos[2], insetCos[2], cent); + add_v3_v3(insetCos[0], cent); + add_v3_v3(insetCos[1], cent); + add_v3_v3(insetCos[2], cent); } @@ -1694,7 +1692,7 @@ static float Vec2Lenf_nosqrt_other(const float *v1, const float v2_1, const floa static int project_bucket_isect_circle(const int bucket_x, const int bucket_y, const float cent[2], const float radius_squared, rctf *bucket_bounds) { - /* Would normally to a simple intersection test, however we know the bounds of these 2 alredy intersect + /* Would normally to a simple intersection test, however we know the bounds of these 2 already intersect * so we only need to test if the center is inside the vertical or horizontal bounds on either axis, * this is even less work then an intersection test * @@ -2513,11 +2511,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 +2569,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); @@ -2808,6 +2807,8 @@ static void project_paint_begin(ProjPaintState *ps) MVert *mv; MemArena *arena; /* at the moment this is just ps->arena_mt[0], but use this to show were not multithreading */ + + const int diameter= 2*brush_size(ps->brush); /* ---- end defines ---- */ @@ -2973,7 +2974,7 @@ static void project_paint_begin(ProjPaintState *ps) VECCOPY(ps->viewPos, viewinv[3]); copy_m3_m4(mat, ps->ob->imat); mul_m3_v3(mat, ps->viewPos); - add_v3_v3v3(ps->viewPos, ps->viewPos, ps->ob->imat[3]); + add_v3_v3(ps->viewPos, ps->ob->imat[3]); } /* calculate vert screen coords @@ -3029,27 +3030,27 @@ 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)(-diameter), (float)(ps->winx + diameter)); + CLAMP(ps->screenMax[0], (float)(-diameter), (float)(ps->winx + diameter)); - 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)(-diameter), (float)(ps->winy + diameter)); + CLAMP(ps->screenMax[1], (float)(-diameter), (float)(ps->winy + diameter)); #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 */ ps->screen_width = ps->screenMax[0] - ps->screenMin[0]; ps->screen_height = ps->screenMax[1] - ps->screenMin[1]; - ps->buckets_x = (int)(ps->screen_width / (((float)ps->brush->size) / PROJ_BUCKET_BRUSH_DIV)); - ps->buckets_y = (int)(ps->screen_height / (((float)ps->brush->size) / PROJ_BUCKET_BRUSH_DIV)); + ps->buckets_x = (int)(ps->screen_width / (((float)diameter) / PROJ_BUCKET_BRUSH_DIV)); + ps->buckets_y = (int)(ps->screen_height / (((float)diameter) / PROJ_BUCKET_BRUSH_DIV)); /* printf("\tscreenspace bucket division x:%d y:%d\n", ps->buckets_x, ps->buckets_y); */ @@ -3083,7 +3084,7 @@ static void project_paint_begin(ProjPaintState *ps) ps->thread_tot = BLI_system_thread_count(); } for (a=0; a<ps->thread_tot; a++) { - ps->arena_mt[a] = BLI_memarena_new(1<<16); + ps->arena_mt[a] = BLI_memarena_new(1<<16, "project paint arena"); } arena = ps->arena_mt[0]; @@ -3447,16 +3448,16 @@ static int project_bucket_iter_init(ProjPaintState *ps, const float mval_f[2]) { if(ps->source==PROJ_SRC_VIEW) { float min_brush[2], max_brush[2]; - float size_half = ((float)ps->brush->size) * 0.5f; + const float radius = (float)brush_size(ps->brush); /* so we dont have a bucket bounds that is way too small to paint into */ - // if (size_half < 1.0f) size_half = 1.0f; // this dosnt work yet :/ + // if (radius < 1.0f) radius = 1.0f; // this doesn't work yet :/ - min_brush[0] = mval_f[0] - size_half; - min_brush[1] = mval_f[1] - size_half; + min_brush[0] = mval_f[0] - radius; + min_brush[1] = mval_f[1] - radius; - max_brush[0] = mval_f[0] + size_half; - max_brush[1] = mval_f[1] + size_half; + max_brush[0] = mval_f[0] + radius; + max_brush[1] = mval_f[1] + radius; /* offset to make this a valid bucket index */ project_paint_bucket_bounds(ps, min_brush, max_brush, ps->bucketMin, ps->bucketMax); @@ -3485,6 +3486,8 @@ static int project_bucket_iter_init(ProjPaintState *ps, const float mval_f[2]) static int project_bucket_iter_next(ProjPaintState *ps, int *bucket_index, rctf *bucket_bounds, const float mval[2]) { + const int diameter= 2*brush_size(ps->brush); + if (ps->thread_tot > 1) BLI_lock_thread(LOCK_CUSTOM1); @@ -3497,7 +3500,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)(diameter*diameter), bucket_bounds) ) { *bucket_index = ps->context_bucket_x + (ps->context_bucket_y * ps->buckets_x); ps->context_bucket_x++; @@ -3545,23 +3548,25 @@ 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]; cp[3]= mfac*cp1[3] + fac*cp2[3]; } -static void blend_color_mix_rgb(unsigned char *cp, const unsigned char *cp1, const unsigned char *cp2, const int fac) +static void blend_color_mix_accum(unsigned char *cp, const unsigned char *cp1, const unsigned char *cp2, const int fac) { /* this and other blending modes previously used >>8 instead of /255. both are not equivalent (>>8 is /256), and the former results in rounding errors that can turn colors black fast after repeated blending */ const int mfac= 255-fac; + const int alpha= cp1[3] + ((fac * cp2[3]) / 255); cp[0]= (mfac*cp1[0]+fac*cp2[0])/255; cp[1]= (mfac*cp1[1]+fac*cp2[1])/255; cp[2]= (mfac*cp1[2]+fac*cp2[2])/255; + cp[3]= alpha > 255 ? 255 : alpha; } static void do_projectpaint_clone(ProjPaintState *ps, ProjPixel *projPixel, float *rgba, float alpha, float mask) @@ -3682,7 +3687,6 @@ static void *do_projectpaint_thread(void *ph_v) float rgba[4], alpha, dist_nosqrt, dist; - float brush_size_sqared; float falloff; int bucket_index; int is_floatbuf = 0; @@ -3694,24 +3698,22 @@ static void *do_projectpaint_thread(void *ph_v) float co[2]; float mask = 1.0f; /* airbrush wont use mask */ unsigned short mask_short; - float size_half = ((float)ps->brush->size) * 0.5f; + const float radius= (float)brush_size(ps->brush); + const float radius_squared= radius*radius; /* avoid a square root with every dist comparison */ + short lock_alpha= ELEM(ps->brush->blend, IMB_BLEND_ERASE_ALPHA, IMB_BLEND_ADD_ALPHA) ? 0 : ps->brush->flag & BRUSH_LOCK_ALPHA; LinkNode *smearPixels = NULL; LinkNode *smearPixels_f = NULL; MemArena *smearArena = NULL; /* mem arena for this brush projection only */ - if (tool==PAINT_TOOL_SMEAR) { pos_ofs[0] = pos[0] - lastpos[0]; pos_ofs[1] = pos[1] - lastpos[1]; - smearArena = BLI_memarena_new(1<<16); + smearArena = BLI_memarena_new(1<<16, "paint smear arena"); } - /* avoid a square root with every dist comparison */ - brush_size_sqared = ps->brush->size * ps->brush->size; - /* printf("brush bounds %d %d %d %d\n", bucketMin[0], bucketMin[1], bucketMax[0], bucketMax[1]); */ while (project_bucket_iter_next(ps, &bucket_index, &bucket_bounds, pos)) { @@ -3732,7 +3734,8 @@ static void *do_projectpaint_thread(void *ph_v) bicubic_interpolation_color(ps->reproject_ibuf, projPixel->newColor.ch, NULL, projPixel->projCoSS[0], projPixel->projCoSS[1]); if(projPixel->newColor.ch[3]) { mask = ((float)projPixel->mask)/65535.0f; - blend_color_mix_rgb(projPixel->pixel.ch_pt, projPixel->origColor.ch, projPixel->newColor.ch, (mask*projPixel->newColor.ch[3])); + blend_color_mix_accum(projPixel->pixel.ch_pt, projPixel->origColor.ch, projPixel->newColor.ch, (int)(mask*projPixel->newColor.ch[3])); + } } } @@ -3746,9 +3749,11 @@ static void *do_projectpaint_thread(void *ph_v) /*dist = len_v2v2(projPixel->projCoSS, pos);*/ /* correct but uses a sqrtf */ dist_nosqrt = Vec2Lenf_nosqrt(projPixel->projCoSS, pos); - /*if (dist < s->brush->size) {*/ /* correct but uses a sqrtf */ - if (dist_nosqrt < brush_size_sqared && (dist=sqrtf(dist_nosqrt)) < size_half) { - falloff = brush_curve_strength_clamp(ps->brush, dist, size_half); + /*if (dist < radius) {*/ /* correct but uses a sqrtf */ + if (dist_nosqrt <= radius_squared) { + dist=sqrtf(dist_nosqrt); + + falloff = brush_curve_strength_clamp(ps->brush, dist, radius); if (falloff > 0.0f) { if (ps->is_texbrush) { @@ -3760,7 +3765,7 @@ static void *do_projectpaint_thread(void *ph_v) if (ps->is_airbrush) { /* for an aurbrush there is no real mask, so just multiply the alpha by it */ - alpha *= falloff * ps->brush->alpha; + alpha *= falloff * brush_alpha(ps->brush); mask = ((float)projPixel->mask)/65535.0f; } else { @@ -3768,7 +3773,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 * (brush_alpha(ps->brush) * falloff)); if (mask_short > projPixel->mask_max) { mask = ((float)mask_short)/65535.0f; projPixel->mask_max = mask_short; @@ -3929,8 +3934,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); @@ -4054,7 +4059,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; } @@ -4515,7 +4521,7 @@ typedef struct PaintOperation { int first; int prevmouse[2]; float prev_pressure; /* need this since we dont get tablet events for pressure change */ - int brush_size_orig; + int orig_brush_size; double starttime; ViewContext vc; @@ -4608,8 +4614,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) @@ -4648,7 +4654,7 @@ static int texture_paint_init(bContext *C, wmOperator *op) if(pop->mode == PAINT_MODE_3D && (pop->s.tool == PAINT_TOOL_CLONE)) pop->s.tool = PAINT_TOOL_DRAW; pop->s.blend = brush->blend; - pop->brush_size_orig= brush->size; + pop->orig_brush_size= brush_size(brush); if(pop->mode != PAINT_MODE_2D) { pop->s.ob = OBACT; @@ -4680,8 +4686,8 @@ static int texture_paint_init(bContext *C, wmOperator *op) return 0; /* Dont allow brush size below 2 */ - if (brush->size <= 1) - brush->size = 2; + if (brush_size(brush) < 2) + brush_set_size(brush, 2); /* allocate and initialize spacial data structures */ project_paint_begin(&pop->ps); @@ -4691,7 +4697,7 @@ static int texture_paint_init(bContext *C, wmOperator *op) } settings->imapaint.flag |= IMAGEPAINT_DRAWING; - undo_paint_push_begin(UNDO_PAINT_IMAGE, "Image Paint", + undo_paint_push_begin(UNDO_PAINT_IMAGE, op->type->name, image_undo_restore, image_undo_free); /* create painter */ @@ -4708,8 +4714,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"); @@ -4751,7 +4757,7 @@ static void paint_exit(bContext *C, wmOperator *op) brush_painter_free(pop->painter); if(pop->mode == PAINT_MODE_3D_PROJECT) { - pop->ps.brush->size = pop->brush_size_orig; + brush_set_size(pop->ps.brush, pop->orig_brush_size); project_paint_end(&pop->ps); } @@ -4822,15 +4828,22 @@ static void paint_apply_event(bContext *C, wmOperator *op, wmEvent *event) /* special exception here for too high pressure values on first touch in windows for some tablets, then we just skip first touch .. */ - if ((pop->s.brush->flag & (BRUSH_ALPHA_PRESSURE|BRUSH_SIZE_PRESSURE|BRUSH_SPACING_PRESSURE)) && tablet && (pressure >= 0.99f)) + if (tablet && (pressure >= 0.99f) && ((pop->s.brush->flag & BRUSH_SPACING_PRESSURE) || brush_use_alpha_pressure(pop->s.brush) || brush_use_size_pressure(pop->s.brush))) + return; + + /* This can be removed once fixed properly in + brush_painter_paint(BrushPainter *painter, BrushFunc func, float *pos, double time, float pressure, void *user) + at zero pressure we should do nothing 1/2^12 is .0002 which is the sensitivity of the most sensitive pen tablet available*/ + if (tablet && (pressure < .0002f) && ((pop->s.brush->flag & BRUSH_SPACING_PRESSURE) || brush_use_alpha_pressure(pop->s.brush) || brush_use_size_pressure(pop->s.brush))) return; + } /* 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); @@ -4872,6 +4885,7 @@ static int paint_modal(bContext *C, wmOperator *op, wmEvent *event) paint_exit(C, op); return OPERATOR_FINISHED; case MOUSEMOVE: + case INBETWEEN_MOUSEMOVE: paint_apply_event(C, op, event); break; case TIMER: @@ -4933,9 +4947,14 @@ static int get_imapaint_zoom(bContext *C, float *zoomx, float *zoomy) static void brush_drawcursor(bContext *C, int x, int y, void *customdata) { Brush *brush= image_paint_brush(C); + Paint *paint= paint_get_active(CTX_data_scene(C)); - if(brush) { + if(paint && brush) { float zoomx, zoomy; + + if(!(paint->flags & PAINT_SHOW_BRUSH)) + return; + glPushMatrix(); glTranslatef((float)x, (float)y, 0.0f); @@ -4943,13 +4962,13 @@ static void brush_drawcursor(bContext *C, int x, int y, void *customdata) if(get_imapaint_zoom(C, &zoomx, &zoomy)) glScalef(zoomx, zoomy, 1.0f); - glColor4ub(255, 255, 255, 128); + glColor4f(brush->add_col[0], brush->add_col[1], brush->add_col[2], 0.5f); 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), (float)brush_size(brush), 40); glDisable(GL_BLEND); glDisable( GL_LINE_SMOOTH ); - + glPopMatrix(); } } @@ -4973,7 +4992,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), zoom); return WM_radial_control_invoke(C, op, event); } @@ -4993,7 +5012,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, 1.0f / zoom); WM_radial_control_string(op, str, 256); WM_event_add_notifier(C, NC_BRUSH|NA_EDITED, brush); @@ -5300,14 +5319,14 @@ static int texture_paint_radial_control_invoke(bContext *C, wmOperator *op, wmEv { ToolSettings *ts = CTX_data_scene(C)->toolsettings; toggle_paint_cursor(C, !ts->imapaint.paintcursor); - brush_radial_control_invoke(op, paint_brush(&ts->imapaint.paint), 0.5); + brush_radial_control_invoke(op, paint_brush(&ts->imapaint.paint), 1); return WM_radial_control_invoke(C, op, event); } static int texture_paint_radial_control_exec(bContext *C, wmOperator *op) { Brush *brush = paint_brush(&CTX_data_scene(C)->toolsettings->imapaint.paint); - int ret = brush_radial_control_exec(op, brush, 2); + int ret = brush_radial_control_exec(op, brush, 1); char str[256]; WM_radial_control_string(op, str, 256); @@ -5343,7 +5362,7 @@ void PAINT_OT_texture_paint_radial_control(wmOperatorType *ot) ot->poll= texture_paint_poll; /* flags */ - ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO|OPTYPE_BLOCKING; } @@ -5412,21 +5431,21 @@ static int texture_paint_camera_project_exec(bContext *C, wmOperator *op) /* override */ ps.is_texbrush= 0; ps.is_airbrush= 1; - orig_brush_size= ps.brush->size; - ps.brush->size= 32; /* cover the whole image */ + orig_brush_size= brush_size(ps.brush); + brush_set_size(ps.brush, 32); /* cover the whole image */ ps.tool= PAINT_TOOL_DRAW; /* so pixels are initialized with minimal info */ scene->toolsettings->imapaint.flag |= IMAGEPAINT_DRAWING; - undo_paint_push_begin(UNDO_PAINT_IMAGE, "Image Paint", + undo_paint_push_begin(UNDO_PAINT_IMAGE, op->type->name, image_undo_restore, image_undo_free); /* allocate and initialize spacial data structures */ project_paint_begin(&ps); if(ps.dm==NULL) { - ps.brush->size= orig_brush_size; + brush_set_size(ps.brush, orig_brush_size); return OPERATOR_CANCELLED; } else { @@ -5450,7 +5469,7 @@ static int texture_paint_camera_project_exec(bContext *C, wmOperator *op) project_paint_end(&ps); scene->toolsettings->imapaint.flag &= ~IMAGEPAINT_DRAWING; - ps.brush->size= orig_brush_size; + brush_set_size(ps.brush, orig_brush_size); return OPERATOR_FINISHED; } @@ -5488,14 +5507,14 @@ static int texture_paint_image_from_view_exec(bContext *C, wmOperator *op) int h= settings->imapaint.screen_grab_size[1]; int maxsize; - RNA_string_get(op->ptr, "filename", filename); + RNA_string_get(op->ptr, "filepath", filename); glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxsize); if(w > maxsize) w= maxsize; if(h > maxsize) h= maxsize; - ibuf= ED_view3d_draw_offscreen_imbuf(CTX_data_scene(C), CTX_wm_view3d(C), CTX_wm_region(C), w, h); + ibuf= ED_view3d_draw_offscreen_imbuf(CTX_data_scene(C), CTX_wm_view3d(C), CTX_wm_region(C), w, h, IB_rect); image= BKE_add_image_imbuf(ibuf); if(image) { @@ -5541,5 +5560,5 @@ void PAINT_OT_image_from_view(wmOperatorType *ot) /* flags */ ot->flag= OPTYPE_REGISTER; - RNA_def_string_file_name(ot->srna, "filename", "", FILE_MAX, "File Name", "Name of the file"); + RNA_def_string_file_name(ot->srna, "filepath", "", FILE_MAX, "File Path", "Name of the file"); } diff --git a/source/blender/editors/sculpt_paint/paint_intern.h b/source/blender/editors/sculpt_paint/paint_intern.h index b25eec70311..3ed314095ef 100644 --- a/source/blender/editors/sculpt_paint/paint_intern.h +++ b/source/blender/editors/sculpt_paint/paint_intern.h @@ -110,11 +110,18 @@ 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); -void undo_paint_push_begin(int type, char *name, UndoRestoreCb restore, UndoFreeCb free); +void undo_paint_push_begin(int type, const char *name, UndoRestoreCb restore, UndoFreeCb free); struct ListBase *undo_paint_push_get_list(int type); void undo_paint_push_count_alloc(int type, int size); void undo_paint_push_end(int type); diff --git a/source/blender/editors/sculpt_paint/paint_ops.c b/source/blender/editors/sculpt_paint/paint_ops.c index abd56babd8d..b5cadb9c484 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)); + struct 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 */ @@ -73,8 +70,63 @@ void BRUSH_OT_add(wmOperatorType *ot) /* flags */ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; +} + + +static int brush_scale_size_exec(bContext *C, wmOperator *op) +{ + Paint *paint= paint_get_active(CTX_data_scene(C)); + struct Brush *brush= paint_brush(paint); + // Object *ob= CTX_data_active_object(C); + float scalar= RNA_float_get(op->ptr, "scalar"); + + if (brush) { + // pixel radius + { + const int old_size= brush_size(brush); + int size= (int)(scalar*old_size); + + if (old_size == size) { + if (scalar > 1) { + size++; + } + else if (scalar < 1) { + size--; + } + } + CLAMP(size, 1, 2000); // XXX magic number + + brush_set_size(brush, size); + } + + // unprojected radius + { + float unprojected_radius= scalar*brush_unprojected_radius(brush); + + if (unprojected_radius < 0.001) // XXX magic number + unprojected_radius= 0.001f; + + brush_set_unprojected_radius(brush, unprojected_radius); + } + } + + 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_REGISTER|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) @@ -102,13 +154,44 @@ void PAINT_OT_vertex_color_set(wmOperatorType *ot) ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; } +static int brush_reset_exec(bContext *C, wmOperator *op) +{ + Paint *paint = paint_get_active(CTX_data_scene(C)); + struct Brush *brush = paint_brush(paint); + Object *ob = CTX_data_active_object(C); + + if(!ob) return OPERATOR_CANCELLED; + + if(ob->mode & OB_MODE_SCULPT) + brush_reset_sculpt(brush); + /* TODO: other modes */ + + return OPERATOR_FINISHED; +} + +void BRUSH_OT_reset(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Reset Brush"; + ot->description= "Return brush to defaults based on current tool"; + ot->idname= "BRUSH_OT_reset"; + + /* api callbacks */ + ot->exec= brush_reset_exec; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; +} + /**************************** registration **********************************/ 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); + WM_operatortype_append(BRUSH_OT_reset); /* image */ WM_operatortype_append(PAINT_OT_texture_paint_toggle); @@ -121,7 +204,6 @@ void ED_operatortypes_paint(void) WM_operatortype_append(PAINT_OT_project_image); WM_operatortype_append(PAINT_OT_image_from_view); - /* weight */ WM_operatortype_append(PAINT_OT_weight_paint_toggle); WM_operatortype_append(PAINT_OT_weight_paint_radial_control); @@ -141,54 +223,83 @@ 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; kmi= WM_keymap_add_item(keymap, "WM_OT_context_set_int", ONEKEY, KM_PRESS, 0, 0); - RNA_string_set(kmi->ptr, "path", path); + RNA_string_set(kmi->ptr, "data_path", path); RNA_int_set(kmi->ptr, "value", 0); kmi= WM_keymap_add_item(keymap, "WM_OT_context_set_int", TWOKEY, KM_PRESS, 0, 0); - RNA_string_set(kmi->ptr, "path", path); + RNA_string_set(kmi->ptr, "data_path", path); RNA_int_set(kmi->ptr, "value", 1); kmi= WM_keymap_add_item(keymap, "WM_OT_context_set_int", THREEKEY, KM_PRESS, 0, 0); - RNA_string_set(kmi->ptr, "path", path); + RNA_string_set(kmi->ptr, "data_path", path); RNA_int_set(kmi->ptr, "value", 2); kmi= WM_keymap_add_item(keymap, "WM_OT_context_set_int", FOURKEY, KM_PRESS, 0, 0); - RNA_string_set(kmi->ptr, "path", path); + RNA_string_set(kmi->ptr, "data_path", path); RNA_int_set(kmi->ptr, "value", 3); kmi= WM_keymap_add_item(keymap, "WM_OT_context_set_int", FIVEKEY, KM_PRESS, 0, 0); - RNA_string_set(kmi->ptr, "path", path); + RNA_string_set(kmi->ptr, "data_path", path); RNA_int_set(kmi->ptr, "value", 4); kmi= WM_keymap_add_item(keymap, "WM_OT_context_set_int", SIXKEY, KM_PRESS, 0, 0); - RNA_string_set(kmi->ptr, "path", path); + RNA_string_set(kmi->ptr, "data_path", path); RNA_int_set(kmi->ptr, "value", 5); kmi= WM_keymap_add_item(keymap, "WM_OT_context_set_int", SEVENKEY, KM_PRESS, 0, 0); - RNA_string_set(kmi->ptr, "path", path); + RNA_string_set(kmi->ptr, "data_path", path); RNA_int_set(kmi->ptr, "value", 6); kmi= WM_keymap_add_item(keymap, "WM_OT_context_set_int", EIGHTKEY, KM_PRESS, 0, 0); - RNA_string_set(kmi->ptr, "path", path); + RNA_string_set(kmi->ptr, "data_path", path); RNA_int_set(kmi->ptr, "value", 7); kmi= WM_keymap_add_item(keymap, "WM_OT_context_set_int", NINEKEY, KM_PRESS, 0, 0); - RNA_string_set(kmi->ptr, "path", path); + RNA_string_set(kmi->ptr, "data_path", path); RNA_int_set(kmi->ptr, "value", 8); kmi= WM_keymap_add_item(keymap, "WM_OT_context_set_int", ZEROKEY, KM_PRESS, 0, 0); - RNA_string_set(kmi->ptr, "path", path); + RNA_string_set(kmi->ptr, "data_path", path); RNA_int_set(kmi->ptr, "value", 9); + kmi= WM_keymap_add_item(keymap, "WM_OT_context_set_int", ONEKEY, KM_PRESS, KM_SHIFT, 0); + RNA_string_set(kmi->ptr, "data_path", path); + RNA_int_set(kmi->ptr, "value", 10); + kmi= WM_keymap_add_item(keymap, "WM_OT_context_set_int", TWOKEY, KM_PRESS, KM_SHIFT, 0); + RNA_string_set(kmi->ptr, "data_path", path); + RNA_int_set(kmi->ptr, "value", 11); + kmi= WM_keymap_add_item(keymap, "WM_OT_context_set_int", THREEKEY, KM_PRESS, KM_SHIFT, 0); + RNA_string_set(kmi->ptr, "data_path", path); + RNA_int_set(kmi->ptr, "value", 12); + kmi= WM_keymap_add_item(keymap, "WM_OT_context_set_int", FOURKEY, KM_PRESS, KM_SHIFT, 0); + RNA_string_set(kmi->ptr, "data_path", path); + RNA_int_set(kmi->ptr, "value", 13); + kmi= WM_keymap_add_item(keymap, "WM_OT_context_set_int", FIVEKEY, KM_PRESS, KM_SHIFT, 0); + RNA_string_set(kmi->ptr, "data_path", path); + RNA_int_set(kmi->ptr, "value", 14); + kmi= WM_keymap_add_item(keymap, "WM_OT_context_set_int", SIXKEY, KM_PRESS, KM_SHIFT, 0); + RNA_string_set(kmi->ptr, "data_path", path); + RNA_int_set(kmi->ptr, "value", 15); + kmi= WM_keymap_add_item(keymap, "WM_OT_context_set_int", SEVENKEY, KM_PRESS, KM_SHIFT, 0); + RNA_string_set(kmi->ptr, "data_path", path); + RNA_int_set(kmi->ptr, "value", 16); + kmi= WM_keymap_add_item(keymap, "WM_OT_context_set_int", EIGHTKEY, KM_PRESS, KM_SHIFT, 0); + RNA_string_set(kmi->ptr, "data_path", path); + RNA_int_set(kmi->ptr, "value", 17); + kmi= WM_keymap_add_item(keymap, "WM_OT_context_set_int", NINEKEY, KM_PRESS, KM_SHIFT, 0); + RNA_string_set(kmi->ptr, "data_path", path); + RNA_int_set(kmi->ptr, "value", 18); + kmi= WM_keymap_add_item(keymap, "WM_OT_context_set_int", ZEROKEY, KM_PRESS, KM_SHIFT, 0); + RNA_string_set(kmi->ptr, "data_path", path); + RNA_int_set(kmi->ptr, "value", 19); } 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, "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, "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 +311,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,44 +335,56 @@ 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, "path", "tool_settings.sculpt.brush.use_anchor"); + RNA_string_set(kmi->ptr, "data_path", "tool_settings.sculpt.brush.use_anchor"); kmi = WM_keymap_add_item(keymap, "WM_OT_context_toggle", SKEY, KM_PRESS, KM_SHIFT, 0); - RNA_string_set(kmi->ptr, "path", "tool_settings.sculpt.brush.use_smooth_stroke"); + RNA_string_set(kmi->ptr, "data_path", "tool_settings.sculpt.brush.use_smooth_stroke"); kmi = WM_keymap_add_item(keymap, "WM_OT_context_toggle", RKEY, KM_PRESS, 0, 0); - RNA_string_set(kmi->ptr, "path", "tool_settings.sculpt.brush.use_rake"); + RNA_string_set(kmi->ptr, "data_path", "tool_settings.sculpt.brush.use_rake"); kmi = WM_keymap_add_item(keymap, "WM_OT_context_toggle", AKEY, KM_PRESS, KM_SHIFT, 0); - RNA_string_set(kmi->ptr, "path", "tool_settings.sculpt.brush.use_airbrush"); + RNA_string_set(kmi->ptr, "data_path", "tool_settings.sculpt.brush.use_airbrush"); /* brush switching */ - kmi = WM_keymap_add_item(keymap, "WM_OT_context_set_enum", DKEY, KM_PRESS, 0, 0); - RNA_string_set(kmi->ptr, "path", "tool_settings.sculpt.active_brush_name"); - RNA_string_set(kmi->ptr, "value", "Draw"); + kmi = WM_keymap_add_item(keymap, "WM_OT_context_set_id", DKEY, KM_PRESS, 0, 0); + RNA_string_set(kmi->ptr, "data_path", "tool_settings.sculpt.brush"); + RNA_string_set(kmi->ptr, "value", "SculptDraw"); - kmi = WM_keymap_add_item(keymap, "WM_OT_context_set_enum", SKEY, KM_PRESS, 0, 0); - RNA_string_set(kmi->ptr, "path", "tool_settings.sculpt.active_brush_name"); + kmi = WM_keymap_add_item(keymap, "WM_OT_context_set_id", SKEY, KM_PRESS, 0, 0); + RNA_string_set(kmi->ptr, "data_path", "tool_settings.sculpt.brush"); RNA_string_set(kmi->ptr, "value", "Smooth"); - kmi = WM_keymap_add_item(keymap, "WM_OT_context_set_enum", PKEY, KM_PRESS, 0, 0); - RNA_string_set(kmi->ptr, "path", "tool_settings.sculpt.active_brush_name"); - RNA_string_set(kmi->ptr, "value", "Pinch"); + kmi = WM_keymap_add_item(keymap, "WM_OT_context_set_id", PKEY, KM_PRESS, 0, 0); + RNA_string_set(kmi->ptr, "data_path", "tool_settings.sculpt.brush"); + RNA_string_set(kmi->ptr, "value", "Pinch/Magnify"); - kmi = WM_keymap_add_item(keymap, "WM_OT_context_set_enum", GKEY, KM_PRESS, 0, 0); - RNA_string_set(kmi->ptr, "path", "tool_settings.sculpt.active_brush_name"); + kmi = WM_keymap_add_item(keymap, "WM_OT_context_set_id", GKEY, KM_PRESS, 0, 0); + RNA_string_set(kmi->ptr, "data_path", "tool_settings.sculpt.brush"); RNA_string_set(kmi->ptr, "value", "Grab"); - kmi = WM_keymap_add_item(keymap, "WM_OT_context_set_enum", LKEY, KM_PRESS, 0, 0); - RNA_string_set(kmi->ptr, "path", "tool_settings.sculpt.active_brush_name"); + kmi = WM_keymap_add_item(keymap, "WM_OT_context_set_id", LKEY, KM_PRESS, 0, 0); + RNA_string_set(kmi->ptr, "data_path", "tool_settings.sculpt.brush"); RNA_string_set(kmi->ptr, "value", "Layer"); + + kmi = WM_keymap_add_item(keymap, "WM_OT_context_set_id", CKEY, KM_PRESS, KM_SHIFT, 0); + RNA_string_set(kmi->ptr, "data_path", "tool_settings.sculpt.brush"); + RNA_string_set(kmi->ptr, "value", "Crease"); + + kmi = WM_keymap_add_item(keymap, "WM_OT_context_set_id", CKEY, KM_PRESS, 0, 0); + RNA_string_set(kmi->ptr, "data_path", "tool_settings.sculpt.brush"); + RNA_string_set(kmi->ptr, "value", "Clay"); - 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, "path", "tool_settings.sculpt.active_brush_name"); - RNA_string_set(kmi->ptr, "value", "Flatten"); + kmi = WM_keymap_add_item(keymap, "WM_OT_context_set_id", IKEY, KM_PRESS, 0, 0); + RNA_string_set(kmi->ptr, "data_path", "tool_settings.sculpt.brush"); + RNA_string_set(kmi->ptr, "value", "Inflate/Deflate"); + kmi = WM_keymap_add_item(keymap, "WM_OT_context_set_id", TKEY, KM_PRESS, KM_SHIFT, 0); // was just T in 2.4x + RNA_string_set(kmi->ptr, "data_path", "tool_settings.sculpt.brush"); + RNA_string_set(kmi->ptr, "value", "Flatten/Contrast"); /* Vertex Paint mode */ keymap= WM_keymap_find(keyconf, "Vertex Paint", 0, 0); @@ -276,7 +402,7 @@ void ED_keymap_paint(wmKeyConfig *keyconf) ed_keymap_paint_brush_size(keymap, "tool_settings.vertex_paint.brush.size"); kmi = WM_keymap_add_item(keymap, "WM_OT_context_toggle", MKEY, KM_PRESS, 0, 0); /* mask toggle */ - RNA_string_set(kmi->ptr, "path", "vertex_paint_object.data.use_paint_mask"); + RNA_string_set(kmi->ptr, "data_path", "vertex_paint_object.data.use_paint_mask"); /* Weight Paint mode */ keymap= WM_keymap_find(keyconf, "Weight Paint", 0, 0); @@ -294,7 +420,7 @@ void ED_keymap_paint(wmKeyConfig *keyconf) ed_keymap_paint_brush_size(keymap, "tool_settings.weight_paint.brush.size"); kmi = WM_keymap_add_item(keymap, "WM_OT_context_toggle", MKEY, KM_PRESS, 0, 0); /* mask toggle */ - RNA_string_set(kmi->ptr, "path", "weight_paint_object.data.use_paint_mask"); + RNA_string_set(kmi->ptr, "data_path", "weight_paint_object.data.use_paint_mask"); WM_keymap_verify_item(keymap, "PAINT_OT_weight_from_bones", WKEY, KM_PRESS, 0, 0); @@ -314,7 +440,7 @@ void ED_keymap_paint(wmKeyConfig *keyconf) ed_keymap_paint_brush_size(keymap, "tool_settings.image_paint.brush.size"); kmi = WM_keymap_add_item(keymap, "WM_OT_context_toggle", MKEY, KM_PRESS, 0, 0); /* mask toggle */ - RNA_string_set(kmi->ptr, "path", "texture_paint_object.data.use_paint_mask"); + RNA_string_set(kmi->ptr, "data_path", "texture_paint_object.data.use_paint_mask"); /* face-mask mode */ keymap= WM_keymap_find(keyconf, "Face Mask", 0, 0); diff --git a/source/blender/editors/sculpt_paint/paint_stroke.c b/source/blender/editors/sculpt_paint/paint_stroke.c index c25b5685844..6d58731d79c 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 ***** * @@ -30,11 +30,13 @@ #include "DNA_object_types.h" #include "DNA_scene_types.h" +#include "DNA_brush_types.h" #include "RNA_access.h" #include "BKE_context.h" #include "BKE_paint.h" +#include "BKE_brush.h" #include "WM_api.h" #include "WM_types.h" @@ -49,6 +51,8 @@ #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 <float.h> #include <math.h> @@ -65,7 +69,7 @@ typedef struct PaintStroke { float last_mouse_position[2]; - /* Set whether any stroke step has yet occured + /* Set whether any stroke step has yet occurred e.g. in sculpt mode, stroke doesn't start until cursor passes over the mesh */ int stroke_started; @@ -96,54 +100,751 @@ 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 && brush_size(brush) <= snap->brush_size) || (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[1]= snap->ofs[2]= -1; + snap->size[0]= snap->size[1]= snap->size[2]= -1; + snap->rot = -1; + } + + snap->brush_size = 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 = 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 radius = 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 / radius; + y *= vc->ar->winy / radius; + } + 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] = 255 - (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, brush_unprojected_radius(stroke->brush), location, &stroke->mats); + + if (*pixel_radius == 0) + *pixel_radius = 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 = 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; + + const float root_alpha = brush_alpha(brush); + float visual_strength = root_alpha*root_alpha; + + 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(!brush_use_locked_size(brush) && !(paint->flags & PAINT_SHOW_BRUSH)) + return; + + hit = sculpt_get_brush_geometry(C, x, y, &pixel_radius, location, modelview, projection, viewport); + + if (brush_use_locked_size(brush)) + brush_set_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_use_size_pressure(brush)) { + 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 { + const int radius= brush_size(brush); + + glTexCoord2f(0, 0); + glVertex2f((float)x-radius, (float)y-radius); + + glTexCoord2f(1, 0); + glVertex2f((float)x+radius, (float)y-radius); + + glTexCoord2f(1, 1); + glVertex2f((float)x+radius, (float)y+radius); + + glTexCoord2f(0, 1); + glVertex2f((float)x-radius, (float)y+radius); + } + } + 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_use_alpha_pressure(brush)) + 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, brush_size(brush)); + } + + if (sd->draw_pressure && brush_use_size_pressure(brush)) + unprojected_radius *= sd->pressure_value; + + if (!brush_use_locked_size(brush)) + brush_set_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, 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; + + glColor4f(brush->add_col[0], brush->add_col[1], brush->add_col[2], 0.5f); + 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(brush), 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[3]; + 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 +855,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 +881,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 +894,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 = brush_use_size_pressure(stroke->brush) ? wmtab->Pressure : 1; + } + + scale = (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); } } @@ -275,21 +993,34 @@ int paint_stroke_modal(bContext *C, wmOperator *op, wmEvent *event) MEM_freeN(stroke); return OPERATOR_FINISHED; } - else if(first || event->type == MOUSEMOVE || (event->type == TIMER && (event->customdata == stroke->timer))) { + else if(first || ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE) || (event->type == TIMER && (event->customdata == stroke->timer))) { if(stroke->stroke_started) { if(paint_smooth_stroke(stroke, mouse, event)) { if(paint_space_stroke_enabled(stroke->brush)) { - if(!paint_space_stroke(C, op, event, mouse)) - ;//ED_region_tag_redraw(ar); + if(!paint_space_stroke(C, op, event, mouse)) { + //ED_region_tag_redraw(ar); + } } - else + else { paint_brush_stroke_add_step(C, op, event, mouse); + } } - else + 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_undo.c b/source/blender/editors/sculpt_paint/paint_undo.c index 5da988f1ae1..c2f82b8e2e0 100644 --- a/source/blender/editors/sculpt_paint/paint_undo.c +++ b/source/blender/editors/sculpt_paint/paint_undo.c @@ -76,7 +76,7 @@ static void undo_elem_free(UndoStack *stack, UndoElem *uel) } } -static void undo_stack_push_begin(UndoStack *stack, char *name, UndoRestoreCb restore, UndoFreeCb free) +static void undo_stack_push_begin(UndoStack *stack, const char *name, UndoRestoreCb restore, UndoFreeCb free) { UndoElem *uel; int nr; @@ -145,27 +145,35 @@ static void undo_stack_push_end(UndoStack *stack) } } -static void undo_stack_step(bContext *C, UndoStack *stack, int step) +static int undo_stack_step(bContext *C, UndoStack *stack, int step, const char *name) { UndoElem *undo; if(step==1) { if(stack->current==NULL); else { - if(G.f & G_DEBUG) printf("undo %s\n", stack->current->name); - undo_restore(C, stack, stack->current); - stack->current= stack->current->prev; + if(!name || strcmp(stack->current->name, name) == 0) { + if(G.f & G_DEBUG) printf("undo %s\n", stack->current->name); + undo_restore(C, stack, stack->current); + stack->current= stack->current->prev; + return 1; + } } } else if(step==-1) { if((stack->current!=NULL && stack->current->next==NULL) || stack->elems.first==NULL); else { - undo= (stack->current && stack->current->next)? stack->current->next: stack->elems.first; - undo_restore(C, stack, undo); - stack->current= undo; - if(G.f & G_DEBUG) printf("redo %s\n", undo->name); + if(!name || strcmp(stack->current->name, name) == 0) { + undo= (stack->current && stack->current->next)? stack->current->next: stack->elems.first; + undo_restore(C, stack, undo); + stack->current= undo; + if(G.f & G_DEBUG) printf("redo %s\n", undo->name); + return 1; + } } } + + return 0; } static void undo_stack_free(UndoStack *stack) @@ -181,7 +189,7 @@ static void undo_stack_free(UndoStack *stack) /* Exported Functions */ -void undo_paint_push_begin(int type, char *name, UndoRestoreCb restore, UndoFreeCb free) +void undo_paint_push_begin(int type, const char *name, UndoRestoreCb restore, UndoFreeCb free) { if(type == UNDO_PAINT_IMAGE) undo_stack_push_begin(&ImageUndoStack, name, restore, free); @@ -219,12 +227,14 @@ void undo_paint_push_end(int type) undo_stack_push_end(&MeshUndoStack); } -void ED_undo_paint_step(bContext *C, int type, int step) +int ED_undo_paint_step(bContext *C, int type, int step, const char *name) { if(type == UNDO_PAINT_IMAGE) - undo_stack_step(C, &ImageUndoStack, step); + return undo_stack_step(C, &ImageUndoStack, step, name); else if(type == UNDO_PAINT_MESH) - undo_stack_step(C, &MeshUndoStack, step); + return undo_stack_step(C, &MeshUndoStack, step, name); + + return 0; } void ED_undo_paint_free(void) diff --git a/source/blender/editors/sculpt_paint/paint_utils.c b/source/blender/editors/sculpt_paint/paint_utils.c index 85fbd5954e8..0098b8ca12c 100644 --- a/source/blender/editors/sculpt_paint/paint_utils.c +++ b/source/blender/editors/sculpt_paint/paint_utils.c @@ -7,6 +7,7 @@ #include "DNA_object_types.h" #include "DNA_scene_types.h" +#include "DNA_brush_types.h" #include "RNA_access.h" #include "RNA_define.h" @@ -14,13 +15,10 @@ #include "BLI_math.h" #include "BKE_brush.h" -#include "BKE_colortools.h" #include "BKE_context.h" #include "BKE_DerivedMesh.h" -#include "BKE_global.h" #include "BKE_paint.h" -#include "BKE_utildefines.h" #include "BIF_gl.h" @@ -216,6 +214,9 @@ 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_LINE, "LINE", 0, "Line", ""}, + {CURVE_PRESET_ROUND, "ROUND", 0, "Round", ""}, + {CURVE_PRESET_ROOT, "ROOT", 0, "Root", ""}, {0, NULL, 0, NULL, NULL}}; ot->name= "Preset"; diff --git a/source/blender/editors/sculpt_paint/paint_vertex.c b/source/blender/editors/sculpt_paint/paint_vertex.c index 7bfb7716c9c..399ba535e57 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex.c +++ b/source/blender/editors/sculpt_paint/paint_vertex.c @@ -38,42 +38,38 @@ #include "MEM_guardedalloc.h" -#include "IMB_imbuf.h" -#include "IMB_imbuf_types.h" - #include "BLI_blenlib.h" #include "BLI_math.h" #include "BLI_ghash.h" +#include "IMB_imbuf.h" +#include "IMB_imbuf_types.h" + #include "DNA_armature_types.h" #include "DNA_mesh_types.h" #include "DNA_particle_types.h" +#include "DNA_scene_types.h" +#include "DNA_brush_types.h" +#include "DNA_object_types.h" +#include "DNA_meshdata_types.h" #include "RNA_access.h" #include "RNA_define.h" -#include "BKE_armature.h" +#include "BKE_DerivedMesh.h" #include "BKE_action.h" #include "BKE_brush.h" -#include "BKE_DerivedMesh.h" -#include "BKE_cloth.h" #include "BKE_context.h" -#include "BKE_customdata.h" #include "BKE_depsgraph.h" #include "BKE_deform.h" -#include "BKE_displist.h" -#include "BKE_global.h" #include "BKE_mesh.h" #include "BKE_modifier.h" #include "BKE_object.h" #include "BKE_paint.h" -#include "BKE_utildefines.h" #include "WM_api.h" #include "WM_types.h" -#include "BIF_gl.h" -#include "BIF_glutil.h" #include "ED_armature.h" #include "ED_mesh.h" @@ -99,7 +95,7 @@ int vertex_paint_mode_poll(bContext *C) { Object *ob = CTX_data_active_object(C); - return ob && ob->mode == OB_MODE_VERTEX_PAINT; + return ob && ob->mode == OB_MODE_VERTEX_PAINT && ((Mesh *)ob->data)->totface; } int vertex_paint_poll(bContext *C) @@ -111,8 +107,8 @@ int vertex_paint_poll(bContext *C) ARegion *ar= CTX_wm_region(C); if(ar->regiontype==RGN_TYPE_WINDOW) return 1; + } } - } return 0; } @@ -120,7 +116,7 @@ int weight_paint_mode_poll(bContext *C) { Object *ob = CTX_data_active_object(C); - return ob && ob->mode == OB_MODE_WEIGHT_PAINT; + return ob && ob->mode == OB_MODE_WEIGHT_PAINT && ((Mesh *)ob->data)->totface; } int weight_paint_poll(bContext *C) @@ -386,9 +382,8 @@ void wpaint_fill(VPaint *wp, Object *ob, float paintweight) int actdef= 0; char name[32]; - BLI_strncpy(name, defgroup->name, 32); - bone_flip_name(name, 0); /* 0 = don't strip off number extensions */ - + flip_side_name(name, defgroup->name, FALSE); + for (curdef = ob->defbase.first; curdef; curdef=curdef->next, actdef++) if (!strcmp(curdef->name, name)) break; @@ -663,7 +658,7 @@ static void vpaint_blend(VPaint *vp, unsigned int *col, unsigned int *colorig, u unsigned int testcol=0, a; char *cp, *ct, *co; - alpha= (int)(255.0*brush->alpha); + alpha= (int)(255.0*brush_alpha(brush)); if(brush->vertexpaint_tool==VP_MIX || brush->vertexpaint_tool==VP_BLUR) testcol= mcol_blend( *colorig, paintcol, alpha); else if(brush->vertexpaint_tool==VP_ADD) testcol= mcol_add( *colorig, paintcol, alpha); @@ -733,23 +728,24 @@ static float calc_vp_alpha_dl(VPaint *vp, ViewContext *vc, float vpimat[][3], fl float fac, fac_2, size, dx, dy; float alpha; short vertco[2]; - + const int radius= brush_size(brush); + project_short_noclip(vc->ar, vert_nor, vertco); dx= mval[0]-vertco[0]; dy= mval[1]-vertco[1]; - if (brush->flag & BRUSH_SIZE_PRESSURE) - size = pressure * brush->size; + if (brush_use_size_pressure(brush)) + size = pressure * radius; else - size = brush->size; + size = radius; fac_2= dx*dx + dy*dy; if(fac_2 > size*size) return 0.f; fac = sqrtf(fac_2); - alpha= brush->alpha * brush_curve_strength_clamp(brush, fac, size); + alpha= brush_alpha(brush) * brush_curve_strength_clamp(brush, fac, size); - if (brush->flag & BRUSH_ALPHA_PRESSURE) + if (brush_use_alpha_pressure(brush)) alpha *= pressure; if(vp->flag & VP_NORMALS) { @@ -813,7 +809,7 @@ static void wpaint_blend(VPaint *wp, MDeformWeight *dw, MDeformWeight *uw, float if((wp->flag & VP_SPRAY)==0) { float testw=0.0f; - alpha= brush->alpha; + alpha= brush_alpha(brush); if(tool==VP_MIX || tool==VP_BLUR) testw = paintval*alpha + uw->weight*(1.0-alpha); else if(tool==VP_ADD) @@ -864,6 +860,7 @@ void sample_wpaint(Scene *scene, ARegion *ar, View3D *v3d, int mode) Mesh *me= get_mesh(ob); int index; short mval[2] = {0, 0}, sco[2]; + int vgroup= ob->actdef-1; if (!me) return; @@ -938,7 +935,6 @@ void sample_wpaint(Scene *scene, ARegion *ar, View3D *v3d, int mode) } else { DerivedMesh *dm; - MDeformWeight *dw; float w1, w2, w3, w4, co[3], fac; dm = mesh_get_derived_final(scene, ob, CD_MASK_BAREMESH); @@ -968,21 +964,17 @@ void sample_wpaint(Scene *scene, ARegion *ar, View3D *v3d, int mode) fac= MIN4(w1, w2, w3, w4); if(w1==fac) { - dw= defvert_find_index(me->dvert+mface->v1, ob->actdef-1); - if(dw) ts->vgroup_weight= dw->weight; else ts->vgroup_weight= 0.0f; + ts->vgroup_weight= defvert_find_weight(me->dvert+mface->v1, vgroup); } else if(w2==fac) { - dw= defvert_find_index(me->dvert+mface->v2, ob->actdef-1); - if(dw) ts->vgroup_weight= dw->weight; else ts->vgroup_weight= 0.0f; + ts->vgroup_weight= defvert_find_weight(me->dvert+mface->v2, vgroup); } else if(w3==fac) { - dw= defvert_find_index(me->dvert+mface->v3, ob->actdef-1); - if(dw) ts->vgroup_weight= dw->weight; else ts->vgroup_weight= 0.0f; + ts->vgroup_weight= defvert_find_weight(me->dvert+mface->v3, vgroup); } else if(w4==fac) { if(mface->v4) { - dw= defvert_find_index(me->dvert+mface->v4, ob->actdef-1); - if(dw) ts->vgroup_weight= dw->weight; else ts->vgroup_weight= 0.0f; + ts->vgroup_weight= defvert_find_weight(me->dvert+mface->v4, vgroup); } } } @@ -1123,8 +1115,6 @@ static int set_wpaint(bContext *C, wmOperator *op) /* toggle */ /* for switching to/from mode */ static int paint_poll_test(bContext *C) { - if(ED_operator_view3d_active(C)==0) - return 0; if(CTX_data_edit_object(C)) return 0; if(CTX_data_active_object(C)==NULL) @@ -1154,10 +1144,16 @@ static int vpaint_radial_control_invoke(bContext *C, wmOperator *op, wmEvent *ev { Paint *p = paint_get_active(CTX_data_scene(C)); Brush *brush = paint_brush(p); + float col[4]; WM_paint_cursor_end(CTX_wm_manager(C), p->paint_cursor); p->paint_cursor = NULL; brush_radial_control_invoke(op, brush, 1); + + copy_v3_v3(col, brush->add_col); + col[3]= 0.5f; + RNA_float_set_array(op->ptr, "color", col); + return WM_radial_control_invoke(C, op, event); } @@ -1183,10 +1179,16 @@ static int wpaint_radial_control_invoke(bContext *C, wmOperator *op, wmEvent *ev { Paint *p = paint_get_active(CTX_data_scene(C)); Brush *brush = paint_brush(p); + float col[4]; WM_paint_cursor_end(CTX_wm_manager(C), p->paint_cursor); p->paint_cursor = NULL; brush_radial_control_invoke(op, brush, 1); + + copy_v3_v3(col, brush->add_col); + col[3]= 0.5f; + RNA_float_set_array(op->ptr, "color", col); + return WM_radial_control_invoke(C, op, event); } @@ -1262,7 +1264,7 @@ static char *wpaint_make_validmap(Mesh *me, Object *ob) bPose *pose; bPoseChannel *chan; ArmatureModifierData *amd; - GHash *gh = BLI_ghash_new(BLI_ghashutil_strhash, BLI_ghashutil_strcmp); + GHash *gh = BLI_ghash_new(BLI_ghashutil_strhash, BLI_ghashutil_strcmp, "wpaint_make_validmap gh"); int i = 0, step1=1; /*add all names to a hash table*/ @@ -1395,10 +1397,9 @@ static int wpaint_stroke_test_start(bContext *C, wmOperator *op, wmEvent *event) bDeformGroup *curdef; int actdef= 0; char name[32]; - - BLI_strncpy(name, defgroup->name, 32); - bone_flip_name(name, 0); /* 0 = don't strip off number extensions */ - + + flip_side_name(name, defgroup->name, FALSE); + for (curdef = ob->defbase.first; curdef; curdef=curdef->next, actdef++) if (!strcmp(curdef->name, name)) break; @@ -1450,7 +1451,7 @@ static void wpaint_stroke_update_step(bContext *C, struct PaintStroke *stroke, P /* load projection matrix */ mul_m4_m4m4(mat, ob->obmat, vc->rv3d->persmat); - flip = RNA_boolean_get(itemptr, "flip"); + flip = RNA_boolean_get(itemptr, "pen_flip"); pressure = RNA_float_get(itemptr, "pressure"); RNA_float_get_array(itemptr, "mouse", mval); mval[0]-= vc->ar->winrct.xmin; @@ -1460,7 +1461,7 @@ static void wpaint_stroke_update_step(bContext *C, struct PaintStroke *stroke, P /* which faces are involved */ if(wp->flag & VP_AREA) { - totindex= sample_backbuf_area(vc, indexar, me->totface, mval[0], mval[1], brush->size); + totindex= sample_backbuf_area(vc, indexar, me->totface, mval[0], mval[1], brush_size(brush)); } else { indexar[0]= view3d_sample_backbuf(vc, mval[0], mval[1]); @@ -1746,7 +1747,6 @@ void PAINT_OT_vertex_paint_toggle(wmOperatorType *ot) /* flags */ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; - } @@ -1866,7 +1866,7 @@ static void vpaint_stroke_update_step(bContext *C, struct PaintStroke *stroke, P float pressure, mval[2]; RNA_float_get_array(itemptr, "mouse", mval); - flip = RNA_boolean_get(itemptr, "flip"); + flip = RNA_boolean_get(itemptr, "pen_flip"); pressure = RNA_float_get(itemptr, "pressure"); view3d_operator_needs_opengl(C); @@ -1880,7 +1880,7 @@ static void vpaint_stroke_update_step(bContext *C, struct PaintStroke *stroke, P /* which faces are involved */ if(vp->flag & VP_AREA) { - totindex= sample_backbuf_area(vc, indexar, me->totface, mval[0], mval[1], brush->size); + totindex= sample_backbuf_area(vc, indexar, me->totface, mval[0], mval[1], brush_size(brush)); } else { indexar[0]= view3d_sample_backbuf(vc, mval[0], mval[1]); diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index 509a1bcc61e..10af35a1b4b 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,32 +38,25 @@ #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" #include "DNA_meshdata_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" +#include "DNA_brush_types.h" #include "BKE_brush.h" #include "BKE_cdderivedmesh.h" #include "BKE_context.h" -#include "BKE_customdata.h" -#include "BKE_DerivedMesh.h" #include "BKE_depsgraph.h" -#include "BKE_global.h" -#include "BKE_image.h" #include "BKE_key.h" #include "BKE_library.h" -#include "BKE_main.h" #include "BKE_mesh.h" #include "BKE_modifier.h" #include "BKE_multires.h" #include "BKE_paint.h" #include "BKE_report.h" -#include "BKE_texture.h" -#include "BKE_utildefines.h" -#include "BKE_colortools.h" #include "BIF_gl.h" #include "BIF_glutil.h" @@ -71,7 +64,6 @@ #include "WM_api.h" #include "WM_types.h" #include "ED_screen.h" -#include "ED_sculpt.h" #include "ED_view3d.h" #include "paint_intern.h" #include "sculpt_intern.h" @@ -81,15 +73,84 @@ #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 ===== + * + */ + +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) +{ + Mesh *me= (Mesh*)ob->data; + ModifierData *md, *nmd; + + if(!CustomData_get_layer(&me->fdata, CD_MDISPS)) { + /* multires can't work without displacement layer */ + return NULL; + } + + 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 ===== * @@ -116,10 +177,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; @@ -136,15 +200,30 @@ 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; } StrokeCache; /* ===== OPENGL ===== @@ -219,10 +298,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); @@ -241,353 +322,354 @@ 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 */ +/************************ Brush Testing *******************/ - /* layer brush */ - float *layer_disp; -} 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; - - for(md= modifiers_getVirtualModifierList(ob); md; md= md->next) { - if(modifier_isEnabled(scene, md, eModifierMode_Realtime)) - if(!ELEM(md->type, eModifierType_Multires, eModifierType_ShapeKey)) - return 1; + float distsq = len_squared_v3v3(co, test->location); + + 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) { - /* regular mesh restore */ - if(ss->totvert != unode->maxvert) - continue; + if(distsq <= test->radius_squared) { + test->dist = distsq; + return 1; + } + else { + return 0; + } +} - index= unode->index; - mvert= ss->mvert; +static int sculpt_brush_test_fast(SculptBrushTest *test, float co[3]) +{ + return len_squared_v3v3(co, test->location) <= test->radius_squared; +} - 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; +static int sculpt_brush_test_cube(SculptBrushTest *test, float co[3], float local[4][4]) +{ + static const float side = 0.70710678118654752440084436210485; // sqrt(.5); - if(dm->getNumGrids(dm) != unode->maxgrid) - continue; - if(dm->getGridSize(dm) != unode->gridsize) - continue; + float local_co[3]; - grids= dm->getGridData(dm); - gridsize= dm->getGridSize(dm); + mul_v3_m4v3(local_co, local, co); - co = unode->co; - for(j=0; j<unode->totgrid; j++) { - grid= grids[unode->grids[j]]; + local_co[0] = fabs(local_co[0]); + local_co[1] = fabs(local_co[1]); + local_co[2] = fabs(local_co[2]); - for(i=0; i<gridsize*gridsize; i++, co++) - swap_v3_v3(grid[i].co, co[0]); - } - } + 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; - update= 1; + return 1; + } + else { + return 0; } +} - if(update) { - if(ss->kb) sculpt_mesh_to_key(ss->ob, ss->kb); - if(ss->refkb) sculpt_key_to_mesh(ss->refkb, ob); +static float frontface(Brush *brush, float sculpt_normal[3], short no[3], float fno[3]) +{ + if (brush->flag & BRUSH_FRONTFACE) { + float dot; - /* 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 (no) { + float tmp[3]; - if((mmd=sculpt_multires_active(ob))) - multires_mark_as_modified(ob); + normal_short_to_float_v3(tmp, no); + dot= dot_v3v3(tmp, sculpt_normal); + } + else { + dot= dot_v3v3(fno, sculpt_normal); + } - if(sculpt_modifiers_active(scene, ob)) - DAG_id_flush_update(&ob->id, OB_RECALC_DATA); + return dot > 0 ? dot : 0; + } + 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; + + overlap= 0; + max= 0; + for(i= 0; i < m; i++) { + overlap = overlapped_curve(br, i*g); + + if (overlap > max) + max = overlap; } - if(unode->grids) - memcpy(unode->grids, grids, sizeof(int)*totgrid); - - 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) { 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 */ + const float root_alpha = brush_alpha(brush); + float alpha = root_alpha*root_alpha; + float dir = brush->flag & BRUSH_DIR_IN ? -1 : 1; + float pressure = brush_use_alpha_pressure(brush) ? cache->pressure : 1; + float pen_flip = cache->pen_flip ? -1 : 1; + float invert = cache->invert ? -1 : 1; + 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 + 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) { @@ -598,7 +680,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; @@ -625,76 +707,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 radius; + + /* 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]; - avg= get_texcache_pixel_bilinear(ss, fx * ss->texcache_side, fy * ss->texcache_side); + radius = 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 */ + { + radius = 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; + + if (mtex->brush_map_mode == MTEX_MAP_MODE_TILED) { + x -= 0.5f; + y -= 0.5f; + } + + x *= ss->cache->vc->ar->winx / radius; + y *= ss->cache->vc->ar->winy / radius; + + /* 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; } @@ -730,7 +833,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 */ @@ -746,64 +849,60 @@ 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]) { if((dot_v3v3(view_vec, fno)) > 0) { - add_v3_v3v3(out, out, fno); + add_v3_v3(out, fno); } else { - add_v3_v3v3(out_flip, out_flip, fno); /* out_flip is used when out is {0,0,0} */ + add_v3_v3(out_flip, fno); /* out_flip is used when out is {0,0,0} */ } } -/* 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; @@ -811,72 +910,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_v3v3(out, out, nout); - add_v3_v3v3(out_flip, 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); - /* 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; - - /* 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); - - 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}; - - sculpt_clip(sd, ss, vd.co, val); - if(vd.mvert) vd.mvert->flag |= ME_VERT_PBVH_UPDATE; - } + 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(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; @@ -903,7 +1000,7 @@ static void neighbor_average(SculptSession *ss, float avg[3], const int vert) for(i=0; i<(f->v4?4:3); ++i) { if(i != skip && (ncount!=2 || BLI_countlist(&ss->fmap[(&f->v1)[i]]) <= 2)) { - add_v3_v3v3(avg, avg, ss->mvert[(&f->v1)[i]].co); + add_v3_v3(avg, ss->mvert[(&f->v1)[i]].co); ++total; } } @@ -917,52 +1014,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]]; @@ -970,75 +1073,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; + + if(x == 0 && adj->index[0] == -1) + continue; + + if(x == gridsize - 1 && adj->index[2] == -1) + continue; - copy_v3_v3(co, data[x + y*gridsize].co); + 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); + + sub_v3_v3v3(val, avg, co); + mul_v3_fl(val, fade); - CLAMP(fade, 0.0f, 1.0f); + add_v3_v3(val, co); - 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); + 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 = 0; 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) @@ -1046,69 +1180,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(brush_alpha(brush) > 0.0f) + crease_correction = brush->crease_pinch_factor*brush->crease_pinch_factor/(brush_alpha(brush)*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); + + /* then we draw */ + mul_v3_v3fl(val2, offset, fade); + + add_v3_v3v3(proxy[vd.i], val1, val2); - 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; + } +} + +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); - BLI_pbvh_node_mark_update(nodes[n]); + 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); + + if(vd.mvert) + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + } + BLI_pbvh_vertex_iter_end; } } @@ -1120,63 +1563,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]); } } @@ -1186,206 +1632,721 @@ 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); - - 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_v3v3(add, add, vd.co); + if(vd.fno) copy_v3_v3(val, vd.fno); + else normal_short_to_float_v3(val, vd.no); - 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 + if (count != 0) { + mul_v3_fl(fc, 1.0f / count); + } + else { + zero_v3(fc); + } +} + +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_v3v3(co, 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); +} + +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_flip(float co[3], float plane_normal[3], float plane_center[3], int flip) +{ + float delta[3]; + float d; + + sub_v3_v3v3(delta, co, plane_center); + d = dot_v3v3(plane_normal, delta); + + 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]; - /* 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_v3v3(intr, intr, p1); + sub_v3_v3v3(delta, co, plane_center); + return dot_v3v3(plane_normal, delta) <= 0.0f; } -static int plane_point_side(float co[3], float plane_normal[3], float plane_center[3], int flip) +static float get_offset(Sculpt *sd, SculptSession *ss) { - float delta[3]; - float d; + Brush* brush = paint_brush(&sd->paint); - sub_v3_v3v3(delta, co, plane_center); - d = dot_v3v3(plane_normal, delta); + float rv = brush->plane_offset; - if(flip) - d = -d; + 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); + + 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); - 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; + 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]; - /* Find the intersection between squash-plane and vertex (along the area normal) */ - point_plane_project(intr, vd.co, area_normal, cntr); + point_plane_project(intr, vd.co, an, 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, 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; } - else - mul_v3_fl(val, fabs(fade)); + } + } + } + BLI_pbvh_vertex_iter_end; + } +} - add_v3_v3v3(val, val, vd.co); +static void do_clay_tubes_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode) +{ + Brush *brush = paint_brush(&sd->paint); - sculpt_clip(sd, ss, vd.co, val); - if(vd.mvert) vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + 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); + + 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 (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; + } } } } BLI_pbvh_vertex_iter_end; + } +} + +static void do_fill_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); + + 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; - BLI_pbvh_node_mark_update(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_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_brush_action(Sculpt *sd, SculptSession *ss, StrokeCache *cache) +static void do_scrape_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode) { - SculptSearchSphereData data; Brush *brush = paint_brush(&sd->paint); - PBVHNode **nodes= NULL; - int totnode; - data.ss = ss; - data.sd = sd; - data.radius_squared = ss->cache->radius * ss->cache->radius; + float bstrength = ss->cache->bstrength; + const float 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); + float an[3]; + float fc[3]; + float offset = get_offset(sd, ss); - 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]); + 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; } - else { - BLI_pbvh_search_gather(ss->pbvh, sculpt_search_sphere_cb, &data, - &nodes, &totnode); +} + +void sculpt_vertcos_to_key(Object *ob, KeyBlock *kb, float (*vertCos)[3]) +{ + Mesh *me= (Mesh*)ob->data; + float (*ofs)[3]= NULL; + int a, is_basis= 0; + KeyBlock *currkey; + + /* for relative keys editing of base should update other keys */ + if (me->key->type == KEY_RELATIVE) + for (currkey = me->key->block.first; currkey; currkey= currkey->next) + if(ob->shapenr-1 == currkey->relative) { + is_basis= 1; + break; + } + + if (is_basis) { + ofs= key_to_vertcos(ob, kb); + + /* calculate key coord offsets (from previous location) */ + for (a= 0; a < me->totvert; a++) { + VECSUB(ofs[a], vertCos[a], ofs[a]); + } + + /* apply offsets on other keys */ + currkey = me->key->block.first; + while (currkey) { + int apply_offset = ((currkey != kb) && (ob->shapenr-1 == currkey->relative)); + + if (apply_offset) + offset_to_key(ob, currkey, ofs); + + currkey= currkey->next; + } + + MEM_freeN(ofs); + } + + /* modifying of basis key should update mesh */ + if (kb == me->key->refkey) { + MVert *mvert= me->mvert; + + for (a= 0; a < me->totvert; a++, mvert++) + VECCOPY(mvert->co, vertCos[a]); + + mesh_calc_normals(me->mvert, me->totvert, me->mface, me->totface, NULL); + } + + /* apply new coords on active key block */ + vertcos_to_key(ob, kb, vertCos); +} + +/* copy the modified vertices from bvh to the active key */ +static void sculpt_update_keyblock(SculptSession *ss) +{ + float (*vertCos)[3]= BLI_pbvh_get_vertCos(ss->pbvh); + + if (vertCos) { + sculpt_vertcos_to_key(ss->ob, ss->kb, vertCos); + MEM_freeN(vertCos); } +} + +static void do_brush_action(Sculpt *sd, SculptSession *ss, Brush *brush) +{ + SculptSearchSphereData data; + 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_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: @@ -1394,6 +2355,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; @@ -1403,60 +2370,246 @@ 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)); + } + else { + smooth(sd, ss, nodes, totnode, brush->autosmooth_factor); + } + } + /* copy the modified vertices from mesh to the active key */ - if(ss->kb) mesh_to_key(ss->ob->data, ss->kb); - - if(nodes) - MEM_freeN(nodes); - } + 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); + + 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); + + cache->bstrength= brush_strength(sd, cache, feather); - 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); + 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; + + 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) { Brush *brush = paint_brush(&sd->paint); + const int radius= brush_size(brush); if(ss->texcache) { MEM_freeN(ss->texcache); @@ -1464,72 +2617,28 @@ 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*radius; 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, radius); 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(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(nmd->mode & eModifierMode_Realtime) - break; - - if(!nmd && mmd->sculptlvl > 0) - return mmd; - } - } - - return NULL; -} - -void sculpt_key_to_mesh(KeyBlock *kb, Object *ob) -{ - Mesh *me= ob->data; - - key_to_mesh(kb, me); - mesh_calc_normals(me->mvert, me->totvert, me->mface, me->totface, NULL); -} - -void sculpt_mesh_to_key(Object *ob, KeyBlock *kb) -{ - Mesh *me= ob->data; - - mesh_to_key(me, kb); -} - void sculpt_update_mesh_elements(Scene *scene, Object *ob, int need_fmap) { - DerivedMesh *dm = mesh_get_derived_final(scene, ob, 0); + DerivedMesh *dm = mesh_get_derived_final(scene, ob, CD_MASK_BAREMESH); SculptSession *ss = ob->sculpt; - + MultiresModifierData *mmd= sculpt_multires_active(scene, ob); + ss->ob= ob; - if((ob->shapeflag & OB_SHAPE_LOCK) && !sculpt_multires_active(ob)) { - ss->kb= ob_get_keyblock(ob); - ss->refkb= ob_get_reference_keyblock(ob); - } - else { - ss->kb= NULL; - ss->refkb= NULL; - } + ss->modifiers_active= sculpt_modifiers_active(scene, ob); - /* need to make PBVH with shape key coordinates */ - if(ss->kb) sculpt_key_to_mesh(ss->kb, ss->ob); + if((ob->shapeflag & OB_SHAPE_LOCK) && !mmd) ss->kb= ob_get_keyblock(ob); + else ss->kb= NULL; - if((ss->multires = sculpt_multires_active(ob))) { + if(mmd) { + ss->multires = mmd; ss->totvert = dm->getNumVerts(dm); ss->totface = dm->getNumFaces(dm); ss->mvert= NULL; @@ -1543,10 +2652,22 @@ void sculpt_update_mesh_elements(Scene *scene, Object *ob, int need_fmap) ss->mvert = me->mvert; ss->mface = me->mface; ss->face_normals = NULL; + ss->multires = NULL; } ss->pbvh = dm->getPBVH(ob, dm); ss->fmap = (need_fmap && dm->getFaceMap)? dm->getFaceMap(ob, dm): NULL; + + /* if pbvh is deformed, key block is already applied to it */ + if (ss->kb && !BLI_pbvh_isDeformed(ss->pbvh)) { + float (*vertCos)[3]= key_to_vertcos(ob, ss->kb); + + if (vertCos) { + /* apply shape keys coordinates to PBVH */ + BLI_pbvh_apply_vertCos(ss->pbvh, vertCos); + MEM_freeN(vertCos); + } + } } static int sculpt_mode_poll(bContext *C) @@ -1569,16 +2690,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; } @@ -1589,10 +2726,24 @@ static int sculpt_radial_control_invoke(bContext *C, wmOperator *op, wmEvent *ev { Paint *p = paint_get_active(CTX_data_scene(C)); Brush *brush = paint_brush(p); + float col[4], tex_col[4]; WM_paint_cursor_end(CTX_wm_manager(C), p->paint_cursor); p->paint_cursor = NULL; brush_radial_control_invoke(op, brush, 1); + + if((brush->flag & BRUSH_DIR_IN) && ELEM4(brush->sculpt_tool, SCULPT_TOOL_DRAW, SCULPT_TOOL_INFLATE, SCULPT_TOOL_CLAY, SCULPT_TOOL_PINCH)) + copy_v3_v3(col, brush->sub_col); + else + copy_v3_v3(col, brush->add_col); + col[3]= 0.5f; + + copy_v3_v3(tex_col, U.sculpt_paint_overlay_col); + tex_col[3]= (brush->texture_overlay_alpha / 100.0f); + + RNA_float_set_array(op->ptr, "color", col); + RNA_float_set_array(op->ptr, "texture_color", tex_col); + return WM_radial_control_invoke(C, op, event); } @@ -1657,19 +2808,69 @@ static void sculpt_cache_free(StrokeCache *cache) } /* 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; + + BLI_strncpy(cache->saved_active_brush_name, brush->id.name+2, sizeof(cache->saved_active_brush_name)); + + br= (Brush *)find_id("BR", "Smooth"); + if(br) { + paint_brush_set(p, br); + brush = br; + } + } copy_v2_v2(cache->mouse, cache->initial_mouse); copy_v2_v2(cache->tex_mouse, cache->initial_mouse); @@ -1677,11 +2878,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 */ @@ -1707,103 +2910,233 @@ 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; +} + +static void sculpt_update_brush_delta(Sculpt *sd, SculptSession *ss, Brush *brush) +{ + StrokeCache *cache = ss->cache; + int tool = brush->sculpt_tool; + + if(ELEM5(tool, + SCULPT_TOOL_GRAB, SCULPT_TOOL_NUDGE, + SCULPT_TOOL_CLAY_TUBES, SCULPT_TOOL_SNAKE_HOOK, + SCULPT_TOOL_THUMB)) { + float grab_location[3], imat[4][4], delta[3]; + + if(cache->first_time) { + copy_v3_v3(cache->orig_grab_location, + cache->true_location); + } + else if(tool == SCULPT_TOOL_SNAKE_HOOK) + 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) { + switch(tool) { + case SCULPT_TOOL_GRAB: + case SCULPT_TOOL_THUMB: + 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); + break; + case SCULPT_TOOL_CLAY_TUBES: + case SCULPT_TOOL_NUDGE: + 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); + break; + case SCULPT_TOOL_SNAKE_HOOK: + 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); + break; + } + } + else { + zero_v3(cache->grab_delta); + } + + copy_v3_v3(cache->old_grab_location, grab_location); + + if(tool == SCULPT_TOOL_GRAB) + copy_v3_v3(sd->anchored_location, cache->true_location); + else if(tool == SCULPT_TOOL_THUMB) + copy_v3_v3(sd->anchored_location, cache->orig_grab_location); + + if(ELEM(tool, SCULPT_TOOL_GRAB, SCULPT_TOOL_THUMB)) { + /* 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_initial_mouse, cache->initial_mouse); + sd->anchored_size = cache->pixel_radius; + } + } } /* 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 = brush_size(brush); + + if(cache->first_time) { + if (!brush_use_locked_size(brush)) { + cache->initial_radius= unproject_brush_radius(ss->ob, cache->vc, cache->true_location, brush_size(brush)); + brush_set_unprojected_radius(brush, cache->initial_radius); + } + else { + cache->initial_radius= brush_unprojected_radius(brush); + } - if(cache->first_time) - cache->initial_radius = unproject_brush_radius(ss->ob, cache->vc, cache->true_location, brush->size); + 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_use_size_pressure(brush)) { 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; + + cache->radius_squared = cache->radius*cache->radius; - if(!(brush->flag & BRUSH_ANCHORED)) + 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); - } - else if(brush->flag & BRUSH_RAKE) { - int update; - dx = cache->last_rake[0] - cache->mouse[0]; - dy = cache->last_rake[1] - cache->mouse[1]; + sd->anchored_size = cache->pixel_radius = sqrt(dx*dx + dy*dy); - update = dx*dx + dy*dy > 100; + cache->special_rotation = atan2(dx, dy) + M_PI; - /* 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 (brush->flag & BRUSH_EDGE_TO_EDGE) { + float halfway[2]; + float out[3]; - if(update || cache->first_time) { - cache->last_rake[0] = cache->mouse[0]; - cache->last_rake[1] = cache->mouse[1]; + halfway[0] = dx*0.5 + cache->initial_mouse[0]; + halfway[1] = dy*0.5 + cache->initial_mouse[1]; + + if (sculpt_stroke_get_location(C, stroke, out, halfway)) { + copy_v3_v3(sd->anchored_location, out); + copy_v2_v2(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; + } } - } - /* Find the grab delta */ - if(brush->sculpt_tool == SCULPT_TOOL_GRAB) { - float grab_location[3], imat[4][4]; + if (!hit) + copy_v2_v2(sd->anchored_initial_mouse, cache->initial_mouse); - if(cache->first_time) - copy_v3_v3(cache->orig_grab_location, cache->true_location); + 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; - /* 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]); + copy_v3_v3(sd->anchored_location, cache->true_location); - /* 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); + sd->draw_anchored = 1; + } + else if(brush->flag & BRUSH_RAKE) { + const float u = 0.5f; + const float v = 1 - u; + const float r = 20; + + const float dx = cache->last_rake[0] - cache->mouse[0]; + const float dy = cache->last_rake[1] - cache->mouse[1]; + + if (cache->first_time) { + copy_v2_v2(cache->last_rake, cache->mouse); } + else if (dx*dx + dy*dy >= r*r) { + cache->special_rotation = atan2(dx, dy); - copy_v3_v3(cache->old_grab_location, grab_location); + 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]; + } + } + + sculpt_update_brush_delta(sd, ss, brush); + + 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); - /* 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_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) { - Scene *scene= CTX_data_scene(C); - - if(sculpt_modifiers_active(scene, ss->ob)) { + if(ss->modifiers_active) { Sculpt *sd = CTX_data_tool_settings(C)->sculpt; Brush *brush = paint_brush(&sd->paint); @@ -1819,19 +3152,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 @@ -1845,10 +3182,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); @@ -1871,48 +3210,11 @@ int sculpt_stroke_get_location(bContext *C, struct PaintStroke *stroke, float ou copy_v3_v3(out, ray_normal); mul_v3_fl(out, srd.dist); - add_v3_v3v3(out, out, ray_start); + add_v3_v3(out, ray_start); 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); @@ -1940,22 +3242,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_use_size_pressure(brush)) || + (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; @@ -1963,8 +3269,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]); } } @@ -1981,17 +3291,17 @@ static void sculpt_restore_mesh(Sculpt *sd, SculptSession *ss) static void sculpt_flush_update(bContext *C) { - Scene *scene = CTX_data_scene(C); Object *ob = CTX_data_active_object(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); + if(ob->derivedFinal) /* VBO no longer valid */ + GPU_drawobject_free(ob->derivedFinal); - if(sculpt_modifiers_active(scene, ob)) { + if(ss->modifiers_active) { DAG_id_flush_update(&ob->id, OB_RECALC_DATA); ED_region_tag_redraw(ar); } @@ -1999,14 +3309,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); } @@ -2017,9 +3335,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, @@ -2033,11 +3354,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_undo_push_begin(sculpt_tool_name(sd)); - sculpt_update_cache_invariants(sd, ss, C, op); +#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; - sculpt_undo_push_begin(ss, sculpt_tool_name(sd)); + num_procs = omp_get_num_procs(); + omp_set_num_threads(2*num_procs); + } +#endif return 1; } @@ -2051,7 +3383,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); @@ -2059,26 +3391,46 @@ 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= (Brush *)find_id("BR", ss->cache->saved_active_brush_name); + if(br) { + paint_brush_set(p, br); + } + } + 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); - if(ss->refkb) sculpt_key_to_mesh(ss->refkb, ob); + /* optimization: if there is locked key and active modifiers present in */ + /* the stack, keyblock is updating at each step. otherwise we could update */ + /* keyblock only when stroke is finished */ + if(ss->kb && !ss->modifiers_active) sculpt_update_keyblock(ss); ss->partial_redraw = 0; - + /* try to avoid calling this, only for e.g. linked duplicates now */ if(((Mesh*)ob->data)->id.us > 1) DAG_id_flush_update(&ob->id, OB_RECALC_DATA); @@ -2105,6 +3457,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; @@ -2129,7 +3482,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); @@ -2141,7 +3494,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"; @@ -2152,36 +3510,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); - /* For mirror modifiers */ - RNA_def_float_vector(ot->srna, "clip_tolerance", 3, NULL, 0.0f, FLT_MAX, "clip_tolerance", "", 0.0f, 1000.0f); + RNA_def_collection_runtime(ot->srna, "stroke", &RNA_OperatorStrokeElement, + "Stroke", ""); - /* 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); @@ -2201,7 +3555,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= OPTYPE_REGISTER|OPTYPE_UNDO; } /**** Toggle operator for turning sculpt mode on or off ****/ @@ -2209,25 +3563,31 @@ 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"); - - sculpt_update_mesh_elements(scene, ob, 0); + ob->sculpt->ob = ob; - if(ob->sculpt->refkb) - sculpt_key_to_mesh(ob->sculpt->refkb, 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(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 */ + //flush_recalc |= sculpt_modifiers_active(scene, ob); if(ob->mode & OB_MODE_SCULPT) { - if(sculpt_multires_active(ob)) + if(mmd) multires_force_update(ob); - if(mmd && mmd->sculptlvl != mmd->lvl) + if(flush_recalc) DAG_id_flush_update(&ob->id, OB_RECALC_DATA); /* Leave sculptmode */ @@ -2239,13 +3599,17 @@ static int sculpt_toggle_mode(bContext *C, wmOperator *op) /* Enter sculptmode */ ob->mode |= OB_MODE_SCULPT; - if(mmd && mmd->sculptlvl != mmd->lvl) + if(flush_recalc) 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); diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h index d3553c008b2..254876d9b68 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; @@ -49,7 +52,7 @@ void sculptmode_draw_mesh(int); void sculpt_paint_brush(char clear); void sculpt_stroke_draw(struct SculptStroke *); void sculpt_radialcontrol_start(int mode); -struct MultiresModifierData *sculpt_multires_active(struct Object *ob); +struct MultiresModifierData *sculpt_multires_active(struct Scene *scene, struct Object *ob); struct Brush *sculptmode_brush(void); //void do_symmetrical_brush_actions(struct Sculpt *sd, struct wmOperator *wm, struct BrushAction *a, short *, short *); @@ -58,8 +61,6 @@ void sculpt(Sculpt *sd); int sculpt_poll(struct bContext *C); void sculpt_update_mesh_elements(struct Scene *scene, struct Object *ob, int need_fmap); -void sculpt_key_to_mesh(struct KeyBlock *kb, struct Object *ob); -void sculpt_mesh_to_key(struct Object *ob, struct KeyBlock *kb); /* Stroke */ struct SculptStroke *sculpt_stroke_new(const int max); @@ -67,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(struct 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..e92740678fd --- /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 "MEM_guardedalloc.h" + +#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 "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_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); +} |