2.5/Posemode:
[blender-staging.git] / source / blender / editors / sculpt_paint / sculpt.c
index f40880b901f1c407e40598af57cfe9b14af1f15d..957293bfa8291e51daf2d21c90985d4df230c3d1 100644 (file)
@@ -145,8 +145,6 @@ typedef struct StrokeCache {
        ViewContext vc;
        bglMats *mats;
 
-       float *layer_disps; /* Displacements for each vertex */
-       float (*mesh_store)[3]; /* Copy of the mesh vertices' locations */
        short (*orig_norms)[3]; /* Copy of the mesh vertices' normals */
        float (*face_norms)[3]; /* Copy of the mesh faces' normals */
        float rotation; /* Texture rotation (radians) for anchored and rake modes */
@@ -156,7 +154,6 @@ typedef struct StrokeCache {
        float old_grab_location[3];
        int symmetry; /* Symmetry index between 0 and 7 */
        float view_normal[3], view_normal_symmetry[3];
-       int last_dot[2]; /* Last location of stroke application */
        int last_rake[2]; /* Last location of updating rake rotation */
 } StrokeCache;
 
@@ -220,12 +217,12 @@ static void project(bglMats *mats, const float v[3], short p[2])
    shrink the brush. Skipped for grab brush because only the first mouse down
    size is used, which is small if the user has just touched the pen to the
    tablet */
-static char brush_size(Sculpt *sd)
+static char brush_size(Sculpt *sd, SculptSession *ss)
 {
        float size= sd->brush->size;
        
        if((sd->brush->sculpt_tool != SCULPT_TOOL_GRAB) && (sd->brush->flag & BRUSH_SIZE_PRESSURE))
-               size *= sd->session->cache->pressure;
+               size *= ss->cache->pressure;
 
        return size;
 }
@@ -264,7 +261,7 @@ static float brush_strength(Sculpt *sd, StrokeCache *cache)
 }
 
 /* Handles clipping against a mirror modifier and SCULPT_LOCK axis flags */
