From 5505697ac508c02b8a2e196c5a8c07431bc687cf Mon Sep 17 00:00:00 2001 From: Jason Wilkins Date: Wed, 14 Jul 2010 14:11:03 +0000 Subject: Merge GSOC Sculpt Branch: 28499-30319 https://svn.blender.org/svnroot/bf-blender/branches/soc-2010-jwilkins See log of that branch for details. --- source/blender/blenkernel/BKE_blender.h | 4 +- source/blender/blenkernel/BKE_brush.h | 10 + source/blender/blenkernel/BKE_paint.h | 4 + source/blender/blenkernel/intern/brush.c | 102 +- source/blender/blenkernel/intern/colortools.c | 56 +- source/blender/blenkernel/intern/icons.c | 5 +- source/blender/blenkernel/intern/image.c | 6 +- source/blender/blenlib/BLI_pbvh.h | 26 +- source/blender/blenlib/intern/math_geom.c | 77 +- source/blender/blenlib/intern/pbvh.c | 321 +- source/blender/blenloader/intern/readfile.c | 77 + source/blender/editors/gpencil/gpencil_buttons.c | 2 +- source/blender/editors/include/UI_interface.h | 6 +- source/blender/editors/interface/interface_icons.c | 12 + .../editors/interface/interface_templates.c | 73 +- source/blender/editors/interface/resources.c | 11 +- source/blender/editors/sculpt_paint/SConscript | 10 +- source/blender/editors/sculpt_paint/paint_image.c | 78 +- source/blender/editors/sculpt_paint/paint_intern.h | 7 + source/blender/editors/sculpt_paint/paint_ops.c | 93 +- source/blender/editors/sculpt_paint/paint_stroke.c | 816 ++++- source/blender/editors/sculpt_paint/paint_utils.c | 6 +- source/blender/editors/sculpt_paint/sculpt.c | 3131 ++++++++++++++------ .../blender/editors/sculpt_paint/sculpt_intern.h | 44 + source/blender/editors/sculpt_paint/sculpt_undo.c | 302 ++ source/blender/editors/space_image/image_buttons.c | 2 +- source/blender/editors/space_logic/logic_window.c | 2 +- source/blender/editors/space_nla/nla_buttons.c | 2 +- source/blender/editors/space_node/drawnode.c | 10 +- source/blender/gpu/intern/gpu_draw.c | 1 + source/blender/makesdna/DNA_ID.h | 2 +- source/blender/makesdna/DNA_brush_types.h | 139 +- source/blender/makesdna/DNA_color_types.h | 7 +- source/blender/makesdna/DNA_scene_types.h | 43 +- source/blender/makesdna/DNA_userdef_types.h | 13 +- source/blender/makesdna/DNA_windowmanager_types.h | 3 +- source/blender/makesrna/SConscript | 1 - source/blender/makesrna/intern/CMakeLists.txt | 1 + source/blender/makesrna/intern/rna_brush.c | 384 ++- source/blender/makesrna/intern/rna_image.c | 17 + source/blender/makesrna/intern/rna_sculpt_paint.c | 25 +- source/blender/makesrna/intern/rna_ui_api.c | 2 + source/blender/makesrna/intern/rna_userdef.c | 41 +- source/blender/windowmanager/SConscript | 4 +- source/blender/windowmanager/intern/wm_draw.c | 4 +- source/blender/windowmanager/intern/wm_operators.c | 99 +- source/blenderplayer/bad_level_call_stubs/stubs.c | 24 + 47 files changed, 4815 insertions(+), 1290 deletions(-) create mode 100644 source/blender/editors/sculpt_paint/sculpt_undo.c (limited to 'source') diff --git a/source/blender/blenkernel/BKE_blender.h b/source/blender/blenkernel/BKE_blender.h index 7ac5815943a..5936805765d 100644 --- a/source/blender/blenkernel/BKE_blender.h +++ b/source/blender/blenkernel/BKE_blender.h @@ -43,9 +43,9 @@ struct bContext; struct ReportList; struct Scene; struct Main; - + #define BLENDER_VERSION 252 -#define BLENDER_SUBVERSION 5 +#define BLENDER_SUBVERSION 6 // XXX: this shouldn't be merged with trunk, this is so Sculpt branch can detect old files #define BLENDER_MINVERSION 250 #define BLENDER_MINSUBVERSION 0 diff --git a/source/blender/blenkernel/BKE_brush.h b/source/blender/blenkernel/BKE_brush.h index fcc636215c9..6a209167f93 100644 --- a/source/blender/blenkernel/BKE_brush.h +++ b/source/blender/blenkernel/BKE_brush.h @@ -84,5 +84,15 @@ unsigned int *brush_gen_texture_cache(struct Brush *br, int half_side); void brush_radial_control_invoke(struct wmOperator *op, struct Brush *br, float size_weight); int brush_radial_control_exec(struct wmOperator *op, struct Brush *br, float size_weight); +/* unified strength and size */ +int sculpt_get_brush_size(struct Brush *brush); +void sculpt_set_brush_size(struct Brush *brush, int size); +int sculpt_get_lock_brush_size(struct Brush *brush); +float sculpt_get_brush_unprojected_radius(struct Brush *brush); +void sculpt_set_brush_unprojected_radius(struct Brush *brush, float unprojected_radius); +float sculpt_get_brush_alpha(struct Brush *brush); +void sculpt_set_brush_alpha(struct Brush *brush, float alpha); + + #endif diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h index cd412ca5a74..f9954b3d55d 100644 --- a/source/blender/blenkernel/BKE_paint.h +++ b/source/blender/blenkernel/BKE_paint.h @@ -28,6 +28,8 @@ #ifndef BKE_PAINT_H #define BKE_PAINT_H +#include "DNA_vec_types.h" + struct Brush; struct MFace; struct MultireModifierData; @@ -96,6 +98,8 @@ typedef struct SculptSession { struct GPUDrawObject *drawobject; int modifiers_active; + + rcti previous_r; } SculptSession; void free_sculptsession(struct Object *ob); diff --git a/source/blender/blenkernel/intern/brush.c b/source/blender/blenkernel/intern/brush.c index 538012ccc41..c423d426e32 100644 --- a/source/blender/blenkernel/intern/brush.c +++ b/source/blender/blenkernel/intern/brush.c @@ -53,8 +53,7 @@ #include "BKE_main.h" #include "BKE_paint.h" #include "BKE_texture.h" - - +#include "BKE_icons.h" #include "IMB_imbuf.h" #include "IMB_imbuf_types.h" @@ -70,29 +69,59 @@ Brush *add_brush(const char *name) brush= alloc_libblock(&G.main->brush, ID_BR, name); - brush->rgb[0]= 1.0f; + /* BRUSH SCULPT TOOL SETTINGS */ + brush->sculpt_tool = SCULPT_TOOL_DRAW; /* sculpting defaults to the draw tool for new brushes */ + brush->size= 35; /* radius of the brush in pixels */ + brush->alpha= 0.5f; /* brush strength/intensity probably variable should be renamed? */ + brush->autosmooth_factor= 0.0f; + brush->crease_pinch_factor= 0.5f; + brush->sculpt_plane = SCULPT_DISP_DIR_VIEW; + brush->plane_offset= 0.0f; /* how far above or below the plane that is found by averaging the faces */ + brush->plane_trim= 0.5f; + brush->clone.alpha= 0.5f; + brush->normal_weight= 0.0f; + + /* BRUSH PAINT TOOL SETTINGS */ + brush->rgb[0]= 1.0f; /* default rgb color of the brush when painting - white */ brush->rgb[1]= 1.0f; brush->rgb[2]= 1.0f; - brush->alpha= 0.2f; - brush->size= 25; - brush->spacing= 3.5f; + + /* BRUSH STROKE SETTINGS */ + brush->flag |= (BRUSH_SPACE|BRUSH_SPACE_ATTEN); + brush->spacing= 10; /* how far each brush dot should be spaced as a percentage of brush diameter */ + brush->smooth_stroke_radius= 75; - brush->smooth_stroke_factor= 0.9; - brush->rate= 0.1f; + brush->smooth_stroke_factor= 0.9f; + + brush->rate= 0.1f; /* time delay between dots of paint or sculpting when doing airbrush mode */ + brush->jitter= 0.0f; - brush->clone.alpha= 0.5; - brush->sculpt_tool = SCULPT_TOOL_DRAW; - brush->flag |= BRUSH_SPACE; - brush_curve_preset(brush, CURVE_PRESET_SMOOTH); - + /* BRUSH TEXTURE SETTINGS */ default_mtex(&brush->mtex); + brush->texture_sample_bias= 0; /* value to added to texture samples */ + + /* brush appearance */ + + brush->image_icon= NULL; + + brush->add_col[0]= 1.00; /* add mode color is light red */ + brush->add_col[1]= 0.39; + brush->add_col[2]= 0.39; + + brush->sub_col[0]= 0.39; /* subtract mode color is light blue */ + brush->sub_col[1]= 0.39; + brush->sub_col[2]= 1.00; + + /* the default alpha falloff curve */ + brush_curve_preset(brush, CURVE_PRESET_SMOOTH); + /* enable fake user by default */ brush->id.flag |= LIB_FAKEUSER; brush_toggled_fake_user(brush); - - return brush; + + return brush; } Brush *copy_brush(Brush *brush) @@ -118,7 +147,7 @@ Brush *copy_brush(Brush *brush) void free_brush(Brush *brush) { if(brush->mtex.tex) brush->mtex.tex->id.us--; - + curvemapping_free(brush->curve); } @@ -731,11 +760,19 @@ static void brush_apply_pressure(BrushPainter *painter, Brush *brush, float pres brush->spacing = MAX2(1.0, painter->startspacing*(1.5f-pressure)); } -static void brush_jitter_pos(Brush *brush, float *pos, float *jitterpos) +void brush_jitter_pos(Brush *brush, float *pos, float *jitterpos) { if(brush->jitter){ - jitterpos[0] = pos[0] + ((BLI_frand()-0.5f) * brush->size * brush->jitter * 2); - jitterpos[1] = pos[1] + ((BLI_frand()-0.5f) * brush->size * brush->jitter * 2); + float rand_pos[2]; + + // find random position within a circle of diameter 1 + do { + rand_pos[0] = BLI_frand()-0.5f; + rand_pos[1] = BLI_frand()-0.5f; + } while (len_v2(rand_pos) > 0.5f); + + jitterpos[0] = pos[0] + 2*rand_pos[0]*brush->size*brush->jitter; + jitterpos[1] = pos[1] + 2*rand_pos[1]*brush->size*brush->jitter; } else { VECCOPY2D(jitterpos, pos); @@ -887,7 +924,7 @@ int brush_painter_paint(BrushPainter *painter, BrushFunc func, float *pos, doubl /* Uses the brush curve control to find a strength value between 0 and 1 */ float brush_curve_strength_clamp(Brush *br, float p, const float len) { - if(p >= len) p= 1.0f; + if(p >= len) return 0; else p= p/len; p= curvemapping_evaluateF(br->curve, 0, p); @@ -899,9 +936,12 @@ float brush_curve_strength_clamp(Brush *br, float p, const float len) * used for sculpt only */ float brush_curve_strength(Brush *br, float p, const float len) { - if(p >= len) p= 1.0f; - else p= p/len; - return curvemapping_evaluateF(br->curve, 0, p); + if(p >= len) + p= 1.0f; + else + p= p/len; + + return curvemapping_evaluateF(br->curve, 0, p); } /* TODO: should probably be unified with BrushPainter stuff? */ @@ -915,7 +955,7 @@ unsigned int *brush_gen_texture_cache(Brush *br, int half_side) memset(&texres, 0, sizeof(TexResult)); - if(mtex && mtex->tex) { + if(mtex->tex) { float x, y, step = 2.0 / side, co[3]; texcache = MEM_callocN(sizeof(int) * side * side, "Brush texture cache"); @@ -993,9 +1033,9 @@ void brush_radial_control_invoke(wmOperator *op, Brush *br, float size_weight) float original_value= 0; if(mode == WM_RADIALCONTROL_SIZE) - original_value = br->size * size_weight; + original_value = sculpt_get_brush_size(br) * size_weight; else if(mode == WM_RADIALCONTROL_STRENGTH) - original_value = br->alpha; + original_value = sculpt_get_brush_alpha(br); else if(mode == WM_RADIALCONTROL_ANGLE) { MTex *mtex = brush_active_texture(br); if(mtex) @@ -1013,9 +1053,15 @@ int brush_radial_control_exec(wmOperator *op, Brush *br, float size_weight) const float conv = 0.017453293; if(mode == WM_RADIALCONTROL_SIZE) - br->size = new_value * size_weight; + if (sculpt_get_lock_brush_size(br)) { + float initial_value = RNA_float_get(op->ptr, "initial_value"); + const float unprojected_radius = sculpt_get_brush_unprojected_radius(br); + sculpt_set_brush_unprojected_radius(br, unprojected_radius * new_value/initial_value * size_weight); + } + else + sculpt_set_brush_size(br, new_value * size_weight); else if(mode == WM_RADIALCONTROL_STRENGTH) - br->alpha = new_value; + sculpt_set_brush_alpha(br, new_value); else if(mode == WM_RADIALCONTROL_ANGLE) { MTex *mtex = brush_active_texture(br); if(mtex) diff --git a/source/blender/blenkernel/intern/colortools.c b/source/blender/blenkernel/intern/colortools.c index c8a01b1c12f..a07c18f42f3 100644 --- a/source/blender/blenkernel/intern/colortools.c +++ b/source/blender/blenkernel/intern/colortools.c @@ -126,6 +126,9 @@ CurveMapping *curvemapping_add(int tot, float minx, float miny, float maxx, floa cumap->cm[a].curve[1].x= maxx; cumap->cm[a].curve[1].y= maxy; } + + cumap->changed_timestamp = 0; + return cumap; } @@ -240,10 +243,12 @@ void curvemap_reset(CurveMap *cuma, rctf *clipr, int preset) switch(preset) { case CURVE_PRESET_LINE: cuma->totpoint= 2; break; - case CURVE_PRESET_SHARP: cuma->totpoint= 3; break; + case CURVE_PRESET_SHARP: cuma->totpoint= 4; break; case CURVE_PRESET_SMOOTH: cuma->totpoint= 4; break; case CURVE_PRESET_MAX: cuma->totpoint= 2; break; - case CURVE_PRESET_MID9: cuma->totpoint= 9; + case CURVE_PRESET_MID9: cuma->totpoint= 9; break; + case CURVE_PRESET_ROUND: cuma->totpoint= 4; break; + case CURVE_PRESET_ROOT: cuma->totpoint= 4; break; } cuma->curve= MEM_callocN(cuma->totpoint*sizeof(CurveMapPoint), "curve points"); @@ -251,27 +256,29 @@ void curvemap_reset(CurveMap *cuma, rctf *clipr, int preset) switch(preset) { case CURVE_PRESET_LINE: cuma->curve[0].x= clipr->xmin; - cuma->curve[0].y= clipr->ymin; + cuma->curve[0].y= clipr->ymax; cuma->curve[0].flag= 0; cuma->curve[1].x= clipr->xmax; - cuma->curve[1].y= clipr->ymax; + cuma->curve[1].y= clipr->ymin; cuma->curve[1].flag= 0; break; case CURVE_PRESET_SHARP: cuma->curve[0].x= 0; cuma->curve[0].y= 1; - cuma->curve[1].x= 0.33; - cuma->curve[1].y= 0.33; - cuma->curve[2].x= 1; - cuma->curve[2].y= 0; + cuma->curve[1].x= 0.25; + cuma->curve[1].y= 0.50; + cuma->curve[2].x= 0.75; + cuma->curve[2].y= 0.04; + cuma->curve[3].x= 1; + cuma->curve[3].y= 0; break; case CURVE_PRESET_SMOOTH: cuma->curve[0].x= 0; cuma->curve[0].y= 1; cuma->curve[1].x= 0.25; - cuma->curve[1].y= 0.92; + cuma->curve[1].y= 0.94; cuma->curve[2].x= 0.75; - cuma->curve[2].y= 0.08; + cuma->curve[2].y= 0.06; cuma->curve[3].x= 1; cuma->curve[3].y= 0; break; @@ -290,8 +297,29 @@ void curvemap_reset(CurveMap *cuma, rctf *clipr, int preset) cuma->curve[i].y= 0.5; } } + break; + case CURVE_PRESET_ROUND: + cuma->curve[0].x= 0; + cuma->curve[0].y= 1; + cuma->curve[1].x= 0.5; + cuma->curve[1].y= 0.90; + cuma->curve[2].x= 0.86; + cuma->curve[2].y= 0.5; + cuma->curve[3].x= 1; + cuma->curve[3].y= 0; + break; + case CURVE_PRESET_ROOT: + cuma->curve[0].x= 0; + cuma->curve[0].y= 1; + cuma->curve[1].x= 0.25; + cuma->curve[1].y= 0.95; + cuma->curve[2].x= 0.75; + cuma->curve[2].y= 0.44; + cuma->curve[3].x= 1; + cuma->curve[3].y= 0; + break; } - + if(cuma->table) { MEM_freeN(cuma->table); cuma->table= NULL; @@ -619,7 +647,9 @@ void curvemapping_changed(CurveMapping *cumap, int rem_doubles) float thresh= 0.01f*(clipr->xmax - clipr->xmin); float dx= 0.0f, dy= 0.0f; int a; - + + cumap->changed_timestamp++; + /* clamp with clip */ if(cumap->flag & CUMA_DO_CLIP) { for(a=0; atotpoint; a++) { @@ -701,7 +731,7 @@ float curvemapping_evaluateF(CurveMapping *cumap, int cur, float value) if(cuma->table==NULL) { curvemap_make_table(cuma, &cumap->clipr); if(cuma->table==NULL) - return value; + return 1.0f-value; } return curvemap_evaluateF(cuma, value); } diff --git a/source/blender/blenkernel/intern/icons.c b/source/blender/blenkernel/intern/icons.c index 29314fb4865..ad2c857be75 100644 --- a/source/blender/blenkernel/intern/icons.c +++ b/source/blender/blenkernel/intern/icons.c @@ -38,6 +38,7 @@ #include "DNA_material_types.h" #include "DNA_texture_types.h" #include "DNA_world_types.h" +#include "DNA_brush_types.h" #include "BLI_ghash.h" @@ -120,6 +121,7 @@ struct PreviewImage* BKE_previewimg_create() for (i=0; ichanged[i] = 1; + prv_img->changed_timestamp[i] = 0; } return prv_img; } @@ -202,7 +204,7 @@ PreviewImage* BKE_previewimg_get(ID *id) Image *img = (Image*)id; if (!img->preview) img->preview = BKE_previewimg_create(); prv_img = img->preview; - } + } return prv_img; } @@ -224,6 +226,7 @@ void BKE_icon_changed(int id) int i; for (i=0; ichanged[i] = 1; + prv->changed_timestamp[i]++; } } } diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c index dc78dac04dd..b66b5c60916 100644 --- a/source/blender/blenkernel/intern/image.c +++ b/source/blender/blenkernel/intern/image.c @@ -200,9 +200,9 @@ void free_image(Image *ima) } BKE_icon_delete(&ima->id); ima->id.icon_id = 0; - if (ima->preview) { - BKE_previewimg_free(&ima->preview); - } + + BKE_previewimg_free(&ima->preview); + for(a=0; arenders[a]) { RE_FreeRenderResult(ima->renders[a]); diff --git a/source/blender/blenlib/BLI_pbvh.h b/source/blender/blenlib/BLI_pbvh.h index 519e3ddde1d..4797aeb2364 100644 --- a/source/blender/blenlib/BLI_pbvh.h +++ b/source/blender/blenlib/BLI_pbvh.h @@ -36,12 +36,17 @@ struct ListBase; typedef struct PBVH PBVH; typedef struct PBVHNode PBVHNode; +typedef struct { + float (*co)[3]; +} PBVHProxyNode; + /* Callbacks */ /* returns 1 if the search should continue from this node, 0 otherwise */ typedef int (*BLI_pbvh_SearchCallback)(PBVHNode *node, void *data); typedef void (*BLI_pbvh_HitCallback)(PBVHNode *node, void *data); +typedef void (*BLI_pbvh_HitOccludedCallback)(PBVHNode *node, void *data, float* tmin); /* Building */ @@ -70,7 +75,7 @@ void BLI_pbvh_search_gather(PBVH *bvh, it's up to the callback to find the primitive within the leaves that is hit first */ -void BLI_pbvh_raycast(PBVH *bvh, BLI_pbvh_HitCallback cb, void *data, +void BLI_pbvh_raycast(PBVH *bvh, BLI_pbvh_HitOccludedCallback cb, void *data, float ray_start[3], float ray_normal[3], int original); int BLI_pbvh_node_raycast(PBVH *bvh, PBVHNode *node, float (*origco)[3], float ray_start[3], float ray_normal[3], float *dist); @@ -106,6 +111,8 @@ void BLI_pbvh_node_get_verts(PBVH *bvh, PBVHNode *node, void BLI_pbvh_node_get_BB(PBVHNode *node, float bb_min[3], float bb_max[3]); void BLI_pbvh_node_get_original_BB(PBVHNode *node, float bb_min[3], float bb_max[3]); +float BLI_pbvh_node_get_tmin(PBVHNode* node); + /* Update Normals/Bounding Box/Draw Buffers/Redraw and clear flags */ void BLI_pbvh_update(PBVH *bvh, int flags, float (*face_nors)[3]); @@ -159,13 +166,21 @@ typedef struct PBVHVertexIter { float *fno; } PBVHVertexIter; +#ifdef _MSC_VER +#pragma warning (disable:4127) // conditional expression is constant +#endif + #define BLI_pbvh_vertex_iter_begin(bvh, node, vi, mode) \ { \ struct DMGridData **grids; \ struct MVert *verts; \ int *grid_indices, totgrid, gridsize, *vert_indices, uniq_verts, totvert; \ \ - memset(&vi, 0, sizeof(PBVHVertexIter)); \ + vi.grid= 0; \ + vi.no= 0; \ + vi.fno= 0; \ + vi.mvert= 0; \ + vi.skip= 0; \ \ BLI_pbvh_node_get_grids(bvh, node, &grid_indices, &totgrid, NULL, &gridsize, &grids, NULL); \ BLI_pbvh_node_num_verts(bvh, node, &uniq_verts, &totvert); \ @@ -222,6 +237,13 @@ typedef struct PBVHVertexIter { } \ } +void BLI_pbvh_node_get_proxies(PBVHNode* node, PBVHProxyNode** proxies, int* proxy_count); +void BLI_pbvh_node_free_proxies(PBVHNode* node); +PBVHProxyNode* BLI_pbvh_node_add_proxy(PBVH* bvh, PBVHNode* node); +void BLI_pbvh_gather_proxies(PBVH* pbvh, PBVHNode*** nodes, int* totnode); + +//void BLI_pbvh_node_BB_reset(PBVHNode* node); +//void BLI_pbvh_node_BB_expand(PBVHNode* node, float co[3]); #endif /* BLI_PBVH_H */ diff --git a/source/blender/blenlib/intern/math_geom.c b/source/blender/blenlib/intern/math_geom.c index 0a06cd10e1e..e8fb922ce4d 100644 --- a/source/blender/blenlib/intern/math_geom.c +++ b/source/blender/blenlib/intern/math_geom.c @@ -62,6 +62,7 @@ float normal_tri_v3(float n[3], const float v1[3], const float v2[3], const floa n[0]= n1[1]*n2[2]-n1[2]*n2[1]; n[1]= n1[2]*n2[0]-n1[0]*n2[2]; n[2]= n1[0]*n2[1]-n1[1]*n2[0]; + return normalize_v3(n); } @@ -401,16 +402,17 @@ int isect_line_tri_v3(float p1[3], float p2[3], float v0[3], float v1[3], float sub_v3_v3v3(s, p1, v0); - cross_v3_v3v3(q, s, e1); - *lambda = f * dot_v3v3(e2, q); - if ((*lambda < 0.0)||(*lambda > 1.0)) return 0; - u = f * dot_v3v3(s, p); if ((u < 0.0)||(u > 1.0)) return 0; - v = f * dot_v3v3(d, q); + cross_v3_v3v3(q, s, e1); + + v = f * dot_v3v3(d, q); if ((v < 0.0)||((u + v) > 1.0)) return 0; + *lambda = f * dot_v3v3(e2, q); + if ((*lambda < 0.0)||(*lambda > 1.0)) return 0; + if(uv) { uv[0]= u; uv[1]= v; @@ -440,17 +442,18 @@ int isect_ray_tri_v3(float p1[3], float d[3], float v0[3], float v1[3], float v2 sub_v3_v3v3(s, p1, v0); - cross_v3_v3v3(q, s, e1); - *lambda = f * dot_v3v3(e2, q); - if ((*lambda < 0.0)) return 0; - u = f * dot_v3v3(s, p); if ((u < 0.0)||(u > 1.0)) return 0; + cross_v3_v3v3(q, s, e1); + v = f * dot_v3v3(d, q); if ((v < 0.0)||((u + v) > 1.0)) return 0; - if(uv) { + *lambda = f * dot_v3v3(e2, q); + if ((*lambda < 0.0)) return 0; + + if(uv) { uv[0]= u; uv[1]= v; } @@ -460,36 +463,36 @@ int isect_ray_tri_v3(float p1[3], float d[3], float v0[3], float v1[3], float v2 int isect_ray_tri_epsilon_v3(float p1[3], float d[3], float v0[3], float v1[3], float v2[3], float *lambda, float *uv, float epsilon) { - float p[3], s[3], e1[3], e2[3], q[3]; - float a, f, u, v; - - sub_v3_v3v3(e1, v1, v0); - sub_v3_v3v3(e2, v2, v0); - - cross_v3_v3v3(p, d, e2); - a = dot_v3v3(e1, p); - if (a == 0.0f) return 0; - f = 1.0f/a; - - sub_v3_v3v3(s, p1, v0); - - cross_v3_v3v3(q, s, e1); + float p[3], s[3], e1[3], e2[3], q[3]; + float a, f, u, v; - u = f * dot_v3v3(s, p); - if ((u < -epsilon)||(u > 1.0f+epsilon)) return 0; - - v = f * dot_v3v3(d, q); - if ((v < -epsilon)||((u + v) > 1.0f+epsilon)) return 0; + sub_v3_v3v3(e1, v1, v0); + sub_v3_v3v3(e2, v2, v0); - *lambda = f * dot_v3v3(e2, q); - if ((*lambda < 0.0f)) return 0; + cross_v3_v3v3(p, d, e2); + a = dot_v3v3(e1, p); + if (a == 0.0f) return 0; + f = 1.0f/a; - if(uv) { - uv[0]= u; - uv[1]= v; - } - - return 1; + sub_v3_v3v3(s, p1, v0); + + u = f * dot_v3v3(s, p); + if ((u < -epsilon)||(u > 1.0f+epsilon)) return 0; + + cross_v3_v3v3(q, s, e1); + + v = f * dot_v3v3(d, q); + if ((v < -epsilon)||((u + v) > 1.0f+epsilon)) return 0; + + *lambda = f * dot_v3v3(e2, q); + if ((*lambda < 0.0f)) return 0; + + if(uv) { + uv[0]= u; + uv[1]= v; + } + + return 1; } int isect_ray_tri_threshold_v3(float p1[3], float d[3], float v0[3], float v1[3], float v2[3], float *lambda, float *uv, float threshold) diff --git a/source/blender/blenlib/intern/pbvh.c b/source/blender/blenlib/intern/pbvh.c index 426181e5304..7069eeea510 100644 --- a/source/blender/blenlib/intern/pbvh.c +++ b/source/blender/blenlib/intern/pbvh.c @@ -92,6 +92,11 @@ struct PBVHNode { unsigned int uniq_verts, face_verts; char flag; + + float tmin; // used for raycasting, is how close bb is to the ray point + + int proxy_count; + PBVHProxyNode* proxies; }; struct PBVH { @@ -227,6 +232,17 @@ static void update_node_vb(PBVH *bvh, PBVHNode *node) node->vb= vb; } +//void BLI_pbvh_node_BB_reset(PBVHNode* node) +//{ +// BB_reset(&node->vb); +//} +// +//void BLI_pbvh_node_BB_expand(PBVHNode* node, float co[3]) +//{ +// BB_expand(&node->vb, co); +//} + + /* Adapted from BLI_kdopbvh.c */ /* Returns the index of the first element on the right of the partition */ static int partition_indices(int *prim_indices, int lo, int hi, int axis, @@ -354,10 +370,10 @@ static void build_mesh_leaf_node(PBVH *bvh, PBVHNode *node) if(!G.background) { node->draw_buffers = GPU_build_mesh_buffers(map, bvh->verts, bvh->faces, - node->prim_indices, - node->totprim, node->vert_indices, - node->uniq_verts, - node->uniq_verts + node->face_verts); + node->prim_indices, + node->totprim, node->vert_indices, + node->uniq_verts, + node->uniq_verts + node->face_verts); } node->flag |= PBVH_UpdateDrawBuffers; @@ -641,13 +657,12 @@ static PBVHNode *pbvh_iter_next(PBVHIter *iter) { PBVHNode *node; int revisiting; - void *search_data; /* purpose here is to traverse tree, visiting child nodes before their parents, this order is necessary for e.g. computing bounding boxes */ while(iter->stacksize) { - /* pop node */ + /* pop node */ iter->stacksize--; node= iter->stack[iter->stacksize].node; @@ -662,10 +677,7 @@ static PBVHNode *pbvh_iter_next(PBVHIter *iter) if(revisiting) return node; - /* check search callback */ - search_data= iter->search_data; - - if(iter->scb && !iter->scb(node, search_data)) + if(iter->scb && !iter->scb(node, iter->search_data)) continue; /* don't traverse, outside of search zone */ if(node->flag & PBVH_Leaf) { @@ -685,6 +697,34 @@ static PBVHNode *pbvh_iter_next(PBVHIter *iter) return NULL; } +static PBVHNode *pbvh_iter_next_occluded(PBVHIter *iter) +{ + PBVHNode *node; + + while(iter->stacksize) { + /* pop node */ + iter->stacksize--; + node= iter->stack[iter->stacksize].node; + + /* on a mesh with no faces this can happen + * can remove this check if we know meshes have at least 1 face */ + if(node==NULL) return NULL; + + if(iter->scb && !iter->scb(node, iter->search_data)) continue; /* don't traverse, outside of search zone */ + + if(node->flag & PBVH_Leaf) { + /* immediately hit leaf node */ + return node; + } + else { + pbvh_stack_push(iter, iter->bvh->nodes+node->children_offset+1, 0); + pbvh_stack_push(iter, iter->bvh->nodes+node->children_offset, 0); + } + } + + return NULL; +} + void BLI_pbvh_search_gather(PBVH *bvh, BLI_pbvh_SearchCallback scb, void *search_data, PBVHNode ***r_array, int *r_tot) @@ -736,12 +776,105 @@ void BLI_pbvh_search_callback(PBVH *bvh, pbvh_iter_begin(&iter, bvh, scb, search_data); while((node=pbvh_iter_next(&iter))) - if(node->flag & PBVH_Leaf) + if (node->flag & PBVH_Leaf) hcb(node, hit_data); pbvh_iter_end(&iter); } +typedef struct node_tree { + PBVHNode* data; + + struct node_tree* left; + struct node_tree* right; +} node_tree; + +static void node_tree_insert(node_tree* tree, node_tree* new_node) +{ + if (new_node->data->tmin < tree->data->tmin) { + if (tree->left) { + node_tree_insert(tree->left, new_node); + } + else { + tree->left = new_node; + } + } + else { + if (tree->right) { + node_tree_insert(tree->right, new_node); + } + else { + tree->right = new_node; + } + } +} + +static void traverse_tree(node_tree* tree, BLI_pbvh_HitOccludedCallback hcb, void* hit_data, float* tmin) +{ + if (tree->left) traverse_tree(tree->left, hcb, hit_data, tmin); + + hcb(tree->data, hit_data, tmin); + + if (tree->right) traverse_tree(tree->right, hcb, hit_data, tmin); +} + +static void free_tree(node_tree* tree) +{ + if (tree->left) { + free_tree(tree->left); + tree->left = 0; + } + + if (tree->right) { + free_tree(tree->right); + tree->right = 0; + } + + free(tree); +} + +float BLI_pbvh_node_get_tmin(PBVHNode* node) +{ + return node->tmin; +} + +void BLI_pbvh_search_callback_occluded(PBVH *bvh, + BLI_pbvh_SearchCallback scb, void *search_data, + BLI_pbvh_HitOccludedCallback hcb, void *hit_data) +{ + PBVHIter iter; + PBVHNode *node; + node_tree *tree = 0; + + pbvh_iter_begin(&iter, bvh, scb, search_data); + + while((node=pbvh_iter_next_occluded(&iter))) { + if(node->flag & PBVH_Leaf) { + node_tree* new_node = malloc(sizeof(node_tree)); + + new_node->data = node; + + new_node->left = NULL; + new_node->right = NULL; + + if (tree) { + node_tree_insert(tree, new_node); + } + else { + tree = new_node; + } + } + } + + pbvh_iter_end(&iter); + + if (tree) { + float tmin = FLT_MAX; + traverse_tree(tree, hcb, hit_data, &tmin); + free_tree(tree); + } +} + static int update_search_cb(PBVHNode *node, void *data_v) { int flag= GET_INT_FROM_POINTER(data_v); @@ -985,7 +1118,8 @@ void BLI_pbvh_get_grid_updates(PBVH *bvh, int clear, void ***gridfaces, int *tot GHashIterator *hiter; GHash *map; void *face, **faces; - int i, tot; + unsigned i; + int tot; map = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "pbvh_get_grid_updates gh"); @@ -1086,6 +1220,18 @@ void BLI_pbvh_node_get_original_BB(PBVHNode *node, float bb_min[3], float bb_max copy_v3_v3(bb_max, node->orig_vb.bmax); } +void BLI_pbvh_node_get_proxies(PBVHNode* node, PBVHProxyNode** proxies, int* proxy_count) +{ + if (node->proxy_count > 0) { + if (proxies) *proxies = node->proxies; + if (proxy_count) *proxy_count = node->proxy_count; + } + else { + if (proxies) *proxies = 0; + if (proxy_count) *proxy_count = 0; + } +} + /********************************* Raycast ***********************************/ typedef struct { @@ -1100,16 +1246,13 @@ typedef struct { static int ray_aabb_intersect(PBVHNode *node, void *data_v) { RaycastData *ray = data_v; - float bb_min[3], bb_max[3], bbox[2][3]; + float bbox[2][3]; float tmin, tmax, tymin, tymax, tzmin, tzmax; if(ray->original) - BLI_pbvh_node_get_original_BB(node, bb_min, bb_max); + BLI_pbvh_node_get_original_BB(node, bbox[0], bbox[1]); else - BLI_pbvh_node_get_BB(node, bb_min, bb_max); - - copy_v3_v3(bbox[0], bb_min); - copy_v3_v3(bbox[1], bb_max); + BLI_pbvh_node_get_BB(node, bbox[0], bbox[1]); tmin = (bbox[ray->sign[0]][0] - ray->start[0]) * ray->inv_dir[0]; tmax = (bbox[1-ray->sign[0]][0] - ray->start[0]) * ray->inv_dir[0]; @@ -1119,8 +1262,10 @@ static int ray_aabb_intersect(PBVHNode *node, void *data_v) if((tmin > tymax) || (tymin > tmax)) return 0; + if(tymin > tmin) tmin = tymin; + if(tymax < tmax) tmax = tymax; @@ -1129,20 +1274,20 @@ static int ray_aabb_intersect(PBVHNode *node, void *data_v) if((tmin > tzmax) || (tzmin > tmax)) return 0; - - return 1; - /* XXX: Not sure about this? - if(tzmin > tmin) - tmin = tzmin; - if(tzmax < tmax) - tmax = tzmax; - return ((tmin < t1) && (tmax > t0)); - */ + if(tzmin > tmin) + tmin = tzmin; + // XXX jwilkins: tmax does not need to be updated since we don't use it + // keeping this here for future reference + //if(tzmax < tmax) tmax = tzmax; + + node->tmin = tmin; + + return 1; } -void BLI_pbvh_raycast(PBVH *bvh, BLI_pbvh_HitCallback cb, void *data, +void BLI_pbvh_raycast(PBVH *bvh, BLI_pbvh_HitOccludedCallback cb, void *data, float ray_start[3], float ray_normal[3], int original) { RaycastData rcd; @@ -1156,37 +1301,24 @@ void BLI_pbvh_raycast(PBVH *bvh, BLI_pbvh_HitCallback cb, void *data, rcd.sign[2] = rcd.inv_dir[2] < 0; rcd.original = original; - BLI_pbvh_search_callback(bvh, ray_aabb_intersect, &rcd, cb, data); + BLI_pbvh_search_callback_occluded(bvh, ray_aabb_intersect, &rcd, cb, data); } -/* XXX: Code largely copied from bvhutils.c, could be unified */ -/* Returns 1 if a better intersection has been found */ static int ray_face_intersection(float ray_start[3], float ray_normal[3], float *t0, float *t1, float *t2, float *t3, float *fdist) { - int hit = 0; - - do - { - float dist = FLT_MAX; - - if(!isect_ray_tri_epsilon_v3(ray_start, ray_normal, t0, t1, t2, - &dist, NULL, 0.1f)) - dist = FLT_MAX; - - if(dist >= 0 && dist < *fdist) { - hit = 1; - *fdist = dist; - } - - t1 = t2; - t2 = t3; - t3 = NULL; - - } while(t2); - - return hit; + float dist; + + if ((isect_ray_tri_epsilon_v3(ray_start, ray_normal, t0, t1, t2, &dist, NULL, 0.1f) && dist < *fdist) || + (t3 && isect_ray_tri_epsilon_v3(ray_start, ray_normal, t0, t2, t3, &dist, NULL, 0.1f) && dist < *fdist)) + { + *fdist = dist; + return 1; + } + else { + return 0; + } } int BLI_pbvh_node_raycast(PBVH *bvh, PBVHNode *node, float (*origco)[3], @@ -1399,3 +1531,86 @@ int BLI_pbvh_isDeformed(PBVH *pbvh) { return pbvh->deformed; } +/* Proxies */ + +PBVHProxyNode* BLI_pbvh_node_add_proxy(PBVH* bvh, PBVHNode* node) +{ + int index, totverts; + + #pragma omp critical + { + + index = node->proxy_count; + + node->proxy_count++; + + if (node->proxies) + node->proxies= MEM_reallocN(node->proxies, node->proxy_count*sizeof(PBVHProxyNode)); + else + node->proxies= MEM_mallocN(sizeof(PBVHProxyNode), "PBVHNodeProxy"); + + if (bvh->grids) + totverts = node->totprim*bvh->gridsize*bvh->gridsize; + else + totverts = node->uniq_verts; + + node->proxies[index].co= MEM_callocN(sizeof(float[3])*totverts, "PBVHNodeProxy.co"); + } + + return node->proxies + index; +} + +void BLI_pbvh_node_free_proxies(PBVHNode* node) +{ + #pragma omp critical + { + int p; + + for (p= 0; p < node->proxy_count; p++) { + MEM_freeN(node->proxies[p].co); + node->proxies[p].co= 0; + } + + MEM_freeN(node->proxies); + node->proxies = 0; + + node->proxy_count= 0; + } +} + +void BLI_pbvh_gather_proxies(PBVH* pbvh, PBVHNode*** r_array, int* r_tot) +{ + PBVHNode **array= NULL, **newarray, *node; + int tot= 0, space= 0; + int n; + + for (n= 0; n < pbvh->totnode; n++) { + node = pbvh->nodes + n; + + if(node->proxy_count > 0) { + if(tot == space) { + /* resize array if needed */ + space= (tot == 0)? 32: space*2; + newarray= MEM_callocN(sizeof(PBVHNode)*space, "BLI_pbvh_gather_proxies"); + + if (array) { + memcpy(newarray, array, sizeof(PBVHNode)*tot); + MEM_freeN(array); + } + + array= newarray; + } + + array[tot]= node; + tot++; + } + } + + if(tot == 0 && array) { + MEM_freeN(array); + array= NULL; + } + + *r_array= array; + *r_tot= tot; +} diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index f6122161130..7b29ab2a666 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -1539,6 +1539,7 @@ static void lib_link_brush(FileData *fd, Main *main) brush->id.flag -= LIB_NEEDLINK; brush->mtex.tex= newlibadr_us(fd, brush->id.lib, brush->mtex.tex); + brush->image_icon= newlibadr_us(fd, brush->id.lib, brush->image_icon); brush->clone.image= newlibadr_us(fd, brush->id.lib, brush->clone.image); } } @@ -10971,6 +10972,82 @@ static void do_versions(FileData *fd, Library *lib, Main *main) } } + { + /* GSOC 2010 Sculpt - New settings for Brush */ + + Brush *brush; + for (brush= main->brush.first; brush; brush= brush->id.next) { + /* Sanity Check */ + + // infinite number of dabs + if (brush->spacing == 0) + brush->spacing = 10; + + // will have no effect + if (brush->alpha == 0) + brush->alpha = 0.5f; + + // bad radius + if (brush->unprojected_radius == 0) + brush->unprojected_radius = 0.125; + + // unusable size + if (brush->size == 0) + brush->size = 35; + + // can't see overlay + if (brush->texture_overlay_alpha == 0) + brush->texture_overlay_alpha = 33; + + // same as draw brush + if (brush->crease_pinch_factor == 0) + brush->crease_pinch_factor = 0.5f; + + // will sculpt no vertexes + if (brush->plane_trim == 0) + brush->plane_trim = 0.5f; + + // same as smooth stroke off + if (brush->smooth_stroke_radius == 0) + brush->smooth_stroke_radius= 75; + + // will keep cursor in one spot + if (brush->smooth_stroke_radius == 1) + brush->smooth_stroke_factor= 0.9f; + + // same as dots + if (brush->rate == 0) + brush->rate = 0.1f; + + /* New Settings */ + if (main->versionfile < 252 || (main->versionfile == 252 && main->subversionfile < 6)) { + brush->flag |= BRUSH_SPACE_ATTEN; // explicitly enable adaptive space + + // spacing was originally in pixels, convert it to percentage for new version + // size should not be zero due to sanity check above + brush->spacing = (int)(100*((float)brush->spacing) / ((float)brush->size)); + + if (brush->add_col[0] == 0 && + brush->add_col[1] == 0 && + brush->add_col[2] == 0) + { + brush->add_col[0] = 1.00; + brush->add_col[1] = 0.39; + brush->add_col[2] = 0.39; + } + + if (brush->sub_col[0] == 0 && + brush->sub_col[1] == 0 && + brush->sub_col[2] == 0) + { + brush->sub_col[0] = 0.39; + brush->sub_col[1] = 0.39; + brush->sub_col[2] = 1.00; + } + } + } + } + /* WATCH IT!!!: pointers from libdata have not been converted yet here! */ /* WATCH IT 2!: Userdef struct init has to be in editors/interface/resources.c! */ diff --git a/source/blender/editors/gpencil/gpencil_buttons.c b/source/blender/editors/gpencil/gpencil_buttons.c index 001773fc02a..17bafa3d399 100644 --- a/source/blender/editors/gpencil/gpencil_buttons.c +++ b/source/blender/editors/gpencil/gpencil_buttons.c @@ -243,7 +243,7 @@ static void draw_gpencil_panel (bContext *C, uiLayout *layout, bGPdata *gpd, Poi col= uiLayoutColumn(layout, 0); /* current Grease Pencil block */ // TODO: show some info about who owns this? - uiTemplateID(col, C, ctx_ptr, "grease_pencil", "GPENCIL_OT_data_add", NULL, "GPENCIL_OT_data_unlink"); + uiTemplateID(col, C, ctx_ptr, "grease_pencil", "GPENCIL_OT_data_add", NULL, "GPENCIL_OT_data_unlink", NULL); /* add new layer button - can be used even when no data, since it can add a new block too */ uiItemO(col, NULL, 0, "GPENCIL_OT_layer_add"); diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index 93e91d02599..4b6b396483d 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -669,11 +669,11 @@ uiBlock *uiLayoutAbsoluteBlock(uiLayout *layout); void uiTemplateHeader(uiLayout *layout, struct bContext *C, int menus); void uiTemplateDopeSheetFilter(uiLayout *layout, struct bContext *C, struct PointerRNA *ptr); void uiTemplateID(uiLayout *layout, struct bContext *C, struct PointerRNA *ptr, char *propname, - char *newop, char *openop, char *unlinkop); + char *newop, char *openop, char *unlinkop, char *filterop); void uiTemplateIDBrowse(uiLayout *layout, struct bContext *C, struct PointerRNA *ptr, char *propname, - char *newop, char *openop, char *unlinkop); + char *newop, char *openop, char *unlinkop, char *filterop); void uiTemplateIDPreview(uiLayout *layout, struct bContext *C, struct PointerRNA *ptr, char *propname, - char *newop, char *openop, char *unlinkop, int rows, int cols); + char *newop, char *openop, char *unlinkop, char *filterop, int rows, int cols); void uiTemplateAnyID(uiLayout *layout, struct bContext *C, struct PointerRNA *ptr, char *propname, char *proptypename, char *text); void uiTemplatePathBuilder(uiLayout *layout, struct bContext *C, struct PointerRNA *ptr, char *propname, diff --git a/source/blender/editors/interface/interface_icons.c b/source/blender/editors/interface/interface_icons.c index 499fe3b9767..232b6f7f317 100644 --- a/source/blender/editors/interface/interface_icons.c +++ b/source/blender/editors/interface/interface_icons.c @@ -46,6 +46,7 @@ #include "DNA_screen_types.h" #include "DNA_userdef_types.h" +#include "DNA_brush_types.h" #include "BKE_context.h" #include "BKE_global.h" @@ -762,6 +763,7 @@ static void icon_create_mipmap(struct PreviewImage* prv_img, int miplevel) prv_img->w[miplevel] = size; prv_img->h[miplevel] = size; prv_img->changed[miplevel] = 1; + prv_img->changed_timestamp[miplevel] = 0; prv_img->rect[miplevel] = MEM_callocN(size*size*sizeof(unsigned int), "prv_rect"); } } @@ -976,6 +978,16 @@ int ui_id_icon_get(bContext *C, ID *id, int preview) /* checks if not exists, or changed */ ui_id_icon_render(C, id, preview); break; + case ID_BR: + { /* use the image in the brush as the icon */ + /* XXX redundancy here can be reduced be rewriting this switch as an if */ + ID* ima_id = (ID*)((Brush*)id)->image_icon; + id = ima_id ? ima_id : id; + iconid= BKE_icon_getid(id); + /* checks if not exists, or changed */ + ui_id_icon_render(C, id, preview); + } + break; default: break; } diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index 7a5e0413f6d..cbe10496a72 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -42,6 +42,7 @@ #include "BKE_main.h" #include "BKE_texture.h" #include "BKE_utildefines.h" +#include "BKE_report.h" #include "ED_screen.h" #include "ED_render.h" @@ -141,6 +142,8 @@ typedef struct TemplateID { ListBase *idlb; int prv_rows, prv_cols; + + char filterop[64]; } TemplateID; /* Search browse menu, assign */ @@ -170,15 +173,53 @@ static void id_search_cb(const bContext *C, void *arg_template, char *str, uiSea /* ID listbase */ for(id= lb->first; id; id= id->next) { if(!((flag & PROP_ID_SELF_CHECK) && id == id_from)) { + int filter_yes; + + filter_yes= 0; + + /* use filter */ + if (template->filterop[0] != 0) { + PointerRNA ptr; + ReportList reports; + FunctionRNA *func; + ParameterList parms; + + RNA_id_pointer_create(id, &ptr); + + BKE_reports_init(&reports, RPT_PRINT); + + func= RNA_struct_find_function(&ptr, template->filterop); + + if (func) { + RNA_parameter_list_create(&parms, &ptr, func); + + RNA_parameter_set_lookup(&parms, "context", &C); + + if (RNA_function_call(C, &reports, &ptr, func, &parms) == 0) { + int* ret; + RNA_parameter_get_lookup(&parms, "ret", &ret); - /* hide dot-datablocks */ - if(U.uiflag & USER_HIDE_DOT) + if (!(*ret)) { + RNA_parameter_list_free(&parms); + continue; + } + else { + filter_yes= 1; + } + } + + RNA_parameter_list_free(&parms); + } + } + + /* hide dot-datablocks, but only if filter does not force it visible */ + if(!filter_yes && U.uiflag & USER_HIDE_DOT) if ((id->name[2]=='.') && (str[0] != '.')) continue; if(BLI_strcasestr(id->name+2, str)) { iconid= ui_id_icon_get((bContext*)C, id, 1); - + if(!uiSearchItemAdd(items, id->name+2, id, iconid)) break; } @@ -340,7 +381,7 @@ static void template_id_cb(bContext *C, void *arg_litem, void *arg_event) } } -static void template_ID(bContext *C, uiLayout *layout, TemplateID *template, StructRNA *type, int flag, char *newop, char *openop, char *unlinkop) +static void template_ID(bContext *C, uiLayout *layout, TemplateID *template, StructRNA *type, int flag, char *newop, char *openop, char *unlinkop, char *filterop) { uiBut *but; uiBlock *block; @@ -480,7 +521,7 @@ static void template_ID(bContext *C, uiLayout *layout, TemplateID *template, Str uiBlockEndAlign(block); } -static void ui_template_id(uiLayout *layout, bContext *C, PointerRNA *ptr, char *propname, char *newop, char *openop, char *unlinkop, int flag, int prv_rows, int prv_cols) +static void ui_template_id(uiLayout *layout, bContext *C, PointerRNA *ptr, char *propname, char *newop, char *openop, char *unlinkop, char* filterop, int flag, int prv_rows, int prv_cols) { TemplateID *template; PropertyRNA *prop; @@ -498,7 +539,12 @@ static void ui_template_id(uiLayout *layout, bContext *C, PointerRNA *ptr, char template->prop= prop; template->prv_rows = prv_rows; template->prv_cols = prv_cols; - + + if (filterop) + BLI_strncpy(template->filterop, filterop, sizeof(template->filterop)); + else + template->filterop[0] = 0; + if(newop) flag |= UI_ID_ADD_NEW; if(openop) @@ -512,26 +558,25 @@ static void ui_template_id(uiLayout *layout, bContext *C, PointerRNA *ptr, char */ if(template->idlb) { uiLayoutRow(layout, 1); - template_ID(C, layout, template, type, flag, newop, openop, unlinkop); + template_ID(C, layout, template, type, flag, newop, openop, unlinkop, filterop); } MEM_freeN(template); - } -void uiTemplateID(uiLayout *layout, bContext *C, PointerRNA *ptr, char *propname, char *newop, char *openop, char *unlinkop) +void uiTemplateID(uiLayout *layout, bContext *C, PointerRNA *ptr, char *propname, char *newop, char *openop, char *unlinkop, char *filterop) { - ui_template_id(layout, C, ptr, propname, newop, openop, unlinkop, UI_ID_BROWSE|UI_ID_RENAME|UI_ID_DELETE, 0, 0); + ui_template_id(layout, C, ptr, propname, newop, openop, unlinkop, filterop, UI_ID_BROWSE|UI_ID_RENAME|UI_ID_DELETE, 0, 0); } -void uiTemplateIDBrowse(uiLayout *layout, bContext *C, PointerRNA *ptr, char *propname, char *newop, char *openop, char *unlinkop) +void uiTemplateIDBrowse(uiLayout *layout, bContext *C, PointerRNA *ptr, char *propname, char *newop, char *openop, char *unlinkop, char *filterop) { - ui_template_id(layout, C, ptr, propname, newop, openop, unlinkop, UI_ID_BROWSE|UI_ID_RENAME, 0, 0); + ui_template_id(layout, C, ptr, propname, newop, openop, unlinkop, filterop, UI_ID_BROWSE|UI_ID_RENAME, 0, 0); } -void uiTemplateIDPreview(uiLayout *layout, bContext *C, PointerRNA *ptr, char *propname, char *newop, char *openop, char *unlinkop, int rows, int cols) +void uiTemplateIDPreview(uiLayout *layout, bContext *C, PointerRNA *ptr, char *propname, char *newop, char *openop, char *unlinkop, char *filterop, int rows, int cols) { - ui_template_id(layout, C, ptr, propname, newop, openop, unlinkop, UI_ID_BROWSE|UI_ID_RENAME|UI_ID_DELETE|UI_ID_PREVIEWS, rows, cols); + ui_template_id(layout, C, ptr, propname, newop, openop, unlinkop, filterop, UI_ID_BROWSE|UI_ID_RENAME|UI_ID_DELETE|UI_ID_PREVIEWS, rows, cols); } /************************ ID Chooser Template ***************************/ diff --git a/source/blender/editors/interface/resources.c b/source/blender/editors/interface/resources.c index cf47feb312b..bea8d3bd1f6 100644 --- a/source/blender/editors/interface/resources.c +++ b/source/blender/editors/interface/resources.c @@ -1463,7 +1463,7 @@ void init_userdef_do_versions(void) SETCOL(btheme->tv3d.lastsel_point, 0xff, 0xff, 0xff, 255); } } - if (G.main->versionfile <= 252 || (G.main->versionfile == 252 && G.main->subversionfile < 5)) { + if (G.main->versionfile < 252 || (G.main->versionfile == 252 && G.main->subversionfile < 5)) { bTheme *btheme; /* interface_widgets.c */ @@ -1521,7 +1521,14 @@ void init_userdef_do_versions(void) /* this timer uses U */ // XXX reset_autosave(); -} + /* GSOC Sculpt 2010 - Sanity check on Sculpt/Paint settings */ + if (U.sculpt_paint_unified_alpha == 0) + U.sculpt_paint_unified_alpha = 0.5f; + if (U.sculpt_paint_unified_unprojected_radius == 0) + U.sculpt_paint_unified_unprojected_radius = 0.125f; + if (U.sculpt_paint_unified_size == 0) + U.sculpt_paint_unified_size = 35; +} diff --git a/source/blender/editors/sculpt_paint/SConscript b/source/blender/editors/sculpt_paint/SConscript index 472ba361059..3d9a5144c93 100644 --- a/source/blender/editors/sculpt_paint/SConscript +++ b/source/blender/editors/sculpt_paint/SConscript @@ -3,6 +3,8 @@ Import ('env') sources = env.Glob('*.c') +defs = [] + incs = '../include ../../blenlib ../../blenkernel ../../makesdna ../../imbuf' incs += ' ../../windowmanager #/intern/guardedalloc #/extern/glew/include' incs += ' ../../render/extern/include' @@ -12,7 +14,11 @@ if env['OURPLATFORM'] == 'linux2': cflags='-pthread' incs += ' ../../../extern/binreloc/include' +if env['OURPLATFORM'] == 'linuxcross': + if env['WITH_BF_OPENMP']: + incs += ' ' + env['BF_OPENMP_INC'] + if env['OURPLATFORM'] in ('win32-vc', 'win32-mingw', 'linuxcross', 'win64-vc'): - incs += ' ' + env['BF_PTHREADS_INC'] + incs += ' ' + env['BF_PTHREADS_INC'] -env.BlenderLib ( 'bf_editors_sculpt_paint', sources, Split(incs), [], libtype=['core'], priority=[40] ) +env.BlenderLib ( 'bf_editors_sculpt_paint', sources, Split(incs), defines=defs, libtype=['core'], priority=[40] ) diff --git a/source/blender/editors/sculpt_paint/paint_image.c b/source/blender/editors/sculpt_paint/paint_image.c index 41908bbe388..45c0396855d 100644 --- a/source/blender/editors/sculpt_paint/paint_image.c +++ b/source/blender/editors/sculpt_paint/paint_image.c @@ -641,8 +641,8 @@ static int project_paint_PickColor(const ProjPaintState *ps, float pt[2], float } } else { - xi = (uv[0]*ibuf->x) + 0.5f; - yi = (uv[1]*ibuf->y) + 0.5f; + xi = (int)((uv[0]*ibuf->x) + 0.5f); + yi = (int)((uv[1]*ibuf->y) + 0.5f); //if (xi<0 || xi>=ibuf->x || yi<0 || yi>=ibuf->y) return 0; @@ -1053,15 +1053,15 @@ static void uv_image_outset(float (*orig_uv)[2], float (*outset_uv)[2], const fl * This is incorrect. Its already given radians but without it wont work. * need to look into a fix - campbell */ if (is_quad) { - a1 = shell_angle_to_dist(angle_normalized_v2v2(dir4, dir1) * (M_PI/180.0f)); - a2 = shell_angle_to_dist(angle_normalized_v2v2(dir1, dir2) * (M_PI/180.0f)); - a3 = shell_angle_to_dist(angle_normalized_v2v2(dir2, dir3) * (M_PI/180.0f)); - a4 = shell_angle_to_dist(angle_normalized_v2v2(dir3, dir4) * (M_PI/180.0f)); + a1 = shell_angle_to_dist(angle_normalized_v2v2(dir4, dir1) * ((float)M_PI/180.0f)); + a2 = shell_angle_to_dist(angle_normalized_v2v2(dir1, dir2) * ((float)M_PI/180.0f)); + a3 = shell_angle_to_dist(angle_normalized_v2v2(dir2, dir3) * ((float)M_PI/180.0f)); + a4 = shell_angle_to_dist(angle_normalized_v2v2(dir3, dir4) * ((float)M_PI/180.0f)); } else { - a1 = shell_angle_to_dist(angle_normalized_v2v2(dir3, dir1) * (M_PI/180.0f)); - a2 = shell_angle_to_dist(angle_normalized_v2v2(dir1, dir2) * (M_PI/180.0f)); - a3 = shell_angle_to_dist(angle_normalized_v2v2(dir2, dir3) * (M_PI/180.0f)); + a1 = shell_angle_to_dist(angle_normalized_v2v2(dir3, dir1) * ((float)M_PI/180.0f)); + a2 = shell_angle_to_dist(angle_normalized_v2v2(dir1, dir2) * ((float)M_PI/180.0f)); + a3 = shell_angle_to_dist(angle_normalized_v2v2(dir2, dir3) * ((float)M_PI/180.0f)); } if (is_quad) { @@ -1197,7 +1197,7 @@ static void screen_px_from_persp( w[2] *= wtot_inv; } else { - w[0] = w[1] = w[2] = 1.0/3.0; /* dummy values for zero area face */ + w[0] = w[1] = w[2] = 1.0f/3.0f; /* dummy values for zero area face */ } /* done re-weighting */ @@ -2513,11 +2513,11 @@ static void project_paint_face_init(const ProjPaintState *ps, const int thread_i w[0]=w[1]=w[2]= 0.0; if (side) { w[fidx1?fidx1-1:0] = fac; - w[fidx2?fidx2-1:0] = 1.0-fac; + w[fidx2?fidx2-1:0] = 1.0f-fac; } else { w[fidx1] = fac; - w[fidx2] = 1.0-fac; + w[fidx2] = 1.0f-fac; } #endif } @@ -2571,11 +2571,12 @@ static void project_paint_face_init(const ProjPaintState *ps, const int thread_i static void project_paint_bucket_bounds(const ProjPaintState *ps, const float min[2], const float max[2], int bucketMin[2], int bucketMax[2]) { /* divide by bucketWidth & bucketHeight so the bounds are offset in bucket grid units */ - bucketMin[0] = (int)(((float)(min[0] - ps->screenMin[0]) / ps->screen_width) * ps->buckets_x) + 0.5f; /* these offsets of 0.5 and 1.5 seem odd but they are correct */ - bucketMin[1] = (int)(((float)(min[1] - ps->screenMin[1]) / ps->screen_height) * ps->buckets_y) + 0.5f; + /* XXX: the offset of 0.5 is always truncated to zero and the offset of 1.5f is always truncated to 1, is this really correct?? - jwilkins */ + bucketMin[0] = (int)((int)(((float)(min[0] - ps->screenMin[0]) / ps->screen_width) * ps->buckets_x) + 0.5f); /* these offsets of 0.5 and 1.5 seem odd but they are correct */ + bucketMin[1] = (int)((int)(((float)(min[1] - ps->screenMin[1]) / ps->screen_height) * ps->buckets_y) + 0.5f); - bucketMax[0] = (int)(((float)(max[0] - ps->screenMin[0]) / ps->screen_width) * ps->buckets_x) + 1.5f; - bucketMax[1] = (int)(((float)(max[1] - ps->screenMin[1]) / ps->screen_height) * ps->buckets_y) + 1.5f; + bucketMax[0] = (int)((int)(((float)(max[0] - ps->screenMin[0]) / ps->screen_width) * ps->buckets_x) + 1.5f); + bucketMax[1] = (int)((int)(((float)(max[1] - ps->screenMin[1]) / ps->screen_height) * ps->buckets_y) + 1.5f); /* incase the rect is outside the mesh 2d bounds */ CLAMP(bucketMin[0], 0, ps->buckets_x); @@ -3029,19 +3030,19 @@ static void project_paint_begin(ProjPaintState *ps) if(ps->source==PROJ_SRC_VIEW) { #ifdef PROJ_DEBUG_WINCLIP - CLAMP(ps->screenMin[0], -ps->brush->size, ps->winx + ps->brush->size); - CLAMP(ps->screenMax[0], -ps->brush->size, ps->winx + ps->brush->size); + CLAMP(ps->screenMin[0], (float)(-ps->brush->size), (float)(ps->winx + ps->brush->size)); + CLAMP(ps->screenMax[0], (float)(-ps->brush->size), (float)(ps->winx + ps->brush->size)); - CLAMP(ps->screenMin[1], -ps->brush->size, ps->winy + ps->brush->size); - CLAMP(ps->screenMax[1], -ps->brush->size, ps->winy + ps->brush->size); + CLAMP(ps->screenMin[1], (float)(-ps->brush->size), (float)(ps->winy + ps->brush->size)); + CLAMP(ps->screenMax[1], (float)(-ps->brush->size), (float)(ps->winy + ps->brush->size)); #endif } else { /* reprojection, use bounds */ ps->screenMin[0]= 0; - ps->screenMax[0]= ps->winx; + ps->screenMax[0]= (float)(ps->winx); ps->screenMin[1]= 0; - ps->screenMax[1]= ps->winy; + ps->screenMax[1]= (float)(ps->winy); } /* only for convenience */ @@ -3497,7 +3498,7 @@ static int project_bucket_iter_next(ProjPaintState *ps, int *bucket_index, rctf project_bucket_bounds(ps, ps->context_bucket_x, ps->context_bucket_y, bucket_bounds); if ( (ps->source != PROJ_SRC_VIEW) || - project_bucket_isect_circle(ps->context_bucket_x, ps->context_bucket_y, mval, ps->brush->size * ps->brush->size, bucket_bounds) + project_bucket_isect_circle(ps->context_bucket_x, ps->context_bucket_y, mval, (float)(ps->brush->size * ps->brush->size), bucket_bounds) ) { *bucket_index = ps->context_bucket_x + (ps->context_bucket_y * ps->buckets_x); ps->context_bucket_x++; @@ -3545,7 +3546,7 @@ static void blend_color_mix(unsigned char *cp, const unsigned char *cp1, const u static void blend_color_mix_float(float *cp, const float *cp1, const float *cp2, const float fac) { - const float mfac= 1.0-fac; + const float mfac= 1.0f-fac; cp[0]= mfac*cp1[0] + fac*cp2[0]; cp[1]= mfac*cp1[1] + fac*cp2[1]; cp[2]= mfac*cp1[2] + fac*cp2[2]; @@ -3712,7 +3713,7 @@ static void *do_projectpaint_thread(void *ph_v) } /* avoid a square root with every dist comparison */ - brush_size_sqared = ps->brush->size * ps->brush->size; + brush_size_sqared = (float)(ps->brush->size * ps->brush->size); /* printf("brush bounds %d %d %d %d\n", bucketMin[0], bucketMin[1], bucketMax[0], bucketMax[1]); */ @@ -3771,7 +3772,7 @@ static void *do_projectpaint_thread(void *ph_v) falloff = 1.0f - falloff; falloff = 1.0f - (falloff * falloff); - mask_short = projPixel->mask * (ps->brush->alpha * falloff); + mask_short = (unsigned short)(projPixel->mask * (ps->brush->alpha * falloff)); if (mask_short > projPixel->mask_max) { mask = ((float)mask_short)/65535.0f; projPixel->mask_max = mask_short; @@ -3932,8 +3933,8 @@ static int project_paint_sub_stroke(ProjPaintState *ps, BrushPainter *painter, i /* Use mouse coords as floats for projection painting */ float pos[2]; - pos[0] = mval_i[0]; - pos[1] = mval_i[1]; + pos[0] = (float)(mval_i[0]); + pos[1] = (float)(mval_i[1]); // we may want to use this later // brush_painter_require_imbuf(painter, ((ibuf->rect_float)? 1: 0), 0, 0); @@ -4057,7 +4058,8 @@ static int imapaint_ibuf_add_if(ImBuf *ibuf, unsigned int x, unsigned int y, flo { float inrgb[3]; - if ((x >= ibuf->x) || (y >= ibuf->y)) { + // XXX: signed unsigned mismatch + if ((x >= (unsigned int)(ibuf->x)) || (y >= (unsigned int)(ibuf->y))) { if (torus) imapaint_ibuf_get_set_rgb(ibuf, x, y, 1, 0, inrgb); else return 0; } @@ -4611,8 +4613,8 @@ static void project_state_init(bContext *C, Object *ob, ProjPaintState *ps) ps->normal_angle_inner= ps->normal_angle= settings->imapaint.normal_angle; } - ps->normal_angle_inner *= M_PI_2 / 90; - ps->normal_angle *= M_PI_2 / 90; + ps->normal_angle_inner *= (float)(M_PI_2 / 90); + ps->normal_angle *= (float)(M_PI_2 / 90); ps->normal_angle_range = ps->normal_angle - ps->normal_angle_inner; if(ps->normal_angle_range <= 0.0f) @@ -4711,8 +4713,8 @@ static void paint_apply(bContext *C, wmOperator *op, PointerRNA *itemptr) int mouse[2], redraw; RNA_float_get_array(itemptr, "mouse", mousef); - mouse[0] = mousef[0]; - mouse[1] = mousef[1]; + mouse[0] = (int)(mousef[0]); + mouse[1] = (int)(mousef[1]); time= RNA_float_get(itemptr, "time"); pressure= RNA_float_get(itemptr, "pressure"); @@ -4832,8 +4834,8 @@ static void paint_apply_event(bContext *C, wmOperator *op, wmEvent *event) /* fill in stroke */ RNA_collection_add(op->ptr, "stroke", &itemptr); - mousef[0] = mouse[0]; - mousef[1] = mouse[1]; + mousef[0] = (float)(mouse[0]); + mousef[1] = (float)(mouse[1]); RNA_float_set_array(&itemptr, "mouse", mousef); RNA_float_set(&itemptr, "time", (float)(time - pop->starttime)); RNA_float_set(&itemptr, "pressure", pressure); @@ -4950,7 +4952,7 @@ static void brush_drawcursor(bContext *C, int x, int y, void *customdata) glColor4ub(255, 255, 255, 128); glEnable( GL_LINE_SMOOTH ); glEnable(GL_BLEND); - glutil_draw_lined_arc(0.0, M_PI*2.0, brush->size*0.5f, 40); + glutil_draw_lined_arc(0, (float)(M_PI*2.0), brush->size*0.5f, 40); glDisable(GL_BLEND); glDisable( GL_LINE_SMOOTH ); @@ -4977,7 +4979,7 @@ static int paint_radial_control_invoke(bContext *C, wmOperator *op, wmEvent *eve ToolSettings *ts = CTX_data_scene(C)->toolsettings; get_imapaint_zoom(C, &zoom, &zoom); toggle_paint_cursor(C, !ts->imapaint.paintcursor); - brush_radial_control_invoke(op, paint_brush(&ts->imapaint.paint), 0.5 * zoom); + brush_radial_control_invoke(op, paint_brush(&ts->imapaint.paint), 0.5f * zoom); return WM_radial_control_invoke(C, op, event); } @@ -4997,7 +4999,7 @@ static int paint_radial_control_exec(bContext *C, wmOperator *op) int ret; char str[256]; get_imapaint_zoom(C, &zoom, &zoom); - ret = brush_radial_control_exec(op, brush, 2.0 / zoom); + ret = brush_radial_control_exec(op, brush, 2.0f / zoom); WM_radial_control_string(op, str, 256); WM_event_add_notifier(C, NC_BRUSH|NA_EDITED, brush); diff --git a/source/blender/editors/sculpt_paint/paint_intern.h b/source/blender/editors/sculpt_paint/paint_intern.h index b25eec70311..0ae7c7fac19 100644 --- a/source/blender/editors/sculpt_paint/paint_intern.h +++ b/source/blender/editors/sculpt_paint/paint_intern.h @@ -110,6 +110,13 @@ void PAINT_OT_face_select_all(struct wmOperatorType *ot); int facemask_paint_poll(struct bContext *C); +/* stroke operator */ +typedef enum wmBrushStrokeMode { + WM_BRUSHSTROKE_NORMAL, + WM_BRUSHSTROKE_INVERT, + WM_BRUSHSTROKE_SMOOTH, +} wmBrushStrokeMode; + /* paint_undo.c */ typedef void (*UndoRestoreCb)(struct bContext *C, struct ListBase *lb); typedef void (*UndoFreeCb)(struct ListBase *lb); diff --git a/source/blender/editors/sculpt_paint/paint_ops.c b/source/blender/editors/sculpt_paint/paint_ops.c index 5470ac83370..5d52b0a3272 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.c +++ b/source/blender/editors/sculpt_paint/paint_ops.c @@ -39,28 +39,25 @@ #include "sculpt_intern.h" #include +//#include /* Brush operators */ static int brush_add_exec(bContext *C, wmOperator *op) { /*int type = RNA_enum_get(op->ptr, "type");*/ - Brush *br = NULL; + Paint *paint = paint_get_active(CTX_data_scene(C)); + Brush *br = paint_brush(paint); - br = add_brush("Brush"); + if (br) + br = copy_brush(br); + else + br = add_brush("Brush"); - if(br) - paint_brush_set(paint_get_active(CTX_data_scene(C)), br); + paint_brush_set(paint_get_active(CTX_data_scene(C)), br); return OPERATOR_FINISHED; } -static EnumPropertyItem brush_type_items[] = { - {OB_MODE_SCULPT, "SCULPT", ICON_SCULPTMODE_HLT, "Sculpt", ""}, - {OB_MODE_VERTEX_PAINT, "VERTEX_PAINT", ICON_VPAINT_HLT, "Vertex Paint", ""}, - {OB_MODE_WEIGHT_PAINT, "WEIGHT_PAINT", ICON_WPAINT_HLT, "Weight Paint", ""}, - {OB_MODE_TEXTURE_PAINT, "TEXTURE_PAINT", ICON_TPAINT_HLT, "Texture Paint", ""}, - {0, NULL, 0, NULL, NULL}}; - void BRUSH_OT_add(wmOperatorType *ot) { /* identifiers */ @@ -72,9 +69,43 @@ void BRUSH_OT_add(wmOperatorType *ot) ot->exec= brush_add_exec; /* flags */ - ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; + ot->flag= OPTYPE_UNDO; +} + + +static int brush_scale_size_exec(bContext *C, wmOperator *op) +{ + /*int type = RNA_enum_get(op->ptr, "type");*/ + Paint *paint = paint_get_active(CTX_data_scene(C)); + Brush *br = paint_brush(paint); + float factor = RNA_float_get(op->ptr, "scalar"); + + if (br) { + if (U.sculpt_paint_settings & SCULPT_PAINT_USE_UNIFIED_SIZE) { + U.sculpt_paint_unified_size *= factor; + } + else { + br->size *= factor; + } + } + + return OPERATOR_FINISHED; +} + +void BRUSH_OT_scale_size(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Scale Sculpt/Paint Brush Size"; + ot->description= "Change brush size by a scalar"; + ot->idname= "BRUSH_OT_scale_size"; + + /* api callbacks */ + ot->exec= brush_scale_size_exec; + + /* flags */ + ot->flag= OPTYPE_UNDO; - RNA_def_enum(ot->srna, "type", brush_type_items, OB_MODE_VERTEX_PAINT, "Type", "Which paint mode to create the brush for."); + RNA_def_float(ot->srna, "scalar", 1, 0, 2, "Scalar", "Factor to scale brush size by", 0, 2); } static int vertex_color_set_exec(bContext *C, wmOperator *op) @@ -108,8 +139,10 @@ void ED_operatortypes_paint(void) { /* brush */ WM_operatortype_append(BRUSH_OT_add); + WM_operatortype_append(BRUSH_OT_scale_size); WM_operatortype_append(BRUSH_OT_curve_preset); + /* image */ WM_operatortype_append(PAINT_OT_texture_paint_toggle); WM_operatortype_append(PAINT_OT_texture_paint_radial_control); @@ -141,6 +174,7 @@ void ED_operatortypes_paint(void) WM_operatortype_append(PAINT_OT_face_select_all); } + static void ed_keymap_paint_brush_switch(wmKeyMap *keymap, const char *path) { wmKeyMapItem *kmi; @@ -180,15 +214,13 @@ static void ed_keymap_paint_brush_switch(wmKeyMap *keymap, const char *path) static void ed_keymap_paint_brush_size(wmKeyMap *keymap, const char *path) { wmKeyMapItem *kmi; - - kmi= WM_keymap_add_item(keymap, "WM_OT_context_scale_int", LEFTBRACKETKEY, KM_PRESS, 0, 0); - RNA_string_set(kmi->ptr, "data_path", path); - RNA_float_set(kmi->ptr, "value", 0.9); - - kmi= WM_keymap_add_item(keymap, "WM_OT_context_scale_int", RIGHTBRACKETKEY, KM_PRESS, 0, 0); - RNA_string_set(kmi->ptr, "data_path", path); - RNA_float_set(kmi->ptr, "value", 10.0/9.0); // 1.1111.... -} + + kmi= WM_keymap_add_item(keymap, "BRUSH_OT_scale_size", LEFTBRACKETKEY, KM_PRESS, 0, 0); + RNA_float_set(kmi->ptr, "scalar", 0.9); + + kmi= WM_keymap_add_item(keymap, "BRUSH_OT_scale_size", RIGHTBRACKETKEY, KM_PRESS, 0, 0); + RNA_float_set(kmi->ptr, "scalar", 10.0/9.0); // 1.1111.... +} void ED_keymap_paint(wmKeyConfig *keyconf) { @@ -200,12 +232,15 @@ void ED_keymap_paint(wmKeyConfig *keyconf) keymap= WM_keymap_find(keyconf, "Sculpt", 0, 0); keymap->poll= sculpt_poll; - RNA_enum_set(WM_keymap_add_item(keymap, "SCULPT_OT_radial_control", FKEY, KM_PRESS, 0, 0)->ptr, "mode", WM_RADIALCONTROL_SIZE); + RNA_enum_set(WM_keymap_add_item(keymap, "SCULPT_OT_radial_control", FKEY, KM_PRESS, 0, 0)->ptr, "mode", WM_RADIALCONTROL_SIZE); RNA_enum_set(WM_keymap_add_item(keymap, "SCULPT_OT_radial_control", FKEY, KM_PRESS, KM_SHIFT, 0)->ptr, "mode", WM_RADIALCONTROL_STRENGTH); - RNA_enum_set(WM_keymap_add_item(keymap, "SCULPT_OT_radial_control", FKEY, KM_PRESS, KM_CTRL, 0)->ptr, "mode", WM_RADIALCONTROL_ANGLE); + RNA_enum_set(WM_keymap_add_item(keymap, "SCULPT_OT_radial_control", FKEY, KM_PRESS, KM_CTRL, 0)->ptr, "mode", WM_RADIALCONTROL_ANGLE); - WM_keymap_add_item(keymap, "SCULPT_OT_brush_stroke", LEFTMOUSE, KM_PRESS, 0, 0); - WM_keymap_add_item(keymap, "SCULPT_OT_brush_stroke", LEFTMOUSE, KM_PRESS, KM_SHIFT, 0); + RNA_enum_set(WM_keymap_add_item(keymap, "SCULPT_OT_brush_stroke", LEFTMOUSE, KM_PRESS, 0, 0)->ptr, "mode", WM_BRUSHSTROKE_NORMAL); + RNA_enum_set(WM_keymap_add_item(keymap, "SCULPT_OT_brush_stroke", LEFTMOUSE, KM_PRESS, KM_CTRL, 0)->ptr, "mode", WM_BRUSHSTROKE_INVERT); + RNA_enum_set(WM_keymap_add_item(keymap, "SCULPT_OT_brush_stroke", LEFTMOUSE, KM_PRESS, KM_SHIFT, 0)->ptr, "mode", WM_BRUSHSTROKE_SMOOTH); + + //stroke_mode_modal_keymap(keyconf); for(i=0; i<=5; i++) RNA_int_set(WM_keymap_add_item(keymap, "OBJECT_OT_subdivision_set", ZEROKEY+i, KM_PRESS, KM_CTRL, 0)->ptr, "level", i); @@ -221,7 +256,8 @@ void ED_keymap_paint(wmKeyConfig *keyconf) ed_keymap_paint_brush_switch(keymap, "tool_settings.sculpt.active_brush_index"); ed_keymap_paint_brush_size(keymap, "tool_settings.sculpt.brush.size"); - + + /* */ kmi = WM_keymap_add_item(keymap, "WM_OT_context_toggle", AKEY, KM_PRESS, 0, 0); RNA_string_set(kmi->ptr, "data_path", "tool_settings.sculpt.brush.use_anchor"); @@ -254,11 +290,10 @@ void ED_keymap_paint(wmKeyConfig *keyconf) kmi = WM_keymap_add_item(keymap, "WM_OT_context_set_enum", LKEY, KM_PRESS, 0, 0); RNA_string_set(kmi->ptr, "data_path", "tool_settings.sculpt.active_brush_name"); RNA_string_set(kmi->ptr, "value", "Layer"); - + kmi = WM_keymap_add_item(keymap, "WM_OT_context_set_enum", TKEY, KM_PRESS, KM_SHIFT, 0); // was just T in 2.4x RNA_string_set(kmi->ptr, "data_path", "tool_settings.sculpt.active_brush_name"); RNA_string_set(kmi->ptr, "value", "Flatten"); - /* Vertex Paint mode */ keymap= WM_keymap_find(keyconf, "Vertex Paint", 0, 0); diff --git a/source/blender/editors/sculpt_paint/paint_stroke.c b/source/blender/editors/sculpt_paint/paint_stroke.c index 441464c5743..e373a254c3e 100644 --- a/source/blender/editors/sculpt_paint/paint_stroke.c +++ b/source/blender/editors/sculpt_paint/paint_stroke.c @@ -20,7 +20,7 @@ * * The Original Code is: all of this file. * - * Contributor(s): none yet. + * Contributor(s): Jason Wilkins, Tom Musgrove. * * ***** END GPL LICENSE BLOCK ***** * @@ -35,6 +35,7 @@ #include "BKE_context.h" #include "BKE_paint.h" +#include "BKE_brush.h" #include "WM_api.h" #include "WM_types.h" @@ -49,6 +50,9 @@ #include "ED_view3d.h" #include "paint_intern.h" +#include "sculpt_intern.h" // XXX, for expedience in getting this working, refactor later (or this just shows that this needs unification) + +#include "BKE_image.h" #include #include @@ -96,54 +100,748 @@ static void paint_draw_smooth_stroke(bContext *C, int x, int y, void *customdata glDisable(GL_LINE_SMOOTH); } -static void paint_draw_cursor(bContext *C, int x, int y, void *customdata) +#if 0 + +// grid texture for testing + +#define GRID_WIDTH 8 +#define GRID_LENGTH 8 + +#define W (0xFFFFFFFF) +#define G (0x00888888) +#define E (0xE1E1E1E1) +#define C (0xC3C3C3C3) +#define O (0xB4B4B4B4) +#define Q (0xA9A9A9A9) + +static unsigned grid_texture0[256] = +{ + W,W,W,W,W,W,W,W,W,W,W,W,W,W,W,W, + W,G,G,G,G,G,G,G,G,G,G,G,G,G,G,W, + W,G,G,G,G,G,G,G,G,G,G,G,G,G,G,W, + W,G,G,G,G,G,G,G,G,G,G,G,G,G,G,W, + W,G,G,G,G,G,G,G,G,G,G,G,G,G,G,W, + W,G,G,G,G,G,G,G,G,G,G,G,G,G,G,W, + W,G,G,G,G,G,G,G,G,G,G,G,G,G,G,W, + W,G,G,G,G,G,G,G,G,G,G,G,G,G,G,W, + W,G,G,G,G,G,G,G,G,G,G,G,G,G,G,W, + W,G,G,G,G,G,G,G,G,G,G,G,G,G,G,W, + W,G,G,G,G,G,G,G,G,G,G,G,G,G,G,W, + W,G,G,G,G,G,G,G,G,G,G,G,G,G,G,W, + W,G,G,G,G,G,G,G,G,G,G,G,G,G,G,W, + W,G,G,G,G,G,G,G,G,G,G,G,G,G,G,W, + W,G,G,G,G,G,G,G,G,G,G,G,G,G,G,W, + W,W,W,W,W,W,W,W,W,W,W,W,W,W,W,W, +}; + +static unsigned grid_texture1[64] = { - Paint *paint = paint_get_active(CTX_data_scene(C)); - Brush *brush = paint_brush(paint); + C,C,C,C,C,C,C,C, + C,G,G,G,G,G,G,C, + C,G,G,G,G,G,G,C, + C,G,G,G,G,G,G,C, + C,G,G,G,G,G,G,C, + C,G,G,G,G,G,G,C, + C,G,G,G,G,G,G,C, + C,C,C,C,C,C,C,C, +}; + +static unsigned grid_texture2[16] = +{ + O,O,O,O, + O,G,G,O, + O,G,G,O, + O,O,O,O, +}; - if(!(paint->flags & PAINT_SHOW_BRUSH)) - return; +static unsigned grid_texture3[4] = +{ + Q,Q, + Q,Q, +}; - glColor4ubv(paint_get_active(CTX_data_scene(C))->paint_cursor_col); - glEnable(GL_LINE_SMOOTH); - glEnable(GL_BLEND); +static unsigned grid_texture4[1] = +{ + Q, +}; - glTranslatef((float)x, (float)y, 0.0f); - glutil_draw_lined_arc(0.0, M_PI*2.0, brush->size, 40); - glTranslatef((float)-x, (float)-y, 0.0f); +#undef W +#undef G +#undef E +#undef C +#undef O +#undef Q - glDisable(GL_BLEND); - glDisable(GL_LINE_SMOOTH); +static void load_grid() +{ + static GLuint overlay_texture; + + if (!overlay_texture) { + //GLfloat largest_supported_anisotropy; + + glGenTextures(1, &overlay_texture); + glBindTexture(GL_TEXTURE_2D, overlay_texture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, grid_texture0); + glTexImage2D(GL_TEXTURE_2D, 1, GL_RGB, 8, 8, 0, GL_RGBA, GL_UNSIGNED_BYTE, grid_texture1); + glTexImage2D(GL_TEXTURE_2D, 2, GL_RGB, 4, 4, 0, GL_RGBA, GL_UNSIGNED_BYTE, grid_texture2); + glTexImage2D(GL_TEXTURE_2D, 3, GL_RGB, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, grid_texture3); + glTexImage2D(GL_TEXTURE_2D, 4, GL_RGB, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, grid_texture4); + glEnable(GL_TEXTURE_2D); + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_LINEAR); + + //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); + //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); + + //glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &largest_supported_anisotropy); + //glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, largest_supported_anisotropy); + } +} + +#endif + +extern float get_tex_pixel(Brush* br, float u, float v); + +typedef struct Snapshot { + float size[3]; + float ofs[3]; + float rot; + int brush_size; + int winx; + int winy; + int brush_map_mode; + int curve_changed_timestamp; +} Snapshot; + +static int same_snap(Snapshot* snap, Brush* brush, ViewContext* vc) +{ + MTex* mtex = &brush->mtex; + + return + (mtex->tex && + mtex->ofs[0] == snap->ofs[0] && + mtex->ofs[1] == snap->ofs[1] && + mtex->ofs[2] == snap->ofs[2] && + mtex->size[0] == snap->size[0] && + mtex->size[1] == snap->size[1] && + mtex->size[2] == snap->size[2] && + mtex->rot == snap->rot) && + ((mtex->brush_map_mode == MTEX_MAP_MODE_FIXED && sculpt_get_brush_size(brush) <= snap->brush_size) || (sculpt_get_brush_size(brush) == snap->brush_size)) && // make brush smaller shouldn't cause a resample + mtex->brush_map_mode == snap->brush_map_mode && + vc->ar->winx == snap->winx && + vc->ar->winy == snap->winy; +} + +static void make_snap(Snapshot* snap, Brush* brush, ViewContext* vc) +{ + if (brush->mtex.tex) { + snap->brush_map_mode = brush->mtex.brush_map_mode; + copy_v3_v3(snap->ofs, brush->mtex.ofs); + copy_v3_v3(snap->size, brush->mtex.size); + snap->rot = brush->mtex.rot; + } + else { + snap->brush_map_mode = -1; + snap->ofs[0]= snap->ofs[0]= snap->ofs[0]= -1; + snap->size[0]= snap->size[0]= snap->size[0]= -1; + snap->rot = -1; + } + + snap->brush_size = sculpt_get_brush_size(brush); + snap->winx = vc->ar->winx; + snap->winy = vc->ar->winy; +} + +int load_tex(Sculpt *sd, Brush* br, ViewContext* vc) +{ + static GLuint overlay_texture = 0; + static int init = 0; + static int tex_changed_timestamp = -1; + static int curve_changed_timestamp = -1; + static Snapshot snap; + static int old_size = -1; + + GLubyte* buffer = 0; + + int size; + int j; + int refresh; + + if (br->mtex.brush_map_mode == MTEX_MAP_MODE_TILED && !br->mtex.tex) return 0; + + refresh = + !overlay_texture || + (br->mtex.tex && + (!br->mtex.tex->preview || + br->mtex.tex->preview->changed_timestamp[0] != tex_changed_timestamp)) || + !br->curve || + br->curve->changed_timestamp != curve_changed_timestamp || + !same_snap(&snap, br, vc); + + if (refresh) { + if (br->mtex.tex && br->mtex.tex->preview) + tex_changed_timestamp = br->mtex.tex->preview->changed_timestamp[0]; + + if (br->curve) + curve_changed_timestamp = br->curve->changed_timestamp; + + make_snap(&snap, br, vc); + + if (br->mtex.brush_map_mode == MTEX_MAP_MODE_FIXED) { + int s = sculpt_get_brush_size(br); + int r = 1; + + for (s >>= 1; s > 0; s >>= 1) + r++; + + size = (1<flags & SCULPT_USE_OPENMP) + for (j= 0; j < size; j++) { + int i; + float y; + float len; + + for (i= 0; i < size; i++) { + + // largely duplicated from tex_strength + + const float rotation = -br->mtex.rot; + float diameter = sculpt_get_brush_size(br); + int index = j*size + i; + float x; + float avg; + + x = (float)i/size; + y = (float)j/size; + + x -= 0.5f; + y -= 0.5f; + + if (br->mtex.brush_map_mode == MTEX_MAP_MODE_TILED) { + x *= vc->ar->winx / diameter; + y *= vc->ar->winy / diameter; + } + else { + x *= 2; + y *= 2; + } + + len = sqrtf(x*x + y*y); + + if ((br->mtex.brush_map_mode == MTEX_MAP_MODE_TILED) || len <= 1) { + /* it is probably worth optimizing for those cases where + the texture is not rotated by skipping the calls to + atan2, sqrtf, sin, and cos. */ + if (br->mtex.tex && (rotation > 0.001 || rotation < -0.001)) { + const float angle = atan2(y, x) + rotation; + + x = len * cos(angle); + y = len * sin(angle); + } + + x *= br->mtex.size[0]; + y *= br->mtex.size[1]; + + x += br->mtex.ofs[0]; + y += br->mtex.ofs[1]; + + avg = br->mtex.tex ? get_tex_pixel(br, x, y) : 1; + + avg += br->texture_sample_bias; + + if (br->mtex.brush_map_mode == MTEX_MAP_MODE_FIXED) + avg *= brush_curve_strength(br, len, 1); /* Falloff curve */ + + buffer[index] = (GLubyte)(255*avg); + } + else { + buffer[index] = 0; + } + } + } + + if (!overlay_texture) + glGenTextures(1, &overlay_texture); + } + else { + size= old_size; + } + + glBindTexture(GL_TEXTURE_2D, overlay_texture); + + if (refresh) { + if (!init) { + glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, size, size, 0, GL_ALPHA, GL_UNSIGNED_BYTE, buffer); + init = 1; + } + else { + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, size, size, GL_ALPHA, GL_UNSIGNED_BYTE, buffer); + } + + if (buffer) + MEM_freeN(buffer); + } + + glEnable(GL_TEXTURE_2D); + + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + if (br->mtex.brush_map_mode == MTEX_MAP_MODE_FIXED) { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); + } + + return 1; +} + +/* Convert a point in model coordinates to 2D screen coordinates. */ +// XXX duplicated from sculpt.c, deal with this later. +static void projectf(bglMats *mats, const float v[3], float p[2]) +{ + double ux, uy, uz; + + gluProject(v[0],v[1],v[2], mats->modelview, mats->projection, + (GLint *)mats->viewport, &ux, &uy, &uz); + p[0]= ux; + p[1]= uy; +} + +static int project_brush_radius(RegionView3D* rv3d, float radius, float location[3], bglMats* mats) +{ + float view[3], nonortho[3], ortho[3], offset[3], p1[2], p2[2]; + + viewvector(rv3d, location, view); + + // create a vector that is not orthogonal to view + + if (fabsf(view[0]) < 0.1) { + nonortho[0] = view[0] + 1; + nonortho[1] = view[1]; + nonortho[2] = view[2]; + } + else if (fabsf(view[1]) < 0.1) { + nonortho[0] = view[0]; + nonortho[1] = view[1] + 1; + nonortho[2] = view[2]; + } + else { + nonortho[0] = view[0]; + nonortho[1] = view[1]; + nonortho[2] = view[2] + 1; + } + + // get a vector in the plane of the view + cross_v3_v3v3(ortho, nonortho, view); + normalize_v3(ortho); + + // make a point on the surface of the brush tagent to the view + mul_v3_fl(ortho, radius); + add_v3_v3v3(offset, location, ortho); + + // project the center of the brush, and the tagent point to the view onto the screen + projectf(mats, location, p1); + projectf(mats, offset, p2); + + // the distance between these points is the size of the projected brush in pixels + return len_v2v2(p1, p2); +} + +int sculpt_get_brush_geometry(bContext* C, int x, int y, int* pixel_radius, float location[3], float modelview[16], float projection[16], int viewport[4]) +{ + struct PaintStroke *stroke; + float window[2]; + int hit; + + stroke = paint_stroke_new(C, NULL, NULL, NULL, NULL); + + window[0] = x + stroke->vc.ar->winrct.xmin; + window[1] = y + stroke->vc.ar->winrct.ymin; + + memcpy(modelview, stroke->vc.rv3d->viewmat, sizeof(float[16])); + memcpy(projection, stroke->vc.rv3d->winmat, sizeof(float[16])); + memcpy(viewport, stroke->mats.viewport, sizeof(int[4])); + + if (stroke->vc.obact->sculpt && stroke->vc.obact->sculpt->pbvh && sculpt_stroke_get_location(C, stroke, location, window)) { + *pixel_radius = project_brush_radius(stroke->vc.rv3d, sculpt_get_brush_unprojected_radius(stroke->brush), location, &stroke->mats); + + if (*pixel_radius == 0) + *pixel_radius = sculpt_get_brush_size(stroke->brush); + + mul_m4_v3(stroke->vc.obact->sculpt->ob->obmat, location); + + hit = 1; + } + else { + Sculpt* sd = CTX_data_tool_settings(C)->sculpt; + Brush* brush = paint_brush(&sd->paint); + + *pixel_radius = sculpt_get_brush_size(brush); + hit = 0; + } + + paint_stroke_free(stroke); + + return hit; +} + +// XXX duplicated from sculpt.c +float unproject_brush_radius(Object *ob, ViewContext *vc, float center[3], float offset) +{ + float delta[3], scale, loc[3]; + + mul_v3_m4v3(loc, ob->obmat, center); + + initgrabz(vc->rv3d, loc[0], loc[1], loc[2]); + window_to_3d_delta(vc->ar, delta, offset, 0); + + scale= fabsf(mat4_to_scale(ob->obmat)); + scale= (scale == 0.0f)? 1.0f: scale; + + return len_v3(delta)/scale; +} + +// XXX paint cursor now does a lot of the same work that is needed during a sculpt stroke +// problem: all this stuff was not intended to be used at this point, so things feel a +// bit hacked. I've put lots of stuff in Brush that probably better goes in Paint +// Functions should be refactored so that they can be used between sculpt.c and +// paint_stroke.c clearly and optimally and the lines of communication between the +// two modules should be more clearly defined. +static void paint_draw_cursor(bContext *C, int x, int y, void *unused) +{ + ViewContext vc; + + (void)unused; + + view3d_set_viewcontext(C, &vc); + + if (vc.obact->sculpt) { + Paint *paint = paint_get_active(CTX_data_scene(C)); + Sculpt *sd = CTX_data_tool_settings(C)->sculpt; + Brush *brush = paint_brush(paint); + + int pixel_radius, viewport[4]; + float location[3], modelview[16], projection[16]; + + int hit; + + int flip; + int sign; + + float* col; + float alpha; + + float visual_strength = sculpt_get_brush_alpha(brush)*sculpt_get_brush_alpha(brush); + + const float min_alpha = 0.20f; + const float max_alpha = 0.80f; + + { + const float u = 0.5f; + const float v = 1 - u; + const float r = 20; + + const float dx = sd->last_x - x; + const float dy = sd->last_y - y; + + if (dx*dx + dy*dy >= r*r) { + sd->last_angle = atan2(dx, dy); + + sd->last_x = u*sd->last_x + v*x; + sd->last_y = u*sd->last_y + v*y; + } + } + + if(!sculpt_get_lock_brush_size(brush) && !(paint->flags & PAINT_SHOW_BRUSH)) + return; + + hit = sculpt_get_brush_geometry(C, x, y, &pixel_radius, location, modelview, projection, viewport); + + if (sculpt_get_lock_brush_size(brush)) + sculpt_set_brush_size(brush, pixel_radius); + + // XXX: no way currently to know state of pen flip or invert key modifier without starting a stroke + flip = 1; + + sign = flip * ((brush->flag & BRUSH_DIR_IN)? -1 : 1); + + if (sign < 0 && ELEM4(brush->sculpt_tool, SCULPT_TOOL_DRAW, SCULPT_TOOL_INFLATE, SCULPT_TOOL_CLAY, SCULPT_TOOL_PINCH)) + col = brush->sub_col; + else + col = brush->add_col; + + alpha = (paint->flags & PAINT_SHOW_BRUSH_ON_SURFACE) ? min_alpha + (visual_strength*(max_alpha-min_alpha)) : 0.50f; + + if (ELEM(brush->mtex.brush_map_mode, MTEX_MAP_MODE_FIXED, MTEX_MAP_MODE_TILED) && brush->flag & BRUSH_TEXTURE_OVERLAY) { + glPushAttrib( + GL_COLOR_BUFFER_BIT| + GL_CURRENT_BIT| + GL_DEPTH_BUFFER_BIT| + GL_ENABLE_BIT| + GL_LINE_BIT| + GL_POLYGON_BIT| + GL_STENCIL_BUFFER_BIT| + GL_TRANSFORM_BIT| + GL_VIEWPORT_BIT| + GL_TEXTURE_BIT); + + if (load_tex(sd, brush, &vc)) { + glEnable(GL_BLEND); + + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + glDepthMask(GL_FALSE); + glDepthFunc(GL_ALWAYS); + + glMatrixMode(GL_TEXTURE); + glPushMatrix(); + glLoadIdentity(); + + if (brush->mtex.brush_map_mode == MTEX_MAP_MODE_FIXED) { + glTranslatef(0.5f, 0.5f, 0); + + if (brush->flag & BRUSH_RAKE) { + glRotatef(sd->last_angle*(float)(180.0/M_PI), 0, 0, 1); + } + else { + glRotatef(sd->special_rotation*(float)(180.0/M_PI), 0, 0, 1); + } + + glTranslatef(-0.5f, -0.5f, 0); + + if (sd->draw_pressure && (brush->flag & BRUSH_SIZE_PRESSURE)) { + glTranslatef(0.5f, 0.5f, 0); + glScalef(1.0f/sd->pressure_value, 1.0f/sd->pressure_value, 1); + glTranslatef(-0.5f, -0.5f, 0); + } + } + + glColor4f( + U.sculpt_paint_overlay_col[0], + U.sculpt_paint_overlay_col[1], + U.sculpt_paint_overlay_col[2], + brush->texture_overlay_alpha / 100.0f); + + glBegin(GL_QUADS); + if (brush->mtex.brush_map_mode == MTEX_MAP_MODE_FIXED) { + if (sd->draw_anchored) { + glTexCoord2f(0, 0); + glVertex2f(sd->anchored_initial_mouse[0]-sd->anchored_size - vc.ar->winrct.xmin, sd->anchored_initial_mouse[1]-sd->anchored_size - vc.ar->winrct.ymin); + + glTexCoord2f(1, 0); + glVertex2f(sd->anchored_initial_mouse[0]+sd->anchored_size - vc.ar->winrct.xmin, sd->anchored_initial_mouse[1]-sd->anchored_size - vc.ar->winrct.ymin); + + glTexCoord2f(1, 1); + glVertex2f(sd->anchored_initial_mouse[0]+sd->anchored_size - vc.ar->winrct.xmin, sd->anchored_initial_mouse[1]+sd->anchored_size - vc.ar->winrct.ymin); + + glTexCoord2f(0, 1); + glVertex2f(sd->anchored_initial_mouse[0]-sd->anchored_size - vc.ar->winrct.xmin, sd->anchored_initial_mouse[1]+sd->anchored_size - vc.ar->winrct.ymin); + } + else { + glTexCoord2f(0, 0); + glVertex2f((float)x-sculpt_get_brush_size(brush), (float)y-sculpt_get_brush_size(brush)); + + glTexCoord2f(1, 0); + glVertex2f((float)x+sculpt_get_brush_size(brush), (float)y-sculpt_get_brush_size(brush)); + + glTexCoord2f(1, 1); + glVertex2f((float)x+sculpt_get_brush_size(brush), (float)y+sculpt_get_brush_size(brush)); + + glTexCoord2f(0, 1); + glVertex2f((float)x-sculpt_get_brush_size(brush), (float)y+sculpt_get_brush_size(brush)); + } + } + else { + glTexCoord2f(0, 0); + glVertex2f(0, 0); + + glTexCoord2f(1, 0); + glVertex2f(viewport[2], 0); + + glTexCoord2f(1, 1); + glVertex2f(viewport[2], viewport[3]); + + glTexCoord2f(0, 1); + glVertex2f(0, viewport[3]); + } + glEnd(); + + glPopMatrix(); + } + + glPopAttrib(); + } + + if (hit) { + float unprojected_radius; + + // XXX duplicated from brush_strength & paint_stroke_add_step, refactor later + //wmEvent* event = CTX_wm_window(C)->eventstate; + + if (sd->draw_pressure && (brush->flag & BRUSH_ALPHA_PRESSURE)) + visual_strength *= sd->pressure_value; + + // don't show effect of strength past the soft limit + if (visual_strength > 1) visual_strength = 1; + + if (sd->draw_anchored) { + unprojected_radius = unproject_brush_radius(CTX_data_active_object(C), &vc, location, sd->anchored_size); + } + else { + if (brush->flag & BRUSH_ANCHORED) + unprojected_radius = unproject_brush_radius(CTX_data_active_object(C), &vc, location, 8); + else + unprojected_radius = unproject_brush_radius(CTX_data_active_object(C), &vc, location, sculpt_get_brush_size(brush)); + } + + if (sd->draw_pressure && (brush->flag & BRUSH_SIZE_PRESSURE)) + unprojected_radius *= sd->pressure_value; + + if (!sculpt_get_lock_brush_size(brush)) + sculpt_set_brush_unprojected_radius(brush, unprojected_radius); + + if(!(paint->flags & PAINT_SHOW_BRUSH)) + return; + + } + + glPushAttrib( + GL_COLOR_BUFFER_BIT| + GL_CURRENT_BIT| + GL_DEPTH_BUFFER_BIT| + GL_ENABLE_BIT| + GL_LINE_BIT| + GL_POLYGON_BIT| + GL_STENCIL_BUFFER_BIT| + GL_TRANSFORM_BIT| + GL_VIEWPORT_BIT| + GL_TEXTURE_BIT); + + glColor4f(col[0], col[1], col[2], alpha); + + glEnable(GL_BLEND); + + glEnable(GL_LINE_SMOOTH); + + if (sd->draw_anchored) { + glTranslatef(sd->anchored_initial_mouse[0] - vc.ar->winrct.xmin, sd->anchored_initial_mouse[1] - vc.ar->winrct.ymin, 0.0f); + glutil_draw_lined_arc(0.0, M_PI*2.0, sd->anchored_size, 40); + glTranslatef(-sd->anchored_initial_mouse[0] + vc.ar->winrct.xmin, -sd->anchored_initial_mouse[1] + vc.ar->winrct.xmin, 0.0f); + } + else { + glTranslatef((float)x, (float)y, 0.0f); + glutil_draw_lined_arc(0.0, M_PI*2.0, sculpt_get_brush_size(brush), 40); + glTranslatef(-(float)x, -(float)y, 0.0f); + } + + glPopAttrib(); + } + else { + Paint *paint = paint_get_active(CTX_data_scene(C)); + Brush *brush = paint_brush(paint); + + if(!(paint->flags & PAINT_SHOW_BRUSH)) + return; + + glColor4ubv(paint_get_active(CTX_data_scene(C))->paint_cursor_col); + glEnable(GL_LINE_SMOOTH); + glEnable(GL_BLEND); + + glTranslatef((float)x, (float)y, 0.0f); + glutil_draw_lined_arc(0.0, M_PI*2.0, brush->size, 40); // XXX: for now use the brushes size instead of potentially using the unified size because the feature has been enabled for sculpt + glTranslatef((float)-x, (float)-y, 0.0f); + + glDisable(GL_BLEND); + glDisable(GL_LINE_SMOOTH); + } } /* Put the location of the next stroke dot into the stroke RNA and apply it to the mesh */ -static void paint_brush_stroke_add_step(bContext *C, wmOperator *op, wmEvent *event, float mouse[2]) +static void paint_brush_stroke_add_step(bContext *C, wmOperator *op, wmEvent *event, float mouse_in[2]) { + Paint *paint = paint_get_active(CTX_data_scene(C)); // XXX + Brush *brush = paint_brush(paint); // XXX + + float mouse[2]; + PointerRNA itemptr; - float pressure = 1; - float center[3] = {0, 0, 0}; - int flip= event->shift?1:0; + + float location[3]; + + float pressure; + int pen_flip; + + ViewContext vc; // XXX + PaintStroke *stroke = op->customdata; - /* XXX: can remove the if statement once all modes have this */ - if(stroke->get_location) - stroke->get_location(C, stroke, center, mouse); + view3d_set_viewcontext(C, &vc); // XXX /* Tablet */ if(event->custom == EVT_DATA_TABLET) { wmTabletData *wmtab= event->customdata; - if(wmtab->Active != EVT_TABLET_NONE) - pressure= wmtab->Pressure; - if(wmtab->Active == EVT_TABLET_ERASER) - flip = 1; + + pressure = (wmtab->Active != EVT_TABLET_NONE) ? wmtab->Pressure : 1; + pen_flip = (wmtab->Active == EVT_TABLET_ERASER); + } + else { + pressure = 1; + pen_flip = 0; } - + + // XXX: temporary check for sculpt mode until things are more unified + if (vc.obact->sculpt) { + float delta[3]; + + brush_jitter_pos(brush, mouse_in, mouse); + + // XXX: meh, this is round about because brush_jitter_pos isn't written in the best way to be reused here + if (brush->flag & BRUSH_JITTER_PRESSURE) { + sub_v3_v3v3(delta, mouse, mouse_in); + mul_v3_fl(delta, pressure); + add_v3_v3v3(mouse, mouse_in, delta); + } + } + else + copy_v3_v3(mouse, mouse_in); + + /* XXX: can remove the if statement once all modes have this */ + if(stroke->get_location) + stroke->get_location(C, stroke, location, mouse); + else + zero_v3(location); + /* Add to stroke */ RNA_collection_add(op->ptr, "stroke", &itemptr); - RNA_float_set_array(&itemptr, "location", center); - RNA_float_set_array(&itemptr, "mouse", mouse); - RNA_boolean_set(&itemptr, "flip", flip); - RNA_float_set(&itemptr, "pressure", pressure); + + RNA_float_set_array(&itemptr, "location", location); + RNA_float_set_array(&itemptr, "mouse", mouse); + RNA_boolean_set (&itemptr, "pen_flip", pen_flip); + RNA_float_set (&itemptr, "pressure", pressure); stroke->last_mouse_position[0] = mouse[0]; stroke->last_mouse_position[1] = mouse[1]; @@ -154,10 +852,14 @@ static void paint_brush_stroke_add_step(bContext *C, wmOperator *op, wmEvent *ev /* Returns zero if no sculpt changes should be made, non-zero otherwise */ static int paint_smooth_stroke(PaintStroke *stroke, float output[2], wmEvent *event) { - output[0] = event->x; + output[0] = event->x; output[1] = event->y; - if(stroke->brush->flag & BRUSH_SMOOTH_STROKE && stroke->brush->sculpt_tool != SCULPT_TOOL_GRAB) { + if ((stroke->brush->flag & BRUSH_SMOOTH_STROKE) && + !ELEM4(stroke->brush->sculpt_tool, SCULPT_TOOL_GRAB, SCULPT_TOOL_THUMB, SCULPT_TOOL_ROTATE, SCULPT_TOOL_SNAKE_HOOK) && + !(stroke->brush->flag & BRUSH_ANCHORED) && + !(stroke->brush->flag & BRUSH_RESTORE_MESH)) + { float u = stroke->brush->smooth_stroke_factor, v = 1.0 - u; float dx = stroke->last_mouse_position[0] - event->x, dy = stroke->last_mouse_position[1] - event->y; @@ -176,7 +878,9 @@ static int paint_smooth_stroke(PaintStroke *stroke, float output[2], wmEvent *ev /* Returns zero if the stroke dots should not be spaced, non-zero otherwise */ static int paint_space_stroke_enabled(Brush *br) { - return (br->flag & BRUSH_SPACE) && !(br->flag & BRUSH_ANCHORED) && (br->sculpt_tool != SCULPT_TOOL_GRAB); + return (br->flag & BRUSH_SPACE) && + !(br->flag & BRUSH_ANCHORED) && + !ELEM4(br->sculpt_tool, SCULPT_TOOL_GRAB, SCULPT_TOOL_THUMB, SCULPT_TOOL_ROTATE, SCULPT_TOOL_SNAKE_HOOK); } /* For brushes with stroke spacing enabled, moves mouse in steps @@ -187,23 +891,34 @@ static int paint_space_stroke(bContext *C, wmOperator *op, wmEvent *event, const int cnt = 0; if(paint_space_stroke_enabled(stroke->brush)) { - float mouse[2] = {stroke->last_mouse_position[0], stroke->last_mouse_position[1]}; - float vec[2] = {final_mouse[0] - mouse[0], final_mouse[1] - mouse[1]}; + float mouse[2]; + float vec[2]; float length, scale; - int steps = 0, i; - /* Normalize the vector between the last stroke dot and the goal */ - length = sqrt(vec[0]*vec[0] + vec[1]*vec[1]); + copy_v2_v2(mouse, stroke->last_mouse_position); + sub_v2_v2v2(vec, final_mouse, mouse); + + length = len_v2(vec); if(length > FLT_EPSILON) { - scale = stroke->brush->spacing / length; - vec[0] *= scale; - vec[1] *= scale; + int steps; + int i; + float pressure = 1; + + // XXX duplicate code + if(event->custom == EVT_DATA_TABLET) { + wmTabletData *wmtab= event->customdata; + if(wmtab->Active != EVT_TABLET_NONE) + pressure = stroke->brush->flag & BRUSH_SIZE_PRESSURE ? wmtab->Pressure : 1; + } + + scale = (sculpt_get_brush_size(stroke->brush)*pressure*stroke->brush->spacing/50.0f) / length; + mul_v2_fl(vec, scale); + + steps = (int)(1.0f / scale); - steps = (int)(length / stroke->brush->spacing); for(i = 0; i < steps; ++i, ++cnt) { - mouse[0] += vec[0]; - mouse[1] += vec[1]; + add_v2_v2(mouse, vec); paint_brush_stroke_add_step(C, op, event, mouse); } } @@ -283,14 +998,25 @@ int paint_stroke_modal(bContext *C, wmOperator *op, wmEvent *event) //ED_region_tag_redraw(ar); } } - else + else { paint_brush_stroke_add_step(C, op, event, mouse); + } } else ;//ED_region_tag_redraw(ar); } } + /* we want the stroke to have the first daub at the start location instead of waiting till we have moved the space distance */ + if(first && + stroke->stroke_started && + paint_space_stroke_enabled(stroke->brush) && + !(stroke->brush->flag & BRUSH_ANCHORED) && + !(stroke->brush->flag & BRUSH_SMOOTH_STROKE)) + { + paint_brush_stroke_add_step(C, op, event, mouse); + } + return OPERATOR_RUNNING_MODAL; } diff --git a/source/blender/editors/sculpt_paint/paint_utils.c b/source/blender/editors/sculpt_paint/paint_utils.c index 85fbd5954e8..21ea6cb7f75 100644 --- a/source/blender/editors/sculpt_paint/paint_utils.c +++ b/source/blender/editors/sculpt_paint/paint_utils.c @@ -216,6 +216,10 @@ void BRUSH_OT_curve_preset(wmOperatorType *ot) {CURVE_PRESET_SHARP, "SHARP", 0, "Sharp", ""}, {CURVE_PRESET_SMOOTH, "SMOOTH", 0, "Smooth", ""}, {CURVE_PRESET_MAX, "MAX", 0, "Max", ""}, + {CURVE_PRESET_MID9, "MID9", 0, "Mid9", ""}, + {CURVE_PRESET_LINE, "LINE", 0, "Line", ""}, + {CURVE_PRESET_ROUND, "ROUND", 0, "Round", ""}, + {CURVE_PRESET_ROOT, "ROOT", 0, "Root", ""}, {0, NULL, 0, NULL, NULL}}; ot->name= "Preset"; @@ -225,7 +229,7 @@ void BRUSH_OT_curve_preset(wmOperatorType *ot) ot->exec= brush_curve_preset_exec; ot->poll= brush_curve_preset_poll; - ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; + ot->flag= OPTYPE_UNDO; RNA_def_enum(ot->srna, "shape", prop_shape_items, CURVE_PRESET_SMOOTH, "Mode", ""); } diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index b53771c92ae..eee0d520747 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -22,7 +22,7 @@ * * The Original Code is: all of this file. * - * Contributor(s): none yet. + * Contributor(s): Jason Wilkins, Tom Musgrove. * * ***** END GPL LICENSE BLOCK ***** * @@ -38,6 +38,8 @@ #include "BLI_ghash.h" #include "BLI_pbvh.h" #include "BLI_threads.h" +#include "BLI_editVert.h" +#include "BLI_rand.h" #include "DNA_key_types.h" #include "DNA_mesh_types.h" @@ -73,6 +75,7 @@ #include "ED_screen.h" #include "ED_sculpt.h" #include "ED_view3d.h" +#include "ED_mesh.h" #include "paint_intern.h" #include "sculpt_intern.h" @@ -81,6 +84,7 @@ #include "RE_render_ext.h" +#include "RE_shader_ext.h" #include "GPU_buffers.h" @@ -88,13 +92,70 @@ #include #include -/* Number of vertices to average in order to determine the flatten distance */ -#define FLATTEN_SAMPLE_SIZE 10 +#ifdef _OPENMP +#include +#endif /* ==== FORWARD DEFINITIONS ===== * */ -static void sculpt_vertcos_to_key(Object *ob, KeyBlock *kb, float (*vertCos)[3]); + +void ED_sculpt_force_update(bContext *C) +{ + Object *ob= CTX_data_active_object(C); + + if(ob && (ob->mode & OB_MODE_SCULPT)) + multires_force_update(ob); +} + +/* Sculpt mode handles multires differently from regular meshes, but only if + it's the last modifier on the stack and it is not on the first level */ +struct MultiresModifierData *sculpt_multires_active(Scene *scene, Object *ob) +{ + ModifierData *md, *nmd; + + for(md= modifiers_getVirtualModifierList(ob); md; md= md->next) { + if(md->type == eModifierType_Multires) { + MultiresModifierData *mmd= (MultiresModifierData*)md; + + /* Check if any of the modifiers after multires are active + * if not it can use the multires struct */ + for(nmd= md->next; nmd; nmd= nmd->next) + if(modifier_isEnabled(scene, nmd, eModifierMode_Realtime)) + break; + + if(!nmd && mmd->sculptlvl > 0) + return mmd; + } + } + + return NULL; +} + +/* Checks whether full update mode (slower) needs to be used to work with modifiers */ +int sculpt_modifiers_active(Scene *scene, Object *ob) +{ + ModifierData *md; + MultiresModifierData *mmd= sculpt_multires_active(scene, ob); + + /* check if there are any modifiers after what we are sculpting, + for a multires modifier with a deform modifier in front, we + do no need to recalculate the modifier stack. note that this + needs to be in sync with ccgDM_use_grid_pbvh! */ + if(mmd) + md= mmd->modifier.next; + else + md= modifiers_getVirtualModifierList(ob); + + /* exception for shape keys because we can edit those */ + for(; md; md= md->next) { + if(modifier_isEnabled(scene, md, eModifierMode_Realtime)) + if(md->type != eModifierType_ShapeKey) + return 1; + } + + return 0; +} /* ===== STRUCTS ===== * @@ -121,10 +182,13 @@ typedef struct StrokeCache { /* Variants */ float radius; + float radius_squared; + //float traced_location[3]; float true_location[3]; float location[3]; - float flip; + float pen_flip; + float invert; float pressure; float mouse[2]; float bstrength; @@ -141,15 +205,32 @@ typedef struct StrokeCache { Brush *brush; float (*face_norms)[3]; /* Copy of the mesh faces' normals */ - float rotation; /* Texture rotation (radians) for anchored and rake modes */ + float special_rotation; /* Texture rotation (radians) for anchored and rake modes */ int pixel_radius, previous_pixel_radius; - float grab_active_location[8][3]; float grab_delta[3], grab_delta_symmetry[3]; float old_grab_location[3], orig_grab_location[3]; - int symmetry; /* Symmetry index between 0 and 7 */ - float view_normal[3], view_normal_symmetry[3]; - int last_rake[2]; /* Last location of updating rake rotation */ + + int symmetry; /* Symmetry index between 0 and 7 bit combo 0 is Brush only; + 1 is X mirror; 2 is Y mirror; 3 is XY; 4 is Z; 5 is XZ; 6 is YZ; 7 is XYZ */ + int mirror_symmetry_pass; /* the symmetry pass we are currently on between 0 and 7*/ + float true_view_normal[3]; + float view_normal[3]; + float last_area_normal[3]; + float last_center[3]; + int radial_symmetry_pass; + float symm_rot_mat[4][4]; + float symm_rot_mat_inv[4][4]; + float last_rake[2]; /* Last location of updating rake rotation */ int original; + + float vertex_rotation; + + char saved_active_brush_name[24]; + int alt_smooth; + + float plane_trim_squared; + + float autosmooth_overlap; } StrokeCache; /* ===== OPENGL ===== @@ -224,10 +305,12 @@ void sculpt_get_redraw_planes(float planes[4][4], ARegion *ar, RegionView3D *rv3d, Object *ob) { PBVH *pbvh= ob->sculpt->pbvh; - BoundBox *bb = MEM_callocN(sizeof(BoundBox), "sculpt boundbox"); + BoundBox bb; bglMats mats; rcti rect; + memset(&bb, 0, sizeof(BoundBox)); + view3d_get_transformation(ar, rv3d, ob, &mats); sculpt_get_redraw_rect(ar, rv3d,ob, &rect); @@ -246,423 +329,369 @@ 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; +/************************ Brush Testing *******************/ - char idname[MAX_ID_NAME]; /* name instead of pointer*/ - void *node; /* only during push, not valid afterwards! */ +typedef struct SculptBrushTest { + float radius_squared; + float location[3]; + float dist; +} SculptBrushTest; - float (*co)[3]; - short (*no)[3]; - int totvert; +static void sculpt_brush_test_init(SculptSession *ss, SculptBrushTest *test) +{ + test->radius_squared= ss->cache->radius_squared; + copy_v3_v3(test->location, ss->cache->location); +} - /* non-multires */ - int maxvert; /* to verify if totvert it still the same */ - int *index; /* to restore into right location */ +static int sculpt_brush_test(SculptBrushTest *test, float co[3]) +{ + float distsq = len_squared_v3v3(co, test->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 */ + if(distsq <= test->radius_squared) { + test->dist = sqrt(distsq); + return 1; + } + else { + return 0; + } +} - /* layer brush */ - float *layer_disp; +static int sculpt_brush_test_sq(SculptBrushTest *test, float co[3]) +{ + float distsq = len_squared_v3v3(co, test->location); - /* shape keys */ - char *shapeName[32]; /* keep size in sync with keyblock dna */ -} SculptUndoNode; + if(distsq <= test->radius_squared) { + test->dist = distsq; + return 1; + } + else { + return 0; + } +} -static void update_cb(PBVHNode *node, void *data) +static int sculpt_brush_test_fast(SculptBrushTest *test, float co[3]) { - BLI_pbvh_node_mark_update(node); + return len_squared_v3v3(co, test->location) <= test->radius_squared; } -/* 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_cube(SculptBrushTest *test, float co[3], float local[4][4]) { - ModifierData *md; - MultiresModifierData *mmd = sculpt_multires_active(scene, ob); + const static float side = 0.70710678118654752440084436210485; // sqrt(.5); - /* 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; + float local_co[3]; + + mul_v3_m4v3(local_co, local, co); + + local_co[0] = fabs(local_co[0]); + local_co[1] = fabs(local_co[1]); + local_co[2] = fabs(local_co[2]); + + 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; + + return 1; + } + else { + return 0; } - - return 0; } -static void sculpt_undo_restore(bContext *C, ListBase *lb) +static float frontface(Brush *brush, float sculpt_normal[3], short no[3], float fno[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); + if (brush->flag & BRUSH_FRONTFACE) { + float dot; - for(unode=lb->first; unode; unode=unode->next) { - if(!(strcmp(unode->idname, ob->id.name)==0)) - continue; + if (no) { + float tmp[3]; - if(unode->maxvert) { - char *shapeName= (char*)unode->shapeName; + normal_short_to_float_v3(tmp, no); + dot= dot_v3v3(tmp, sculpt_normal); + } + else { + dot= dot_v3v3(fno, sculpt_normal); + } - /* regular mesh restore */ - if(ss->totvert != unode->maxvert) - continue; + return dot > 0 ? dot : 0; + } + else { + return 1; + } +} - if (ss->kb && strcmp(ss->kb->name, shapeName)) { - /* shape key has been changed before calling undo operator */ +#if 0 - Key *key= ob_get_key(ob); - KeyBlock *kb= key_get_named_keyblock(key, shapeName); +static int sculpt_brush_test_cyl(SculptBrushTest *test, float co[3], float location[3], float an[3]) +{ + if (sculpt_brush_test_fast(test, co)) { + float t1[3], t2[3], t3[3], dist; - if (kb) { - ob->shapenr= BLI_findindex(&key->block, kb) + 1; - ob->shapeflag|= OB_SHAPE_LOCK; + sub_v3_v3v3(t1, location, co); + sub_v3_v3v3(t2, x2, location); - 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; - } - } + cross_v3_v3v3(t3, an, t1); - index= unode->index; - mvert= ss->mvert; + dist = len_v3(t3)/len_v3(t2); - if (ss->kb) { - float (*vertCos)[3]; - vertCos= key_to_vertcos(ob, ss->kb); + test->dist = dist; - for(i=0; itotvert; i++) - swap_v3_v3(vertCos[index[i]], unode->co[i]); + return 1; + } - /* propagate new coords to keyblock */ - sculpt_vertcos_to_key(ob, ss->kb, vertCos); + return 0; +} - /* pbvh uses it's own mvert array, so coords should be */ - /* propagated to pbvh here */ - BLI_pbvh_apply_vertCos(ss->pbvh, vertCos); +#endif - MEM_freeN(vertCos); - } else { - for(i=0; itotvert; 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; +/* ===== Sculpting ===== + * + */ + - if(dm->getNumGrids(dm) != unode->maxgrid) - continue; - if(dm->getGridSize(dm) != unode->gridsize) - continue; +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; - grids= dm->getGridData(dm); - gridsize= dm->getGridSize(dm); + float sum; - co = unode->co; - for(j=0; jtotgrid; j++) { - grid= grids[unode->grids[j]]; + sum = 0; + for (i= 0; i < n; i++) { + float xx; - for(i=0; ipbvh, NULL, NULL, update_cb, NULL); - BLI_pbvh_update(ss->pbvh, PBVH_UpdateBB|PBVH_UpdateOriginalBB|PBVH_UpdateRedraw, NULL); + return sum; +} + +static float integrate_overlap(Brush* br) +{ + int i; + int m= 10; + float g = 1.0f/m; + float overlap; + float max; - if((mmd=sculpt_multires_active(scene, ob))) - multires_mark_as_modified(ob); + overlap= 0; + max= 0; + for(i= 0; i < m; i++) { + overlap = overlapped_curve(br, i*g); - if(ss->modifiers_active || ((Mesh*)ob->data)->id.us > 1) - DAG_id_flush_update(&ob->id, OB_RECALC_DATA); + if (overlap > max) + max = overlap; } + + return max; } -static void sculpt_undo_free(ListBase *lb) +/* Uses symm to selectively flip any axis of a coordinate. */ +static void flip_coord(float out[3], float in[3], const char symm) { - 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); - } + 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 SculptUndoNode *sculpt_undo_get_node(SculptSession *ss, PBVHNode *node) +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; + float mirror[3]; + float distsq; + float mat[4][4]; + + //flip_coord(mirror, cache->traced_location, symm); + flip_coord(mirror, cache->true_location, symm); - if(!lb) - return NULL; + unit_m4(mat); + rotate_m4(mat, axis, angle); - for(unode=lb->first; unode; unode=unode->next) - if(unode->node == node) - return unode; + mul_m4_v3(mat, mirror); - return NULL; + //distsq = len_squared_v3v3(mirror, cache->traced_location); + distsq = len_squared_v3v3(mirror, cache->true_location); + + if (distsq <= 4*(cache->radius_squared)) + return (2*(cache->radius) - sqrt(distsq)) / (2*(cache->radius)); + else + return 0; } -static SculptUndoNode *sculpt_undo_push_node(SculptSession *ss, PBVHNode *node) +static float calc_radial_symmetry_feather(Sculpt *sd, StrokeCache *cache, const char symm, const char axis) { - 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); + int i; + float overlap; - if((unode= sculpt_undo_get_node(ss, node))) { - BLI_unlock_thread(LOCK_CUSTOM1); - return unode; + 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); } - unode= MEM_callocN(sizeof(SculptUndoNode), "SculptUndoNode"); - strcpy(unode->idname, ob->id.name); - unode->node= node; + return overlap; +} + +static float calc_symmetry_feather(Sculpt *sd, StrokeCache* cache) +{ + if (sd->flags & SCULPT_SYMMETRY_FEATHER) { + float overlap; + int symm = cache->symmetry; + int i; + + overlap = 0; + for (i = 0; i <= symm; i++) { + if(i == 0 || (symm & i && (symm != 5 || i != 3) && (symm != 6 || (i != 3 && i != 5)))) { - BLI_pbvh_node_num_verts(ss->pbvh, node, &totvert, &allvert); - BLI_pbvh_node_get_grids(ss->pbvh, node, &grids, &totgrid, - &maxgrid, &gridsize, NULL, NULL); + overlap += calc_overlap(cache, i, 0, 0); - 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); + 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'); + } + } - if(maxgrid) { - /* multires */ - unode->maxgrid= maxgrid; - unode->totgrid= totgrid; - unode->gridsize= gridsize; - unode->grids= MEM_mapallocN(sizeof(int)*totgrid, "SculptUndoNode.grids"); + return 1/overlap; } else { - /* regular mesh */ - unode->maxvert= ss->totvert; - unode->index= MEM_mapallocN(sizeof(int)*allvert, "SculptUndoNode.index"); + return 1; } +} + +/* Return modified brush strength. Includes the direction of the brush, positive + values pull vertices, negative values push. Uses tablet pressure and a + special multiplier found experimentally to scale the strength factor. */ +static float brush_strength(Sculpt *sd, StrokeCache *cache, float feather, float overlap) +{ + Brush *brush = paint_brush(&sd->paint); - BLI_unlock_thread(LOCK_CUSTOM1); + /* Primary strength input; square it to make lower values more sensitive */ + float alpha = sculpt_get_brush_alpha(brush)*sculpt_get_brush_alpha(brush); + float dir = brush->flag & BRUSH_DIR_IN ? -1 : 1; + float pressure = brush->flag & BRUSH_ALPHA_PRESSURE ? cache->pressure : 1; + float pen_flip = cache->pen_flip ? -1 : 1; + float invert = cache->invert ? -1 : 1; + float flip = dir * invert * pen_flip; - /* copy threaded, hopefully this is the performance critical part */ - { - PBVHVertexIter vd; + switch(brush->sculpt_tool){ + case SCULPT_TOOL_CLAY: + case SCULPT_TOOL_CLAY_TUBES: + case SCULPT_TOOL_DRAW: + case SCULPT_TOOL_LAYER: + return alpha * flip * pressure * overlap * feather; - 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; - } + 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; + } - if(unode->grids) - memcpy(unode->grids, grids, sizeof(int)*totgrid); + 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; + } - /* store active shape key */ - if(ss->kb) BLI_strncpy((char*)unode->shapeName, ss->kb->name, sizeof(ss->kb->name)); - else unode->shapeName[0]= '\0'; + case SCULPT_TOOL_SMOOTH: + return alpha * pressure * feather; - return unode; -} + case SCULPT_TOOL_PINCH: + if (flip > 0) { + return alpha * flip * pressure * overlap * feather; + } + else { + return 0.25f * alpha * flip * pressure * overlap * feather; + } -static void sculpt_undo_push_begin(SculptSession *ss, char *name) -{ - undo_paint_push_begin(UNDO_PAINT_MESH, name, - sculpt_undo_restore, sculpt_undo_free); -} + case SCULPT_TOOL_NUDGE: + overlap = (1+overlap) / 2; + return alpha * pressure * overlap * feather; -static void sculpt_undo_push_end(SculptSession *ss) -{ - ListBase *lb= undo_paint_push_get_list(UNDO_PAINT_MESH); - SculptUndoNode *unode; + case SCULPT_TOOL_THUMB: + return alpha*pressure*feather; - /* 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; - } + case SCULPT_TOOL_SNAKE_HOOK: + return feather; - if(unode->layer_disp) { - MEM_freeN(unode->layer_disp); - unode->layer_disp= NULL; - } - } + case SCULPT_TOOL_GRAB: + case SCULPT_TOOL_ROTATE: + return feather; - undo_paint_push_end(UNDO_PAINT_MESH); + default: + return 0; + } } -void ED_sculpt_force_update(bContext *C) +float get_tex_pixel(Brush* br, float u, float v) { - Object *ob= CTX_data_active_object(C); + TexResult texres; + float co[3]; + int hasrgb; - if(ob && (ob->mode & OB_MODE_SCULPT)) - multires_force_update(ob); -} + co[0] = u; + co[1] = v; + co[2] = 0; -/************************ Brush Testing *******************/ + memset(&texres, 0, sizeof(TexResult)); + hasrgb = multitex_ext(br->mtex.tex, co, NULL, NULL, 1, &texres); -typedef struct SculptBrushTest { - float radius_squared; - float location[3]; + if (hasrgb & TEX_RGB) + texres.tin = (0.35*texres.tr + 0.45*texres.tg + 0.2*texres.tb)*texres.ta; - float dist; -} SculptBrushTest; + return texres.tin; +} -static void sculpt_brush_test_init(SculptSession *ss, SculptBrushTest *test) +#if 0 + +/* Get a pixel from the texcache at (px, py) */ +static unsigned char get_texcache_pixel(const SculptSession *ss, int px, int py) { - test->radius_squared= ss->cache->radius*ss->cache->radius; - copy_v3_v3(test->location, ss->cache->location); + unsigned *p; + p = ss->texcache + py * ss->texcache_side + px; + return ((unsigned char*)(p))[0]; } -static int sculpt_brush_test(SculptBrushTest *test, float co[3]) +static float get_texcache_pixel_bilinear(const SculptSession *ss, float u, float v) { - float distsq, delta[3]; - - sub_v3_v3v3(delta, co, test->location); - distsq = INPR(delta, delta); + unsigned x, y, x2, y2; + const int tc_max = ss->texcache_side - 1; + float urat, vrat, uopp; - if(distsq < test->radius_squared) { - test->dist = sqrt(distsq); - 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) -{ - 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; - - 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; - } -} - -/* Uses symm to selectively flip any axis of a coordinate. */ -static void flip_coord(float out[3], float in[3], const char symm) -{ - 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]; -} - -/* Get a pixel from the texcache at (px, py) */ -static unsigned char get_texcache_pixel(const SculptSession *ss, int px, int py) -{ - unsigned *p; - p = ss->texcache + py * ss->texcache_side + px; - return ((unsigned char*)(p))[0]; -} - -static float get_texcache_pixel_bilinear(const SculptSession *ss, float u, float v) -{ - int x, y, x2, y2; - const int tc_max = ss->texcache_side - 1; - float urat, vrat, uopp; - - if(u < 0) u = 0; - else if(u >= ss->texcache_side) u = tc_max; - if(v < 0) v = 0; - else if(v >= ss->texcache_side) v = tc_max; + if(u < 0) u = 0; + else if(u >= ss->texcache_side) u = tc_max; + if(v < 0) v = 0; + else if(v >= ss->texcache_side) v = tc_max; x = floor(u); y = floor(v); @@ -682,76 +711,97 @@ static float get_texcache_pixel_bilinear(const SculptSession *ss, float u, float get_texcache_pixel(ss, x2, y2) * urat) * vrat) / 255.0; } +#endif + /* Return a multiplier for brush strength on a particular vertex. */ static float tex_strength(SculptSession *ss, Brush *br, float *point, const float len) { - MTex *tex = &br->mtex; + MTex *mtex = &br->mtex; float avg= 1; - if(!tex) { + if(!mtex->tex) { avg= 1; } - else if(tex->brush_map_mode == MTEX_MAP_MODE_3D) { + else if(mtex->brush_map_mode == MTEX_MAP_MODE_3D) { float jnk; /* Get strength by feeding the vertex location directly into a texture */ - externtex(tex, point, &avg, + externtex(mtex, point, &avg, &jnk, &jnk, &jnk, &jnk); } else if(ss->texcache) { - const float bsize= ss->cache->pixel_radius * 2; - const float rot= tex->rot + ss->cache->rotation; - int px, py; - float flip[3], point_2d[2]; - - /* If the active area is being applied for symmetry, flip it - across the symmetry axis in order to project it. This insures - that the brush texture will be oriented correctly. */ - copy_v3_v3(flip, point); - flip_coord(flip, flip, ss->cache->symmetry); - projectf(ss->cache->mats, flip, point_2d); - - /* For Tile and Drag modes, get the 2D screen coordinates of the - and scale them up or down to the texture size. */ - if(tex->brush_map_mode == MTEX_MAP_MODE_TILED) { - const int sx= (const int)tex->size[0]; - const int sy= (const int)tex->size[1]; - - float fx= point_2d[0]; - float fy= point_2d[1]; - - float angle= atan2(fy, fx) - rot; - float flen= sqrtf(fx*fx + fy*fy); - - if(rot<0.001 && rot>-0.001) { - px= point_2d[0]; - py= point_2d[1]; - } else { - px= flen * cos(angle) + 2000; - py= flen * sin(angle) + 2000; - } - if(sx != 1) - px %= sx-1; - if(sy != 1) - py %= sy-1; - avg= get_texcache_pixel_bilinear(ss, ss->texcache_side*px/sx, ss->texcache_side*py/sy); - } - else if(tex->brush_map_mode == MTEX_MAP_MODE_FIXED) { - float fx= (point_2d[0] - ss->cache->tex_mouse[0]) / bsize; - float fy= (point_2d[1] - ss->cache->tex_mouse[1]) / bsize; - - float angle= atan2(fy, fx) - rot; - float flen= sqrtf(fx*fx + fy*fy); - - fx = flen * cos(angle) + 0.5; - fy = flen * sin(angle) + 0.5; + float rotation = -mtex->rot; + float x, y, point_2d[3]; + float diameter; + + /* if the active area is being applied for symmetry, flip it + across the symmetry axis and rotate it back to the orignal + position in order to project it. This insures that the + brush texture will be oriented correctly. */ + + flip_coord(point_2d, point, ss->cache->mirror_symmetry_pass); + + if (ss->cache->radial_symmetry_pass) + mul_m4_v3(ss->cache->symm_rot_mat_inv, point_2d); + + projectf(ss->cache->mats, point_2d, point_2d); + + /* if fixed mode, keep coordinates relative to mouse */ + if(mtex->brush_map_mode == MTEX_MAP_MODE_FIXED) { + rotation += ss->cache->special_rotation; + + point_2d[0] -= ss->cache->tex_mouse[0]; + point_2d[1] -= ss->cache->tex_mouse[1]; + + diameter = ss->cache->pixel_radius; // use pressure adjusted size for fixed mode - avg= get_texcache_pixel_bilinear(ss, fx * ss->texcache_side, fy * ss->texcache_side); + x = point_2d[0]; + y = point_2d[1]; } + else /* else (mtex->brush_map_mode == MTEX_MAP_MODE_TILED), + leave the coordinates relative to the screen */ + { + diameter = sculpt_get_brush_size(br); // use unadjusted size for tiled mode + + x = point_2d[0] - ss->cache->vc->ar->winrct.xmin; + y = point_2d[1] - ss->cache->vc->ar->winrct.ymin; + } + + x /= ss->cache->vc->ar->winx; + y /= ss->cache->vc->ar->winy; + + if (mtex->brush_map_mode == MTEX_MAP_MODE_TILED) { + x -= 0.5f; + y -= 0.5f; + } + + x *= ss->cache->vc->ar->winx / diameter; + y *= ss->cache->vc->ar->winy / diameter; + + /* it is probably worth optimizing for those cases where + the texture is not rotated by skipping the calls to + atan2, sqrtf, sin, and cos. */ + if (rotation > 0.001 || rotation < -0.001) { + const float angle = atan2(y, x) + rotation; + const float flen = sqrtf(x*x + y*y); + + x = flen * cos(angle); + y = flen * sin(angle); + } + + x *= br->mtex.size[0]; + y *= br->mtex.size[1]; + + x += br->mtex.ofs[0]; + y += br->mtex.ofs[1]; + + avg = get_tex_pixel(br, x, y); } - avg*= brush_curve_strength(br, len, ss->cache->radius); /* Falloff curve */ + avg += br->texture_sample_bias; + + avg *= brush_curve_strength(br, len, ss->cache->radius); /* Falloff curve */ return avg; } @@ -787,7 +837,7 @@ static int sculpt_search_sphere_cb(PBVHNode *node, void *data_v) sub_v3_v3v3(t, center, nearest); - return t[0] * t[0] + t[1] * t[1] + t[2] * t[2] < data->radius_squared; + return dot_v3v3(t, t) < data->radius_squared; } /* Handles clipping against a mirror modifier and SCULPT_LOCK axis flags */ @@ -803,7 +853,7 @@ static void sculpt_clip(Sculpt *sd, SculptSession *ss, float *co, const float va co[i]= 0.0f; else co[i]= val[i]; - } + } } static void add_norm_if(float view_vec[3], float out[3], float out_flip[3], float fno[3]) @@ -815,52 +865,48 @@ static void add_norm_if(float view_vec[3], float out[3], float out_flip[3], floa } } -/* For draw/layer/flatten; finds average normal for all active vertices */ -static void calc_area_normal(Sculpt *sd, SculptSession *ss, float area_normal[3], PBVHNode **nodes, int totnode) +static void calc_area_normal(Sculpt *sd, SculptSession *ss, float an[3], PBVHNode **nodes, int totnode) { - PBVH *bvh= ss->pbvh; - StrokeCache *cache = ss->cache; - const int view = 0; /* XXX: should probably be a flag, not number: brush_type==SCULPT_TOOL_DRAW ? sculptmode_brush()->view : 0; */ - float out[3] = {0.0f, 0.0f, 0.0f}; - float out_flip[3] = {0.0f, 0.0f, 0.0f}; - float out_dir[3]; int n; - copy_v3_v3(out_dir, cache->view_normal_symmetry); + float out_flip[3] = {0.0f, 0.0f, 0.0f}; + + zero_v3(an); - /* threaded loop over nodes */ - //#pragma omp parallel for private(n) schedule(static) + #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) for(n=0; ncache->original) { - BLI_pbvh_vertex_iter_begin(bvh, nodes[n], vd, PBVH_ITER_UNIQUE) { - if(sculpt_brush_test(&test, unode->co[vd.i])) { + BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { + if(sculpt_brush_test_fast(&test, unode->co[vd.i])) { + float fno[3]; + normal_short_to_float_v3(fno, unode->no[vd.i]); - add_norm_if(out_dir, nout, nout_flip, fno); + add_norm_if(ss->cache->view_normal, private_an, private_out_flip, fno); } } BLI_pbvh_vertex_iter_end; } else { - BLI_pbvh_vertex_iter_begin(bvh, nodes[n], vd, PBVH_ITER_UNIQUE) { - if(sculpt_brush_test(&test, vd.co)) { + BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { + if(sculpt_brush_test_fast(&test, vd.co)) { if(vd.no) { + float fno[3]; + normal_short_to_float_v3(fno, vd.no); - add_norm_if(out_dir, nout, nout_flip, fno); + add_norm_if(ss->cache->view_normal, private_an, private_out_flip, fno); + } + else { + add_norm_if(ss->cache->view_normal, private_an, private_out_flip, vd.fno); } - else - add_norm_if(out_dir, nout, nout_flip, vd.fno); } } BLI_pbvh_vertex_iter_end; @@ -868,72 +914,70 @@ static void calc_area_normal(Sculpt *sd, SculptSession *ss, float area_normal[3] //#pragma omp critical { - /* we sum per node and add together later for threads */ - add_v3_v3(out, nout); - add_v3_v3(out_flip, nout_flip); + add_v3_v3(an, private_an); + add_v3_v3(out_flip, private_out_flip); } } - if (out[0]==0.0 && out[1]==0.0 && out[2]==0.0) { - copy_v3_v3(out, out_flip); - } - - normalize_v3(out); + if (is_zero_v3(an)) + copy_v3_v3(an, out_flip); - out[0] = out_dir[0] * view + out[0] * (10-view); - out[1] = out_dir[1] * view + out[1] * (10-view); - out[2] = out_dir[2] * view + out[2] * (10-view); - - normalize_v3(out); - copy_v3_v3(area_normal, out); + normalize_v3(an); } -static void do_draw_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode) +/* This initializes the faces to be moved for this sculpt for draw/layer/flatten; then it + finds average normal for all active vertices - note that this is called once for each mirroring direction */ +static void calc_sculpt_normal(Sculpt *sd, SculptSession *ss, float an[3], PBVHNode **nodes, int totnode) { Brush *brush = paint_brush(&sd->paint); - float offset[3], area_normal[3]; - float bstrength= ss->cache->bstrength; - int n; - /* area normal */ - calc_area_normal(sd, ss, area_normal, nodes, totnode); + if (ss->cache->mirror_symmetry_pass == 0 && + ss->cache->radial_symmetry_pass == 0 && + (ss->cache->first_time || !(brush->flag & BRUSH_ORIGINAL_NORMAL))) + { + switch (brush->sculpt_plane) { + case SCULPT_DISP_DIR_VIEW: + viewvector(ss->cache->vc->rv3d, ss->cache->vc->rv3d->twmat[3], an); + break; - /* offset with as much as possible factored in already */ - offset[0]= area_normal[0]*ss->cache->radius*ss->cache->scale[0]*bstrength; - offset[1]= area_normal[1]*ss->cache->radius*ss->cache->scale[1]*bstrength; - offset[2]= area_normal[2]*ss->cache->radius*ss->cache->scale[2]*bstrength; + case SCULPT_DISP_DIR_X: + an[1] = 0.0; + an[2] = 0.0; + an[0] = 1.0; + break; - /* threaded loop over nodes */ - //#pragma omp parallel for private(n) schedule(static) - for(n=0; npbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { - if(sculpt_brush_test(&test, vd.co)) { - /* offset vertex */ - float fade = tex_strength(ss, brush, vd.co, test.dist); - float val[3]= {vd.co[0] + offset[0]*fade, - vd.co[1] + offset[1]*fade, - vd.co[2] + offset[2]*fade}; + case SCULPT_DISP_DIR_Z: + an[0] = 0.0; + an[1] = 0.0; + an[2] = 1.0; + break; - sculpt_clip(sd, ss, vd.co, val); - if(vd.mvert) vd.mvert->flag |= ME_VERT_PBVH_UPDATE; - } + case SCULPT_DISP_DIR_AREA: + calc_area_normal(sd, ss, an, nodes, totnode); + + default: + break; } - BLI_pbvh_vertex_iter_end; - BLI_pbvh_node_mark_update(nodes[n]); + copy_v3_v3(ss->cache->last_area_normal, an); + } + else { + copy_v3_v3(an, ss->cache->last_area_normal); + flip_coord(an, an, ss->cache->mirror_symmetry_pass); + mul_m4_v3(ss->cache->symm_rot_mat, an); } } /* For the smooth brush, uses the neighboring vertices around vert to calculate a smoothed location for vert. Skips corner vertices (used by only one polygon.) */ -static void neighbor_average(SculptSession *ss, float avg[3], const int vert) +static void neighbor_average(SculptSession *ss, float avg[3], const unsigned vert) { int i, skip= -1, total=0; IndexNode *node= ss->fmap[vert].first; @@ -974,52 +1018,58 @@ static void neighbor_average(SculptSession *ss, float avg[3], const int vert) copy_v3_v3(avg, ss->mvert[vert].co); } -static void do_mesh_smooth_brush(Sculpt *sd, SculptSession *ss, PBVHNode *node) +static void do_mesh_smooth_brush(Sculpt *sd, SculptSession *ss, PBVHNode *node, float bstrength) { Brush *brush = paint_brush(&sd->paint); - float bstrength= ss->cache->bstrength; PBVHVertexIter vd; SculptBrushTest test; + CLAMP(bstrength, 0.0f, 1.0f); + sculpt_brush_test_init(ss, &test); BLI_pbvh_vertex_iter_begin(ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { if(sculpt_brush_test(&test, vd.co)) { - float fade = tex_strength(ss, brush, vd.co, test.dist)*bstrength; + const float fade = bstrength*tex_strength(ss, brush, vd.co, test.dist)*frontface(brush, ss->cache->view_normal, vd.no, vd.fno); float avg[3], val[3]; - CLAMP(fade, 0.0f, 1.0f); - neighbor_average(ss, avg, vd.vert_indices[vd.i]); - val[0] = vd.co[0]+(avg[0]-vd.co[0])*fade; - val[1] = vd.co[1]+(avg[1]-vd.co[1])*fade; - val[2] = vd.co[2]+(avg[2]-vd.co[2])*fade; - - sculpt_clip(sd, ss, vd.co, val); - if(vd.mvert) vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + sub_v3_v3v3(val, avg, vd.co); + mul_v3_fl(val, fade); + + add_v3_v3(val, vd.co); + + sculpt_clip(sd, ss, vd.co, val); + + if(vd.mvert) + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; } } BLI_pbvh_vertex_iter_end; } -static void do_multires_smooth_brush(Sculpt *sd, SculptSession *ss, PBVHNode *node) +static void do_multires_smooth_brush(Sculpt *sd, SculptSession *ss, PBVHNode *node, float bstrength) { Brush *brush = paint_brush(&sd->paint); SculptBrushTest test; DMGridData **griddata, *data; DMGridAdjacency *gridadj, *adj; - float bstrength= ss->cache->bstrength; - float co[3], (*tmpgrid)[3]; + float (*tmpgrid)[3], (*tmprow)[3]; int v1, v2, v3, v4; int *grid_indices, totgrid, gridsize, i, x, y; - + sculpt_brush_test_init(ss, &test); + CLAMP(bstrength, 0.0f, 1.0f); + BLI_pbvh_node_get_grids(ss->pbvh, node, &grid_indices, &totgrid, NULL, &gridsize, &griddata, &gridadj); //#pragma omp critical - tmpgrid= MEM_mallocN(sizeof(float)*3*gridsize*gridsize, "tmpgrid"); + { + tmpgrid= MEM_mallocN(sizeof(float)*3*gridsize*gridsize, "tmpgrid"); + tmprow= MEM_mallocN(sizeof(float)*3*gridsize, "tmprow"); + } for(i = 0; i < totgrid; ++i) { data = griddata[grid_indices[i]]; @@ -1027,75 +1077,106 @@ static void do_multires_smooth_brush(Sculpt *sd, SculptSession *ss, PBVHNode *no memset(tmpgrid, 0, sizeof(float)*3*gridsize*gridsize); - /* average grid values */ - for(y = 0; y < gridsize-1; ++y) { - for(x = 0; x < gridsize-1; ++x) { + for (y= 0; y < gridsize-1; y++) { + float tmp[3]; + + v1 = y*gridsize; + add_v3_v3v3(tmprow[0], data[v1].co, data[v1+gridsize].co); + + for (x= 0; x < gridsize-1; x++) { v1 = x + y*gridsize; - v2 = (x + 1) + y*gridsize; - v3 = (x + 1) + (y + 1)*gridsize; - v4 = x + (y + 1)*gridsize; + v2 = v1 + 1; + v3 = v1 + gridsize; + v4 = v3 + 1; - cent_quad_v3(co, data[v1].co, data[v2].co, data[v3].co, data[v4].co); - mul_v3_fl(co, 0.25f); + add_v3_v3v3(tmprow[x+1], data[v2].co, data[v4].co); + add_v3_v3v3(tmp, tmprow[x+1], tmprow[x]); - add_v3_v3(tmpgrid[v1], co); - add_v3_v3(tmpgrid[v2], co); - add_v3_v3(tmpgrid[v3], co); - add_v3_v3(tmpgrid[v4], co); + add_v3_v3(tmpgrid[v1], tmp); + add_v3_v3(tmpgrid[v2], tmp); + add_v3_v3(tmpgrid[v3], tmp); + add_v3_v3(tmpgrid[v4], tmp); } } /* blend with existing coordinates */ for(y = 0; y < gridsize; ++y) { for(x = 0; x < gridsize; ++x) { - if(x == 0 && adj->index[0] == -1) continue; - if(x == gridsize - 1 && adj->index[2] == -1) continue; - if(y == 0 && adj->index[3] == -1) continue; - if(y == gridsize - 1 && adj->index[1] == -1) continue; + float *co; + float *fno; + int index; + + 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); - CLAMP(fade, 0.0f, 1.0f); + sub_v3_v3v3(val, avg, co); + mul_v3_fl(val, fade); - val[0] = co[0]+(avg[0]-co[0])*fade; - val[1] = co[1]+(avg[1]-co[1])*fade; - val[2] = co[2]+(avg[2]-co[2])*fade; - - sculpt_clip(sd, ss, data[x + y*gridsize].co, val); + add_v3_v3(val, co); + + sculpt_clip(sd, ss, co, val); } } } } //#pragma omp critical - MEM_freeN(tmpgrid); + { + MEM_freeN(tmpgrid); + MEM_freeN(tmprow); + } } -static void do_smooth_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode) +static void smooth(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode, float bstrength) { - int iteration, n; + const int max_iterations = 4; + const float fract = 1.0f/max_iterations; + int iteration, n, count; + float last; - for(iteration = 0; iteration < 2; ++iteration) { - //#pragma omp parallel for private(n) schedule(static) - for(n=0; nmultires) - do_multires_smooth_brush(sd, ss, nodes[n]); - else if(ss->fmap) - do_mesh_smooth_brush(sd, ss, nodes[n]); + count = (int)(bstrength*max_iterations); + last = max_iterations*(bstrength - count*fract); - BLI_pbvh_node_mark_update(nodes[n]); + for(iteration = 1; iteration <= count; ++iteration) { + #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) + for(n=0; nmultires) { + do_multires_smooth_brush(sd, ss, nodes[n], iteration != count ? 1.0f : last); + } + else if(ss->fmap) + do_mesh_smooth_brush(sd, ss, nodes[n], iteration != count ? 1.0f : last); } if(ss->multires) @@ -1103,317 +1184,1086 @@ 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; npbvh, 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}; + 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); - 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]); } } -static void do_grab_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode) +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 grab_delta[3]; + float flippedbstrength, crease_correction; int n; + + calc_sculpt_normal(sd, ss, area_normal, nodes, totnode); - copy_v3_v3(grab_delta, ss->cache->grab_delta_symmetry); + /* offset with as much as possible factored in already */ + mul_v3_v3fl(offset, area_normal, ss->cache->radius); + mul_v3_v3(offset, ss->cache->scale); + mul_v3_fl(offset, bstrength); + + /* we divide out the squared alpha and multiply by the squared crease to give us the pinch strength */ + + if(sculpt_get_brush_alpha(brush) > 0.0f) + crease_correction = brush->crease_pinch_factor*brush->crease_pinch_factor/(sculpt_get_brush_alpha(brush)*sculpt_get_brush_alpha(brush)); + else + crease_correction = brush->crease_pinch_factor*brush->crease_pinch_factor; + + /* we always want crease to pinch or blob to relax even when draw is negative */ + flippedbstrength = (bstrength < 0) ? -crease_correction*bstrength : crease_correction*bstrength; + + if(brush->sculpt_tool == SCULPT_TOOL_BLOB) flippedbstrength *= -1.0f; - //#pragma omp parallel for private(n) schedule(static) + /* threaded loop over nodes */ + #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) for(n=0; nco; + 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, 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]}; + if(sculpt_brush_test(&test, vd.co)) { + /* 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]; - sculpt_clip(sd, ss, vd.co, add); - if(vd.mvert) vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + /* 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); + + if(vd.mvert) + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; } } BLI_pbvh_vertex_iter_end; - - BLI_pbvh_node_mark_update(nodes[n]); } } -static void do_layer_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode) +static void do_pinch_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode) { Brush *brush = paint_brush(&sd->paint); float bstrength= ss->cache->bstrength; - float area_normal[3], offset[3]; - float lim= ss->cache->radius / 4; int n; - if(ss->cache->flip) - lim = -lim; - - calc_area_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]; - - //#pragma omp parallel for private(n) schedule(static) + #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) for(n=0; nco; - if(!unode->layer_disp) - unode->layer_disp= MEM_callocN(sizeof(float)*unode->totvert, "layer disp"); - layer_disp= unode->layer_disp; + 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 *disp= &layer_disp[vd.i]; + float fade = bstrength*tex_strength(ss, brush, vd.co, test.dist)*frontface(brush, ss->cache->view_normal, vd.no, vd.fno); 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; - - 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]; - } - 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]; - } + sub_v3_v3v3(val, test.location, vd.co); + mul_v3_v3fl(proxy[vd.i], val, fade); - 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]); } } -static void do_inflate_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode) +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], 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; nco; + 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])) { + 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); + + 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; nco; + origno= unode->no; + + proxy= BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co; + + sculpt_brush_test_init(ss, &test); + + BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { + if(sculpt_brush_test(&test, origco[vd.i])) { + const float fade = bstrength*tex_strength(ss, brush, origco[vd.i], test.dist)*frontface(brush, an, origno[vd.i], NULL); + + mul_v3_m3v3(proxy[vd.i], m, origco[vd.i]); + sub_v3_v3(proxy[vd.i], origco[vd.i]); + mul_v3_fl(proxy[vd.i], fade); + + if(vd.mvert) + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + } + BLI_pbvh_vertex_iter_end; + } +} + +static void do_layer_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode) +{ + Brush *brush = paint_brush(&sd->paint); + float bstrength= ss->cache->bstrength; + float area_normal[3], offset[3]; + float lim= ss->cache->radius / 4; + int n; + + if(bstrength < 0) + lim = -lim; + + calc_sculpt_normal(sd, ss, area_normal, nodes, totnode); + + mul_v3_v3v3(offset, ss->cache->scale, area_normal); + + #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) + for(n=0; npbvh, nodes[n])->co; + + unode= sculpt_undo_push_node(ss, nodes[n]); + origco=unode->co; + if(!unode->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)) { + 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 */ + add_v3_v3(val, ss->layer_co[index]); + } + else { + add_v3_v3(val, origco[vd.i]); + } + + 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_inflate_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; npbvh, 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, ss->cache->view_normal, vd.no, vd.fno); + float val[3]; + + if(vd.fno) copy_v3_v3(val, vd.fno); + else normal_short_to_float_v3(val, vd.no); + + 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; + } +} + +static void calc_flatten_center(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode, float fc[3]) +{ + int n; + + float count = 0; + + zero_v3(fc); + + #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) + for(n=0; ncache->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; + } + } + + 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; ncache->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; + } + + #pragma omp critical + { + // an + add_v3_v3(an, private_an); + add_v3_v3(out_flip, private_out_flip); + + // fc + add_v3_v3(fc, private_fc); + count += private_count; + } + } + + // an + if (is_zero_v3(an)) + copy_v3_v3(an, out_flip); + + normalize_v3(an); + + // fc + mul_v3_fl(fc, 1.0f / count); +} + +static void calc_sculpt_plane(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode, float an[3], float fc[3]) +{ + Brush *brush = paint_brush(&sd->paint); + + if (ss->cache->mirror_symmetry_pass == 0 && + ss->cache->radial_symmetry_pass == 0 && + (ss->cache->first_time || !(brush->flag & BRUSH_ORIGINAL_NORMAL))) + { + switch (brush->sculpt_plane) { + case SCULPT_DISP_DIR_VIEW: + viewvector(ss->cache->vc->rv3d, ss->cache->vc->rv3d->twmat[3], an); + break; + + case SCULPT_DISP_DIR_X: + an[1] = 0.0; + an[2] = 0.0; + an[0] = 1.0; + break; + + case SCULPT_DISP_DIR_Y: + an[0] = 0.0; + an[2] = 0.0; + an[1] = 1.0; + break; + + case SCULPT_DISP_DIR_Z: + an[0] = 0.0; + an[1] = 0.0; + an[2] = 1.0; + break; + + case SCULPT_DISP_DIR_AREA: + calc_area_normal_and_flatten_center(sd, ss, nodes, totnode, an, fc); + + default: + break; + } + + // fc + /* flatten center has not been calculated yet if we are not using the area normal */ + if (brush->sculpt_plane != SCULPT_DISP_DIR_AREA) + calc_flatten_center(sd, ss, nodes, totnode, fc); + + // an + copy_v3_v3(ss->cache->last_area_normal, an); + + // fc + copy_v3_v3(ss->cache->last_center, fc); + } + else { + // an + copy_v3_v3(an, ss->cache->last_area_normal); + + // fc + copy_v3_v3(fc, ss->cache->last_center); + + // an + flip_coord(an, an, ss->cache->mirror_symmetry_pass); + + // fc + flip_coord(fc, fc, ss->cache->mirror_symmetry_pass); + + // an + mul_m4_v3(ss->cache->symm_rot_mat, an); + + // fc + mul_m4_v3(ss->cache->symm_rot_mat, fc); + } +} + +/* 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]) +{ + 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]; + + sub_v3_v3v3(delta, co, plane_center); + return dot_v3v3(plane_normal, delta) <= 0.0f; +} + +static float get_offset(Sculpt *sd, SculptSession *ss) +{ + Brush* brush = paint_brush(&sd->paint); + + float rv = brush->plane_offset; + + if (brush->flag & BRUSH_OFFSET_PRESSURE) { + rv *= ss->cache->pressure; + } + + return rv; +} + +static void do_flatten_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; + 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]; + if (sculpt_brush_test_sq(&test, vd.co)) { + float intr[3]; + 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_v3(add, vd.co); - - sculpt_clip(sd, ss, vd.co, add); - if(vd.mvert) vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + 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; - - 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 do_clay_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode) { - float outer_dist[FLATTEN_SAMPLE_SIZE]; - float outer_co[FLATTEN_SAMPLE_SIZE][3]; - int i, n; + Brush *brush = paint_brush(&sd->paint); + + float bstrength = ss->cache->bstrength; + float radius = ss->cache->radius; + float offset = get_offset(sd, ss); - for(i = 0; i < FLATTEN_SAMPLE_SIZE; ++i) { - zero_v3(outer_co[i]); - outer_dist[i]= -1.0f; + 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; } - - //#pragma omp parallel for private(n) schedule(static) - for(n=0; ncache->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; - int j; - - 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)) { - 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 (sculpt_brush_test_sq(&test, vd.co)) { + if (plane_point_side_flip(vd.co, an, fc, flip)) { + //if (sculpt_brush_test_cyl(&test, vd.co, ss->cache->location, p)) { + float intr[3]; + float val[3]; + + point_plane_project(intr, vd.co, an, fc); + + sub_v3_v3v3(val, intr, vd.co); + + if (plane_trim(ss->cache, brush, val)) { + const float fade = bstrength*tex_strength(ss, brush, vd.co, sqrt(test.dist))*frontface(brush, an, vd.no, vd.fno); + + mul_v3_v3fl(proxy[vd.i], val, fade); + + if(vd.mvert) + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; } } } } BLI_pbvh_vertex_iter_end; - - BLI_pbvh_node_mark_update(nodes[n]); } - - co[0] = co[1] = co[2] = 0.0f; - for(i = 0; i < FLATTEN_SAMPLE_SIZE; ++i) - if(outer_dist[i] >= 0.0f) - add_v3_v3(co, outer_co[i]); - mul_v3_fl(co, 1.0f / FLATTEN_SAMPLE_SIZE); } -/* Projects a point onto a plane along the plane's normal */ -static void point_plane_project(float intr[3], float co[3], float plane_normal[3], float plane_center[3]) +static void do_clay_tubes_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode) { - float p1[3], sub1[3], sub2[3]; + Brush *brush = paint_brush(&sd->paint); + + float bstrength = ss->cache->bstrength; + float radius = ss->cache->radius; + float offset = get_offset(sd, ss); + + float displace; + + float sn[3]; // sculpt normal + float an[3]; // area normal + float fc[3]; // flatten center + + int n; + + float temp[3]; + float mat[4][4]; + float scale[4][4]; + float tmat[4][4]; + + int flip; + + calc_sculpt_plane(sd, ss, nodes, totnode, sn, fc); + + if (brush->sculpt_plane != SCULPT_DISP_DIR_AREA || (brush->flag & BRUSH_ORIGINAL_NORMAL)) + calc_area_normal(sd, ss, an, nodes, totnode); + else + copy_v3_v3(an, sn); + + 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); - /* Find the intersection between squash-plane and vertex (along the area normal) */ - sub_v3_v3v3(p1, co, plane_normal); - sub_v3_v3v3(sub1, plane_center, p1); - sub_v3_v3v3(sub2, co, p1); - sub_v3_v3v3(intr, co, p1); - mul_v3_fl(intr, dot_v3v3(plane_normal, sub1) / dot_v3v3(plane_normal, sub2)); - add_v3_v3(intr, p1); + if(vd.mvert) + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + } + } + } + BLI_pbvh_vertex_iter_end; + } } -static int plane_point_side(float co[3], float plane_normal[3], float plane_center[3], int flip) +static void do_fill_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode) { - float delta[3]; - float d; + 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; + + 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]; - sub_v3_v3v3(delta, co, plane_center); - d = dot_v3v3(plane_normal, delta); + point_plane_project(intr, vd.co, an, fc); - if(flip) - d = -d; + 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); - return d <= 0.0f; + 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_flatten_clay_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode, int clay) +static void do_scrape_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; - 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; - } + float an[3]; + float fc[3]; + float offset = get_offset(sd, ss); - //#pragma omp parallel for private(n) schedule(static) - for(n=0; ncache->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; - - 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(vd.co, an, fc)) { + 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; - } - else - mul_v3_fl(val, fabs(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); - add_v3_v3(val, vd.co); + mul_v3_v3fl(proxy[vd.i], val, fade); - 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]); } } -static void sculpt_vertcos_to_key(Object *ob, KeyBlock *kb, float (*vertCos)[3]) +void sculpt_vertcos_to_key(Object *ob, KeyBlock *kb, float (*vertCos)[3]) { Mesh *me= (Mesh*)ob->data; float (*ofs)[3]= NULL; @@ -1475,36 +2325,27 @@ static void sculpt_update_keyblock(SculptSession *ss) } } -static void do_brush_action(Sculpt *sd, SculptSession *ss, StrokeCache *cache) +static void do_brush_action(Sculpt *sd, SculptSession *ss, Brush *brush) { SculptSearchSphereData data; - Brush *brush = paint_brush(&sd->paint); - PBVHNode **nodes= NULL; - int totnode; + PBVHNode **nodes = NULL; + int n, totnode; + /* Build a list of all nodes that are potentially within the brush's area of influence */ data.ss = ss; data.sd = sd; - data.radius_squared = ss->cache->radius * ss->cache->radius; - - /* Build a list of all nodes that are potentially within the brush's - area of influence */ - if(brush->sculpt_tool == SCULPT_TOOL_GRAB) { - data.original= 1; - BLI_pbvh_search_gather(ss->pbvh, sculpt_search_sphere_cb, &data, - &nodes, &totnode); - - if(cache->first_time) - copy_v3_v3(ss->cache->grab_active_location[ss->cache->symmetry], ss->cache->location); - else - copy_v3_v3(ss->cache->location, ss->cache->grab_active_location[ss->cache->symmetry]); - } - else { - BLI_pbvh_search_gather(ss->pbvh, sculpt_search_sphere_cb, &data, - &nodes, &totnode); - } + data.radius_squared = ss->cache->radius_squared; + data.original = ELEM4(brush->sculpt_tool, SCULPT_TOOL_GRAB, SCULPT_TOOL_ROTATE, SCULPT_TOOL_THUMB, SCULPT_TOOL_LAYER); + BLI_pbvh_search_gather(ss->pbvh, sculpt_search_sphere_cb, &data, &nodes, &totnode); /* Only act if some verts are inside the brush area */ - if(totnode) { + if (totnode) { + #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) + for (n= 0; n < totnode; n++) { + sculpt_undo_push_node(ss, nodes[n]); + BLI_pbvh_node_mark_update(nodes[n]); + } + /* Apply one type of brush action */ switch(brush->sculpt_tool){ case SCULPT_TOOL_DRAW: @@ -1513,6 +2354,12 @@ static void do_brush_action(Sculpt *sd, SculptSession *ss, StrokeCache *cache) case SCULPT_TOOL_SMOOTH: do_smooth_brush(sd, ss, nodes, totnode); break; + case SCULPT_TOOL_CREASE: + do_crease_brush(sd, ss, nodes, totnode); + break; + case SCULPT_TOOL_BLOB: + do_crease_brush(sd, ss, nodes, totnode); + break; case SCULPT_TOOL_PINCH: do_pinch_brush(sd, ss, nodes, totnode); break; @@ -1522,57 +2369,244 @@ static void do_brush_action(Sculpt *sd, SculptSession *ss, StrokeCache *cache) case SCULPT_TOOL_GRAB: do_grab_brush(sd, ss, nodes, totnode); break; + case SCULPT_TOOL_ROTATE: + do_rotate_brush(sd, ss, nodes, totnode); + break; + case SCULPT_TOOL_SNAKE_HOOK: + do_snake_hook_brush(sd, ss, nodes, totnode); + break; + case SCULPT_TOOL_NUDGE: + do_nudge_brush(sd, ss, nodes, totnode); + break; + case SCULPT_TOOL_THUMB: + do_thumb_brush(sd, ss, nodes, totnode); + break; case SCULPT_TOOL_LAYER: do_layer_brush(sd, ss, nodes, totnode); break; case SCULPT_TOOL_FLATTEN: - do_flatten_clay_brush(sd, ss, nodes, totnode, 0); + do_flatten_brush(sd, ss, nodes, totnode); break; case SCULPT_TOOL_CLAY: - do_flatten_clay_brush(sd, ss, nodes, totnode, 1); + do_clay_brush(sd, ss, nodes, totnode); + break; + case SCULPT_TOOL_CLAY_TUBES: + do_clay_tubes_brush(sd, ss, nodes, totnode); + break; + case SCULPT_TOOL_FILL: + do_fill_brush(sd, ss, nodes, totnode); + break; + case SCULPT_TOOL_SCRAPE: + do_scrape_brush(sd, ss, nodes, totnode); break; } + if (brush->sculpt_tool != SCULPT_TOOL_SMOOTH && brush->autosmooth_factor > 0) { + if (brush->flag & BRUSH_INVERSE_SMOOTH_PRESSURE) { + smooth(sd, ss, nodes, totnode, brush->autosmooth_factor*(1-ss->cache->pressure)*ss->cache->autosmooth_overlap); + } + else { + smooth(sd, ss, nodes, totnode, brush->autosmooth_factor*ss->cache->autosmooth_overlap); + } + } + + /* copy the modified vertices from mesh to the active key */ + if(ss->kb) + mesh_to_key(ss->ob->data, ss->kb); + /* optimization: we could avoid copying new coords to keyblock at each */ /* stroke step if there are no modifiers due to pbvh is used for displaying */ /* so to increase speed we'll copy new coords to keyblock when stroke is done */ if(ss->kb && ss->modifiers_active) sculpt_update_keyblock(ss); - if(nodes) - MEM_freeN(nodes); + MEM_freeN(nodes); + } +} + +static void sculpt_combine_proxies(Sculpt *sd, SculptSession *ss) +{ + Brush *brush= paint_brush(&sd->paint); + PBVHNode** nodes; + int totnode; + int n; + + BLI_pbvh_gather_proxies(ss->pbvh, &nodes, &totnode); + + switch (brush->sculpt_tool) { + case SCULPT_TOOL_GRAB: + case SCULPT_TOOL_ROTATE: + case SCULPT_TOOL_THUMB: + #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) + for (n= 0; n < totnode; n++) { + PBVHVertexIter vd; + PBVHProxyNode* proxies; + int proxy_count; + float (*origco)[3]; + + origco= sculpt_undo_push_node(ss, nodes[n])->co; + + BLI_pbvh_node_get_proxies(nodes[n], &proxies, &proxy_count); + + BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { + float val[3]; + int p; + + copy_v3_v3(val, origco[vd.i]); + + for (p= 0; p < proxy_count; p++) + add_v3_v3(val, proxies[p].co[vd.i]); + + sculpt_clip(sd, ss, vd.co, val); + } + BLI_pbvh_vertex_iter_end; + + BLI_pbvh_node_free_proxies(nodes[n]); + } + + break; + + case SCULPT_TOOL_DRAW: + case SCULPT_TOOL_CLAY: + case SCULPT_TOOL_CLAY_TUBES: + case SCULPT_TOOL_CREASE: + case SCULPT_TOOL_BLOB: + case SCULPT_TOOL_FILL: + case SCULPT_TOOL_FLATTEN: + case SCULPT_TOOL_INFLATE: + case SCULPT_TOOL_NUDGE: + case SCULPT_TOOL_PINCH: + case SCULPT_TOOL_SCRAPE: + case SCULPT_TOOL_SNAKE_HOOK: + #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) + for (n= 0; n < totnode; n++) { + PBVHVertexIter vd; + PBVHProxyNode* proxies; + int proxy_count; + + BLI_pbvh_node_get_proxies(nodes[n], &proxies, &proxy_count); + + BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { + float val[3]; + int p; + + copy_v3_v3(val, vd.co); + + for (p= 0; p < proxy_count; p++) + add_v3_v3(val, proxies[p].co[vd.i]); + + sculpt_clip(sd, ss, vd.co, val); + } + BLI_pbvh_vertex_iter_end; + + BLI_pbvh_node_free_proxies(nodes[n]); + + } + + break; + + case SCULPT_TOOL_SMOOTH: + case SCULPT_TOOL_LAYER: + default: + break; } + + if (nodes) + MEM_freeN(nodes); } +//static int max_overlap_count(Sculpt *sd) +//{ +// int count[3]; +// int i, j; +// +// for (i= 0; i < 3; i++) { +// count[i] = sd->radial_symm[i]; +// +// for (j= 0; j < 3; j++) { +// if (i != j && sd->flags & (SCULPT_SYMM_X<location, cache->true_location, symm); - flip_coord(cache->view_normal_symmetry, cache->view_normal, symm); flip_coord(cache->grab_delta_symmetry, cache->grab_delta, symm); - cache->symmetry= symm; + flip_coord(cache->view_normal, cache->true_view_normal, symm); + + // XXX This reduces the length of the grab delta if it approaches the line of symmetry + // XXX However, a different approach appears to be needed + //if (sd->flags & SCULPT_SYMMETRY_FEATHER) { + // float frac = 1.0f/max_overlap_count(sd); + // float reduce = (feather-frac)/(1-frac); + + // printf("feather: %f frac: %f reduce: %f\n", feather, frac, reduce); + + // if (frac < 1) + // mul_v3_fl(cache->grab_delta_symmetry, reduce); + //} + + unit_m4(cache->symm_rot_mat); + unit_m4(cache->symm_rot_mat_inv); + rotate_m4(cache->symm_rot_mat, axis, angle); + rotate_m4(cache->symm_rot_mat_inv, axis, -angle); + + mul_m4_v3(cache->symm_rot_mat, cache->location); + mul_m4_v3(cache->symm_rot_mat, cache->grab_delta_symmetry); +} + +static void do_radial_symmetry(Sculpt *sd, SculptSession *ss, Brush *brush, const char symm, const int axis, const float feather) +{ + int i; + + for(i = 1; i < sd->radial_symm[axis-'X']; ++i) { + const float angle = 2*M_PI*i/sd->radial_symm[axis-'X']; + ss->cache->radial_symmetry_pass= i; + calc_brushdata_symm(sd, ss->cache, symm, axis, angle, feather); + do_brush_action(sd, ss, brush); + } } static void do_symmetrical_brush_actions(Sculpt *sd, SculptSession *ss) { + Brush *brush = paint_brush(&sd->paint); StrokeCache *cache = ss->cache; const char symm = sd->flags & 7; int i; - copy_v3_v3(cache->location, cache->true_location); - copy_v3_v3(cache->grab_delta_symmetry, cache->grab_delta); - cache->symmetry = 0; - cache->bstrength = brush_strength(sd, cache); - do_brush_action(sd, ss, cache); + float feather = calc_symmetry_feather(sd, ss->cache); + float accum = integrate_overlap(brush); + float overlap = (brush->flag & BRUSH_SPACE_ATTEN && brush->flag & BRUSH_SPACE && !(brush->flag & BRUSH_ANCHORED)) && (brush->spacing < 100) ? 1.0f/accum : 1; // spacing is integer percentage of radius, divide by 50 to get normalized diameter + + ss->cache->autosmooth_overlap = overlap; + + cache->bstrength= brush_strength(sd, cache, feather, overlap); + + cache->symmetry= symm; + + /* symm is a bit combination of XYZ - 1 is mirror X; 2 is Y; 3 is XY; 4 is Z; 5 is XZ; 6 is YZ; 7 is XYZ */ + for(i = 0; i <= symm; ++i) { + if(i == 0 || (symm & i && (symm != 5 || i != 3) && (symm != 6 || (i != 3 && i != 5)))) { + cache->mirror_symmetry_pass= i; + cache->radial_symmetry_pass= 0; - for(i = 1; i <= symm; ++i) { - if(symm & i && (symm != 5 || i != 3) && (symm != 6 || (i != 3 && i != 5))) { - calc_brushdata_symm(cache, i); - do_brush_action(sd, ss, cache); + calc_brushdata_symm(sd, cache, i, 0, 0, feather); + do_brush_action(sd, ss, brush); + + do_radial_symmetry(sd, ss, brush, i, 'X', feather); + do_radial_symmetry(sd, ss, brush, i, 'Y', feather); + do_radial_symmetry(sd, ss, brush, i, 'Z', feather); } } - cache->first_time = 0; + sculpt_combine_proxies(sd, ss); + + cache->first_time= 0; } static void sculpt_update_tex(Sculpt *sd, SculptSession *ss) @@ -1585,37 +2619,13 @@ static void sculpt_update_tex(Sculpt *sd, SculptSession *ss) } /* Need to allocate a bigger buffer for bigger brush size */ - ss->texcache_side = brush->size * 2; + ss->texcache_side = 2*sculpt_get_brush_size(brush); if(!ss->texcache || ss->texcache_side > ss->texcache_actual) { - ss->texcache = brush_gen_texture_cache(brush, brush->size); + ss->texcache = brush_gen_texture_cache(brush, sculpt_get_brush_size(brush)); ss->texcache_actual = ss->texcache_side; } } -/* Sculpt mode handles multires differently from regular meshes, but only if - it's the last modifier on the stack and it is not on the first level */ -struct MultiresModifierData *sculpt_multires_active(Scene *scene, Object *ob) -{ - ModifierData *md, *nmd; - - for(md= modifiers_getVirtualModifierList(ob); md; md= md->next) { - if(md->type == eModifierType_Multires) { - MultiresModifierData *mmd= (MultiresModifierData*)md; - - /* Check if any of the modifiers after multires are active - * if not it can use the multires struct */ - for(nmd= md->next; nmd; nmd= nmd->next) - if(modifier_isEnabled(scene, nmd, eModifierMode_Realtime)) - break; - - if(!nmd && mmd->sculptlvl > 0) - return mmd; - } - } - - return NULL; -} - void sculpt_update_mesh_elements(Scene *scene, Object *ob, int need_fmap) { DerivedMesh *dm = mesh_get_derived_final(scene, ob, CD_MASK_BAREMESH); @@ -1682,16 +2692,32 @@ static char *sculpt_tool_name(Sculpt *sd) return "Draw Brush"; break; case SCULPT_TOOL_SMOOTH: return "Smooth Brush"; break; + case SCULPT_TOOL_CREASE: + return "Crease Brush"; break; + case SCULPT_TOOL_BLOB: + return "Blob Brush"; break; case SCULPT_TOOL_PINCH: return "Pinch Brush"; break; case SCULPT_TOOL_INFLATE: return "Inflate Brush"; break; case SCULPT_TOOL_GRAB: return "Grab Brush"; break; + case SCULPT_TOOL_NUDGE: + return "Nudge Brush"; break; + case SCULPT_TOOL_THUMB: + return "Thumb Brush"; break; case SCULPT_TOOL_LAYER: return "Layer Brush"; break; case SCULPT_TOOL_FLATTEN: - return "Flatten Brush"; break; + return "Flatten Brush"; break; + case SCULPT_TOOL_CLAY: + return "Clay Brush"; break; + case SCULPT_TOOL_CLAY_TUBES: + return "Clay Tubes Brush"; break; + case SCULPT_TOOL_FILL: + return "Fill Brush"; break; + case SCULPT_TOOL_SCRAPE: + return "Scrape Brush"; break; default: return "Sculpting"; break; } @@ -1769,20 +2795,119 @@ static void sculpt_cache_free(StrokeCache *cache) MEM_freeN(cache); } +int sculpt_get_brush_size(Brush *brush) +{ + return (U.sculpt_paint_settings & SCULPT_PAINT_USE_UNIFIED_SIZE) ? U.sculpt_paint_unified_size : brush->size; +} + +void sculpt_set_brush_size(Brush *brush, int size) +{ + if (U.sculpt_paint_settings & SCULPT_PAINT_USE_UNIFIED_SIZE) + U.sculpt_paint_unified_size = size; + else + brush->size = size; +} + +int sculpt_get_lock_brush_size(Brush *brush) +{ + return (U.sculpt_paint_settings & SCULPT_PAINT_USE_UNIFIED_SIZE) ? (U.sculpt_paint_settings & SCULPT_PAINT_UNIFIED_LOCK_BRUSH_SIZE) : (brush->flag & BRUSH_LOCK_SIZE); +} + +float sculpt_get_brush_unprojected_radius(Brush *brush) +{ + return (U.sculpt_paint_settings & SCULPT_PAINT_USE_UNIFIED_SIZE) ? U.sculpt_paint_unified_unprojected_radius : brush->unprojected_radius; +} + +void sculpt_set_brush_unprojected_radius(Brush *brush, float unprojected_radius) +{ + if (U.sculpt_paint_settings & SCULPT_PAINT_USE_UNIFIED_SIZE) + U.sculpt_paint_unified_unprojected_radius = unprojected_radius; + else + brush->unprojected_radius = unprojected_radius; +} + +float sculpt_get_brush_alpha(Brush *brush) +{ + return (U.sculpt_paint_settings & SCULPT_PAINT_USE_UNIFIED_ALPHA) ? U.sculpt_paint_unified_alpha : brush->alpha; +} + +void sculpt_set_brush_alpha(Brush *brush, float alpha) +{ + if (U.sculpt_paint_settings & SCULPT_PAINT_USE_UNIFIED_ALPHA) + U.sculpt_paint_unified_alpha = alpha; + else + brush->alpha = alpha; +} + /* Initialize the stroke cache invariants from operator properties */ -static void sculpt_update_cache_invariants(Sculpt *sd, SculptSession *ss, bContext *C, wmOperator *op) +static void sculpt_update_cache_invariants(bContext* C, Sculpt *sd, SculptSession *ss, wmOperator *op, wmEvent *event) { StrokeCache *cache = MEM_callocN(sizeof(StrokeCache), "stroke cache"); Brush *brush = paint_brush(&sd->paint); ViewContext *vc = paint_stroke_view_context(op->customdata); + Object *ob= CTX_data_active_object(C); + ModifierData *md; int i; + int mode; ss->cache = cache; - RNA_float_get_array(op->ptr, "scale", cache->scale); - cache->flag = RNA_int_get(op->ptr, "flag"); - RNA_float_get_array(op->ptr, "clip_tolerance", cache->clip_tolerance); - RNA_float_get_array(op->ptr, "initial_mouse", cache->initial_mouse); + /* Set scaling adjustment */ + ss->cache->scale[0] = 1.0f / ob->size[0]; + ss->cache->scale[1] = 1.0f / ob->size[1]; + ss->cache->scale[2] = 1.0f / ob->size[2]; + + ss->cache->plane_trim_squared = brush->plane_trim * brush->plane_trim; + + /* Initialize mirror modifier clipping */ + + ss->cache->flag = 0; + + for(md= ob->modifiers.first; md; md= md->next) { + if(md->type==eModifierType_Mirror && (md->mode & eModifierMode_Realtime)) { + const MirrorModifierData *mmd = (MirrorModifierData*) md; + + /* Mark each axis that needs clipping along with its tolerance */ + if(mmd->flag & MOD_MIR_CLIPPING) { + ss->cache->flag |= CLIP_X << mmd->axis; + if(mmd->tolerance > ss->cache->clip_tolerance[mmd->axis]) + ss->cache->clip_tolerance[mmd->axis] = mmd->tolerance; + } + } + } + + /* Initial mouse location */ + if (event) { + ss->cache->initial_mouse[0] = event->x; + ss->cache->initial_mouse[1] = event->y; + } + else { + ss->cache->initial_mouse[0] = 0; + ss->cache->initial_mouse[1] = 0; + } + + mode = RNA_int_get(op->ptr, "mode"); + cache->invert = mode == WM_BRUSHSTROKE_INVERT; + cache->alt_smooth = mode == WM_BRUSHSTROKE_SMOOTH; + + /* Alt-Smooth */ + if (ss->cache->alt_smooth) { + Paint *p= &sd->paint; + Brush *br; + int i; + + BLI_strncpy(cache->saved_active_brush_name, brush->id.name+2, sizeof(cache->saved_active_brush_name)); + + for(i = 0; i < p->brush_count; ++i) { + br = p->brushes[i]; + + if (strcmp(br->id.name+2, "Smooth")==0) { + paint_brush_set(p, br); + brush = br; + break; + } + } + } copy_v2_v2(cache->mouse, cache->initial_mouse); copy_v2_v2(cache->tex_mouse, cache->initial_mouse); @@ -1790,11 +2915,13 @@ static void sculpt_update_cache_invariants(Sculpt *sd, SculptSession *ss, bConte /* Truly temporary data that isn't stored in properties */ cache->vc = vc; + cache->brush = brush; cache->mats = MEM_callocN(sizeof(bglMats), "sculpt bglMats"); view3d_get_transformation(vc->ar, vc->rv3d, vc->obact, cache->mats); + viewvector(cache->vc->rv3d, cache->vc->rv3d->twmat[3], cache->true_view_normal); /* Initialize layer brush displacements and persistent coords */ if(brush->sculpt_tool == SCULPT_TOOL_LAYER) { /* not supported yet for multires */ @@ -1820,69 +2947,143 @@ static void sculpt_update_cache_invariants(Sculpt *sd, SculptSession *ss, bConte cache->original = 1; } - if(ELEM3(brush->sculpt_tool, SCULPT_TOOL_DRAW, SCULPT_TOOL_LAYER, SCULPT_TOOL_INFLATE)) + if(ELEM7(brush->sculpt_tool, SCULPT_TOOL_DRAW, SCULPT_TOOL_CREASE, SCULPT_TOOL_BLOB, SCULPT_TOOL_LAYER, SCULPT_TOOL_INFLATE, SCULPT_TOOL_CLAY, SCULPT_TOOL_CLAY_TUBES)) if(!(brush->flag & BRUSH_ACCUMULATE)) cache->original = 1; - cache->rotation = 0; - cache->first_time = 1; + cache->special_rotation = (brush->flag & BRUSH_RAKE) ? sd->last_angle : 0; + //cache->last_rake[0] = sd->last_x; + //cache->last_rake[1] = sd->last_y; + + cache->first_time= 1; + + cache->vertex_rotation= 0; } /* Initialize the stroke cache variants from operator properties */ -static void sculpt_update_cache_variants(Sculpt *sd, SculptSession *ss, struct PaintStroke *stroke, PointerRNA *ptr) +static void sculpt_update_cache_variants(bContext *C, Sculpt *sd, SculptSession *ss, struct PaintStroke *stroke, PointerRNA *ptr) { StrokeCache *cache = ss->cache; Brush *brush = paint_brush(&sd->paint); - + int dx, dy; - if(!(brush->flag & BRUSH_ANCHORED) || cache->first_time) + //RNA_float_get_array(ptr, "location", cache->traced_location); + + if (cache->first_time || + !((brush->flag & BRUSH_ANCHORED)|| + (brush->sculpt_tool == SCULPT_TOOL_SNAKE_HOOK)|| + (brush->sculpt_tool == SCULPT_TOOL_ROTATE)) + ) + { RNA_float_get_array(ptr, "location", cache->true_location); - cache->flip = RNA_boolean_get(ptr, "flip"); + } + + cache->pen_flip = RNA_boolean_get(ptr, "pen_flip"); RNA_float_get_array(ptr, "mouse", cache->mouse); + cache->pressure = RNA_float_get(ptr, "pressure"); - + /* Truly temporary data that isn't stored in properties */ + sd->draw_pressure= 1; + sd->pressure_value= cache->pressure; + cache->previous_pixel_radius = cache->pixel_radius; - cache->pixel_radius = brush->size; + cache->pixel_radius = sculpt_get_brush_size(brush); + + if(cache->first_time) { + if (!sculpt_get_lock_brush_size(brush)) { + cache->initial_radius= unproject_brush_radius(ss->ob, cache->vc, cache->true_location, sculpt_get_brush_size(brush)); + sculpt_set_brush_unprojected_radius(brush, cache->initial_radius); + } + else { + cache->initial_radius= sculpt_get_brush_unprojected_radius(brush); + } - if(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->flag & BRUSH_SIZE_PRESSURE) { cache->pixel_radius *= cache->pressure; - cache->radius = cache->initial_radius * cache->pressure; + cache->radius= cache->initial_radius * cache->pressure; } else - cache->radius = cache->initial_radius; + cache->radius= cache->initial_radius; - if(!(brush->flag & BRUSH_ANCHORED)) + cache->radius_squared = cache->radius*cache->radius; + + if(!(brush->flag & BRUSH_ANCHORED || ELEM4(brush->sculpt_tool, SCULPT_TOOL_GRAB, SCULPT_TOOL_SNAKE_HOOK, SCULPT_TOOL_THUMB, SCULPT_TOOL_ROTATE))) { copy_v2_v2(cache->tex_mouse, cache->mouse); + if ( (brush->mtex.brush_map_mode == MTEX_MAP_MODE_FIXED) && + (brush->flag & BRUSH_RANDOM_ROTATION) && + !(brush->flag & BRUSH_RAKE)) + { + cache->special_rotation = 2*M_PI*BLI_frand(); + } + } + if(brush->flag & BRUSH_ANCHORED) { + int hit = 0; + dx = cache->mouse[0] - cache->initial_mouse[0]; dy = cache->mouse[1] - cache->initial_mouse[1]; - cache->pixel_radius = sqrt(dx*dx + dy*dy); - cache->radius = unproject_brush_radius(ss->ob, paint_stroke_view_context(stroke), - cache->true_location, cache->pixel_radius); - cache->rotation = atan2(dy, dx); + + sd->anchored_size = cache->pixel_radius = sqrt(dx*dx + dy*dy); + + cache->special_rotation = atan2(dx, dy) + M_PI; + + if (brush->flag & BRUSH_EDGE_TO_EDGE) { + float d[3]; + float halfway[3]; + float out[3]; + + d[0] = dx; + d[1] = dy; + d[2] = 0; + + mul_v3_v3fl(halfway, d, 0.5f); + add_v3_v3(halfway, cache->initial_mouse); + + if (sculpt_stroke_get_location(C, stroke, out, halfway)) { + copy_v3_v3(sd->anchored_location, out); + copy_v3_v3(sd->anchored_initial_mouse, halfway); + copy_v2_v2(cache->tex_mouse, halfway); + copy_v3_v3(cache->true_location, sd->anchored_location); + sd->anchored_size /= 2.0f; + cache->pixel_radius /= 2.0f; + hit = 1; + } + } + + if (!hit) + copy_v2_v2(sd->anchored_initial_mouse, cache->initial_mouse); + + cache->radius= unproject_brush_radius(ss->ob, paint_stroke_view_context(stroke), cache->true_location, cache->pixel_radius); + cache->radius_squared = cache->radius*cache->radius; + + copy_v3_v3(sd->anchored_location, cache->true_location); + + sd->draw_anchored = 1; } else if(brush->flag & BRUSH_RAKE) { - int update; + const float u = 0.5f; + const float v = 1 - u; + const float r = 20; - dx = cache->last_rake[0] - cache->mouse[0]; - dy = cache->last_rake[1] - cache->mouse[1]; + const float dx = cache->last_rake[0] - cache->mouse[0]; + const float dy = cache->last_rake[1] - cache->mouse[1]; - update = dx*dx + dy*dy > 100; - - /* To prevent jitter, only update the angle if the mouse has moved over 10 pixels */ - if(update && !cache->first_time) - cache->rotation = M_PI_2 + atan2(dy, dx); + if (cache->first_time) { + copy_v3_v3(cache->last_rake, cache->mouse); + } + else if (dx*dx + dy*dy >= r*r) { + cache->special_rotation = atan2(dx, dy); - if(update || cache->first_time) { - cache->last_rake[0] = cache->mouse[0]; - cache->last_rake[1] = cache->mouse[1]; + cache->last_rake[0] = u*cache->last_rake[0] + v*cache->mouse[0]; + cache->last_rake[1] = u*cache->last_rake[1] + v*cache->mouse[1]; } } @@ -1900,16 +3101,123 @@ static void sculpt_update_cache_variants(Sculpt *sd, SculptSession *ss, struct P /* compute delta to move verts by */ if(!cache->first_time) { + float delta[3]; + sub_v3_v3v3(delta, grab_location, cache->old_grab_location); + invert_m4_m4(imat, ss->ob->obmat); + mul_mat3_m4_v3(imat, delta); + add_v3_v3(cache->grab_delta, delta); + } + else { + zero_v3(cache->grab_delta); + } + + copy_v3_v3(cache->old_grab_location, grab_location); + + /* location stays the same for finding vertices in brush radius */ + copy_v3_v3(cache->true_location, cache->orig_grab_location); + + sd->draw_anchored = 1; + copy_v3_v3(sd->anchored_location, cache->true_location); + copy_v3_v3(sd->anchored_initial_mouse, cache->initial_mouse); + sd->anchored_size = cache->pixel_radius; + } + /* Find the nudge/clay tubes delta */ + else if(brush->sculpt_tool == SCULPT_TOOL_NUDGE || brush->sculpt_tool == SCULPT_TOOL_CLAY_TUBES) { + float grab_location[3], imat[4][4]; + + if(cache->first_time) + copy_v3_v3(cache->orig_grab_location, cache->true_location); + + /* compute 3d coordinate at same z from original location + mouse */ + initgrabz(cache->vc->rv3d, cache->orig_grab_location[0], + cache->orig_grab_location[1], cache->orig_grab_location[2]); + window_to_3d_delta(cache->vc->ar, grab_location, cache->mouse[0], cache->mouse[1]); + + /* compute delta to move verts by */ + if (!cache->first_time) { + sub_v3_v3v3(cache->grab_delta, grab_location, cache->old_grab_location); + invert_m4_m4(imat, ss->ob->obmat); + mul_mat3_m4_v3(imat, cache->grab_delta); + } + else { + zero_v3(cache->grab_delta); + } + + copy_v3_v3(cache->old_grab_location, grab_location); + } + /* Find the snake hook delta */ + else if(brush->sculpt_tool == SCULPT_TOOL_SNAKE_HOOK) { + float grab_location[3], imat[4][4]; + + if(cache->first_time) + copy_v3_v3(cache->orig_grab_location, cache->true_location); + else + add_v3_v3(cache->true_location, cache->grab_delta); + + /* compute 3d coordinate at same z from original location + mouse */ + initgrabz(cache->vc->rv3d, cache->orig_grab_location[0], + cache->orig_grab_location[1], cache->orig_grab_location[2]); + window_to_3d_delta(cache->vc->ar, grab_location, cache->mouse[0], cache->mouse[1]); + + /* compute delta to move verts by */ + if (!cache->first_time) { sub_v3_v3v3(cache->grab_delta, grab_location, cache->old_grab_location); invert_m4_m4(imat, ss->ob->obmat); mul_mat3_m4_v3(imat, cache->grab_delta); } + else { + zero_v3(cache->grab_delta); + } + + copy_v3_v3(cache->old_grab_location, grab_location); + } + /* Find the thumb delta */ + else if(brush->sculpt_tool == SCULPT_TOOL_THUMB) { + float grab_location[3], imat[4][4]; + + if(cache->first_time) + copy_v3_v3(cache->orig_grab_location, cache->true_location); + + /* compute 3d coordinate at same z from original location + mouse */ + initgrabz(cache->vc->rv3d, cache->orig_grab_location[0], + cache->orig_grab_location[1], cache->orig_grab_location[2]); + window_to_3d_delta(cache->vc->ar, grab_location, cache->mouse[0], cache->mouse[1]); + + /* compute delta to move verts by */ + if (!cache->first_time) { + float delta[3]; + sub_v3_v3v3(delta, grab_location, cache->old_grab_location); + invert_m4_m4(imat, ss->ob->obmat); + mul_mat3_m4_v3(imat, delta); + add_v3_v3(cache->grab_delta, delta); + } + else { + zero_v3(cache->grab_delta); + } copy_v3_v3(cache->old_grab_location, grab_location); /* location stays the same for finding vertices in brush radius */ copy_v3_v3(cache->true_location, cache->orig_grab_location); + + sd->draw_anchored = 1; + copy_v3_v3(sd->anchored_location, cache->orig_grab_location); + copy_v3_v3(sd->anchored_initial_mouse, cache->initial_mouse); + sd->anchored_size = cache->pixel_radius; } + else if(brush->sculpt_tool == SCULPT_TOOL_ROTATE) { + dx = cache->mouse[0] - cache->initial_mouse[0]; + dy = cache->mouse[1] - cache->initial_mouse[1]; + + cache->vertex_rotation = -atan2(dx, dy) / 4.0f; + + sd->draw_anchored = 1; + copy_v2_v2(sd->anchored_initial_mouse, cache->initial_mouse); + copy_v3_v3(sd->anchored_location, cache->true_location); + sd->anchored_size = cache->pixel_radius; + } + + sd->special_rotation = cache->special_rotation; } static void sculpt_stroke_modifiers_check(bContext *C, SculptSession *ss) @@ -1930,19 +3238,23 @@ typedef struct { int original; } SculptRaycastData; -void sculpt_raycast_cb(PBVHNode *node, void *data_v) +void sculpt_raycast_cb(PBVHNode *node, void *data_v, float* tmin) { - SculptRaycastData *srd = data_v; - float (*origco)[3]= NULL; + if (BLI_pbvh_node_get_tmin(node) < *tmin) { + SculptRaycastData *srd = data_v; + float (*origco)[3]= NULL; - if(srd->original && srd->ss->cache) { - /* intersect with coordinates from before we started stroke */ - SculptUndoNode *unode= sculpt_undo_get_node(srd->ss, node); - origco= (unode)? unode->co: NULL; - } + if(srd->original && srd->ss->cache) { + /* intersect with coordinates from before we started stroke */ + SculptUndoNode *unode= sculpt_undo_get_node(node); + origco= (unode)? unode->co: NULL; + } - srd->hit |= BLI_pbvh_node_raycast(srd->ss->pbvh, node, origco, - srd->ray_start, srd->ray_normal, &srd->dist); + if (BLI_pbvh_node_raycast(srd->ss->pbvh, node, origco, srd->ray_start, srd->ray_normal, &srd->dist)) { + srd->hit = 1; + *tmin = srd->dist; + } + } } /* Do a raycast in the tree to find the 3d brush location @@ -1956,10 +3268,12 @@ int sculpt_stroke_get_location(bContext *C, struct PaintStroke *stroke, float ou StrokeCache *cache= ss->cache; float ray_start[3], ray_end[3], ray_normal[3], dist; float obimat[4][4]; - float mval[2] = {mouse[0] - vc->ar->winrct.xmin, - mouse[1] - vc->ar->winrct.ymin}; + float mval[2]; SculptRaycastData srd; + mval[0] = mouse[0] - vc->ar->winrct.xmin; + mval[1] = mouse[1] - vc->ar->winrct.ymin; + sculpt_stroke_modifiers_check(C, ss); viewline(vc->ar, vc->v3d, mval, ray_start, ray_end); @@ -1987,43 +3301,6 @@ int sculpt_stroke_get_location(bContext *C, struct PaintStroke *stroke, float ou return srd.hit; } -/* Initialize stroke operator properties */ -static void sculpt_brush_stroke_init_properties(bContext *C, wmOperator *op, wmEvent *event, SculptSession *ss) -{ - Object *ob= CTX_data_active_object(C); - ModifierData *md; - float scale[3], clip_tolerance[3] = {0,0,0}; - float mouse[2]; - int flag = 0; - - /* Set scaling adjustment */ - scale[0] = 1.0f / ob->size[0]; - scale[1] = 1.0f / ob->size[1]; - scale[2] = 1.0f / ob->size[2]; - RNA_float_set_array(op->ptr, "scale", scale); - - /* Initialize mirror modifier clipping */ - for(md= ob->modifiers.first; md; md= md->next) { - if(md->type==eModifierType_Mirror && (md->mode & eModifierMode_Realtime)) { - const MirrorModifierData *mmd = (MirrorModifierData*) md; - - /* Mark each axis that needs clipping along with its tolerance */ - if(mmd->flag & MOD_MIR_CLIPPING) { - flag |= CLIP_X << mmd->axis; - if(mmd->tolerance > clip_tolerance[mmd->axis]) - clip_tolerance[mmd->axis] = mmd->tolerance; - } - } - } - RNA_int_set(op->ptr, "flag", flag); - RNA_float_set_array(op->ptr, "clip_tolerance", clip_tolerance); - - /* Initial mouse location */ - mouse[0] = event->x; - mouse[1] = event->y; - RNA_float_set_array(op->ptr, "initial_mouse", mouse); -} - static int sculpt_brush_stroke_init(bContext *C, ReportList *reports) { Scene *scene= CTX_data_scene(C); @@ -2051,22 +3328,26 @@ static int sculpt_brush_stroke_init(bContext *C, ReportList *reports) static void sculpt_restore_mesh(Sculpt *sd, SculptSession *ss) { - StrokeCache *cache = ss->cache; Brush *brush = paint_brush(&sd->paint); - int i; /* Restore the mesh before continuing with anchored stroke */ - if(brush->flag & BRUSH_ANCHORED) { + if((brush->flag & BRUSH_ANCHORED) || + (brush->sculpt_tool == SCULPT_TOOL_GRAB && (brush->flag & BRUSH_SIZE_PRESSURE)) || + (brush->flag & BRUSH_RESTORE_MESH)) + { + StrokeCache *cache = ss->cache; + int i; + PBVHNode **nodes; int n, totnode; BLI_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, &totnode); - //#pragma omp parallel for private(n) schedule(static) + #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) for(n=0; nco[vd.i]); if(vd.no) VECCOPY(vd.no, unode->no[vd.i]) else normal_short_to_float_v3(vd.fno, unode->no[vd.i]); + + if(vd.mvert) vd.mvert->flag |= ME_VERT_PBVH_UPDATE; } BLI_pbvh_vertex_iter_end; + + BLI_pbvh_node_mark_update(nodes[n]); } } @@ -2096,7 +3381,6 @@ static void sculpt_flush_update(bContext *C) SculptSession *ss = ob->sculpt; ARegion *ar = CTX_wm_region(C); MultiresModifierData *mmd = ss->multires; - int redraw = 0; if(mmd) multires_mark_as_modified(ob); @@ -2109,14 +3393,22 @@ static void sculpt_flush_update(bContext *C) rcti r; BLI_pbvh_update(ss->pbvh, PBVH_UpdateBB, NULL); - redraw = sculpt_get_redraw_rect(ar, CTX_wm_region_view3d(C), ob, &r); - if(redraw) { + if (sculpt_get_redraw_rect(ar, CTX_wm_region_view3d(C), ob, &r)) { + //rcti tmp; + r.xmin += ar->winrct.xmin + 1; r.xmax += ar->winrct.xmin - 1; r.ymin += ar->winrct.ymin + 1; r.ymax += ar->winrct.ymin - 1; - + + //tmp = r; + + //if (!BLI_rcti_is_empty(&ss->previous_r)) + // BLI_union_rcti(&r, &ss->previous_r); + + //ss->previous_r= tmp; + ss->partial_redraw = 1; ED_region_tag_redraw_partial(ar, &r); } @@ -2127,9 +3419,12 @@ static void sculpt_flush_update(bContext *C) or over the background (0) */ static int over_mesh(bContext *C, struct wmOperator *op, float x, float y) { - float mouse[2] = {x, y}, co[3]; - - return (int)sculpt_stroke_get_location(C, op->customdata, co, mouse); + float mouse[2], co[3]; + + mouse[0] = x; + mouse[1] = y; + + return sculpt_stroke_get_location(C, op->customdata, co, mouse); } static int sculpt_stroke_test_start(bContext *C, struct wmOperator *op, @@ -2143,11 +3438,22 @@ static int sculpt_stroke_test_start(bContext *C, struct wmOperator *op, ED_view3d_init_mats_rv3d(ob, CTX_wm_region_view3d(C)); - sculpt_brush_stroke_init_properties(C, op, event, ss); + sculpt_update_cache_invariants(C, sd, ss, op, event); + + sculpt_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; } @@ -2161,7 +3467,7 @@ static void sculpt_stroke_update_step(bContext *C, struct PaintStroke *stroke, P SculptSession *ss = CTX_data_active_object(C)->sculpt; sculpt_stroke_modifiers_check(C, ss); - sculpt_update_cache_variants(sd, ss, stroke, itemptr); + sculpt_update_cache_variants(C, sd, ss, stroke, itemptr); sculpt_restore_mesh(sd, ss); do_symmetrical_brush_actions(sd, ss); @@ -2169,19 +3475,43 @@ static void sculpt_stroke_update_step(bContext *C, struct PaintStroke *stroke, P sculpt_flush_update(C); } -static void sculpt_stroke_done(bContext *C, struct PaintStroke *stroke) +static void sculpt_stroke_done(bContext *C, struct PaintStroke *unused) { Object *ob= CTX_data_active_object(C); SculptSession *ss = ob->sculpt; + Sculpt *sd = CTX_data_tool_settings(C)->sculpt; + + (void)unused; + + // reset values used to draw brush after completing the stroke + sd->draw_anchored= 0; + sd->draw_pressure= 0; + sd->special_rotation= 0; /* Finished */ if(ss->cache) { sculpt_stroke_modifiers_check(C, ss); + /* Alt-Smooth */ + if (ss->cache->alt_smooth) { + Paint *p= &sd->paint; + Brush *br; + int i; + + for(i = 0; i < p->brush_count; ++i) { + br = p->brushes[i]; + + if (strcmp(br->id.name+2, ss->cache->saved_active_brush_name)==0) { + paint_brush_set(p, br); + break; + } + } + } + sculpt_cache_free(ss->cache); ss->cache = NULL; - sculpt_undo_push_end(ss); + sculpt_undo_push_end(); BLI_pbvh_update(ss->pbvh, PBVH_UpdateOriginalBB, NULL); @@ -2218,6 +3548,7 @@ static int sculpt_brush_stroke_invoke(bContext *C, wmOperator *op, wmEvent *even /* For tablet rotation */ ignore_background_click = RNA_boolean_get(op->ptr, "ignore_background_click"); + if(ignore_background_click && !over_mesh(C, op, event->x, event->y)) { paint_stroke_free(stroke); return OPERATOR_PASS_THROUGH; @@ -2242,7 +3573,7 @@ static int sculpt_brush_stroke_exec(bContext *C, wmOperator *op) op->customdata = paint_stroke_new(C, sculpt_stroke_get_location, sculpt_stroke_test_start, sculpt_stroke_update_step, sculpt_stroke_done); - sculpt_update_cache_invariants(sd, ss, C, op); + sculpt_update_cache_invariants(C, sd, ss, op, NULL); paint_stroke_exec(C, op); @@ -2254,7 +3585,12 @@ static int sculpt_brush_stroke_exec(bContext *C, wmOperator *op) static void SCULPT_OT_brush_stroke(wmOperatorType *ot) { - ot->flag |= OPTYPE_REGISTER; + static EnumPropertyItem stroke_mode_items[] = { + { WM_BRUSHSTROKE_NORMAL, "NORMAL", 0, "Normal", "Apply brush normally" }, + { WM_BRUSHSTROKE_INVERT, "INVERT", 0, "Invert", "Invert action of brush for duration of stroke" }, + { WM_BRUSHSTROKE_SMOOTH, "SMOOTH", 0, "Smooth", "Switch brush to smooth mode for duration of stroke" }, + { 0 } + }; /* identifiers */ ot->name= "Sculpt Mode"; @@ -2265,36 +3601,32 @@ static void SCULPT_OT_brush_stroke(wmOperatorType *ot) ot->modal= paint_stroke_modal; ot->exec= sculpt_brush_stroke_exec; ot->poll= sculpt_poll; - + /* flags (sculpt does own undo? (ton) */ - ot->flag= OPTYPE_REGISTER|OPTYPE_BLOCKING; + ot->flag= OPTYPE_BLOCKING; /* properties */ - RNA_def_collection_runtime(ot->srna, "stroke", &RNA_OperatorStrokeElement, "Stroke", ""); - - /* If the object has a scaling factor, brushes also need to be scaled - to work as expected. */ - RNA_def_float_vector(ot->srna, "scale", 3, NULL, 0.0f, FLT_MAX, "Scale", "", 0.0f, 1000.0f); - RNA_def_int(ot->srna, "flag", 0, 0, INT_MAX, "flag", "", 0, INT_MAX); + RNA_def_collection_runtime(ot->srna, "stroke", &RNA_OperatorStrokeElement, + "Stroke", ""); - /* For mirror modifiers */ - RNA_def_float_vector(ot->srna, "clip_tolerance", 3, NULL, 0.0f, FLT_MAX, "clip_tolerance", "", 0.0f, 1000.0f); - - /* The initial 2D location of the mouse */ - RNA_def_float_vector(ot->srna, "initial_mouse", 2, NULL, INT_MIN, INT_MAX, "initial_mouse", "", INT_MIN, INT_MAX); + RNA_def_enum(ot->srna, "mode", stroke_mode_items, WM_BRUSHSTROKE_NORMAL, + "Sculpt Stroke Mode", + "Action taken when a sculpt stroke is made"); RNA_def_boolean(ot->srna, "ignore_background_click", 0, "Ignore Background Click", - "Clicks on the background don't start the stroke"); + "Clicks on the background do not start the stroke"); } /**** Reset the copy of the mesh that is being sculpted on (currently just for the layer brush) ****/ -static int sculpt_set_persistent_base(bContext *C, wmOperator *op) +static int sculpt_set_persistent_base(bContext *C, wmOperator *unused) { SculptSession *ss = CTX_data_active_object(C)->sculpt; + (void)unused; + if(ss) { if(ss->layer_co) MEM_freeN(ss->layer_co); @@ -2314,7 +3646,7 @@ static void SCULPT_OT_set_persistent_base(wmOperatorType *ot) ot->exec= sculpt_set_persistent_base; ot->poll= sculpt_mode_poll; - ot->flag= OPTYPE_REGISTER; + ot->flag= 0;//OPTYPE_REGISTER; } /**** Toggle operator for turning sculpt mode on or off ****/ @@ -2322,18 +3654,21 @@ static void SCULPT_OT_set_persistent_base(wmOperatorType *ot) static void sculpt_init_session(Scene *scene, Object *ob) { ob->sculpt = MEM_callocN(sizeof(SculptSession), "sculpt session"); + ob->sculpt->ob = ob; sculpt_update_mesh_elements(scene, ob, 0); } -static int sculpt_toggle_mode(bContext *C, wmOperator *op) +static int sculpt_toggle_mode(bContext *C, wmOperator *unused) { Scene *scene = CTX_data_scene(C); ToolSettings *ts = CTX_data_tool_settings(C); Object *ob = CTX_data_active_object(C); - MultiresModifierData *mmd = sculpt_multires_active(scene, ob); + MultiresModifierData *mmd= sculpt_multires_active(scene, ob); int flush_recalc= 0; + (void)unused; + /* multires in sculpt mode could have different from object mode subdivision level */ flush_recalc |= mmd && mmd->sculptlvl != mmd->lvl; /* if object has got active modifiers, it's dm could be different in sculpt mode */ @@ -2359,9 +3694,13 @@ static int sculpt_toggle_mode(bContext *C, wmOperator *op) DAG_id_flush_update(&ob->id, OB_RECALC_DATA); /* Create persistent sculpt mode data */ - if(!ts->sculpt) + if(!ts->sculpt) { ts->sculpt = MEM_callocN(sizeof(Sculpt), "sculpt mode data"); + /* Turn on X plane mirror symmetry by default */ + ts->sculpt->flags |= SCULPT_SYMM_X; + } + /* Create sculpt mode session data */ if(ob->sculpt) free_sculptsession(ob); @@ -2388,7 +3727,7 @@ static void SCULPT_OT_sculptmode_toggle(wmOperatorType *ot) ot->exec= sculpt_toggle_mode; ot->poll= ED_operator_object_active; - ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; + ot->flag= 0; } void ED_operatortypes_sculpt() diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h index c7c6833b695..a46823a0b28 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.h +++ b/source/blender/editors/sculpt_paint/sculpt_intern.h @@ -32,6 +32,9 @@ #include "DNA_listBase.h" #include "DNA_vec_types.h" +#include "DNA_key_types.h" + +#include "BLI_pbvh.h" struct bContext; struct Brush; @@ -65,8 +68,49 @@ void sculpt_stroke_free(struct SculptStroke *); void sculpt_stroke_add_point(struct SculptStroke *, const short x, const short y); void sculpt_stroke_apply(struct Sculpt *sd, struct SculptStroke *); void sculpt_stroke_apply_all(struct Sculpt *sd, struct SculptStroke *); +int sculpt_stroke_get_location(bContext *C, struct PaintStroke *stroke, float out[3], float mouse[2]); /* Partial Mesh Visibility */ void sculptmode_pmv(int mode); +/* Undo */ + +typedef struct SculptUndoNode { + struct SculptUndoNode *next, *prev; + + char idname[MAX_ID_NAME]; /* name instead of pointer*/ + void *node; /* only during push, not valid afterwards! */ + + float (*co)[3]; + short (*no)[3]; + int totvert; + + /* non-multires */ + int maxvert; /* to verify if totvert it still the same */ + int *index; /* to restore into right location */ + + /* multires */ + int maxgrid; /* same for grid */ + int gridsize; /* same for grid */ + int totgrid; /* to restore into right location */ + int *grids; /* to restore into right location */ + + /* layer brush */ + float *layer_disp; + + /* shape keys */ + char *shapeName[32]; /* keep size in sync with keyblock dna */ +} SculptUndoNode; + +SculptUndoNode *sculpt_undo_push_node(SculptSession *ss, PBVHNode *node); +SculptUndoNode *sculpt_undo_get_node(PBVHNode *node); +void sculpt_undo_push_begin(char *name); +void sculpt_undo_push_end(void); + +struct MultiresModifierData *sculpt_multires_active(struct Scene *scene, struct Object *ob); +int sculpt_modifiers_active(Scene *scene, Object *ob); +void sculpt_vertcos_to_key(Object *ob, KeyBlock *kb, float (*vertCos)[3]); + +void brush_jitter_pos(Brush *brush, float *pos, float *jitterpos); + #endif diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.c b/source/blender/editors/sculpt_paint/sculpt_undo.c new file mode 100644 index 00000000000..303a7686a96 --- /dev/null +++ b/source/blender/editors/sculpt_paint/sculpt_undo.c @@ -0,0 +1,302 @@ +/* + * $Id: sculpt.c 29425 2010-06-12 15:05:19Z jwilkins $ + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2006 by Nicholas Bishop + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + * + * Implements the Sculpt Mode tools + * + */ + +#include "BLI_math.h" +#include "BLI_ghash.h" +#include "BLI_threads.h" + +#include "DNA_meshdata_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" +#include "DNA_mesh_types.h" +#include "DNA_key_types.h" + +#include "BKE_cdderivedmesh.h" +#include "BKE_context.h" +#include "BKE_depsgraph.h" +#include "BKE_modifier.h" +#include "BKE_multires.h" +#include "BKE_paint.h" +#include "BKE_mesh.h" +#include "BKE_key.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "ED_sculpt.h" +#include "paint_intern.h" +#include "sculpt_intern.h" + +/************************** Undo *************************/ + +static void update_cb(PBVHNode *node, void *unused) +{ + (void)unused; + BLI_pbvh_node_mark_update(node); +} + +static void sculpt_undo_restore(bContext *C, ListBase *lb) +{ + Scene *scene = CTX_data_scene(C); + Object *ob = CTX_data_active_object(C); + DerivedMesh *dm = mesh_get_derived_final(scene, ob, 0); + SculptSession *ss = ob->sculpt; + SculptUndoNode *unode; + MVert *mvert; + MultiresModifierData *mmd; + int *index; + int i, j, update= 0; + + sculpt_update_mesh_elements(scene, ob, 0); + + for(unode=lb->first; unode; unode=unode->next) { + if(!(strcmp(unode->idname, ob->id.name)==0)) + continue; + + if(unode->maxvert) { + char *shapeName= (char*)unode->shapeName; + + /* regular mesh restore */ + if(ss->totvert != unode->maxvert) + continue; + + if (ss->kb && strcmp(ss->kb->name, shapeName)) { + /* shape key has been changed before calling undo operator */ + + Key *key= ob_get_key(ob); + KeyBlock *kb= key_get_named_keyblock(key, shapeName); + + if (kb) { + ob->shapenr= BLI_findindex(&key->block, kb) + 1; + ob->shapeflag|= OB_SHAPE_LOCK; + + sculpt_update_mesh_elements(scene, ob, 0); + WM_event_add_notifier(C, NC_OBJECT|ND_DATA, ob); + } else { + /* key has been removed -- skip this undo node */ + continue; + } + } + + index= unode->index; + mvert= ss->mvert; + + if (ss->kb) { + float (*vertCos)[3]; + vertCos= key_to_vertcos(ob, ss->kb); + + for(i=0; itotvert; 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; itotvert; 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; jtotgrid; j++) { + grid= grids[unode->grids[j]]; + + for(i=0; ipbvh, 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); +} diff --git a/source/blender/editors/space_image/image_buttons.c b/source/blender/editors/space_image/image_buttons.c index 357aa9dacdf..f58326239ae 100644 --- a/source/blender/editors/space_image/image_buttons.c +++ b/source/blender/editors/space_image/image_buttons.c @@ -792,7 +792,7 @@ void uiTemplateImage(uiLayout *layout, bContext *C, PointerRNA *ptr, char *propn uiLayoutSetContextPointer(layout, "edit_image", &imaptr); if(!compact) - uiTemplateID(layout, C, ptr, propname, "IMAGE_OT_new", "IMAGE_OT_open", NULL); + uiTemplateID(layout, C, ptr, propname, "IMAGE_OT_new", "IMAGE_OT_open", NULL, NULL); // XXX missing: reload, pack diff --git a/source/blender/editors/space_logic/logic_window.c b/source/blender/editors/space_logic/logic_window.c index 3a264cbb259..005ebfb5b88 100644 --- a/source/blender/editors/space_logic/logic_window.c +++ b/source/blender/editors/space_logic/logic_window.c @@ -4278,7 +4278,7 @@ static void draw_actuator_sound(uiLayout *layout, PointerRNA *ptr, bContext *C) { uiLayout *row, *col; - uiTemplateID(layout, C, ptr, "sound", NULL, "SOUND_OT_open", NULL); + uiTemplateID(layout, C, ptr, "sound", NULL, "SOUND_OT_open", NULL, NULL); if (!RNA_pointer_get(ptr, "sound").data) { uiItemL(layout, "Select a sound from the list or load a new one", 0); diff --git a/source/blender/editors/space_nla/nla_buttons.c b/source/blender/editors/space_nla/nla_buttons.c index 9b7bc9a8002..81d4e8b6b01 100644 --- a/source/blender/editors/space_nla/nla_buttons.c +++ b/source/blender/editors/space_nla/nla_buttons.c @@ -237,7 +237,7 @@ static void nla_panel_animdata (const bContext *C, Panel *pa) /* Active Action Properties ------------------------------------- */ /* action */ row= uiLayoutRow(layout, 1); - uiTemplateID(row, (bContext *)C, &adt_ptr, "action", "ACTION_OT_new", NULL, NULL /*"ACTION_OT_unlink"*/); // XXX: need to make these operators + uiTemplateID(row, (bContext *)C, &adt_ptr, "action", "ACTION_OT_new", NULL, NULL /*"ACTION_OT_unlink"*/, NULL); // XXX: need to make these operators /* extrapolation */ row= uiLayoutRow(layout, 1); diff --git a/source/blender/editors/space_node/drawnode.c b/source/blender/editors/space_node/drawnode.c index 2d111b731ad..082f3f97dfd 100644 --- a/source/blender/editors/space_node/drawnode.c +++ b/source/blender/editors/space_node/drawnode.c @@ -81,7 +81,7 @@ void node_buts_group(uiLayout *layout, bContext *C, PointerRNA *ptr) { - uiTemplateIDBrowse(layout, C, ptr, "nodetree", NULL, NULL, ""); + uiTemplateIDBrowse(layout, C, ptr, "nodetree", NULL, NULL, "", NULL); } static void node_buts_value(uiLayout *layout, bContext *C, PointerRNA *ptr) @@ -306,7 +306,7 @@ static void node_shader_buts_material(uiLayout *layout, bContext *C, PointerRNA bNode *node= ptr->data; uiLayout *col; - uiTemplateID(layout, C, ptr, "material", "MATERIAL_OT_new", NULL, NULL); + uiTemplateID(layout, C, ptr, "material", "MATERIAL_OT_new", NULL, NULL, NULL); if(!node->id) return; @@ -467,7 +467,7 @@ static void node_composit_buts_image(uiLayout *layout, bContext *C, PointerRNA * PointerRNA imaptr; PropertyRNA *prop; - uiTemplateID(layout, C, ptr, "image", NULL, "IMAGE_OT_open", NULL); + uiTemplateID(layout, C, ptr, "image", NULL, "IMAGE_OT_open", NULL, NULL); if(!node->id) return; @@ -504,7 +504,7 @@ static void node_composit_buts_renderlayers(uiLayout *layout, bContext *C, Point const char *layer_name; char scene_name[19]; - uiTemplateID(layout, C, ptr, "scene", NULL, NULL, NULL); + uiTemplateID(layout, C, ptr, "scene", NULL, NULL, NULL, NULL); if(!node->id) return; @@ -1204,7 +1204,7 @@ static void node_texture_buts_proc(uiLayout *layout, bContext *C, PointerRNA *pt static void node_texture_buts_image(uiLayout *layout, bContext *C, PointerRNA *ptr) { - uiTemplateID(layout, C, ptr, "image", NULL, "IMAGE_OT_open", NULL); + uiTemplateID(layout, C, ptr, "image", NULL, "IMAGE_OT_open", NULL, NULL); } static void node_texture_buts_output(uiLayout *layout, bContext *C, PointerRNA *ptr) diff --git a/source/blender/gpu/intern/gpu_draw.c b/source/blender/gpu/intern/gpu_draw.c index 488eea40500..14068fe5f47 100644 --- a/source/blender/gpu/intern/gpu_draw.c +++ b/source/blender/gpu/intern/gpu_draw.c @@ -69,6 +69,7 @@ #include "GPU_draw.h" #include "GPU_extensions.h" #include "GPU_material.h" +#include "gpu_buffers.h" #include "smoke_API.h" diff --git a/source/blender/makesdna/DNA_ID.h b/source/blender/makesdna/DNA_ID.h index 7c3641db379..76468ada523 100644 --- a/source/blender/makesdna/DNA_ID.h +++ b/source/blender/makesdna/DNA_ID.h @@ -130,7 +130,7 @@ typedef struct PreviewImage { unsigned int w[2]; unsigned int h[2]; short changed[2]; - short pad0, pad1; + short changed_timestamp[2]; unsigned int * rect[2]; } PreviewImage; diff --git a/source/blender/makesdna/DNA_brush_types.h b/source/blender/makesdna/DNA_brush_types.h index 14930f85e63..3b2ce6b436e 100644 --- a/source/blender/makesdna/DNA_brush_types.h +++ b/source/blender/makesdna/DNA_brush_types.h @@ -33,9 +33,9 @@ #include "DNA_ID.h" #include "DNA_texture_types.h" -#ifndef MAX_MTEX -#define MAX_MTEX 18 -#endif +//#ifndef MAX_MTEX // XXX Not used? +//#define MAX_MTEX 18 +//#endif struct CurveMapping; struct MTex; @@ -43,8 +43,8 @@ struct Image; typedef struct BrushClone { struct Image *image; /* image for clone tool */ - float offset[2]; /* offset of clone image from canvas */ - float alpha, pad; /* transparency for drawing of clone image */ + float offset[2]; /* offset of clone image from canvas */ + float alpha, pad; /* transparency for drawing of clone image */ } BrushClone; typedef struct Brush { @@ -53,50 +53,94 @@ typedef struct Brush { struct BrushClone clone; struct CurveMapping *curve; /* falloff curve */ struct MTex mtex; - - short flag, blend; /* general purpose flag, blend mode */ - int size; /* brush diameter */ - float jitter; /* jitter the position of the brush */ - float spacing; /* spacing of paint operations */ - int smooth_stroke_radius; /* turning radius (in pixels) for smooth stroke */ - float smooth_stroke_factor; /* higher values limit fast changes in the stroke direction */ - float rate; /* paint operations / second (airbrush) */ - - float rgb[3]; /* color */ - float alpha; /* opacity */ - - char sculpt_tool; /* active sculpt tool */ + struct Image *image_icon; + + float normal_weight; + + short blend, pad; /* blend mode */ + int size; /* brush diameter */ + int flag; /* general purpose flag */ + float jitter; /* jitter the position of the brush */ + int spacing; /* spacing of paint operations */ + int smooth_stroke_radius; /* turning radius (in pixels) for smooth stroke */ + float smooth_stroke_factor; /* higher values limit fast changes in the stroke direction */ + float rate; /* paint operations / second (airbrush) */ + + float rgb[3]; /* color */ + float alpha; /* opacity */ + + int sculpt_plane; /* the direction of movement for sculpt vertices */ + + float plane_offset; /* offset for plane brushes (clay, flatten, fill, scrape) */ + + char sculpt_tool; /* active sculpt tool */ char vertexpaint_tool; /* active vertex/weight paint tool/blend mode */ char imagepaint_tool; /* active image paint tool */ - char pad2; + char pad3; + + float autosmooth_factor; + + float crease_pinch_factor; + + float plane_trim; + + float texture_sample_bias; + int texture_overlay_alpha; + + float unprojected_radius; + + float add_col[3]; + float sub_col[3]; } Brush; /* Brush.flag */ -#define BRUSH_AIRBRUSH 1 -#define BRUSH_TORUS 2 -#define BRUSH_ALPHA_PRESSURE 4 -#define BRUSH_SIZE_PRESSURE 8 -#define BRUSH_JITTER_PRESSURE 16 /* was BRUSH_RAD_PRESSURE */ -#define BRUSH_SPACING_PRESSURE 32 -#define BRUSH_FIXED_TEX 64 -#define BRUSH_RAKE 128 -#define BRUSH_ANCHORED 256 -#define BRUSH_DIR_IN 512 -#define BRUSH_SPACE 1024 -#define BRUSH_SMOOTH_STROKE 2048 -#define BRUSH_PERSISTENT 4096 -#define BRUSH_ACCUMULATE 8192 -#define BRUSH_LOCK_ALPHA 16384 +#define BRUSH_AIRBRUSH (1<<0) +#define BRUSH_TORUS (1<<1) +#define BRUSH_ALPHA_PRESSURE (1<<2) +#define BRUSH_SIZE_PRESSURE (1<<3) +#define BRUSH_JITTER_PRESSURE (1<<4) /* was BRUSH_RAD_PRESSURE */ +#define BRUSH_SPACING_PRESSURE (1<<5) +#define BRUSH_FIXED_TEX (1<<6) +#define BRUSH_RAKE (1<<7) +#define BRUSH_ANCHORED (1<<8) +#define BRUSH_DIR_IN (1<<9) +#define BRUSH_SPACE (1<<10) +#define BRUSH_SMOOTH_STROKE (1<<11) +#define BRUSH_PERSISTENT (1<<12) +#define BRUSH_ACCUMULATE (1<<13) +#define BRUSH_LOCK_ALPHA (1<<14) +#define BRUSH_ORIGINAL_NORMAL (1<<15) +#define BRUSH_OFFSET_PRESSURE (1<<16) +#define BRUSH_SPACE_ATTEN (1<<18) +#define BRUSH_ADAPTIVE_SPACE (1<<19) +#define BRUSH_LOCK_SIZE (1<<20) +#define BRUSH_TEXTURE_OVERLAY (1<<21) +#define BRUSH_EDGE_TO_EDGE (1<<22) +#define BRUSH_RESTORE_MESH (1<<23) +#define BRUSH_INVERSE_SMOOTH_PRESSURE (1<<24) +#define BRUSH_RANDOM_ROTATION (1<<25) +#define BRUSH_PLANE_TRIM (1<<26) +#define BRUSH_FRONTFACE (1<<27) /* Brush.sculpt_tool */ -#define SCULPT_TOOL_DRAW 1 -#define SCULPT_TOOL_SMOOTH 2 -#define SCULPT_TOOL_PINCH 3 -#define SCULPT_TOOL_INFLATE 4 -#define SCULPT_TOOL_GRAB 5 -#define SCULPT_TOOL_LAYER 6 -#define SCULPT_TOOL_FLATTEN 7 -#define SCULPT_TOOL_CLAY 8 +#define SCULPT_TOOL_DRAW 1 +#define SCULPT_TOOL_SMOOTH 2 +#define SCULPT_TOOL_PINCH 3 +#define SCULPT_TOOL_INFLATE 4 +#define SCULPT_TOOL_GRAB 5 +#define SCULPT_TOOL_LAYER 6 +#define SCULPT_TOOL_FLATTEN 7 +#define SCULPT_TOOL_CLAY 8 +#define SCULPT_TOOL_FILL 9 +#define SCULPT_TOOL_SCRAPE 10 +#define SCULPT_TOOL_NUDGE 11 +#define SCULPT_TOOL_THUMB 12 +#define SCULPT_TOOL_SNAKE_HOOK 13 +#define SCULPT_TOOL_ROTATE 14 +//#define SCULPT_TOOL_WAX 15 // XXX: reuse this slot later +#define SCULPT_TOOL_CREASE 16 +#define SCULPT_TOOL_BLOB 17 +#define SCULPT_TOOL_CLAY_TUBES 18 /* ImagePaintSettings.tool */ #define PAINT_TOOL_DRAW 0 @@ -104,5 +148,16 @@ typedef struct Brush { #define PAINT_TOOL_SMEAR 2 #define PAINT_TOOL_CLONE 3 +/* direction that the brush displaces along */ +enum { + SCULPT_DISP_DIR_AREA, + SCULPT_DISP_DIR_VIEW, + SCULPT_DISP_DIR_X, + SCULPT_DISP_DIR_Y, + SCULPT_DISP_DIR_Z, +}; + +#define MAX_BRUSH_PIXEL_RADIUS 200 + #endif diff --git a/source/blender/makesdna/DNA_color_types.h b/source/blender/makesdna/DNA_color_types.h index bc35d379334..83cd7979ce7 100644 --- a/source/blender/makesdna/DNA_color_types.h +++ b/source/blender/makesdna/DNA_color_types.h @@ -64,7 +64,8 @@ typedef struct CurveMap { typedef struct CurveMapping { int flag, cur; /* cur; for buttons, to show active curve */ - int preset, pad; + int preset; + int changed_timestamp; rctf curr, clipr; /* current rect, clip rect (is default rect too) */ @@ -87,7 +88,9 @@ typedef enum CurveMappingPreset { CURVE_PRESET_SHARP, CURVE_PRESET_SMOOTH, CURVE_PRESET_MAX, - CURVE_PRESET_MID9 + CURVE_PRESET_MID9, + CURVE_PRESET_ROUND, + CURVE_PRESET_ROOT, } CurveMappingPreset; /* histogram->mode */ diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index a4e9deaa7d9..3ac11eabe54 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -569,12 +569,30 @@ typedef struct Sculpt { Paint paint; /* For rotating around a pivot point */ - float pivot[3]; + //float pivot[3]; XXX not used? int flags; /* Control tablet input */ - char tablet_size, tablet_strength; - char pad[6]; + //char tablet_size, tablet_strength; XXX not used? + int radial_symm[3]; + + // all this below is used to communicate with the cursor drawing routine + + /* record movement of mouse so that rake can start at an intuitive angle */ + float last_x, last_y; + float last_angle; + + int draw_anchored; + int anchored_size; + float anchored_location[3]; + float anchored_initial_mouse[2]; + + int draw_pressure; + float pressure_value; + + float special_rotation; + + int pad; } Sculpt; typedef struct VPaint { @@ -1105,19 +1123,22 @@ typedef struct Scene { /* Paint.flags */ typedef enum { - PAINT_SHOW_BRUSH = 1, - PAINT_FAST_NAVIGATE = 2 + PAINT_SHOW_BRUSH = (1<<0), + PAINT_FAST_NAVIGATE = (1<<1), + PAINT_SHOW_BRUSH_ON_SURFACE = (1<<2), } PaintFlags; /* Sculpt.flags */ /* These can eventually be moved to paint flags? */ typedef enum SculptFlags { - SCULPT_SYMM_X = 1, - SCULPT_SYMM_Y = 2, - SCULPT_SYMM_Z = 4, - SCULPT_LOCK_X = 64, - SCULPT_LOCK_Y = 128, - SCULPT_LOCK_Z = 256 + SCULPT_SYMM_X = (1<<0), + SCULPT_SYMM_Y = (1<<1), + SCULPT_SYMM_Z = (1<<2), + SCULPT_LOCK_X = (1<<3), + SCULPT_LOCK_Y = (1<<4), + SCULPT_LOCK_Z = (1<<5), + SCULPT_SYMMETRY_FEATHER = (1<<6), + SCULPT_USE_OPENMP = (1<<7), } SculptFlags; /* ImagePaintSettings.flag */ diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h index b6e52699340..19db6316659 100644 --- a/source/blender/makesdna/DNA_userdef_types.h +++ b/source/blender/makesdna/DNA_userdef_types.h @@ -341,9 +341,10 @@ typedef struct UserDef { short gp_settings; short tb_leftmouse, tb_rightmouse; struct SolidLight light[3]; + short sculpt_paint_settings; /* user preferences for sculpt and paint */ short tw_hotspot, tw_flag, tw_handlesize, tw_size; short textimeout,texcollectrate; - short wmdrawmethod, wmpad; + short wmdrawmethod; /* removed wmpad */ int memcachelimit; int prefetchframes; short frameserverport; @@ -373,6 +374,11 @@ typedef struct UserDef { short autokey_flag; /* flags for autokeying */ struct ColorBand coba_weight; /* from texture.h */ + + int sculpt_paint_unified_size; /* unified radius of brush in pixels */ + float sculpt_paint_unified_unprojected_radius;/* unified radius of brush in Blender units */ + float sculpt_paint_unified_alpha; /* unified strength of brush */ + float sculpt_paint_overlay_col[3]; } UserDef; extern UserDef U; /* from blenkernel blender.c */ @@ -524,6 +530,11 @@ extern UserDef U; /* from blenkernel blender.c */ #define GP_PAINT_DOSMOOTH (1<<0) #define GP_PAINT_DOSIMPLIFY (1<<1) +/* sculpt_paint_settings */ +#define SCULPT_PAINT_USE_UNIFIED_SIZE (1<<0) +#define SCULPT_PAINT_USE_UNIFIED_ALPHA (1<<1) +#define SCULPT_PAINT_UNIFIED_LOCK_BRUSH_SIZE (1<<2) + /* color picker types */ #define USER_CP_CIRCLE 0 #define USER_CP_SQUARE_SV 1 diff --git a/source/blender/makesdna/DNA_windowmanager_types.h b/source/blender/makesdna/DNA_windowmanager_types.h index ed52316990e..e6d0772f425 100644 --- a/source/blender/makesdna/DNA_windowmanager_types.h +++ b/source/blender/makesdna/DNA_windowmanager_types.h @@ -307,8 +307,7 @@ typedef struct wmOperator { typedef enum wmRadialControlMode { WM_RADIALCONTROL_SIZE, WM_RADIALCONTROL_STRENGTH, - WM_RADIALCONTROL_ANGLE + WM_RADIALCONTROL_ANGLE, } wmRadialControlMode; #endif /* DNA_WINDOWMANAGER_TYPES_H */ - diff --git a/source/blender/makesrna/SConscript b/source/blender/makesrna/SConscript index bd500adc8e4..1558eef713e 100644 --- a/source/blender/makesrna/SConscript +++ b/source/blender/makesrna/SConscript @@ -45,7 +45,6 @@ if env['WITH_BF_GAMEENGINE']: if env['BF_UNIT_TEST']: defs.append('UNIT_TEST') - if env['OURPLATFORM'] == 'linux2': cflags='-pthread' incs += ' ../../../extern/binreloc/include' diff --git a/source/blender/makesrna/intern/CMakeLists.txt b/source/blender/makesrna/intern/CMakeLists.txt index 9b824279d8d..207e4e15a72 100644 --- a/source/blender/makesrna/intern/CMakeLists.txt +++ b/source/blender/makesrna/intern/CMakeLists.txt @@ -52,6 +52,7 @@ INCLUDE_DIRECTORIES( ../../gpu ../../imbuf ../../render/extern/include + ../../../../extern/glew/include . ) FILE(GLOB INC_FILES ../*.h ../../makesdna/*.h) diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c index fff29ba8f56..6d5a06d4f81 100644 --- a/source/blender/makesrna/intern/rna_brush.c +++ b/source/blender/makesrna/intern/rna_brush.c @@ -30,6 +30,7 @@ #include "DNA_brush_types.h" #include "DNA_texture_types.h" +#include "DNA_scene_types.h" #include "BLI_math.h" @@ -51,6 +52,58 @@ static void rna_Brush_update(Main *bmain, Scene *scene, PointerRNA *ptr) WM_main_add_notifier(NC_BRUSH|NA_EDITED, br); } +static int rna_Brush_is_sculpt_brush(Brush *me, bContext *C) +{ + Sculpt *sd = CTX_data_tool_settings(C)->sculpt; + int i; + + for (i= 0; i < sd->paint.brush_count; i++) { + if (strcmp(me->id.name+2, sd->paint.brushes[i]->id.name+2) == 0) + return 1; + } + + return 0; +} + +static int rna_Brush_is_vpaint_brush(Brush *me, bContext *C) +{ + VPaint *vp = CTX_data_tool_settings(C)->vpaint; + int i; + + for (i= 0; i < vp->paint.brush_count; i++) { + if (strcmp(me->id.name+2, vp->paint.brushes[i]->id.name+2) == 0) + return 1; + } + + return 0; +} + +static int rna_Brush_is_wpaint_brush(Brush *me, bContext *C) +{ + VPaint *vp = CTX_data_tool_settings(C)->wpaint; + int i; + + for (i= 0; i < vp->paint.brush_count; i++) { + if (strcmp(me->id.name+2, vp->paint.brushes[i]->id.name+2) == 0) + return 1; + } + + return 0; +} + +static int rna_Brush_is_imapaint_brush(Brush *me, bContext *C) +{ + ImagePaintSettings *data = &(CTX_data_tool_settings(C)->imapaint); + int i; + + for (i= 0; i < data->paint.brush_count; i++) { + if (strcmp(me->id.name+2, data->paint.brushes[i]->id.name+2) == 0) + return 1; + } + + return 0; +} + #else static void rna_def_brush_texture_slot(BlenderRNA *brna) @@ -100,14 +153,42 @@ static void rna_def_brush(BlenderRNA *brna) static EnumPropertyItem brush_sculpt_tool_items[] = { {SCULPT_TOOL_DRAW, "DRAW", 0, "Draw", ""}, {SCULPT_TOOL_SMOOTH, "SMOOTH", 0, "Smooth", ""}, + {SCULPT_TOOL_CREASE, "CREASE", 0, "Crease", ""}, + {SCULPT_TOOL_BLOB, "BLOB", 0, "Blob", ""}, {SCULPT_TOOL_PINCH, "PINCH", 0, "Pinch", ""}, {SCULPT_TOOL_INFLATE, "INFLATE", 0, "Inflate", ""}, {SCULPT_TOOL_GRAB, "GRAB", 0, "Grab", ""}, + {SCULPT_TOOL_SNAKE_HOOK, "SNAKE_HOOK", 0, "Snake Hook", ""}, + {SCULPT_TOOL_ROTATE, "ROTATE", 0, "Rotate", ""}, + {SCULPT_TOOL_THUMB, "THUMB", 0, "Thumb", ""}, + {SCULPT_TOOL_NUDGE, "NUDGE", 0, "Nudge", ""}, {SCULPT_TOOL_LAYER, "LAYER", 0, "Layer", ""}, {SCULPT_TOOL_FLATTEN, "FLATTEN", 0, "Flatten", ""}, {SCULPT_TOOL_CLAY, "CLAY", 0, "Clay", ""}, + //{SCULPT_TOOL_CLAY_TUBES, "CLAY_TUBES", 0, "Clay Tubes", ""}, XXX: remove clay tubes from UI + {SCULPT_TOOL_FILL, "FILL", 0, "Fill", ""}, + {SCULPT_TOOL_SCRAPE, "SCRAPE", 0, "Scrape", ""}, {0, NULL, 0, NULL, NULL}}; - + + static EnumPropertyItem brush_stroke_method_items[] = { + {0, "DOTS", 0, "Dots", ""}, + {BRUSH_RESTORE_MESH, "DRAG_DOT", 0, "Drag Dot", ""}, + {BRUSH_SPACE, "SPACE", 0, "Space", ""}, + {BRUSH_ANCHORED, "ANCHORED", 0, "Anchored", ""}, + {BRUSH_AIRBRUSH, "AIRBRUSH", 0, "Airbrush", ""}, + {0, NULL, 0, NULL, NULL}}; + + static EnumPropertyItem texture_angle_source_items[] = { + {0, "USER", 0, "User", ""}, + {BRUSH_RAKE, "RAKE", 0, "Rake", ""}, + {BRUSH_RANDOM_ROTATION, "RANDOM", 0, "Random", ""}, + {0, NULL, 0, NULL, NULL}}; + + static EnumPropertyItem texture_angle_source_no_random_items[] = { + {0, "USER", 0, "User", ""}, + {BRUSH_RAKE, "RAKE", 0, "Rake", ""}, + {0, NULL, 0, NULL, NULL}}; + static EnumPropertyItem brush_vertexpaint_tool_items[] = { {0, "MIX", 0, "Mix", "Use mix blending mode while painting"}, {1, "ADD", 0, "Add", "Use add blending mode while painting"}, @@ -129,11 +210,76 @@ static void rna_def_brush(BlenderRNA *brna) {0, "ADD", 0, "Add", "Add effect of brush"}, {BRUSH_DIR_IN, "SUBTRACT", 0, "Subtract", "Subtract effect of brush"}, {0, NULL, 0, NULL, NULL}}; - + + static const EnumPropertyItem prop_flatten_contrast_items[]= { + {0, "FLATTEN", 0, "Flatten", "Add effect of brush"}, + {BRUSH_DIR_IN, "CONTRAST", 0, "Contrast", "Subtract effect of brush"}, + {0, NULL, 0, NULL, NULL}}; + + static const EnumPropertyItem prop_fill_deepen_items[]= { + {0, "FILL", 0, "Fill", "Add effect of brush"}, + {BRUSH_DIR_IN, "DEEPEN", 0, "Deepen", "Subtract effect of brush"}, + {0, NULL, 0, NULL, NULL}}; + + static const EnumPropertyItem prop_scrape_peaks_items[]= { + {0, "SCRAPE", 0, "Scrape", "Add effect of brush"}, + {BRUSH_DIR_IN, "PEAKS", 0, "Peaks", "Subtract effect of brush"}, + {0, NULL, 0, NULL, NULL}}; + + static const EnumPropertyItem prop_pinch_magnify_items[]= { + {0, "PINCH", 0, "Pinch", "Add effect of brush"}, + {BRUSH_DIR_IN, "MAGNIFY", 0, "Magnify", "Subtract effect of brush"}, + {0, NULL, 0, NULL, NULL}}; + + static const EnumPropertyItem prop_inflate_deflate_items[]= { + {0, "INFLATE", 0, "Inflate", "Add effect of brush"}, + {BRUSH_DIR_IN, "DEFLATE", 0, "Deflate", "Subtract effect of brush"}, + {0, NULL, 0, NULL, NULL}}; + + static EnumPropertyItem brush_sculpt_plane_items[] = { + {SCULPT_DISP_DIR_AREA, "AREA", 0, "Area Plane", ""}, + {SCULPT_DISP_DIR_VIEW, "VIEW", 0, "View Plane", ""}, + {SCULPT_DISP_DIR_X, "X", 0, "X Plane", ""}, + {SCULPT_DISP_DIR_Y, "Y", 0, "Y Plane", ""}, + {SCULPT_DISP_DIR_Z, "Z", 0, "Z Plane", ""}, + {0, NULL, 0, NULL, NULL}}; + + FunctionRNA *func; + PropertyRNA *parm; + srna= RNA_def_struct(brna, "Brush", "ID"); RNA_def_struct_ui_text(srna, "Brush", "Brush datablock for storing brush settings for painting and sculpting"); RNA_def_struct_ui_icon(srna, ICON_BRUSH_DATA); - + + /* functions */ + func= RNA_def_function(srna, "is_sculpt_brush", "rna_Brush_is_sculpt_brush"); + RNA_def_function_ui_description(func, "Returns true if Brush can be used for sculpting"); + parm= RNA_def_pointer(func, "context", "Context", "", ""); + RNA_def_property_flag(parm, PROP_REQUIRED); + parm= RNA_def_boolean(func, "ret", 0, "", ""); + RNA_def_function_return(func, parm); + + func= RNA_def_function(srna, "is_vpaint_brush", "rna_Brush_is_vpaint_brush"); + RNA_def_function_ui_description(func, "Returns true if Brush can be used for vertex painting"); + parm= RNA_def_pointer(func, "context", "Context", "", ""); + RNA_def_property_flag(parm, PROP_REQUIRED); + parm= RNA_def_boolean(func, "ret", 0, "", ""); + RNA_def_function_return(func, parm); + + func= RNA_def_function(srna, "is_wpaint_brush", "rna_Brush_is_wpaint_brush"); + RNA_def_function_ui_description(func, "Returns true if Brush can be used for weight painting"); + parm= RNA_def_pointer(func, "context", "Context", "", ""); + RNA_def_property_flag(parm, PROP_REQUIRED); + parm= RNA_def_boolean(func, "ret", 0, "", ""); + RNA_def_function_return(func, parm); + + func= RNA_def_function(srna, "is_imapaint_brush", "rna_Brush_is_imapaint_brush"); + RNA_def_function_ui_description(func, "Returns true if Brush can be used for image painting"); + parm= RNA_def_pointer(func, "context", "Context", "", ""); + RNA_def_property_flag(parm, PROP_REQUIRED); + parm= RNA_def_boolean(func, "ret", 0, "", ""); + RNA_def_function_return(func, parm); + /* enums */ prop= RNA_def_property(srna, "blend", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, prop_blend_items); @@ -144,7 +290,7 @@ static void rna_def_brush(BlenderRNA *brna) RNA_def_property_enum_items(prop, brush_sculpt_tool_items); RNA_def_property_ui_text(prop, "Sculpt Tool", ""); RNA_def_property_update(prop, 0, "rna_Brush_update"); - + prop= RNA_def_property(srna, "vertexpaint_tool", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, brush_vertexpaint_tool_items); RNA_def_property_ui_text(prop, "Vertex/Weight Paint Tool", ""); @@ -158,33 +304,94 @@ static void rna_def_brush(BlenderRNA *brna) prop= RNA_def_property(srna, "direction", PROP_ENUM, PROP_NONE); RNA_def_property_enum_bitflag_sdna(prop, NULL, "flag"); RNA_def_property_enum_items(prop, prop_flip_direction_items); - RNA_def_property_ui_text(prop, "Direction", "Mapping type to use for this image in the game engine"); + RNA_def_property_ui_text(prop, "Direction", ""); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + + prop= RNA_def_property(srna, "stroke_method", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_bitflag_sdna(prop, NULL, "flag"); + RNA_def_property_enum_items(prop, brush_stroke_method_items); + RNA_def_property_ui_text(prop, "Stroke Method", ""); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + + prop= RNA_def_property(srna, "texture_angle_source", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_bitflag_sdna(prop, NULL, "flag"); + RNA_def_property_enum_items(prop, texture_angle_source_items); + RNA_def_property_ui_text(prop, "Texture Angle Source", ""); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + + prop= RNA_def_property(srna, "texture_angle_source_no_random", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_bitflag_sdna(prop, NULL, "flag"); + RNA_def_property_enum_items(prop, texture_angle_source_no_random_items); + RNA_def_property_ui_text(prop, "Texture Angle Source", ""); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + + prop= RNA_def_property(srna, "flatten_contrast", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_bitflag_sdna(prop, NULL, "flag"); + RNA_def_property_enum_items(prop, prop_flatten_contrast_items); + RNA_def_property_ui_text(prop, "Flatten/Contrast", ""); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + + prop= RNA_def_property(srna, "inflate_deflate", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_bitflag_sdna(prop, NULL, "flag"); + RNA_def_property_enum_items(prop, prop_inflate_deflate_items); + RNA_def_property_ui_text(prop, "Inflate/Deflate", ""); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + + prop= RNA_def_property(srna, "fill_deepen", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_bitflag_sdna(prop, NULL, "flag"); + RNA_def_property_enum_items(prop, prop_fill_deepen_items); + RNA_def_property_ui_text(prop, "Fill/Deepen", ""); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + + prop= RNA_def_property(srna, "scrape_peaks", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_bitflag_sdna(prop, NULL, "flag"); + RNA_def_property_enum_items(prop, prop_scrape_peaks_items); + RNA_def_property_ui_text(prop, "Scrape/Peaks", ""); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + + prop= RNA_def_property(srna, "pinch_magnify", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_bitflag_sdna(prop, NULL, "flag"); + RNA_def_property_enum_items(prop, prop_pinch_magnify_items); + RNA_def_property_ui_text(prop, "Pinch/Magnify", ""); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + + prop= RNA_def_property(srna, "sculpt_plane", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, brush_sculpt_plane_items); + RNA_def_property_ui_text(prop, "Sculpt Plane", ""); RNA_def_property_update(prop, 0, "rna_Brush_update"); /* number values */ - prop= RNA_def_property(srna, "size", PROP_INT, PROP_NONE); - RNA_def_property_range(prop, 1, 200); - RNA_def_property_ui_text(prop, "Size", "Diameter of the brush"); + prop= RNA_def_property(srna, "size", PROP_INT, PROP_DISTANCE); + RNA_def_property_range(prop, 1, MAX_BRUSH_PIXEL_RADIUS*10); + RNA_def_property_ui_range(prop, 1, MAX_BRUSH_PIXEL_RADIUS, 1, 0); + RNA_def_property_ui_text(prop, "Size", "Radius of the brush in pixels"); RNA_def_property_update(prop, 0, "rna_Brush_update"); + prop= RNA_def_property(srna, "unprojected_radius", PROP_FLOAT, PROP_DISTANCE); + RNA_def_property_range(prop, 0, FLT_MAX); + RNA_def_property_ui_range(prop, 0, 1, 0, 0); + RNA_def_property_ui_text(prop, "Surface Size", "Radius of brush in Blender units"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + prop= RNA_def_property(srna, "jitter", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "jitter"); RNA_def_property_range(prop, 0.0f, 1.0f); RNA_def_property_ui_text(prop, "Jitter", "Jitter the position of the brush while painting"); RNA_def_property_update(prop, 0, "rna_Brush_update"); - prop= RNA_def_property(srna, "spacing", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_sdna(prop, NULL, "spacing"); - RNA_def_property_range(prop, 1.0f, 100.0f); - RNA_def_property_ui_text(prop, "Spacing", "Spacing between brush stamps"); + prop= RNA_def_property(srna, "spacing", PROP_INT, PROP_PERCENTAGE); + RNA_def_property_int_sdna(prop, NULL, "spacing"); + RNA_def_property_range(prop, 1, 1000); + RNA_def_property_ui_range(prop, 1, 500, 5, 0); + RNA_def_property_ui_text(prop, "Spacing", "Spacing between brush daubs as a percentage of brush diameter"); RNA_def_property_update(prop, 0, "rna_Brush_update"); - prop= RNA_def_property(srna, "smooth_stroke_radius", PROP_INT, PROP_NONE); + prop= RNA_def_property(srna, "smooth_stroke_radius", PROP_INT, PROP_DISTANCE); RNA_def_property_range(prop, 10, 200); RNA_def_property_ui_text(prop, "Smooth Stroke Radius", "Minimum distance from last point before stroke continues"); RNA_def_property_update(prop, 0, "rna_Brush_update"); - prop= RNA_def_property(srna, "smooth_stroke_factor", PROP_FLOAT, PROP_NONE); + prop= RNA_def_property(srna, "smooth_stroke_factor", PROP_FLOAT, PROP_FACTOR); RNA_def_property_range(prop, 0.5, 0.99); RNA_def_property_ui_text(prop, "Smooth Stroke Factor", "Higher values give a smoother stroke"); RNA_def_property_update(prop, 0, "rna_Brush_update"); @@ -202,10 +409,56 @@ static void rna_def_brush(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Color", ""); RNA_def_property_update(prop, 0, "rna_Brush_update"); - prop= RNA_def_property(srna, "strength", PROP_FLOAT, PROP_NONE); + prop= RNA_def_property(srna, "strength", PROP_FLOAT, PROP_FACTOR); RNA_def_property_float_sdna(prop, NULL, "alpha"); + RNA_def_property_float_default(prop, 0.5f); + RNA_def_property_range(prop, 0.0f, 10.0f); + RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.001, 0.001); + RNA_def_property_ui_text(prop, "Strength", "How powerful the effect of the brush is when applied"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + + prop= RNA_def_property(srna, "plane_offset", PROP_FLOAT, PROP_DISTANCE); + RNA_def_property_float_sdna(prop, NULL, "plane_offset"); + RNA_def_property_float_default(prop, 0); + RNA_def_property_range(prop, -2.0f, 2.0f); + RNA_def_property_ui_range(prop, -0.5f, 0.5f, 0.001, 0.001); + RNA_def_property_ui_text(prop, "Plane Offset", "Adjusts plane on which the brush acts towards or away from the object surface"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + + prop= RNA_def_property(srna, "plane_trim", PROP_FLOAT, PROP_DISTANCE); + RNA_def_property_float_sdna(prop, NULL, "plane_trim"); + RNA_def_property_float_default(prop, 0.5f); + RNA_def_property_range(prop, 0, 1.0f); + RNA_def_property_ui_text(prop, "Plane Trim", "If a vertex is further from offset plane than this then it is not affected"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + + prop= RNA_def_property(srna, "texture_sample_bias", PROP_FLOAT, PROP_DISTANCE); + RNA_def_property_float_sdna(prop, NULL, "texture_sample_bias"); + RNA_def_property_float_default(prop, 0); + RNA_def_property_range(prop, -1, 1); + RNA_def_property_ui_text(prop, "Texture Sample Bias", "Value added to texture samples"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + + prop= RNA_def_property(srna, "normal_weight", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_float_sdna(prop, NULL, "normal_weight"); + RNA_def_property_float_default(prop, 0); RNA_def_property_range(prop, 0.0f, 1.0f); - RNA_def_property_ui_text(prop, "Strength", "The amount of pressure on the brush"); + RNA_def_property_ui_text(prop, "Normal Weight", "How much grab will pull vertexes out of surface during a grab"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + + prop= RNA_def_property(srna, "crease_pinch_factor", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_float_sdna(prop, NULL, "crease_pinch_factor"); + RNA_def_property_float_default(prop, 2.0f/3.0f); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_ui_text(prop, "Crease Brush Pinch Factor", "How much the crease brush pinches"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + + prop= RNA_def_property(srna, "autosmooth_factor", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_float_sdna(prop, NULL, "autosmooth_factor"); + RNA_def_property_float_default(prop, 0); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.001, 0.001); + RNA_def_property_ui_text(prop, "Autosmooth", "Amount of smoothing to automatically apply to each stroke"); RNA_def_property_update(prop, 0, "rna_Brush_update"); /* flag */ @@ -214,6 +467,11 @@ static void rna_def_brush(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Airbrush", "Keep applying paint effect while holding mouse (spray)"); RNA_def_property_update(prop, 0, "rna_Brush_update"); + prop= RNA_def_property(srna, "use_original_normal", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", BRUSH_ORIGINAL_NORMAL); + RNA_def_property_ui_text(prop, "Original Normal", "When locked keep using normal of surface where stroke was initiated"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + prop= RNA_def_property(srna, "use_wrap", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", BRUSH_TORUS); RNA_def_property_ui_text(prop, "Wrap", "Enable torus wrapping while painting"); @@ -225,6 +483,12 @@ static void rna_def_brush(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Strength Pressure", "Enable tablet pressure sensitivity for strength"); RNA_def_property_update(prop, 0, "rna_Brush_update"); + prop= RNA_def_property(srna, "use_offset_pressure", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", BRUSH_OFFSET_PRESSURE); + RNA_def_property_ui_icon(prop, ICON_STYLUS_PRESSURE, 0); + RNA_def_property_ui_text(prop, "Plane Offset Pressure", "Enable tablet pressure sensitivity for offset"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + prop= RNA_def_property(srna, "use_size_pressure", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", BRUSH_SIZE_PRESSURE); RNA_def_property_ui_icon(prop, ICON_STYLUS_PRESSURE, 0); @@ -243,11 +507,32 @@ static void rna_def_brush(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Spacing Pressure", "Enable tablet pressure sensitivity for spacing"); RNA_def_property_update(prop, 0, "rna_Brush_update"); + prop= RNA_def_property(srna, "use_inverse_smooth_pressure", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", BRUSH_INVERSE_SMOOTH_PRESSURE); + RNA_def_property_ui_icon(prop, ICON_STYLUS_PRESSURE, 0); + RNA_def_property_ui_text(prop, "Inverse Smooth Pressure", "Lighter pressure causes more smoothing to be applied"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + prop= RNA_def_property(srna, "use_rake", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", BRUSH_RAKE); RNA_def_property_ui_text(prop, "Rake", "Rotate the brush texture to match the stroke direction"); RNA_def_property_update(prop, 0, "rna_Brush_update"); + prop= RNA_def_property(srna, "use_random_rotation", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", BRUSH_RANDOM_ROTATION); + RNA_def_property_ui_text(prop, "Random Rotation", "Rotate the brush texture at random"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + + prop= RNA_def_property(srna, "use_plane_trim", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", BRUSH_PLANE_TRIM); + RNA_def_property_ui_text(prop, "Use Plane Trim", "Enable Plane Trim"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + + prop= RNA_def_property(srna, "use_frontface", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", BRUSH_FRONTFACE); + RNA_def_property_ui_text(prop, "Use Front-Face", "Brush only affects vertexes that face the viewer"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + prop= RNA_def_property(srna, "use_anchor", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", BRUSH_ANCHORED); RNA_def_property_ui_text(prop, "Anchored", "Keep the brush anchored to the initial location"); @@ -273,6 +558,37 @@ static void rna_def_brush(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Accumulate", "Accumulate stroke dabs on top of each other"); RNA_def_property_update(prop, 0, "rna_Brush_update"); + prop= RNA_def_property(srna, "use_space_atten", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", BRUSH_SPACE_ATTEN); + RNA_def_property_ui_text(prop, "Use Automatic Strength Adjustment", "Automatically adjusts strength to give consistent results for different spacings"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + + /* adaptive space is not implemented yet */ + prop= RNA_def_property(srna, "use_adaptive_space", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", BRUSH_ADAPTIVE_SPACE); + RNA_def_property_ui_text(prop, "Adaptive Spacing", "Space daubs according to surface orientation instead of screen space"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + + prop= RNA_def_property(srna, "lock_brush_size", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", BRUSH_LOCK_SIZE); + RNA_def_property_ui_text(prop, "Use Blender Units", "When locked brush stays same size relative to object; when unlocked brush size is given in pixels"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + + prop= RNA_def_property(srna, "use_texture_overlay", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", BRUSH_TEXTURE_OVERLAY); + RNA_def_property_ui_text(prop, "Use Texture Overlay", "Show texture in viewport"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + + prop= RNA_def_property(srna, "edge_to_edge", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", BRUSH_EDGE_TO_EDGE); + RNA_def_property_ui_text(prop, "Edge-to-edge", "Drag anchor brush from edge-to-edge"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + + prop= RNA_def_property(srna, "restore_mesh", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", BRUSH_RESTORE_MESH); + RNA_def_property_ui_text(prop, "Restore Mesh", "Allows a single dot to be carefully positioned"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + /* not exposed in the interface yet prop= RNA_def_property(srna, "fixed_tex", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", BRUSH_FIXED_TEX); @@ -303,6 +619,31 @@ static void rna_def_brush(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Texture", ""); RNA_def_property_update(prop, NC_TEXTURE, "rna_Brush_update"); + prop= RNA_def_property(srna, "texture_overlay_alpha", PROP_INT, PROP_PERCENTAGE); + RNA_def_property_int_sdna(prop, NULL, "texture_overlay_alpha"); + RNA_def_property_range(prop, 1, 100); + RNA_def_property_ui_text(prop, "Texture Overlay Alpha", ""); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + + prop= RNA_def_property(srna, "add_col", PROP_FLOAT, PROP_COLOR); + RNA_def_property_float_sdna(prop, NULL, "add_col"); + RNA_def_property_array(prop, 3); + RNA_def_property_ui_text(prop, "Add Color", "Color of cursor when adding"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + + prop= RNA_def_property(srna, "sub_col", PROP_FLOAT, PROP_COLOR); + RNA_def_property_float_sdna(prop, NULL, "sub_col"); + RNA_def_property_array(prop, 3); + RNA_def_property_ui_text(prop, "Subract Color", "Color of cursor when subtracting"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + + prop= RNA_def_property(srna, "image_icon", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "image_icon"); + RNA_def_property_struct_type(prop, "Image"); + RNA_def_property_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Image Icon", ""); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + /* clone tool */ prop= RNA_def_property(srna, "clone_image", PROP_POINTER, PROP_NONE); RNA_def_property_pointer_sdna(prop, NULL, "clone.image"); @@ -357,16 +698,19 @@ static void rna_def_operator_stroke_element(BlenderRNA *brna) RNA_def_property_range(prop, 0.0f, 1.0f); RNA_def_property_ui_text(prop, "Pressure", "Tablet pressure"); - prop= RNA_def_property(srna, "time", PROP_FLOAT, PROP_UNSIGNED); + prop= RNA_def_property(srna, "pen_flip", PROP_BOOLEAN, PROP_NONE); RNA_def_property_flag(prop, PROP_IDPROPERTY); - RNA_def_property_ui_text(prop, "Time", ""); + RNA_def_property_ui_text(prop, "Flip", ""); - prop= RNA_def_property(srna, "flip", PROP_BOOLEAN, PROP_NONE); + // used in uv painting + prop= RNA_def_property(srna, "time", PROP_FLOAT, PROP_UNSIGNED); RNA_def_property_flag(prop, PROP_IDPROPERTY); - RNA_def_property_ui_text(prop, "Flip", ""); + RNA_def_property_ui_text(prop, "Time", ""); /* XXX: Tool (this will be for pressing a modifier key for a different brush, e.g. switching to a Smooth brush in the middle of the stroke */ + + // XXX: i don't think blender currently supports the ability to properly do a remappable modifier in the middle of a stroke } void RNA_def_brush(BlenderRNA *brna) diff --git a/source/blender/makesrna/intern/rna_image.c b/source/blender/makesrna/intern/rna_image.c index 7d8248b58ed..7144b409299 100644 --- a/source/blender/makesrna/intern/rna_image.c +++ b/source/blender/makesrna/intern/rna_image.c @@ -213,6 +213,12 @@ static int rna_Image_depth_get(PointerRNA *ptr) return depth; } +static int rna_Image_is_image_icon(Image *me, bContext *C) +{ + const char prefix[] = ".imageicon."; + return strncmp(me->id.name+2, prefix, sizeof(prefix)-1) == 0; +} + #else static void rna_def_imageuser(BlenderRNA *brna) @@ -292,6 +298,9 @@ static void rna_def_image(BlenderRNA *brna) {IMA_STD_FIELD, "ODD", 0, "Lower First", "Lower field first"}, {0, NULL, 0, NULL, NULL}}; + FunctionRNA *func; + PropertyRNA *parm; + srna= RNA_def_struct(brna, "Image", "ID"); RNA_def_struct_ui_text(srna, "Image", "Image datablock referencing an external or packed image"); RNA_def_struct_ui_icon(srna, ICON_IMAGE_DATA); @@ -333,6 +342,14 @@ static void rna_def_image(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Field Order", "Order of video fields. Select which lines are displayed first"); RNA_def_property_update(prop, NC_IMAGE|ND_DISPLAY, NULL); + /* functions */ + func= RNA_def_function(srna, "is_image_icon", "rna_Image_is_image_icon"); + RNA_def_function_ui_description(func, "Returns true if Image name is prefixed with .imageicon."); + parm= RNA_def_pointer(func, "context", "Context", "", ""); + RNA_def_property_flag(parm, PROP_REQUIRED); + parm= RNA_def_boolean(func, "ret", 0, "", ""); + RNA_def_function_return(func, parm); + /* booleans */ prop= RNA_def_property(srna, "fields", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", IMA_FIELDS); diff --git a/source/blender/makesrna/intern/rna_sculpt_paint.c b/source/blender/makesrna/intern/rna_sculpt_paint.c index 3a7e7a02837..604f7776d56 100644 --- a/source/blender/makesrna/intern/rna_sculpt_paint.c +++ b/source/blender/makesrna/intern/rna_sculpt_paint.c @@ -193,7 +193,7 @@ static void rna_Paint_active_brush_name_get(PointerRNA *ptr, char *value) Paint *p= ptr->data; Brush *br = paint_brush(p); - BLI_strncpy(value, br->id.name+2, sizeof(br->id.name-2)); + BLI_strncpy(value, br->id.name+2, sizeof(br->id.name)-2); } @@ -212,7 +212,7 @@ static void rna_Paint_active_brush_name_set(PointerRNA *ptr, const char *value) for(i = 0; i < p->brush_count; ++i) { br = p->brushes[i]; - + if (strcmp(br->id.name+2, value)==0) { paint_brush_set(p, br); return; @@ -262,6 +262,10 @@ static void rna_def_paint(BlenderRNA *brna) RNA_def_property_boolean_sdna(prop, NULL, "flags", PAINT_SHOW_BRUSH); RNA_def_property_ui_text(prop, "Show Brush", ""); + prop= RNA_def_property(srna, "show_brush_on_surface", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flags", PAINT_SHOW_BRUSH_ON_SURFACE); + RNA_def_property_ui_text(prop, "Show Brush On Surface", ""); + prop= RNA_def_property(srna, "fast_navigate", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flags", PAINT_FAST_NAVIGATE); RNA_def_property_ui_text(prop, "Fast Navigate", "For multires, show low resolution while navigating the view"); @@ -274,7 +278,14 @@ static void rna_def_sculpt(BlenderRNA *brna) srna= RNA_def_struct(brna, "Sculpt", "Paint"); RNA_def_struct_ui_text(srna, "Sculpt", ""); - + + prop= RNA_def_property(srna, "radial_symm", PROP_INT, PROP_XYZ); + RNA_def_property_int_sdna(prop, NULL, "radial_symm"); + RNA_def_property_int_default(prop, 1); + RNA_def_property_range(prop, 1, 64); + RNA_def_property_ui_range(prop, 0, 32, 1, 1); + RNA_def_property_ui_text(prop, "Radial Symmetry Count X Axis", "Number of times to copy strokes across the surface"); + prop= RNA_def_property(srna, "symmetry_x", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flags", SCULPT_SYMM_X); RNA_def_property_ui_text(prop, "Symmetry X", "Mirror brush across the X axis"); @@ -298,6 +309,14 @@ static void rna_def_sculpt(BlenderRNA *brna) prop= RNA_def_property(srna, "lock_z", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flags", SCULPT_LOCK_Z); RNA_def_property_ui_text(prop, "Lock Z", "Disallow changes to the Z axis of vertices"); + + prop= RNA_def_property(srna, "use_symmetry_feather", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flags", SCULPT_SYMMETRY_FEATHER); + RNA_def_property_ui_text(prop, "Symmetry Feathering", "Reduce the strength of the brush where it overlaps symmetrical daubs"); + + prop= RNA_def_property(srna, "use_openmp", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flags", SCULPT_USE_OPENMP); + RNA_def_property_ui_text(prop, "Use OpenMP", "Take advantage of multiple CPU cores to improve sculpting performance"); } static void rna_def_vertex_paint(BlenderRNA *brna) diff --git a/source/blender/makesrna/intern/rna_ui_api.c b/source/blender/makesrna/intern/rna_ui_api.c index 1c751433e31..b2831c4b1d3 100644 --- a/source/blender/makesrna/intern/rna_ui_api.c +++ b/source/blender/makesrna/intern/rna_ui_api.c @@ -274,6 +274,7 @@ void RNA_api_ui_layout(StructRNA *srna) RNA_def_string(func, "new", "", 0, "", "Operator identifier to create a new ID block."); RNA_def_string(func, "open", "", 0, "", "Operator identifier to open a file for creating a new ID block."); RNA_def_string(func, "unlink", "", 0, "", "Operator identifier to unlink the ID block."); + RNA_def_string(func, "filter", "", 0, "", "Function identifier to filter the ID block."); func= RNA_def_function(srna, "template_ID_preview", "uiTemplateIDPreview"); RNA_def_function_flag(func, FUNC_USE_CONTEXT); @@ -281,6 +282,7 @@ void RNA_api_ui_layout(StructRNA *srna) RNA_def_string(func, "new", "", 0, "", "Operator identifier to create a new ID block."); RNA_def_string(func, "open", "", 0, "", "Operator identifier to open a file for creating a new ID block."); RNA_def_string(func, "unlink", "", 0, "", "Operator identifier to unlink the ID block."); + RNA_def_string(func, "filter", "", 0, "", "Function identifier to filter the ID block."); RNA_def_int(func, "rows", 0, 0, INT_MAX, "Number of thumbnail preview rows to display", "", 0, INT_MAX); RNA_def_int(func, "cols", 0, 0, INT_MAX, "Number of thumbnail preview columns to display", "", 0, INT_MAX); diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index d6aec37d6fb..a03488d3878 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -32,6 +32,7 @@ #include "DNA_curve_types.h" #include "DNA_space_types.h" #include "DNA_userdef_types.h" +#include "DNA_brush_types.h" #include "WM_api.h" #include "WM_types.h" @@ -867,7 +868,7 @@ static void rna_def_userdef_theme_space_view3d(BlenderRNA *brna) RNA_def_property_array(prop, 3); RNA_def_property_ui_text(prop, "Transform", ""); RNA_def_property_update(prop, 0, "rna_userdef_update"); - + rna_def_userdef_theme_spaces_vertex(srna); rna_def_userdef_theme_spaces_edge(srna); rna_def_userdef_theme_spaces_face(srna); @@ -2194,7 +2195,43 @@ static void rna_def_userdef_edit(BlenderRNA *brna) RNA_def_property_int_sdna(prop, NULL, "gp_eraser"); RNA_def_property_range(prop, 0, 100); RNA_def_property_ui_text(prop, "Grease Pencil Eraser Radius", "Radius of eraser 'brush'"); - + + /* sculpt and paint */ + + prop= RNA_def_property(srna, "sculpt_paint_overlay_col", PROP_FLOAT, PROP_COLOR); + RNA_def_property_float_sdna(prop, NULL, "sculpt_paint_overlay_col"); + RNA_def_property_array(prop, 3); + RNA_def_property_ui_text(prop, "Sculpt/Paint Overlay Color", "Color of texture overlay"); + + prop= RNA_def_property(srna, "sculpt_paint_use_unified_size", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "sculpt_paint_settings", SCULPT_PAINT_USE_UNIFIED_SIZE); + RNA_def_property_ui_text(prop, "Sculpt/Paint Use Unified Radius", "Instead of per brush radius, the radius is shared across brushes"); + + prop= RNA_def_property(srna, "sculpt_paint_use_unified_strength", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "sculpt_paint_settings", SCULPT_PAINT_USE_UNIFIED_ALPHA); + RNA_def_property_ui_text(prop, "Sculpt/Paint Use Unified Strength", "Instead of per brush strength, the strength is shared across brushes"); + + prop= RNA_def_property(srna, "sculpt_paint_unified_lock_brush_size", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "sculpt_paint_settings", SCULPT_PAINT_UNIFIED_LOCK_BRUSH_SIZE); + RNA_def_property_ui_text(prop, "Sculpt/Paint Use Unified Blender Units", "When locked all brushes stay same size relative to object; when unlocked all brush sizes are given in pixels"); + + prop= RNA_def_property(srna, "sculpt_paint_unified_size", PROP_INT, PROP_DISTANCE); + RNA_def_property_range(prop, 1, MAX_BRUSH_PIXEL_RADIUS*10); + RNA_def_property_ui_range(prop, 1, MAX_BRUSH_PIXEL_RADIUS, 1, 0); + RNA_def_property_ui_text(prop, "Sculpt/Paint Unified Size", "Unified radius of the brush in pixels"); + + prop= RNA_def_property(srna, "sculpt_paint_unified_unprojected_radius", PROP_FLOAT, PROP_DISTANCE); + RNA_def_property_range(prop, 0, FLT_MAX); + RNA_def_property_ui_range(prop, 0, 1, 0, 0); + RNA_def_property_ui_text(prop, "Sculpt/Paint Unified Surface Size", "Unified radius of brush in Blender units"); + + prop= RNA_def_property(srna, "sculpt_paint_unified_strength", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_float_sdna(prop, NULL, "sculpt_paint_unified_alpha"); + RNA_def_property_float_default(prop, 0.5f); + RNA_def_property_range(prop, 0.0f, 10.0f); + RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.001, 0.001); + RNA_def_property_ui_text(prop, "Sculpt/Paint Unified Strength", "Unified power of effect of brushes when applied"); + /* duplication linking */ prop= RNA_def_property(srna, "duplicate_mesh", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "dupflag", USER_DUP_MESH); diff --git a/source/blender/windowmanager/SConscript b/source/blender/windowmanager/SConscript index 0a86133e614..179b1ddc998 100644 --- a/source/blender/windowmanager/SConscript +++ b/source/blender/windowmanager/SConscript @@ -24,7 +24,7 @@ if env['WITH_BF_COLLADA']: if env['OURPLATFORM'] == 'linux2': cflags='-pthread' - incs += ' ../../../extern/binreloc/include' + incs += ' ../../../extern/binreloc/include' if env['OURPLATFORM'] in ('win32-vc', 'win32-mingw', 'linuxcross', 'win64-vc'): incs += ' ' + env['BF_PTHREADS_INC'] @@ -35,4 +35,4 @@ if env['WITH_GHOST_COCOA']: if env['BF_BUILDINFO']: defs.append('NAN_BUILDINFO') -env.BlenderLib ( 'bf_windowmanager', sources, Split(incs), defs, libtype=['core'], priority=[5] ) +env.BlenderLib ( 'bf_windowmanager', sources, Split(incs), defines=defs, libtype=['core'], priority=[5] ) diff --git a/source/blender/windowmanager/intern/wm_draw.c b/source/blender/windowmanager/intern/wm_draw.c index 5dbbf35796f..38322b66bbb 100644 --- a/source/blender/windowmanager/intern/wm_draw.c +++ b/source/blender/windowmanager/intern/wm_draw.c @@ -389,6 +389,7 @@ static void wm_draw_triple_free(wmWindow *win) wmDrawTriple *triple= win->drawdata; glDeleteTextures(triple->nx*triple->ny, triple->bind); + MEM_freeN(triple); win->drawdata= NULL; @@ -560,7 +561,8 @@ static void wm_method_draw_triple(bContext *C, wmWindow *win) else { win->drawdata= MEM_callocN(sizeof(wmDrawTriple), "wmDrawTriple"); - if(!wm_triple_gen_textures(win, win->drawdata)) { + if(!wm_triple_gen_textures(win, win->drawdata)) + { wm_draw_triple_fail(C, win); return; } diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c index 0023ded23f0..78e1c7d87a3 100644 --- a/source/blender/windowmanager/intern/wm_operators.c +++ b/source/blender/windowmanager/intern/wm_operators.c @@ -61,6 +61,7 @@ #include "BKE_scene.h" #include "BKE_screen.h" /* BKE_ST_MAXNAME */ #include "BKE_utildefines.h" +#include "BKE_brush.h" // JW #include "BIF_gl.h" #include "BIF_glutil.h" /* for paint cursor */ @@ -69,6 +70,7 @@ #include "ED_screen.h" #include "ED_util.h" +#include "ED_view3d.h" // JW #include "RNA_access.h" #include "RNA_define.h" @@ -2595,19 +2597,28 @@ typedef struct wmRadialControl { GLuint tex; } wmRadialControl; +extern Paint *paint_get_active(Scene *sce); +extern struct Brush *paint_brush(struct Paint *paint); + static void wm_radial_control_paint(bContext *C, int x, int y, void *customdata) { wmRadialControl *rc = (wmRadialControl*)customdata; ARegion *ar = CTX_wm_region(C); float r1=0.0f, r2=0.0f, r3=0.0f, angle=0.0f; - /* Keep cursor in the original place */ - x = rc->initial_mouse[0] - ar->winrct.xmin; - y = rc->initial_mouse[1] - ar->winrct.ymin; + Paint *paint = paint_get_active(CTX_data_scene(C)); + Brush *brush = paint_brush(paint); - glPushMatrix(); - - glTranslatef((float)x, (float)y, 0.0f); + ViewContext vc; + + int hit = 0; + + int flip; + int sign; + + float* col; + + const float str = rc->mode == WM_RADIALCONTROL_STRENGTH ? (rc->value + 0.5) : (brush->texture_overlay_alpha / 100.0f); if(rc->mode == WM_RADIALCONTROL_SIZE) { r1= rc->value; @@ -2615,29 +2626,37 @@ static void wm_radial_control_paint(bContext *C, int x, int y, void *customdata) r3= r1; } else if(rc->mode == WM_RADIALCONTROL_STRENGTH) { r1= (1 - rc->value) * WM_RADIAL_CONTROL_DISPLAY_SIZE; - r2= WM_RADIAL_CONTROL_DISPLAY_SIZE; - r3= WM_RADIAL_CONTROL_DISPLAY_SIZE; + r2= r3= WM_RADIAL_CONTROL_DISPLAY_SIZE; } else if(rc->mode == WM_RADIALCONTROL_ANGLE) { - r1= r2= WM_RADIAL_CONTROL_DISPLAY_SIZE; - r3= WM_RADIAL_CONTROL_DISPLAY_SIZE; + r1= r2= r3= WM_RADIAL_CONTROL_DISPLAY_SIZE; angle = rc->value; } - glColor4ub(255, 255, 255, 128); - glEnable( GL_LINE_SMOOTH ); - glEnable(GL_BLEND); + /* Keep cursor in the original place */ + x = rc->initial_mouse[0] - ar->winrct.xmin; + y = rc->initial_mouse[1] - ar->winrct.ymin; - if(rc->mode == WM_RADIALCONTROL_ANGLE) - fdrawline(0, 0, WM_RADIAL_CONTROL_DISPLAY_SIZE, 0); + view3d_set_viewcontext(C, &vc); - if(rc->tex) { - const float str = rc->mode == WM_RADIALCONTROL_STRENGTH ? (rc->value + 0.5) : 1; + // XXX: no way currently to know state of pen flip or invert key modifier without starting a stroke + flip = 1; - if(rc->mode == WM_RADIALCONTROL_ANGLE) { - glRotatef(angle, 0, 0, 1); - fdrawline(0, 0, WM_RADIAL_CONTROL_DISPLAY_SIZE, 0); - } + 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; + + glTranslatef((float)x, (float)y, 0.0f); + + glEnable(GL_BLEND); + if(rc->mode == WM_RADIALCONTROL_ANGLE) { + glRotatef(angle, 0, 0, 1); + } + + if (rc->tex) { glBindTexture(GL_TEXTURE_2D, rc->tex); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); @@ -2645,7 +2664,7 @@ static void wm_radial_control_paint(bContext *C, int x, int y, void *customdata) glEnable(GL_TEXTURE_2D); glBegin(GL_QUADS); - glColor4f(0,0,0, str); + glColor4f(U.sculpt_paint_overlay_col[0],U.sculpt_paint_overlay_col[1],U.sculpt_paint_overlay_col[2], str); glTexCoord2f(0,0); glVertex2f(-r3, -r3); glTexCoord2f(1,0); @@ -2658,11 +2677,20 @@ static void wm_radial_control_paint(bContext *C, int x, int y, void *customdata) glDisable(GL_TEXTURE_2D); } - glColor4ub(255, 255, 255, 128); + if(rc->mode == WM_RADIALCONTROL_ANGLE) { + glColor4f(col[0], col[1], col[2], 0.5f); + glEnable(GL_LINE_SMOOTH); + glRotatef(-angle, 0, 0, 1); + fdrawline(0, 0, WM_RADIAL_CONTROL_DISPLAY_SIZE, 0); + glRotatef(angle, 0, 0, 1); + fdrawline(0, 0, WM_RADIAL_CONTROL_DISPLAY_SIZE, 0); + glDisable(GL_LINE_SMOOTH); + } + + glColor4f(col[0], col[1], col[2], 0.5f); glutil_draw_lined_arc(0.0, M_PI*2.0, r1, 40); glutil_draw_lined_arc(0.0, M_PI*2.0, r2, 40); glDisable(GL_BLEND); - glDisable( GL_LINE_SMOOTH ); glPopMatrix(); } @@ -2674,6 +2702,7 @@ int WM_radial_control_modal(bContext *C, wmOperator *op, wmEvent *event) float dist; double new_value = RNA_float_get(op->ptr, "new_value"); int ret = OPERATOR_RUNNING_MODAL; + float initial_value = RNA_float_get(op->ptr, "initial_value"); mode = RNA_int_get(op->ptr, "mode"); RNA_int_get_array(op->ptr, "initial_mouse", initial_mouse); @@ -2682,6 +2711,16 @@ int WM_radial_control_modal(bContext *C, wmOperator *op, wmEvent *event) case MOUSEMOVE: delta[0]= initial_mouse[0] - event->x; delta[1]= initial_mouse[1] - event->y; + + //if (mode == WM_RADIALCONTROL_SIZE) + // delta[0]+= initial_value; + //else if(mode == WM_RADIALCONTROL_STRENGTH) + // delta[0]+= WM_RADIAL_CONTROL_DISPLAY_SIZE * (1 - initial_value); + //else if(mode == WM_RADIALCONTROL_ANGLE) { + // delta[0]+= WM_RADIAL_CONTROL_DISPLAY_SIZE * cos(initial_value*M_PI/180.0f); + // delta[1]+= WM_RADIAL_CONTROL_DISPLAY_SIZE * sin(initial_value*M_PI/180.0f); + //} + dist= sqrt(delta[0]*delta[0]+delta[1]*delta[1]); if(mode == WM_RADIALCONTROL_SIZE) @@ -2728,6 +2767,11 @@ int WM_radial_control_modal(bContext *C, wmOperator *op, wmEvent *event) ED_region_tag_redraw(CTX_wm_region(C)); + //if (ret != OPERATOR_RUNNING_MODAL) { + // wmWindow *win = CTX_wm_window(C); + // WM_cursor_restore(win); + //} + return ret; } @@ -2735,10 +2779,15 @@ int WM_radial_control_modal(bContext *C, wmOperator *op, wmEvent *event) int WM_radial_control_invoke(bContext *C, wmOperator *op, wmEvent *event) { wmRadialControl *rc = MEM_callocN(sizeof(wmRadialControl), "radial control"); + wmWindow *win = CTX_wm_window(C); int mode = RNA_int_get(op->ptr, "mode"); float initial_value = RNA_float_get(op->ptr, "initial_value"); + //float initial_size = RNA_float_get(op->ptr, "initial_size"); int mouse[2] = {event->x, event->y}; + //if (initial_size == 0) + // initial_size = WM_RADIAL_CONTROL_DISPLAY_SIZE; + if(mode == WM_RADIALCONTROL_SIZE) { rc->max_value = 200; mouse[0]-= initial_value; @@ -2775,6 +2824,8 @@ int WM_radial_control_invoke(bContext *C, wmOperator *op, wmEvent *event) rc->cursor = WM_paint_cursor_activate(CTX_wm_manager(C), op->type->poll, wm_radial_control_paint, op->customdata); + //WM_cursor_modal(win, CURSOR_NONE); + /* add modal handler */ WM_event_add_modal_handler(C, op); diff --git a/source/blenderplayer/bad_level_call_stubs/stubs.c b/source/blenderplayer/bad_level_call_stubs/stubs.c index add9ac05189..4599f8ff17b 100644 --- a/source/blenderplayer/bad_level_call_stubs/stubs.c +++ b/source/blenderplayer/bad_level_call_stubs/stubs.c @@ -36,12 +36,14 @@ struct ARegion; struct ARegionType; struct Base; +struct Brush; struct bNodeTree; struct CSG_FaceIteratorDescriptor; struct CSG_VertexIteratorDescriptor; struct ColorBand; struct CurveMapping; struct EditBone; +struct EditFace; struct EditMesh; struct ID; struct FCurve; @@ -54,16 +56,20 @@ struct LOD_Decimation_Info; struct MTex; struct Main; struct Material; +struct MCol; struct MenuType; struct Mesh; struct ModifierData; +struct MultiresModifierData; struct NodeBlurData; struct Object; +struct PBVHNode; struct Render; struct RenderEngine; struct RenderLayer; struct RenderResult; struct ScrArea; +struct SculptSession; struct ShadeInput; struct ShadeResult; struct SpaceImage; @@ -375,6 +381,24 @@ void smoke_get_index(void) {return;} void smoke_step(void) {return;} */ +/* sculpt */ +/* + void ED_sculpt_force_update(struct bContext *C) {} +struct SculptUndoNode *sculpt_undo_push_node(struct SculptSession *ss, struct PBVHNode *node) {return (struct SculptUndoNode *)NULL;} +void sculpt_undo_push_end(void) {} +void sculpt_undo_push_begin(char *name) {} +struct SculptUndoNode *sculpt_undo_get_node(struct PBVHNode *node) {return (struct SculptUndoNode *) NULL;} +struct MultiresModifierData *sculpt_multires_active(struct Scene *scene, struct Object *ob) {return (struct MultiresModifierData *) NULL;} +int sculpt_modifiers_active(struct Scene *scene, struct Object *ob) {return 0;} +*/ +int sculpt_get_brush_size(struct Brush *brush) {return 0;} +void sculpt_set_brush_size(struct Brush *brush, int size) {} +int sculpt_get_lock_brush_size(struct Brush *brush){ return 0;} +float sculpt_get_brush_unprojected_radius(struct Brush *brush){return 0.0f;} +void sculpt_set_brush_unprojected_radius(struct Brush *brush, float unprojected_radius){} +float sculpt_get_brush_alpha(struct Brush *brush){return 0.0f;} +void sculpt_set_brush_alpha(struct Brush *brush, float alpha){} + char blender_path[] = ""; /* CSG */ -- cgit v1.2.3