-static void sculpt_clip(Sculpt *sd, float *co, const float val[3])
+static void sculpt_clip(Sculpt *sd, SculptSession *ss, float *co, const float val[3])
 {
        int i;
 
@@ -272,7 +269,7 @@ static void sculpt_clip(Sculpt *sd, float *co, const float val[3])
                if(sd->flags & (SCULPT_LOCK_X << i))
                        continue;
 
-               if((sd->session->cache->flag & (CLIP_X << i)) && (fabs(co[i]) <= sd->session->cache->clip_tolerance[i]))
+               if((ss->cache->flag & (CLIP_X << i)) && (fabs(co[i]) <= ss->cache->clip_tolerance[i]))
                        co[i]= 0.0f;
                else
                        co[i]= val[i];
@@ -294,9 +291,9 @@ static void add_norm_if(float view_vec[3], float out[3], float out_flip[3], cons
 
 /* Currently only for the draw brush; finds average normal for all active
    vertices */
-static void calc_area_normal(Sculpt *sd, float out[3], const ListBase* active_verts)
+static void calc_area_normal(Sculpt *sd, SculptSession *ss, float out[3], const ListBase* active_verts)
 {
-       StrokeCache *cache = sd->session->cache;
+       StrokeCache *cache = ss->cache;
        ActiveData *node = active_verts->first;
        const int view = 0; /* XXX: should probably be a flag, not number: sd->brush_type==SCULPT_TOOL_DRAW ? sculptmode_brush()->view : 0; */
        float out_flip[3];
@@ -310,7 +307,7 @@ static void calc_area_normal(Sculpt *sd, float out[3], const ListBase* active_ve
        }
        else {
                for(; node; node = node->next)
-                       add_norm_if(out_dir, out, out_flip, sd->session->mvert[node->Index].no);
+                       add_norm_if(out_dir, out, out_flip, ss->mvert[node->Index].no);
        }
 
        if (out[0]==0.0 && out[1]==0.0 && out[2]==0.0) {
@@ -333,7 +330,7 @@ static void do_draw_brush(Sculpt *sd, SculptSession *ss, const ListBase* active_
        float area_normal[3];
        ActiveData *node= active_verts->first;
 
-       calc_area_normal(sd, area_normal, active_verts);
+       calc_area_normal(sd, ss, area_normal, active_verts);
        
        while(node){
                float *co= ss->mvert[node->Index].co;
@@ -342,7 +339,7 @@ static void do_draw_brush(Sculpt *sd, SculptSession *ss, const ListBase* active_
                                     co[1]+area_normal[1]*ss->cache->radius*node->Fade*ss->cache->scale[1],
                                     co[2]+area_normal[2]*ss->cache->radius*node->Fade*ss->cache->scale[2]};
                                     
-               sculpt_clip(sd, co, val);
+               sculpt_clip(sd, ss, co, val);
                
                node= node->next;
        }
@@ -392,37 +389,37 @@ static void neighbor_average(SculptSession *ss, float avg[3], const int vert)
                VecCopyf(avg, ss->mvert[vert].co);
 }
 
-static void do_smooth_brush(Sculpt *s, const ListBase* active_verts)
+static void do_smooth_brush(Sculpt *s, SculptSession *ss, const ListBase* active_verts)
 {
        ActiveData *node= active_verts->first;
        int i;
        
        for(i = 0; i < 2; ++i) {
                while(node){
-                       float *co= s->session->mvert[node->Index].co;
+                       float *co= ss->mvert[node->Index].co;
                        float avg[3], val[3];
                        
-                       neighbor_average(s->session, avg, node->Index);
+                       neighbor_average(ss, avg, node->Index);
                        val[0] = co[0]+(avg[0]-co[0])*node->Fade;
                        val[1] = co[1]+(avg[1]-co[1])*node->Fade;
                        val[2] = co[2]+(avg[2]-co[2])*node->Fade;
                        
-                       sculpt_clip(s, co, val);
+                       sculpt_clip(s, ss, co, val);
                        node= node->next;
                }
        }
 }
 
-static void do_pinch_brush(Sculpt *s, const ListBase* active_verts)
+static void do_pinch_brush(Sculpt *s, SculptSession *ss, const ListBase* active_verts)
 {
        ActiveData *node= active_verts->first;
 
        while(node) {
-               float *co= s->session->mvert[node->Index].co;
-               const float val[3]= {co[0]+(s->session->cache->location[0]-co[0])*node->Fade,
-                                    co[1]+(s->session->cache->location[1]-co[1])*node->Fade,
-                                    co[2]+(s->session->cache->location[2]-co[2])*node->Fade};
-               sculpt_clip(s, co, val);
+               float *co= ss->mvert[node->Index].co;
+               const float val[3]= {co[0]+(ss->cache->location[0]-co[0])*node->Fade,
+                                    co[1]+(ss->cache->location[1]-co[1])*node->Fade,
+                                    co[2]+(ss->cache->location[2]-co[2])*node->Fade};
+               sculpt_clip(s, ss, co, val);
                node= node->next;
        }
 }
@@ -441,7 +438,7 @@ static void do_grab_brush(Sculpt *sd, SculptSession *ss)
                VecCopyf(add, grab_delta);
                VecMulf(add, node->Fade);
                VecAddf(add, add, co);
-               sculpt_clip(sd, co, add);
+               sculpt_clip(sd, ss, co, add);
 
                node= node->next;
        }
@@ -457,10 +454,10 @@ static void do_layer_brush(Sculpt *sd, SculptSession *ss, const ListBase *active
        if(ss->cache->flip)
                lim = -lim;
 
-       calc_area_normal(sd, area_normal, active_verts);
+       calc_area_normal(sd, ss, area_normal, active_verts);
 
        while(node){
-               float *disp= &ss->cache->layer_disps[node->Index];
+               float *disp= &ss->layer_disps[node->Index];
                float *co= ss->mvert[node->Index].co;
                float val[3];
                
@@ -470,20 +467,19 @@ static void do_layer_brush(Sculpt *sd, SculptSession *ss, const ListBase *active
                if((lim < 0 && *disp < lim) || (lim > 0 && *disp > lim))
                        *disp = lim;
                
-               val[0] = ss->cache->mesh_store[node->Index][0]+area_normal[0] * *disp*ss->cache->scale[0];
-               val[1] = ss->cache->mesh_store[node->Index][1]+area_normal[1] * *disp*ss->cache->scale[1];
-               val[2] = ss->cache->mesh_store[node->Index][2]+area_normal[2] * *disp*ss->cache->scale[2];
+               val[0] = ss->mesh_co_orig[node->Index][0]+area_normal[0] * *disp*ss->cache->scale[0];
+               val[1] = ss->mesh_co_orig[node->Index][1]+area_normal[1] * *disp*ss->cache->scale[1];
+               val[2] = ss->mesh_co_orig[node->Index][2]+area_normal[2] * *disp*ss->cache->scale[2];
 
-               sculpt_clip(sd, co, val);
+               sculpt_clip(sd, ss, co, val);
 
                node= node->next;
        }
 }
 
-static void do_inflate_brush(Sculpt *s, const ListBase *active_verts)
+static void do_inflate_brush(Sculpt *s, SculptSession *ss, const ListBase *active_verts)
 {
        ActiveData *node= active_verts->first;
-       SculptSession *ss = s->session;
        float add[3];
        
        while(node) {
@@ -499,7 +495,7 @@ static void do_inflate_brush(Sculpt *s, const ListBase *active_verts)
                add[2]*= ss->cache->scale[2];
                VecAddf(add, add, co);
                
-               sculpt_clip(s, co, add);
+               sculpt_clip(s, ss, co, add);
 
                node= node->next;
        }
@@ -528,46 +524,77 @@ static void calc_flatten_center(SculptSession *ss, ActiveData *node, float co[3]
        VecMulf(co, 1.0f / FLATTEN_SAMPLE_SIZE);
 }
 
+/* Projects a point onto a plane along the plane's normal */
+static void point_plane_project(float intr[3], float co[3], float plane_normal[3], float plane_center[3])
+{
+       float p1[3], sub1[3], sub2[3];
+
+       /* Find the intersection between squash-plane and vertex (along the area normal) */
+       VecSubf(p1, co, plane_normal);
+       VecSubf(sub1, plane_center, p1);
+       VecSubf(sub2, co, p1);
+       VecSubf(intr, co, p1);
+       VecMulf(intr, Inpf(plane_normal, sub1) / Inpf(plane_normal, sub2));
+       VecAddf(intr, intr, p1);
+}
+
 static void do_flatten_clay_brush(Sculpt *sd, SculptSession *ss, const ListBase *active_verts, int clay)
 {
        ActiveData *node= active_verts->first;
        /* area_normal and cntr define the plane towards which vertices are squashed */
        float area_normal[3];
-       float cntr[3];
+       float cntr[3], cntr2[3], bstr;
 
-       calc_area_normal(sd, area_normal, active_verts);
+       calc_area_normal(sd, ss, area_normal, active_verts);
        calc_flatten_center(ss, node, cntr);
 
+       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];
+       }
+       
        while(node){
                float *co= ss->mvert[node->Index].co;
-               float p1[3], sub1[3], sub2[3], intr[3], val[3];
-               
-               /* Find the intersection between squash-plane and vertex (along the area normal) */
-               VecSubf(p1, co, area_normal);
-               VecSubf(sub1, cntr, p1);
-               VecSubf(sub2, co, p1);
-               VecSubf(intr, co, p1);
-               VecMulf(intr, Inpf(area_normal, sub1) / Inpf(area_normal, sub2));
-               VecAddf(intr, intr, p1);
-               
-               VecSubf(val, intr, co);
-               VecMulf(val, fabs(node->Fade));
-               VecAddf(val, val, co);
+               float intr[3], val[3], d;
                
                if(clay) {
-                       /* Clay brush displaces after flattening */
-                       float tmp[3];
-                       VecCopyf(tmp, area_normal);
-                       VecMulf(tmp, ss->cache->radius * node->Fade * 0.1);
-                       VecAddf(val, val, tmp);
+                       float delta[3];
+
+                       VecSubf(delta, co, cntr2);
+                       d = Inpf(area_normal, delta);
+
+                       /* Check for subtractive mode */
+                       if(bstr < 0)
+                               d = -d;
                }
 
-               sculpt_clip(sd, co, val);
+               if(!clay || d <= 0.0f) {
+                       /* Find the intersection between squash-plane and vertex (along the area normal) */             
+                       point_plane_project(intr, co, area_normal, cntr);
+
+                       VecSubf(val, intr, co);
+
+                       if(clay) {
+                               VecMulf(val, node->Fade / bstr);
+                               /* Clay displacement */
+                               val[0]+=area_normal[0] * ss->cache->scale[0]*node->Fade;
+                               val[1]+=area_normal[1] * ss->cache->scale[1]*node->Fade;
+                               val[2]+=area_normal[2] * ss->cache->scale[2]*node->Fade;
+                       }
+                       else
+                               VecMulf(val, fabs(node->Fade));
+
+                       VecAddf(val, val, co);
+                       sculpt_clip(sd, ss, co, val);
+               }
                
                node= node->next;
        }
 }
+
 /* Uses symm to selectively flip any axis of a coordinate. */
 static void flip_coord(float out[3], float in[3], const char symm)
 {
@@ -623,35 +650,29 @@ static float get_texcache_pixel_bilinear(const SculptSession *ss, float u, float
 }
 
 /* Return a multiplier for brush strength on a particular vertex. */
-static float tex_strength(Sculpt *sd, float *point, const float len)
+static float tex_strength(Sculpt *sd, SculptSession *ss, float *point, const float len)
 {
-       SculptSession *ss= sd->session;
        Brush *br = sd->brush;
+       MTex *tex = NULL;
        float avg= 1;
 
-       if(br->texact==-1 || !br->mtex[br->texact])
+       if(br->texact >= 0)
+               tex = br->mtex[br->texact];
+
+       if(!tex) {
                avg= 1;
-       else if(br->tex_mode==BRUSH_TEX_3D) {
-               /* Get strength by feeding the vertex location directly
-                  into a texture */
+       }
+       else if(tex->brush_map_mode == MTEX_MAP_MODE_3D) {
                float jnk;
-               const float factor= 0.01;
-               MTex mtex;
-               memset(&mtex,0,sizeof(MTex));
-               mtex.tex= br->mtex[br->texact]->tex;
-               mtex.projx= 1;
-               mtex.projy= 2;
-               mtex.projz= 3;
-               VecCopyf(mtex.size, br->mtex[br->texact]->size);
-               VecMulf(mtex.size, factor);
-               if(!sd->texsep)
-                       mtex.size[1]= mtex.size[2]= mtex.size[0];
-               
-               externtex(&mtex,point,&avg,&jnk,&jnk,&jnk,&jnk);
+
+               /* Get strength by feeding the vertex 
+                  location directly into a texture */
+               externtex(tex, point, &avg,
+                         &jnk, &jnk, &jnk, &jnk);
        }
        else if(ss->texcache) {
                const float bsize= ss->cache->pixel_radius * 2;
-               const float rot= sd->brush->rot + ss->cache->rotation;
+               const float rot= tex->rot + ss->cache->rotation;
                int px, py;
                float flip[3], point_2d[2];
 
@@ -664,9 +685,9 @@ static float tex_strength(Sculpt *sd, float *point, const float len)
 
                /* For Tile and Drag modes, get the 2D screen coordinates of the
                   and scale them up or down to the texture size. */
-               if(br->tex_mode==BRUSH_TEX_TILE) {
-                       const int sx= (const int)br->mtex[br->texact]->size[0];
-                       const int sy= (const int)sd->texsep ? br->mtex[br->texact]->size[1] : sx;
+               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];
@@ -686,7 +707,8 @@ static float tex_strength(Sculpt *sd, float *point, const float len)
                        if(sy != 1)
                                py %= sy-1;
                        avg= get_texcache_pixel_bilinear(ss, ss->texcache_side*px/sx, ss->texcache_side*py/sy);
-               } else {
+               }
+               else if(tex->brush_map_mode == MTEX_MAP_MODE_FIXED) {
                        float fx= (point_2d[0] - ss->cache->mouse[0]) / bsize;
                        float fy= (point_2d[1] - ss->cache->mouse[1]) / bsize;
 
@@ -738,40 +760,8 @@ static void sculpt_add_damaged_rect(SculptSession *ss)
        }
 }
 
-/* Clears the depth buffer in each modified area. */
-#if 0
-static void sculpt_clear_damaged_areas(SculptSession *ss)
-{
-       RectNode *rn= NULL;
-
-       for(rn = ss->damaged_rects.first; rn; rn = rn->next) {
-               rcti clp = rn->r;
-               rcti *win = NULL; /*XXX: &curarea->winrct; */
-               
-               clp.xmin += win->xmin;
-               clp.xmax += win->xmin;
-               clp.ymin += win->ymin;
-               clp.ymax += win->ymin;
-               
-               if(clp.xmin < win->xmax && clp.xmax > win->xmin &&
-                  clp.ymin < win->ymax && clp.ymax > win->ymin) {
-                       if(clp.xmin < win->xmin) clp.xmin = win->xmin;
-                       if(clp.ymin < win->ymin) clp.ymin = win->ymin;
-                       if(clp.xmax > win->xmax) clp.xmax = win->xmax;
-                       if(clp.ymax > win->ymax) clp.ymax = win->ymax;
-
-                       glScissor(clp.xmin + 1, clp.ymin + 1,
-                                 clp.xmax - clp.xmin - 2,
-                                 clp.ymax - clp.ymin - 2);
-               }
-               
-               glClear(GL_DEPTH_BUFFER_BIT);
-       }
-}
-#endif
-static void do_brush_action(Sculpt *sd, StrokeCache *cache)
+static void do_brush_action(Sculpt *sd, SculptSession *ss, StrokeCache *cache)
 {
-       SculptSession *ss = sd->session;
        float av_dist;
        ListBase active_verts={0,0};
        ListBase *grab_active_verts = &ss->cache->grab_active_verts[ss->cache->symmetry];
@@ -800,7 +790,7 @@ static void do_brush_action(Sculpt *sd, StrokeCache *cache)
                                        adata->Index = i;
                                        /* Fade is used to store the final strength at which the brush
                                           should modify a particular vertex. */
-                                       adata->Fade= tex_strength(sd, vert, av_dist) * bstrength;
+                                       adata->Fade= tex_strength(sd, ss, vert, av_dist) * bstrength;
                                        adata->dist = av_dist;
 
                                        if(b->sculpt_tool == SCULPT_TOOL_GRAB && cache->first_time)
@@ -820,13 +810,13 @@ static void do_brush_action(Sculpt *sd, StrokeCache *cache)
                        do_draw_brush(sd, ss, &active_verts);
                        break;
                case SCULPT_TOOL_SMOOTH:
-                       do_smooth_brush(sd, &active_verts);
+                       do_smooth_brush(sd, ss, &active_verts);
                        break;
                case SCULPT_TOOL_PINCH:
-                       do_pinch_brush(sd, &active_verts);
+                       do_pinch_brush(sd, ss, &active_verts);
                        break;
                case SCULPT_TOOL_INFLATE:
-                       do_inflate_brush(sd, &active_verts);
+                       do_inflate_brush(sd, ss, &active_verts);
                        break;
                case SCULPT_TOOL_GRAB:
                        do_grab_brush(sd, ss);
@@ -875,29 +865,21 @@ static void calc_brushdata_symm(StrokeCache *cache, const char symm)
        cache->symmetry= symm;
 }
 
-static void do_symmetrical_brush_actions(Sculpt *sd, StrokeCache *cache)
+static void do_symmetrical_brush_actions(Sculpt *sd, SculptSession *ss)
 {
+       StrokeCache *cache = ss->cache;
        const char symm = sd->flags & 7;
        int i;
 
-       /* Brush spacing: only apply dot if next dot is far enough away */
-       if((sd->brush->flag & BRUSH_SPACE) && !(sd->brush->flag & BRUSH_ANCHORED) && !cache->first_time) {
-               int dx = cache->last_dot[0] - cache->mouse[0];
-               int dy = cache->last_dot[1] - cache->mouse[1];
-               if(sqrt(dx*dx+dy*dy) < sd->brush->spacing)
-                       return;
-       }
-       memcpy(cache->last_dot, cache->mouse, sizeof(int) * 2);
-
        VecCopyf(cache->location, cache->true_location);
        VecCopyf(cache->grab_delta_symmetry, cache->grab_delta);
        cache->symmetry = 0;
-       do_brush_action(sd, cache);
+       do_brush_action(sd, ss, cache);
 
        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, cache);
+                       do_brush_action(sd, ss, cache);
                }
        }
 
@@ -970,10 +952,8 @@ static void projverts_clear_inside(SculptSession *ss)
 }
 #endif
 
-static void sculpt_update_tex(Sculpt *sd)
+static void sculpt_update_tex(Sculpt *sd, SculptSession *ss)
 {
-       SculptSession *ss= sd->session;
-
        if(ss->texcache) {
                MEM_freeN(ss->texcache);
                ss->texcache= NULL;
@@ -987,25 +967,6 @@ static void sculpt_update_tex(Sculpt *sd)
        }
 }
 
-void sculptmode_selectbrush_menu(void)
-{
-       /* XXX: I guess menus belong elsewhere too?
-
-       Sculpt *sd= sculpt_data();
-       int val;
-       
-       pupmenu_set_active(sd->brush_type);
-       
-       val= pupmenu("Select Brush%t|Draw|Smooth|Pinch|Inflate|Grab|Layer|Flatten");
-
-       if(val>0) {
-               sd->brush_type= val;
-
-               allqueue(REDRAWVIEW3D, 1);
-               allqueue(REDRAWBUTSEDIT, 1);
-       }*/
-}
-
 static void sculptmode_update_all_projverts(SculptSession *ss)
 {
        unsigned i;
@@ -1052,8 +1013,8 @@ static struct MultiresModifierData *sculpt_multires_active(Object *ob)
 
 static void sculpt_update_mesh_elements(bContext *C)
 {
-       SculptSession *ss = CTX_data_tool_settings(C)->sculpt->session;
        Object *ob = CTX_data_active_object(C);
+       SculptSession *ss = ob->sculpt;
        int oldtotvert = ss->totvert;
 
        if((ss->multires = sculpt_multires_active(ob))) {
@@ -1084,97 +1045,15 @@ static void sculpt_update_mesh_elements(bContext *C)
        }
 }
 
-/* XXX: lots of drawing code (partial redraw), has to go elsewhere */
-#if 0
-void sculptmode_draw_wires(SculptSession *ss, int only_damaged)
-{
-       Mesh *me = get_mesh(OBACT);
-       int i;
-
-       bglPolygonOffset(1.0);
-       glDepthMask(0);
-       BIF_ThemeColor((OBACT==OBACT)?TH_ACTIVE:TH_SELECT);
-
-       for(i=0; i<me->totedge; i++) {
-               MEdge *med= &me->medge[i];
-
-               if((!only_damaged || (ss->projverts[med->v1].inside || ss->projverts[med->v2].inside)) &&
-                  (med->flag & ME_EDGEDRAW)) {
-                       glDrawElements(GL_LINES, 2, GL_UNSIGNED_INT, &med->v1);
-               }
-       }
-
-       glDepthMask(1);
-       bglPolygonOffset(0.0);
-}
-
-void sculptmode_draw_mesh(int only_damaged) 
-{
-       int i, j, dt, drawCurrentMat = 1, matnr= -1;
-       SculptSession *ss = sculpt_session();
-
-       sculpt_update_mesh_elements(ss, OBACT);
-
-       persp(PERSP_VIEW);
-       mymultmatrix(OBACT->obmat);
-       glEnable(GL_DEPTH_TEST);
-       glEnable(GL_LIGHTING);
-       /* XXX: GPU_set_object_materials(G.scene, OBACT, 0, NULL); */
-       glEnable(GL_CULL_FACE);
-
-       glShadeModel(GL_SMOOTH);
-
-       glVertexPointer(3, GL_FLOAT, sizeof(MVert), &cache->mvert[0].co);
-       glNormalPointer(GL_SHORT, sizeof(MVert), &cache->mvert[0].no);
-
-       dt= MIN2(G.vd->drawtype, OBACT->dt);
-       if(dt==OB_WIRE)
-               glColorMask(0,0,0,0);
-
-       for(i=0; i<ss->totface; ++i) {
-               MFace *f= &ss->mface[i];
-               char inside= 0;
-               int new_matnr= f->mat_nr + 1;
-               
-               if(new_matnr != matnr)
-                       drawCurrentMat= GPU_enable_material(matnr = new_matnr, NULL);
-               
-               /* If only_damaged!=0, only draw faces that are partially
-                  inside the area(s) modified by the brush */
-               if(only_damaged) {
-                       for(j=0; j<(f->v4?4:3); ++j) {
-                               if(ss->projverts[*((&f->v1)+j)].inside) {
-                                       inside= 1;
-                                       break;
-                               }
-                       }
-               }
-               else
-                       inside= 1;
-                       
-               if(inside && drawCurrentMat)
-                       glDrawElements(f->v4?GL_QUADS:GL_TRIANGLES, f->v4?4:3, GL_UNSIGNED_INT, &f->v1);
-       }
-
-       glDisable(GL_CULL_FACE);
-       glDisable(GL_LIGHTING);
-       glColorMask(1,1,1,1);
-
-       if(dt==OB_WIRE || (OBACT->dtx & OB_DRAWWIRE))
-               sculptmode_draw_wires(ss, only_damaged);
-
-       glDisable(GL_DEPTH_TEST);
-}
-#endif
-
 static int sculpt_mode_poll(bContext *C)
 {
-       return G.f & G_SCULPTMODE;
+       Object *ob = CTX_data_active_object(C);
+       return ob && ob->mode & OB_MODE_SCULPT;
 }
 
 static int sculpt_poll(bContext *C)
 {
-       return G.f & G_SCULPTMODE && CTX_wm_area(C)->spacetype == SPACE_VIEW3D &&
+       return sculpt_mode_poll(C) && CTX_wm_area(C)->spacetype == SPACE_VIEW3D &&
                CTX_wm_region(C)->regiontype == RGN_TYPE_WINDOW;
 }
 
@@ -1182,29 +1061,35 @@ static int sculpt_poll(bContext *C)
 static void draw_paint_cursor(bContext *C, int x, int y, void *customdata)
 {
        Sculpt *sd= CTX_data_tool_settings(C)->sculpt;
-       
-       glTranslatef((float)x, (float)y, 0.0f);
+       SculptSession *ss= CTX_data_active_object(C)->sculpt;
        
        glColor4ub(255, 100, 100, 128);
        glEnable( GL_LINE_SMOOTH );
        glEnable(GL_BLEND);
+
+       glTranslatef((float)x, (float)y, 0.0f);
        glutil_draw_lined_arc(0.0, M_PI*2.0, sd->brush->size, 40);
+       glTranslatef((float)-x, (float)-y, 0.0f);
+
+       if(ss && ss->cache && sd->brush && (sd->brush->flag & BRUSH_SMOOTH_STROKE)) {
+               ARegion *ar = CTX_wm_region(C);
+               sdrawline(x, y, ss->cache->mouse[0] - ar->winrct.xmin, ss->cache->mouse[1] - ar->winrct.ymin);
+       }
+
        glDisable(GL_BLEND);
        glDisable( GL_LINE_SMOOTH );
-       
-       glTranslatef((float)-x, (float)-y, 0.0f);
 }
 
 static void toggle_paint_cursor(bContext *C)
 {
        Sculpt *s = CTX_data_scene(C)->toolsettings->sculpt;
 
-       if(s->session->cursor) {
-               WM_paint_cursor_end(CTX_wm_manager(C), s->session->cursor);
-               s->session->cursor = NULL;
+       if(s->cursor) {
+               WM_paint_cursor_end(CTX_wm_manager(C), s->cursor);
+               s->cursor = NULL;
        }
        else {
-               s->session->cursor =
+               s->cursor =
                        WM_paint_cursor_activate(CTX_wm_manager(C), sculpt_poll, draw_paint_cursor, NULL);
        }
 }
@@ -1231,31 +1116,6 @@ static void sculpt_undo_push(bContext *C, Sculpt *sd)
        }
 }
 
-static int sculpt_brush_curve_preset_exec(bContext *C, wmOperator *op)
-{
-       brush_curve_preset(CTX_data_scene(C)->toolsettings->sculpt->brush, RNA_enum_get(op->ptr, "mode"));
-       return OPERATOR_FINISHED;
-}
-
-static void SCULPT_OT_brush_curve_preset(wmOperatorType *ot)
-{
-       static EnumPropertyItem prop_mode_items[] = {
-               {BRUSH_PRESET_SHARP, "SHARP", 0, "Sharp Curve", ""},
-               {BRUSH_PRESET_SMOOTH, "SMOOTH", 0, "Smooth Curve", ""},
-               {BRUSH_PRESET_MAX, "MAX", 0, "Max Curve", ""},
-               {0, NULL, 0, NULL, NULL}};
-
-       ot->name= "Preset";
-       ot->idname= "SCULPT_OT_brush_curve_preset";
-
-       ot->exec= sculpt_brush_curve_preset_exec;
-       ot->poll= sculpt_mode_poll;
-
-       ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
-
-       RNA_def_enum(ot->srna, "mode", prop_mode_items, BRUSH_PRESET_SHARP, "Mode", "");
-}
-
 /**** Radial control ****/
 static int sculpt_radial_control_invoke(bContext *C, wmOperator *op, wmEvent *event)
 {
@@ -1289,7 +1149,7 @@ static void SCULPT_OT_radial_control(wmOperatorType *ot)
        ot->exec= sculpt_radial_control_exec;
        ot->poll= sculpt_poll;
 
-       ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+       ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO|OPTYPE_BLOCKING;
 }
 
 /**** Operator for applying a stroke (various attributes including mouse path)
@@ -1308,10 +1168,6 @@ static float unproject_brush_radius(SculptSession *ss, float offset)
 
 static void sculpt_cache_free(StrokeCache *cache)
 {
-       if(cache->layer_disps)
-               MEM_freeN(cache->layer_disps);
-       if(cache->mesh_store)
-               MEM_freeN(cache->mesh_store);
        if(cache->orig_norms)
                MEM_freeN(cache->orig_norms);
        if(cache->face_norms)
@@ -1322,12 +1178,12 @@ static void sculpt_cache_free(StrokeCache *cache)
 }
 
 /* Initialize the stroke cache invariants from operator properties */
-static void sculpt_update_cache_invariants(Sculpt *sd, bContext *C, wmOperator *op)
+static void sculpt_update_cache_invariants(Sculpt *sd, SculptSession *ss, bContext *C, wmOperator *op)
 {
        StrokeCache *cache = MEM_callocN(sizeof(StrokeCache), "stroke cache");
        int i;
 
-       sd->session->cache = cache;
+       ss->cache = cache;
 
        RNA_float_get_array(op->ptr, "scale", cache->scale);
        cache->flag = RNA_int_get(op->ptr, "flag");
@@ -1335,6 +1191,9 @@ static void sculpt_update_cache_invariants(Sculpt *sd, bContext *C, wmOperator *
        RNA_int_get_array(op->ptr, "initial_mouse", cache->initial_mouse);
        cache->depth = RNA_float_get(op->ptr, "depth");
 
+       cache->mouse[0] = cache->initial_mouse[0];
+       cache->mouse[1] = cache->initial_mouse[1];
+
        /* Truly temporary data that isn't stored in properties */
 
        view3d_set_viewcontext(C, &cache->vc);
@@ -1344,42 +1203,52 @@ static void sculpt_update_cache_invariants(Sculpt *sd, bContext *C, wmOperator *
 
        sculpt_update_mesh_elements(C);
 
-       if(sd->brush->sculpt_tool == SCULPT_TOOL_LAYER)
-               cache->layer_disps = MEM_callocN(sizeof(float) * sd->session->totvert, "layer brush displacements");
+       /* Initialize layer brush displacements */
+       if(sd->brush->sculpt_tool == SCULPT_TOOL_LAYER &&
+          (!ss->layer_disps || !(sd->brush->flag & BRUSH_PERSISTENT))) {
+               if(ss->layer_disps)
+                       MEM_freeN(ss->layer_disps);
+               ss->layer_disps = MEM_callocN(sizeof(float) * ss->totvert, "layer brush displacements");
+       }
 
        /* Make copies of the mesh vertex locations and normals for some tools */
        if(sd->brush->sculpt_tool == SCULPT_TOOL_LAYER || (sd->brush->flag & BRUSH_ANCHORED)) {
-               cache->mesh_store= MEM_mallocN(sizeof(float) * 3 * sd->session->totvert, "sculpt mesh vertices copy");
-               for(i = 0; i < sd->session->totvert; ++i)
-                       VecCopyf(cache->mesh_store[i], sd->session->mvert[i].co);
+               if(sd->brush->sculpt_tool != SCULPT_TOOL_LAYER ||
+                  !ss->mesh_co_orig || !(sd->brush->flag & BRUSH_PERSISTENT)) {
+                       if(!ss->mesh_co_orig)
+                               ss->mesh_co_orig= MEM_mallocN(sizeof(float) * 3 * ss->totvert,
+                                                                      "sculpt mesh vertices copy");
+                       for(i = 0; i < ss->totvert; ++i)
+                               VecCopyf(ss->mesh_co_orig[i], ss->mvert[i].co);
+               }
 
                if(sd->brush->flag & BRUSH_ANCHORED) {
-                       cache->orig_norms= MEM_mallocN(sizeof(short) * 3 * sd->session->totvert, "Sculpt orig norm");
-                       for(i = 0; i < sd->session->totvert; ++i) {
-                               cache->orig_norms[i][0] = sd->session->mvert[i].no[0];
-                               cache->orig_norms[i][1] = sd->session->mvert[i].no[1];
-                               cache->orig_norms[i][2] = sd->session->mvert[i].no[2];
+                       cache->orig_norms= MEM_mallocN(sizeof(short) * 3 * ss->totvert, "Sculpt orig norm");
+                       for(i = 0; i < ss->totvert; ++i) {
+                               cache->orig_norms[i][0] = ss->mvert[i].no[0];
+                               cache->orig_norms[i][1] = ss->mvert[i].no[1];
+                               cache->orig_norms[i][2] = ss->mvert[i].no[2];
                        }
 
-                       if(sd->session->face_normals) {
-                               float *fn = sd->session->face_normals;
-                               cache->face_norms= MEM_mallocN(sizeof(float) * 3 * sd->session->totface, "Sculpt face norms");
-                               for(i = 0; i < sd->session->totface; ++i, fn += 3)
+                       if(ss->face_normals) {
+                               float *fn = ss->face_normals;
+                               cache->face_norms= MEM_mallocN(sizeof(float) * 3 * ss->totface, "Sculpt face norms");
+                               for(i = 0; i < ss->totface; ++i, fn += 3)
                                        VecCopyf(cache->face_norms[i], fn);
                        }
                }
        }
 
        unproject(cache->mats, cache->true_location, cache->initial_mouse[0], cache->initial_mouse[1], cache->depth);
-       cache->radius = unproject_brush_radius(sd->session, brush_size(sd));
+       cache->radius = unproject_brush_radius(ss, brush_size(sd, ss));
        cache->rotation = 0;
        cache->first_time = 1;
 }
 
 /* Initialize the stroke cache variants from operator properties */
-static void sculpt_update_cache_variants(Sculpt *sd, PointerRNA *ptr)
+static void sculpt_update_cache_variants(Sculpt *sd, SculptSession *ss, PointerRNA *ptr)
 {
-       StrokeCache *cache = sd->session->cache;
+       StrokeCache *cache = ss->cache;
        float grab_location[3];
        int dx, dy;
 
@@ -1391,13 +1260,13 @@ static void sculpt_update_cache_variants(Sculpt *sd, PointerRNA *ptr)
        /* Truly temporary data that isn't stored in properties */
 
        cache->previous_pixel_radius = cache->pixel_radius;
-       cache->pixel_radius = brush_size(sd);
+       cache->pixel_radius = brush_size(sd, ss);
 
        if(sd->brush->flag & BRUSH_ANCHORED) {
                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(sd->session, cache->pixel_radius);
+               cache->radius = unproject_brush_radius(ss, cache->pixel_radius);
                cache->rotation = atan2(dy, dx);
        }
        else if(sd->brush->flag & BRUSH_RAKE) {
@@ -1468,22 +1337,20 @@ static void sculpt_brush_stroke_init_properties(bContext *C, wmOperator *op, wmE
        view3d_set_viewcontext(C, &vc);
        RNA_float_set(op->ptr, "depth", read_cached_depth(&vc, event->x, event->y));
 
-       sculpt_update_cache_invariants(sd, C, op);
+       sculpt_update_cache_invariants(sd, ss, C, op);
 }
 
 static int sculpt_brush_stroke_invoke(bContext *C, wmOperator *op, wmEvent *event)
 {
        Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
+       SculptSession *ss = CTX_data_active_object(C)->sculpt;
 
        view3d_operator_needs_opengl(C);
-       sculpt_brush_stroke_init_properties(C, op, event, sd->session);
-
-       sculptmode_update_all_projverts(sd->session);
 
        /* TODO: Shouldn't really have to do this at the start of every
           stroke, but sculpt would need some sort of notification when
           changes are made to the texture. */
-       sculpt_update_tex(sd);
+       sculpt_update_tex(sd, ss);
 
        /* add modal handler */
        WM_event_add_modal_handler(C, &CTX_wm_window(C)->handlers, op);
@@ -1491,16 +1358,15 @@ static int sculpt_brush_stroke_invoke(bContext *C, wmOperator *op, wmEvent *even
        return OPERATOR_RUNNING_MODAL;
 }
 
-static void sculpt_restore_mesh(Sculpt *sd)
+static void sculpt_restore_mesh(Sculpt *sd, SculptSession *ss)
 {
-       SculptSession *ss = sd->session;
        StrokeCache *cache = ss->cache;
        int i;
        
        /* Restore the mesh before continuing with anchored stroke */
-       if((sd->brush->flag & BRUSH_ANCHORED) && cache->mesh_store) {
+       if((sd->brush->flag & BRUSH_ANCHORED) && ss->mesh_co_orig) {
                for(i = 0; i < ss->totvert; ++i) {
-                       VecCopyf(ss->mvert[i].co, cache->mesh_store[i]);
+                       VecCopyf(ss->mvert[i].co, ss->mesh_co_orig[i]);
                        ss->mvert[i].no[0] = cache->orig_norms[i][0];
                        ss->mvert[i].no[1] = cache->orig_norms[i][1];
                        ss->mvert[i].no[2] = cache->orig_norms[i][2];
@@ -1513,7 +1379,7 @@ static void sculpt_restore_mesh(Sculpt *sd)
                }
 
                if(sd->brush->sculpt_tool == SCULPT_TOOL_LAYER)
-                       memset(cache->layer_disps, 0, sizeof(float) * ss->totvert);
+                       memset(ss->layer_disps, 0, sizeof(float) * ss->totvert);
        }
 }
 
@@ -1525,56 +1391,162 @@ static void sculpt_post_stroke_free(SculptSession *ss)
 
 static void sculpt_flush_update(bContext *C)
 {
-       Sculpt *s = CTX_data_tool_settings(C)->sculpt;
+       Object *ob = CTX_data_active_object(C);
+       SculptSession *ss = ob->sculpt;
        ARegion *ar = CTX_wm_region(C);
-       MultiresModifierData *mmd = s->session->multires;
+       MultiresModifierData *mmd = ss->multires;
 
-       calc_damaged_verts(s->session);
+       calc_damaged_verts(ss);
 
        if(mmd) {
-               if(mmd->undo_verts && mmd->undo_verts != s->session->mvert)
+               if(mmd->undo_verts && mmd->undo_verts != ss->mvert)
                        MEM_freeN(mmd->undo_verts);
                
-               mmd->undo_verts = s->session->mvert;
-               mmd->undo_verts_tot = s->session->totvert;
-               multires_mark_as_modified(CTX_data_active_object(C));
+               mmd->undo_verts = ss->mvert;
+               mmd->undo_verts_tot = ss->totvert;
+               multires_mark_as_modified(ob);
        }
 
        ED_region_tag_redraw(ar);
 }
 
-static int sculpt_brush_stroke_modal(bContext *C, wmOperator *op, wmEvent *event)
+/* Returns zero if no sculpt changes should be made, non-zero otherwise */
+static int sculpt_smooth_stroke(Sculpt *s, SculptSession *ss, int output[2], wmEvent *event)
 {
-       PointerRNA itemptr;
-       Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
-       float center[3];
-       int mouse[2] = {event->x, event->y};
+       output[0] = event->x;
+       output[1] = event->y;
 
-       sculpt_update_mesh_elements(C);
+       if(s->brush->flag & BRUSH_SMOOTH_STROKE && s->brush->sculpt_tool != SCULPT_TOOL_GRAB) {
+               StrokeCache *cache = ss->cache;
+               float u = .9, v = 1.0 - u;
+               int dx = cache->mouse[0] - event->x, dy = cache->mouse[1] - event->y;
+               int radius = 50;
+
+               /* If the mouse is moving within the radius of the last move,
+                  don't update the mouse position. This allows sharp turns. */
+               if(dx*dx + dy*dy < radius*radius)
+                       return 0;
+
+               output[0] = event->x * v + cache->mouse[0] * u;
+               output[1] = event->y * v + cache->mouse[1] * u;
+       }
 
-       unproject(sd->session->cache->mats, center, event->x, event->y,
-                 read_cached_depth(&sd->session->cache->vc, event->x, event->y));
+       return 1;
+}
 
+/* Returns zero if the stroke dots should not be spaced, non-zero otherwise */
+int sculpt_space_stroke_enabled(Sculpt *s)
+{
+       Brush *br = s->brush;
+       return (br->flag & BRUSH_SPACE) && !(br->flag & BRUSH_ANCHORED) && (br->sculpt_tool != SCULPT_TOOL_GRAB);
+}
+
+/* Put the location of the next sculpt stroke dot into the stroke RNA and apply it to the mesh */
+static void sculpt_brush_stroke_add_step(bContext *C, wmOperator *op, wmEvent *event, int mouse[2])
+{
+       Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
+       SculptSession *ss = CTX_data_active_object(C)->sculpt;
+       StrokeCache *cache = ss->cache;
+       PointerRNA itemptr;
+       float cur_depth;
+       float center[3];
+
+       cur_depth = read_cached_depth(&cache->vc, mouse[0], mouse[1]);
+       unproject(ss->cache->mats, center, mouse[0], mouse[1], cur_depth);
+                               
        /* Add to stroke */
        RNA_collection_add(op->ptr, "stroke", &itemptr);
        RNA_float_set_array(&itemptr, "location", center);
        RNA_int_set_array(&itemptr, "mouse", mouse);
        RNA_boolean_set(&itemptr, "flip", event->shift);
-       sculpt_update_cache_variants(sd, &itemptr);
+       sculpt_update_cache_variants(sd, ss, &itemptr);
+                               
+       sculpt_restore_mesh(sd, ss);
+       do_symmetrical_brush_actions(sd, ss);
+}
 
-       sculpt_restore_mesh(sd);
-       do_symmetrical_brush_actions(CTX_data_tool_settings(C)->sculpt, sd->session->cache);
+/* For brushes with stroke spacing enabled, moves mouse in steps
+   towards the final mouse location. */
+static int sculpt_space_stroke(bContext *C, wmOperator *op, wmEvent *event, Sculpt *s, SculptSession *ss, const int final_mouse[2])
+{
+       StrokeCache *cache = ss->cache;
+       int cnt = 0;
+
+       if(sculpt_space_stroke_enabled(s)) {
+               float vec[2] = {final_mouse[0] - cache->mouse[0], final_mouse[1] - cache->mouse[1]};
+               int mouse[2] = {cache->mouse[0], cache->mouse[1]};
+               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]);
+
+               if(length > FLT_EPSILON) {
+                       scale = s->brush->spacing / length;
+                       vec[0] *= scale;
+                       vec[1] *= scale;
+
+                       steps = (int)(length / s->brush->spacing);
+                       for(i = 0; i < steps; ++i, ++cnt) {
+                               mouse[0] += vec[0];
+                               mouse[1] += vec[1];
+                               sculpt_brush_stroke_add_step(C, op, event, mouse);
+                       }
+               }
+       }
 
-       sculpt_flush_update(C);
-       sculpt_post_stroke_free(sd->session);
+       return cnt;
+}
 
-       /* Finished */
-       if(event->type == LEFTMOUSE && event->val == 0) {
-               request_depth_update(sd->session->cache->vc.rv3d);
+static int sculpt_brush_stroke_modal(bContext *C, wmOperator *op, wmEvent *event)
+{
+       Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
+       SculptSession *ss = CTX_data_active_object(C)->sculpt;
+       ARegion *ar = CTX_wm_region(C);
+       float cur_depth;
+
+       sculpt_update_mesh_elements(C);
+
+       if(!ss->cache) {
+               ViewContext vc;
+               view3d_set_viewcontext(C, &vc);
+               cur_depth = read_cached_depth(&vc, event->x, event->y);
+
+               /* Don't start the stroke until a valid depth is found */
+               if(cur_depth < 1.0 - FLT_EPSILON) {
+                       sculpt_brush_stroke_init_properties(C, op, event, ss);
+                       sculptmode_update_all_projverts(ss);
+               }
+
+               ED_region_tag_redraw(ar);
+       }
+
+       if(ss->cache) {
+               int mouse[2];
 
-               sculpt_cache_free(sd->session->cache);
+               if(sculpt_smooth_stroke(sd, ss, mouse, event)) {
+                       if(sculpt_space_stroke_enabled(sd)) {
+                               if(!sculpt_space_stroke(C, op, event, sd, ss, mouse))
+                                       ED_region_tag_redraw(ar);
+                       }
+                       else
+                               sculpt_brush_stroke_add_step(C, op, event, mouse);
+
+                       sculpt_flush_update(C);
+                       sculpt_post_stroke_free(ss);
+               }
+               else
+                       ED_region_tag_redraw(ar);
+       }
 
-               sculpt_undo_push(C, sd);
+       /* Finished */
+       if(event->type == LEFTMOUSE && event->val == 0) {
+               if(ss->cache) {
+                       request_depth_update(ss->cache->vc.rv3d);
+                       sculpt_cache_free(ss->cache);
+                       ss->cache = NULL;
+                       sculpt_undo_push(C, sd);
+               }
 
                return OPERATOR_FINISHED;
        }
@@ -1585,24 +1557,25 @@ static int sculpt_brush_stroke_modal(bContext *C, wmOperator *op, wmEvent *event
 static int sculpt_brush_stroke_exec(bContext *C, wmOperator *op)
 {
        Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
+       SculptSession *ss = CTX_data_active_object(C)->sculpt;
 
        view3d_operator_needs_opengl(C);
-       sculpt_update_cache_invariants(sd, C, op);
-       sculptmode_update_all_projverts(sd->session);
-       sculpt_update_tex(sd);
+       sculpt_update_cache_invariants(sd, ss, C, op);
+       sculptmode_update_all_projverts(ss);
+       sculpt_update_tex(sd, ss);
 
        RNA_BEGIN(op->ptr, itemptr, "stroke") {
-               sculpt_update_cache_variants(sd, &itemptr);
+               sculpt_update_cache_variants(sd, ss, &itemptr);
 
-               sculpt_restore_mesh(sd);
-               do_symmetrical_brush_actions(sd, sd->session->cache);
+               sculpt_restore_mesh(sd, ss);
+               do_symmetrical_brush_actions(sd, ss);
 
-               sculpt_post_stroke_free(sd->session);
+               sculpt_post_stroke_free(ss);
        }
        RNA_END;
 
        sculpt_flush_update(C);
-       sculpt_cache_free(sd->session->cache);
+       sculpt_cache_free(ss->cache);
 
        sculpt_undo_push(C, sd);
 
@@ -1624,7 +1597,7 @@ static void SCULPT_OT_brush_stroke(wmOperatorType *ot)
        ot->poll= sculpt_poll;
        
        /* flags (sculpt does own undo? (ton) */
-       ot->flag= OPTYPE_REGISTER;
+       ot->flag= OPTYPE_REGISTER|OPTYPE_BLOCKING;
 
        /* properties */
        RNA_def_collection_runtime(ot->srna, "stroke", &RNA_OperatorStrokeElement, "Stroke", "");
@@ -1645,54 +1618,74 @@ static void SCULPT_OT_brush_stroke(wmOperatorType *ot)
        RNA_def_float(ot->srna, "depth", 0.0f, 0.0f, FLT_MAX, "depth", "", 0.0f, FLT_MAX);
 }
 
+/**** 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)
+{
+       SculptSession *ss = CTX_data_active_object(C)->sculpt;
+
+       if(ss) {
+               if(ss->layer_disps)
+                       MEM_freeN(ss->layer_disps);
+               ss->layer_disps = NULL;
+
+               if(ss->mesh_co_orig)
+                       MEM_freeN(ss->mesh_co_orig);
+               ss->mesh_co_orig = NULL;
+       }
+
+       return OPERATOR_FINISHED;
+}
+
+static void SCULPT_OT_set_persistent_base(wmOperatorType *ot)
+{
+       /* identifiers */
+       ot->name= "Set Persistent Base";
+       ot->idname= "SCULPT_OT_set_persistent_base";
+       
+       /* api callbacks */
+       ot->exec= sculpt_set_persistent_base;
+       ot->poll= sculpt_mode_poll;
+       
+       ot->flag= OPTYPE_REGISTER;
+}
+
 /**** Toggle operator for turning sculpt mode on or off ****/
 
 static int sculpt_toggle_mode(bContext *C, wmOperator *op)
 {
        ToolSettings *ts = CTX_data_tool_settings(C);
+       Object *ob = CTX_data_active_object(C);
 
-       if(G.f & G_SCULPTMODE) {
-               multires_force_update(CTX_data_active_object(C));
+       if(ob->mode & OB_MODE_SCULPT) {
+               multires_force_update(ob);
 
                /* Leave sculptmode */
-               G.f &= ~G_SCULPTMODE;
-
-               toggle_paint_cursor(C);
+               ob->mode &= ~OB_MODE_SCULPT;
 
-               sculptsession_free(ts->sculpt);
+               free_sculptsession(&ob->sculpt);
        }
        else {
-               MTex *mtex; // XXX: temporary
-
                /* Enter sculptmode */
 
-               G.f |= G_SCULPTMODE;
+               ob->mode |= OB_MODE_SCULPT;
                
                /* Create persistent sculpt mode data */
                if(!ts->sculpt)
                        ts->sculpt = MEM_callocN(sizeof(Sculpt), "sculpt mode data");
 
                /* Create sculpt mode session data */
-               if(ts->sculpt->session)
-                       MEM_freeN(ts->sculpt->session);
-               ts->sculpt->session = MEM_callocN(sizeof(SculptSession), "sculpt session");
+               if(ob->sculpt)
+                       free_sculptsession(&ob->sculpt);
+               ob->sculpt = MEM_callocN(sizeof(SculptSession), "sculpt session");
 
-               toggle_paint_cursor(C);
+               if(!ts->sculpt->cursor)
+                       toggle_paint_cursor(C);
 
                /* If there's no brush, create one */
                brush_check_exists(&ts->sculpt->brush);
 
-               /* XXX: testing: set the brush texture to the first available one */
-               if(G.main->tex.first) {
-                       Tex *tex = G.main->tex.first;
-                       if(tex->type) {
-                               mtex = MEM_callocN(sizeof(MTex), "test mtex");
-                               ts->sculpt->brush->texact = 0;
-                               ts->sculpt->brush->mtex[0] = mtex;
-                               mtex->tex = tex;
-                               mtex->size[0] = mtex->size[1] = mtex->size[2] = 50;
-                       }
-               }
+               WM_event_add_notifier(C, NC_SCENE|ND_MODE, CTX_data_scene(C));
        }
 
        return OPERATOR_FINISHED;
@@ -1716,456 +1709,5 @@ void ED_operatortypes_sculpt()
        WM_operatortype_append(SCULPT_OT_radial_control);
        WM_operatortype_append(SCULPT_OT_brush_stroke);
        WM_operatortype_append(SCULPT_OT_sculptmode_toggle);
-       WM_operatortype_append(SCULPT_OT_brush_curve_preset);
-}
-
-void sculpt(Sculpt *sd)
-{
-#if 0
-       SculptSession *ss= sd->session;
-       Object *ob= NULL; /*XXX */
-       Mesh *me;
-       MultiresModifierData *mmd = NULL;
-       /* lastSigMouse is for the rake, to store the last place the mouse movement was significant */
-       short mouse[2], mvalo[2], lastSigMouse[2],firsttime=1, mousebut;
-       short modifier_calculations= 0;
-       BrushAction *a = MEM_callocN(sizeof(BrushAction), "brush action");
-       short spacing= 32000;
-       int scissor_box[4];
-       float offsetRot;
-       int smooth_stroke = 0, i;
-       int anchored, rake = 0 /* XXX: rake = ? */;
-
-       /* XXX: checking that sculpting is allowed
-       if(!(G.f & G_SCULPTMODE) || G.obedit || !ob || ob->id.lib || !get_mesh(ob) || (get_mesh(ob)->totface == 0))
-               return;
-       if(!(ob->lay & G.vd->lay))
-               error("Active object is not in this layer");
-       if(ob_get_keyblock(ob)) {
-               if(!(ob->shapeflag & OB_SHAPE_LOCK)) {
-                       error("Cannot sculpt on unlocked shape key");
-                       return;
-               }
-       }*/
-       
-       anchored = sd->brush->flag & BRUSH_ANCHORED;
-       smooth_stroke = (sd->flags & SCULPT_INPUT_SMOOTH) && (sd->brush->sculpt_tool != SCULPT_TOOL_GRAB) && !anchored;
-
-       if(smooth_stroke)
-               sculpt_stroke_new(256);
-
-       ss->damaged_rects.first = ss->damaged_rects.last = NULL;
-       ss->damaged_verts.first = ss->damaged_verts.last = NULL;
-       ss->vertexcosnos = NULL;
-
-       mmd = sculpt_multires_active(ob);
-
-       /* Check that vertex users are up-to-date */
-       if(ob != active_ob || !ss->vertex_users || ss->vertex_users_size != cache->totvert) {
-               sculpt_vertexusers_free(ss);
-               calc_vertex_users(ss);
-               if(ss->projverts)
-                       MEM_freeN(ss->projverts);
-               ss->projverts = NULL;
-               active_ob= ob;
-       }
-               
-       glEnableClientState(GL_VERTEX_ARRAY);
-       glEnableClientState(GL_NORMAL_ARRAY);
-
-       /*XXX:
-       persp(PERSP_VIEW);
-       getmouseco_areawin(mvalo);*/
-
-       /* Init texture
-          FIXME: Shouldn't be doing this every time! */
-       if(sd->tex_mode!=SCULPTREPT_3D)
-               sculptmode_update_tex(sd);
-
-       /*XXX: getmouseco_areawin(mouse); */
-       mvalo[0]= mouse[0];
-       mvalo[1]= mouse[1];
-       lastSigMouse[0]=mouse[0];
-       lastSigMouse[1]=mouse[1];
-       mousebut = 0; /* XXX: L_MOUSE; */
-
-       /* If modifier_calculations is true, then extra time must be spent
-          updating the mesh. This takes a *lot* longer, so it's worth
-          skipping if the modifier stack is empty. */
-       modifier_calculations= sculpt_modifiers_active(ob);
-
-       if(modifier_calculations)
-               ss->vertexcosnos= mesh_get_mapped_verts_nors(NULL, ob); /* XXX: scene = ? */
-       sculptmode_update_all_projverts(ss);
-
-       /* Capture original copy */
-       if(sd->flags & SCULPT_DRAW_FAST)
-               glAccum(GL_LOAD, 1);
-
-       /* Get original scissor box */
-       glGetIntegerv(GL_SCISSOR_BOX, scissor_box);
-       
-       /* For raking, get the original angle*/
-       offsetRot=sculpt_tex_angle(sd);
-
-       me = get_mesh(ob);
-
-       while (/*XXX:get_mbut() & mousebut*/0) {
-               /* XXX: getmouseco_areawin(mouse); */
-               /* If rake, and the mouse has moved over 10 pixels (euclidean) (prevents jitter) then get the new angle */
-               if (rake && (pow(lastSigMouse[0]-mouse[0],2)+pow(lastSigMouse[1]-mouse[1],2))>100){
-                       /*Nasty looking, but just orig + new angle really*/
-                       set_tex_angle(sd, offsetRot+180.+to_deg(atan2((float)(mouse[1]-lastSigMouse[1]),(float)(mouse[0]-lastSigMouse[0]))));
-                       lastSigMouse[0]=mouse[0];
-                       lastSigMouse[1]=mouse[1];
-               }
-               
-               if(firsttime || mouse[0]!=mvalo[0] || mouse[1]!=mvalo[1] ||
-                  sd->brush->flag & BRUSH_AIRBRUSH) {
-                       a->firsttime = firsttime;
-                       firsttime= 0;
-
-                       if(smooth_stroke)
-                               sculpt_stroke_add_point(ss->stroke, mouse[0], mouse[1]);
-
-                       spacing+= sqrt(pow(mvalo[0]-mouse[0],2)+pow(mvalo[1]-mouse[1],2));
-
-                       if(modifier_calculations && !ss->vertexcosnos)
-                               ss->vertexcosnos= mesh_get_mapped_verts_nors(NULL, ob); /*XXX scene = ? */
-
-                       if(sd->brush->sculpt_tool != SCULPT_TOOL_GRAB) {
-                               if(anchored) {
-                                       /* Restore the mesh before continuing with anchored stroke */
-                                       /*if(a->mesh_store) {
-                                               for(i = 0; i < cache->totvert; ++i) {
-                                                       VecCopyf(cache->mvert[i].co, &a->mesh_store[i].x);
-                                                       cache->mvert[i].no[0] = a->orig_norms[i][0];
-                                                       cache->mvert[i].no[1] = a->orig_norms[i][1];
-                                                       cache->mvert[i].no[2] = a->orig_norms[i][2];
-                                               }
-                                               }*/
-                                       
-                                       //do_symmetrical_brush_actions(sd, a, mouse, NULL);
-                               }
-                               else {
-                                       if(smooth_stroke) {
-                                               sculpt_stroke_apply(sd, ss->stroke);
-                                       }
-                                       else if(sd->spacing==0 || spacing>sd->spacing) {
-                                               //do_symmetrical_brush_actions(sd, a, mouse, NULL);
-                                               spacing= 0;
-                                       }
-                               }
-                       }
-                       else {
-                               //do_symmetrical_brush_actions(sd, a, mouse, mvalo);
-                               //unproject(ss, sd->pivot, mouse[0], mouse[1], a->depth);
-                       }
-
-                       if((!ss->multires && modifier_calculations) || ob_get_keyblock(ob)) {
-                               /* XXX: DAG_object_flush_update(G.scene, ob, OB_RECALC_DATA); */ }
-
-                       if(modifier_calculations || sd->brush->sculpt_tool == SCULPT_TOOL_GRAB || !(sd->flags & SCULPT_DRAW_FAST)) {
-                               calc_damaged_verts(ss, a);
-                               /*XXX: scrarea_do_windraw(curarea);
-                               screen_swapbuffers(); */
-                       } else { /* Optimized drawing */
-                               calc_damaged_verts(ss, a);
-
-                               /* Draw the stored image to the screen */
-                               glAccum(GL_RETURN, 1);
-
-                               sculpt_clear_damaged_areas(ss);
-                               
-                               /* Draw all the polygons that are inside the modified area(s) */
-                               glScissor(scissor_box[0], scissor_box[1], scissor_box[2], scissor_box[3]);
-                               /* XXX: sculptmode_draw_mesh(1); */
-                               glAccum(GL_LOAD, 1);
-
-                               projverts_clear_inside(ss);
-
-                               /* XXX: persp(PERSP_WIN); */
-                               glDisable(GL_DEPTH_TEST);
-                               
-                               /* Draw cursor */
-                               if(sd->flags & SCULPT_TOOL_DRAW)
-                                       fdrawXORcirc((float)mouse[0],(float)mouse[1],sd->brush->size);
-                               /* XXX: if(smooth_stroke)
-                                  sculpt_stroke_draw();
-                               
-                               myswapbuffers(); */
-                       }
-
-                       BLI_freelistN(&ss->damaged_rects);
-                       ss->damaged_rects.first = ss->damaged_rects.last = NULL;
-       
-                       mvalo[0]= mouse[0];
-                       mvalo[1]= mouse[1];
-
-                       if(ss->vertexcosnos) {
-                               MEM_freeN(ss->vertexcosnos);
-                               ss->vertexcosnos= NULL;
-                       }
-
-               }
-               else { /*XXX:BIF_wait_for_statechange();*/ }
-       }
-
-       /* Set the rotation of the brush back to what it was before any rake */
-       set_tex_angle(sd, offsetRot);
-       
-       if(smooth_stroke) {
-               sculpt_stroke_apply_all(sd, ss->stroke);
-               calc_damaged_verts(ss, a);
-               BLI_freelistN(&ss->damaged_rects);
-       }
-
-       //if(a->layer_disps) MEM_freeN(a->layer_disps);
-       //if(a->mesh_store) MEM_freeN(a->mesh_store);
-       //if(a->orig_norms) MEM_freeN(a->orig_norms);
-       for(i=0; i<8; ++i)
-               BLI_freelistN(&a->grab_active_verts[i]);
-       MEM_freeN(a);
-       sculpt_stroke_free(ss->stroke);
-       ss->stroke = NULL;
-
-       if(mmd) {
-               if(mmd->undo_verts && mmd->undo_verts != cache->mvert)
-                       MEM_freeN(mmd->undo_verts);
-               
-               mmd->undo_verts = cache->mvert;
-               mmd->undo_verts_tot = cache->totvert;
-       }
-
-       //sculpt_undo_push(sd);
-
-       /* XXX: if(G.vd->depths) G.vd->depths->damaged= 1;
-          allqueue(REDRAWVIEW3D, 0); */
-#endif
-}
-
-/* Partial Mesh Visibility */
-
-/* XXX: Partial vis. always was a mess, have to figure something out */
-#if 0
-/* mode: 0=hide outside selection, 1=hide inside selection */
-static void sculptmode_do_pmv(Object *ob, rcti *hb_2d, int mode)
-{
-       Mesh *me= get_mesh(ob);
-       float hidebox[6][3];
-       vec3f plane_normals[4];
-       float plane_ds[4];
-       unsigned i, j;
-       unsigned ndx_show, ndx_hide;
-       MVert *nve;
-       unsigned face_cnt_show= 0, face_ndx_show= 0;
-       unsigned edge_cnt_show= 0, edge_ndx_show= 0;
-       unsigned *old_map= NULL;
-       const unsigned SHOW= 0, HIDE=1;
-
-       /* Convert hide box from 2D to 3D */
-       unproject(hidebox[0], hb_2d->xmin, hb_2d->ymax, 1);
-       unproject(hidebox[1], hb_2d->xmax, hb_2d->ymax, 1);
-       unproject(hidebox[2], hb_2d->xmax, hb_2d->ymin, 1);
-       unproject(hidebox[3], hb_2d->xmin, hb_2d->ymin, 1);
-       unproject(hidebox[4], hb_2d->xmin, hb_2d->ymax, 0);
-       unproject(hidebox[5], hb_2d->xmax, hb_2d->ymin, 0);
-       
-       /* Calculate normals for each side of hide box */
-       CalcNormFloat(hidebox[0], hidebox[1], hidebox[4], &plane_normals[0].x);
-       CalcNormFloat(hidebox[1], hidebox[2], hidebox[5], &plane_normals[1].x);
-       CalcNormFloat(hidebox[2], hidebox[3], hidebox[5], &plane_normals[2].x);
-       CalcNormFloat(hidebox[3], hidebox[0], hidebox[4], &plane_normals[3].x);
-       
-       /* Calculate D for each side of hide box */
-       for(i= 0; i<4; ++i)
-               plane_ds[i]= hidebox[i][0]*plane_normals[i].x + hidebox[i][1]*plane_normals[i].y +
-                       hidebox[i][2]*plane_normals[i].z;
-       
-       /* Add partial visibility to mesh */
-       if(!me->pv) {
-               me->pv= MEM_callocN(sizeof(PartialVisibility),"PartialVisibility");
-       } else {
-               old_map= MEM_callocN(sizeof(unsigned)*me->pv->totvert,"PMV oldmap");
-               for(i=0; i<me->pv->totvert; ++i) {
-                       old_map[i]= me->pv->vert_map[i]<me->totvert?0:1;
-               }
-               mesh_pmv_revert(ob, me);
-       }
-       
-       /* Kill sculpt data */
-       active_ob= NULL;
-       
-       /* Initalize map with which verts are to be hidden */
-       me->pv->vert_map= MEM_mallocN(sizeof(unsigned)*me->totvert, "PMV vertmap");
-       me->pv->totvert= me->totvert;
-       me->totvert= 0;
-       for(i=0; i<me->pv->totvert; ++i) {
-               me->pv->vert_map[i]= mode ? HIDE:SHOW;
-               for(j=0; j<4; ++j) {
-                       if(me->mvert[i].co[0] * plane_normals[j].x +
-                          me->mvert[i].co[1] * plane_normals[j].y +
-                          me->mvert[i].co[2] * plane_normals[j].z < plane_ds[j] ) {
-                               me->pv->vert_map[i]= mode ? SHOW:HIDE; /* Vert is outside the hide box */
-                               break;
-                       }
-               }
-               if(old_map && old_map[i]) me->pv->vert_map[i]= 1;
-               if(!me->pv->vert_map[i]) ++me->totvert;
-
-       }
-       if(old_map) MEM_freeN(old_map);
-
-       /* Find out how many faces to show */
-       for(i=0; i<me->totface; ++i) {
-               if(!me->pv->vert_map[me->mface[i].v1] &&
-                  !me->pv->vert_map[me->mface[i].v2] &&
-                  !me->pv->vert_map[me->mface[i].v3]) {
-                       if(me->mface[i].v4) {
-                               if(!me->pv->vert_map[me->mface[i].v4])
-                                       ++face_cnt_show;
-                       }
-                       else ++face_cnt_show;
-               }
-       }
-       /* Find out how many edges to show */
-       for(i=0; i<me->totedge; ++i) {
-               if(!me->pv->vert_map[me->medge[i].v1] &&
-                  !me->pv->vert_map[me->medge[i].v2])
-                       ++edge_cnt_show;
-       }
-
-       /* Create new vert array and reset each vert's map with map[old]=new index */
-       nve= MEM_mallocN(sizeof(MVert)*me->pv->totvert, "PMV verts");
-       ndx_show= 0; ndx_hide= me->totvert;
-       for(i=0; i<me->pv->totvert; ++i) {
-               if(me->pv->vert_map[i]) {
-                       me->pv->vert_map[i]= ndx_hide;
-                       nve[me->pv->vert_map[i]]= me->mvert[i];
-                       ++ndx_hide;
-               } else {
-                       me->pv->vert_map[i]= ndx_show;
-                       nve[me->pv->vert_map[i]]= me->mvert[i];
-                       ++ndx_show;
-               }
-       }
-       CustomData_free_layer_active(&me->vdata, CD_MVERT, me->pv->totvert);
-       me->mvert= CustomData_add_layer(&me->vdata, CD_MVERT, CD_ASSIGN, nve, me->totvert);
-
-       /* Create new face array */
-       me->pv->old_faces= me->mface;
-       me->pv->totface= me->totface;
-       me->mface= MEM_mallocN(sizeof(MFace)*face_cnt_show, "PMV faces");
-       for(i=0; i<me->totface; ++i) {
-               MFace *pr_f= &me->pv->old_faces[i];
-               char show= 0;
-
-               if(me->pv->vert_map[pr_f->v1] < me->totvert &&
-                  me->pv->vert_map[pr_f->v2] < me->totvert &&
-                  me->pv->vert_map[pr_f->v3] < me->totvert) {
-                       if(pr_f->v4) {
-                               if(me->pv->vert_map[pr_f->v4] < me->totvert)
-                                       show= 1;
-                       }
-                       else show= 1;
-               }
-
-               if(show) {
-                       MFace *cr_f= &me->mface[face_ndx_show];
-                       *cr_f= *pr_f;
-                       cr_f->v1= me->pv->vert_map[pr_f->v1];
-                       cr_f->v2= me->pv->vert_map[pr_f->v2];
-                       cr_f->v3= me->pv->vert_map[pr_f->v3];
-                       cr_f->v4= pr_f->v4 ? me->pv->vert_map[pr_f->v4] : 0;
-                       test_index_face(cr_f,NULL,0,pr_f->v4?4:3);
-                       ++face_ndx_show;
-               }
-       }
-       me->totface= face_cnt_show;
-       CustomData_set_layer(&me->fdata, CD_MFACE, me->mface);
-
-       /* Create new edge array */
-       me->pv->old_edges= me->medge;
-       me->pv->totedge= me->totedge;
-       me->medge= MEM_mallocN(sizeof(MEdge)*edge_cnt_show, "PMV edges");
-       me->pv->edge_map= MEM_mallocN(sizeof(int)*me->pv->totedge,"PMV edgemap");
-       for(i=0; i<me->totedge; ++i) {
-               if(me->pv->vert_map[me->pv->old_edges[i].v1] < me->totvert &&
-                  me->pv->vert_map[me->pv->old_edges[i].v2] < me->totvert) {
-                       MEdge *cr_e= &me->medge[edge_ndx_show];
-                       me->pv->edge_map[i]= edge_ndx_show;
-                       *cr_e= me->pv->old_edges[i];
-                       cr_e->v1= me->pv->vert_map[me->pv->old_edges[i].v1];
-                       cr_e->v2= me->pv->vert_map[me->pv->old_edges[i].v2];
-                       ++edge_ndx_show;
-               }
-               else me->pv->edge_map[i]= -1;
-       }
-       me->totedge= edge_cnt_show;
-       CustomData_set_layer(&me->edata, CD_MEDGE, me->medge);
-
-       /* XXX: DAG_object_flush_update(G.scene, OBACT, OB_RECALC_DATA); */
+       WM_operatortype_append(SCULPT_OT_set_persistent_base);
 }
-
-static rcti sculptmode_pmv_box()
-{
-       /*XXX:  short down[2], mouse[2];
-       rcti ret;
-
-       getmouseco_areawin(down);
-
-       while((get_mbut()&L_MOUSE) || (get_mbut()&R_MOUSE)) {
-               getmouseco_areawin(mouse);
-
-               scrarea_do_windraw(curarea);
-
-               persp(PERSP_WIN);
-               glLineWidth(2);
-               setlinestyle(2);
-               sdrawXORline(down[0],down[1],mouse[0],down[1]);
-               sdrawXORline(mouse[0],down[1],mouse[0],mouse[1]);
-               sdrawXORline(mouse[0],mouse[1],down[0],mouse[1]);
-               sdrawXORline(down[0],mouse[1],down[0],down[1]);
-               setlinestyle(0);
-               glLineWidth(1);
-               persp(PERSP_VIEW);
-
-               screen_swapbuffers();
-               backdrawview3d(0);
-       }
-
-       ret.xmin= down[0]<mouse[0]?down[0]:mouse[0];
-       ret.ymin= down[1]<mouse[1]?down[1]:mouse[1];
-       ret.xmax= down[0]>mouse[0]?down[0]:mouse[0];
-       ret.ymax= down[1]>mouse[1]?down[1]:mouse[1];
-       return ret;*/
-}
-
-void sculptmode_pmv(int mode)
-{
-       Object *ob= NULL; /*XXX: OBACT; */
-       rcti hb_2d;
-       
-       if(ob_get_key(ob)) {
-               error("Cannot hide mesh with shape keys enabled");
-               return;
-       }
-       
-       hb_2d= sculptmode_pmv_box(); /* Get 2D hide box */
-       
-       sculptmode_correct_state();
-
-       waitcursor(1);
-
-       if(hb_2d.xmax-hb_2d.xmin > 3 && hb_2d.ymax-hb_2d.ymin > 3) {
-               init_sculptmatrices();
-
-               sculptmode_do_pmv(ob,&hb_2d,mode);
-       }
-       else mesh_pmv_off(ob, get_mesh(ob));
-
-       /*XXX: scrarea_do_windraw(curarea); */
-
-       waitcursor(0);
-}
-#endif