Brought back sculpt code. Commented out a lot of things, so it compiles.
authorNicholas Bishop <nicholasbishop@gmail.com>
Tue, 6 Jan 2009 21:23:42 +0000 (21:23 +0000)
committerNicholas Bishop <nicholasbishop@gmail.com>
Tue, 6 Jan 2009 21:23:42 +0000 (21:23 +0000)
Does nothing more impressive than that for now.

source/blender/editors/sculpt/sculpt.c [new file with mode: 0644]
source/blender/editors/sculpt/sculpt_intern.h [new file with mode: 0644]
source/blender/editors/sculpt/stroke.c [new file with mode: 0644]

diff --git a/source/blender/editors/sculpt/sculpt.c b/source/blender/editors/sculpt/sculpt.c
new file mode 100644 (file)
index 0000000..0783c06
--- /dev/null
@@ -0,0 +1,2137 @@
+/*
+ * $Id: sculptmode.c 18309 2009-01-04 07:47:11Z nicholasbishop $
+ *
+ * ***** 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, 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
+ *
+ * BDR_sculptmode.h
+ *
+ */
+
+#include "GHOST_Types.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_arithb.h"
+#include "BLI_blenlib.h"
+#include "BLI_dynstr.h"
+
+#include "DNA_armature_types.h"
+#include "DNA_brush_types.h"
+#include "DNA_image_types.h"
+#include "DNA_key_types.h"
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_modifier_types.h"
+#include "DNA_object_types.h"
+#include "DNA_screen_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_texture_types.h"
+#include "DNA_view3d_types.h"
+#include "DNA_userdef_types.h"
+#include "DNA_color_types.h"
+
+#include "BKE_customdata.h"
+#include "BKE_DerivedMesh.h"
+#include "BKE_depsgraph.h"
+#include "BKE_global.h"
+#include "BKE_image.h"
+#include "BKE_key.h"
+#include "BKE_library.h"
+#include "BKE_main.h"
+#include "BKE_mesh.h"
+#include "BKE_modifier.h"
+#include "BKE_multires.h"
+#include "BKE_sculpt.h"
+#include "BKE_texture.h"
+#include "BKE_utildefines.h"
+#include "BKE_colortools.h"
+
+#include "BIF_gl.h"
+#include "BIF_glutil.h"
+
+#include "sculpt_intern.h"
+
+#include "IMB_imbuf_types.h"
+
+#include "RE_render_ext.h"
+#include "RE_shader_ext.h" /*for multitex_ext*/
+
+#include "GPU_draw.h"
+
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* Number of vertices to average in order to determine the flatten distance */
+#define FLATTEN_SAMPLE_SIZE 10
+
+/* Texture cache size */
+#define TC_SIZE 256
+
+/* ===== STRUCTS =====
+ *
+ */
+
+/* ActiveData stores an Index into the mvert array of Mesh, plus Fade, which
+   stores how far the vertex is from the brush center, scaled to the range [0,1]. */
+typedef struct ActiveData {
+       struct ActiveData *next, *prev;
+       unsigned int Index;
+       float Fade;
+       float dist;
+} ActiveData;
+
+typedef struct BrushActionSymm {
+       float center_3d[3];
+       char index;
+
+       float up[3], right[3], out[3];
+
+       /* Grab brush */
+       float grab_delta[3];
+} BrushActionSymm;
+
+typedef struct BrushAction {
+       BrushActionSymm symm;
+
+       char firsttime;
+
+       /* Some brushes need access to original mesh vertices */
+       vec3f *mesh_store;
+       short (*orig_norms)[3];
+
+       short mouse[2];
+       float size_3d;
+
+       float prev_radius;
+       float radius;
+
+       float *layer_disps;
+       char flip;
+
+       char clip[3];
+       float cliptol[3];
+
+       float anchored_rot;
+
+       /* Grab brush */
+       ListBase grab_active_verts[8];
+       float depth;
+
+       /* Adjust brush strength along each axis
+          to adjust for object scaling */
+       float scale[3];
+} BrushAction;
+
+typedef struct RectNode {
+       struct RectNode *next, *prev;
+       rcti r;
+} RectNode;
+
+/* Used to store to 2D screen coordinates of each vertex in the mesh. */
+typedef struct ProjVert {
+       short co[2];
+       
+       /* Used to mark whether a vertex is inside a rough bounding box
+          containing the brush. */
+       char inside;
+} ProjVert;
+
+static Object *active_ob= NULL;
+
+SculptData *sculpt_data(void)
+{
+       return NULL; /*XXX: return &G.scene->sculptdata; */
+}
+
+static void sculpt_init_session(void);
+static void init_brushaction(BrushAction *a, short *, short *);
+static void sculpt_undo_push(const short);
+
+SculptSession *sculpt_session(void)
+{
+       if(!sculpt_data()->session)
+               sculpt_init_session();
+       return sculpt_data()->session;
+}
+
+/* ===== MEMORY =====
+ * 
+ * Allocate/initialize/free data
+ */
+
+static void sculpt_init_session(void)
+{
+       if(sculpt_data()->session)
+               ;/*XXX: sculptsession_free(G.scene); */
+       sculpt_data()->session= MEM_callocN(sizeof(SculptSession), "SculptSession");
+}
+
+/* vertex_users is an array of Lists that store all the faces that use a
+   particular vertex. vertex_users is in the same order as mesh.mvert */
+static void calc_vertex_users()
+{
+       SculptSession *ss= sculpt_session();
+       int i,j;
+       IndexNode *node= NULL;
+
+       sculpt_vertexusers_free(ss);
+       
+       /* For efficiency, use vertex_users_mem as a memory pool (may be larger
+          than necessary if mesh has triangles, but only one alloc is needed.) */
+       ss->vertex_users= MEM_callocN(sizeof(ListBase) * ss->totvert, "vertex_users");
+       ss->vertex_users_size= ss->totvert;
+       ss->vertex_users_mem= MEM_callocN(sizeof(IndexNode)*ss->totface*4, "vertex_users_mem");
+       node= ss->vertex_users_mem;
+
+       /* Find the users */
+       for(i=0; i<ss->totface; ++i){
+               for(j=0; j<(ss->mface[i].v4?4:3); ++j, ++node) {
+                       node->index=i;
+                       BLI_addtail(&ss->vertex_users[((unsigned int*)(&ss->mface[i]))[j]], node);
+               }
+       }
+}
+
+/* ===== INTERFACE =====
+ */
+
+/* XXX: this can probably removed entirely */
+#if 0
+void sculptmode_rem_tex(void *junk0,void *junk1)
+{
+       MTex *mtex= G.scene->sculptdata.mtex[G.scene->sculptdata.texact];
+       if(mtex) {
+               SculptSession *ss= sculpt_session();
+               if(mtex->tex) mtex->tex->id.us--;
+               MEM_freeN(mtex);
+               G.scene->sculptdata.mtex[G.scene->sculptdata.texact]= NULL;
+               /* Clear brush preview */
+               if(ss->texcache) {
+                       MEM_freeN(ss->texcache);
+                       ss->texcache= NULL;
+               }
+               BIF_undo_push("Unlink brush texture");
+               allqueue(REDRAWBUTSEDIT, 0);
+               allqueue(REDRAWOOPS, 0);
+       }
+}
+#endif
+
+/* ===== OPENGL =====
+ *
+ * Simple functions to get data from the GL
+ */
+
+/* Store the modelview and projection matrices and viewport. */
+void init_sculptmatrices()
+{
+       /* XXX: probably becomes context data?
+
+       SculptSession *ss= sculpt_session();
+
+       glMatrixMode(GL_MODELVIEW);
+       glPushMatrix();
+       glMultMatrixf(OBACT->obmat);
+
+       if(!ss->mats)
+               ss->mats = MEM_callocN(sizeof(bglMats), "sculpt bglmats");
+       bgl_get_mats(ss->mats);
+       
+       glPopMatrix();
+       */
+}
+
+/* Uses window coordinates (x,y) to find the depth in the GL depth buffer. If
+   available, G.vd->depths is used so that the brush doesn't sculpt on top of
+   itself (G.vd->depths is only updated at the end of a brush stroke.) */
+float get_depth(short x, short y)
+{
+       /* XXX
+       float depth;
+
+       if(x<0 || y<0) return 1;
+       if(x>=curarea->winx || y>=curarea->winy) return 1;
+
+       if(G.vd->depths && x<G.vd->depths->w && y<G.vd->depths->h)
+               return G.vd->depths->depths[y*G.vd->depths->w+x];
+       
+       x+= curarea->winrct.xmin;
+       y+= curarea->winrct.ymin;
+       
+       glReadPixels(x, y, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &depth);
+
+       return depth; */
+       return 0;
+}
+
+/* Uses window coordinates (x,y) and depth component z to find a point in
+   modelspace */
+void unproject(float out[3], const short x, const short y, const float z)
+{
+       SculptSession *ss= sculpt_session();
+       double ux, uy, uz;
+
+        gluUnProject(x,y,z, ss->mats->modelview, ss->mats->projection,
+                    (GLint *)ss->mats->viewport, &ux, &uy, &uz );
+       out[0] = ux;
+       out[1] = uy;
+       out[2] = uz;
+}
+
+/* Convert a point in model coordinates to 2D screen coordinates. */
+static void projectf(const float v[3], float p[2])
+{
+       SculptSession *ss= sculpt_session();
+       double ux, uy, uz;
+
+       gluProject(v[0],v[1],v[2], ss->mats->modelview, ss->mats->projection,
+                  (GLint *)ss->mats->viewport, &ux, &uy, &uz);
+       p[0]= ux;
+       p[1]= uy;
+}
+
+static void project(const float v[3], short p[2])
+{
+       float f[2];
+       projectf(v, f);
+
+       p[0]= f[0];
+       p[1]= f[1];
+}
+
+/* ===== Sculpting =====
+ *
+ */
+
+/* Return modified brush size. Uses current tablet pressure (if available) to
+   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 */
+char brush_size()
+{
+       SculptData *sd = NULL; /* XXX */
+       const BrushData *b= sculptmode_brush();
+       float size= b->size;
+       float pressure= get_pressure();
+       short activedevice= get_activedevice();
+       
+       if(sculpt_data()->brush_type!=GRAB_BRUSH) {
+               const float size_factor= sd->tablet_size / 10.0f;
+               
+               /* XXX: tablet stuff
+               if(ELEM(activedevice, DEV_STYLUS, DEV_ERASER))
+                       size*= sd->tablet_size==0?1:
+                       (1-size_factor) + pressure*size_factor;*/
+       }
+       
+       return size;
+}
+
+/* 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. */
+float brush_strength(BrushAction *a)
+{
+       SculptData *sd = NULL; /* XXX */
+       const BrushData* b= sculptmode_brush();
+       float dir= b->dir==1 ? 1 : -1;
+       float pressure= 1;
+       short activedevice= get_activedevice();
+       float flip= a->flip ? -1:1;
+       float anchored = b->flag & SCULPT_BRUSH_ANCHORED ? 25 : 1;
+
+       const float strength_factor= sd->tablet_strength / 10.0f;
+
+       /* XXX: tablet stuff */
+#if 0
+       if(ELEM(activedevice, DEV_STYLUS, DEV_ERASER))
+               pressure= sd->sculptdata.tablet_strength==0?1:
+               (1-strength_factor) + get_pressure()*strength_factor;
+       
+       /* Flip direction for eraser */
+       if(activedevice==DEV_ERASER)
+               dir= -dir;
+#endif
+
+       switch(sd->brush_type){
+       case DRAW_BRUSH:
+       case LAYER_BRUSH:
+               return b->strength / 5000.0f * dir * pressure * flip * anchored; /*XXX: not sure why? multiplied by G.vd->grid */;
+       case SMOOTH_BRUSH:
+               return b->strength / 50.0f * pressure * anchored;
+       case PINCH_BRUSH:
+               return b->strength / 1000.0f * dir * pressure * flip * anchored;
+       case GRAB_BRUSH:
+               return 1;
+       case INFLATE_BRUSH:
+               return b->strength / 5000.0f * dir * pressure * flip * anchored;
+       case FLATTEN_BRUSH:
+               return b->strength / 500.0f * pressure * anchored;
+       default:
+               return 0;
+       }
+}
+
+/* For clipping against a mirror modifier */
+void sculpt_clip(const BrushAction *a, float *co, const float val[3])
+{
+       char i;
+       for(i=0; i<3; ++i) {
+               if(a->clip[i] && (fabs(co[i]) <= a->cliptol[i]))
+                       co[i]= 0.0f;
+               else
+                       co[i]= val[i];
+       }               
+}
+
+void sculpt_axislock(float *co)
+{
+       SculptData *sd = sculpt_data();
+       if (sd->axislock == AXISLOCK_X+AXISLOCK_Y+AXISLOCK_Z) return;
+       /* XXX: if(G.vd->twmode == V3D_MANIP_LOCAL) { */
+       if(0) {
+               float mat[3][3], imat[3][3];
+               /* XXX: Mat3CpyMat4(mat, OBACT->obmat); */
+               Mat3Inv(imat, mat);
+               Mat3MulVecfl(mat, co);
+               if (sd->axislock & AXISLOCK_X) co[0] = 0.0;
+               if (sd->axislock & AXISLOCK_Y) co[1] = 0.0;
+               if (sd->axislock & AXISLOCK_Z) co[2] = 0.0;             
+               Mat3MulVecfl(imat, co);
+       } else {
+               if (sd->axislock & AXISLOCK_X) co[0] = 0.0;
+               if (sd->axislock & AXISLOCK_Y) co[1] = 0.0;
+               if (sd->axislock & AXISLOCK_Z) co[2] = 0.0;             
+       }
+}
+
+static void add_norm_if(float view_vec[3], float out[3], float out_flip[3], const short no[3])
+{
+       float fno[3] = {no[0], no[1], no[2]};
+
+       Normalize(fno);
+
+       if((Inpf(view_vec, fno)) > 0) {
+               VecAddf(out, out, fno);
+       } else {
+               VecAddf(out_flip, out_flip, fno); /* out_flip is used when out is {0,0,0} */
+       }
+}
+
+/* Currently only for the draw brush; finds average normal for all active
+   vertices */
+void calc_area_normal(float out[3], const BrushAction *a, const float *outdir, const ListBase* active_verts)
+{
+       ActiveData *node = active_verts->first;
+       SculptData *sd = sculpt_data();
+       const int view = sd->brush_type==DRAW_BRUSH ? sculptmode_brush()->view : 0;
+       float out_flip[3];
+       
+       out[0]=out[1]=out[2] = out_flip[0]=out_flip[1]=out_flip[2] = 0;
+
+       if(sculptmode_brush()->flag & SCULPT_BRUSH_ANCHORED) {
+               for(; node; node = node->next)
+                       add_norm_if(((BrushAction*)a)->symm.out, out, out_flip, a->orig_norms[node->Index]);
+       }
+       else {
+               for(; node; node = node->next)
+                       add_norm_if(((BrushAction*)a)->symm.out, out, out_flip, sd->session->mvert[node->Index].no);
+       }
+
+       if (out[0]==0.0 && out[1]==0.0 && out[2]==0.0) {
+               VECCOPY(out, out_flip);
+       }
+       
+       Normalize(out);
+
+       if(outdir) {
+               out[0] = outdir[0] * view + out[0] * (10-view);
+               out[1] = outdir[1] * view + out[1] * (10-view);
+               out[2] = outdir[2] * view + out[2] * (10-view);
+       }
+       
+       Normalize(out);
+}
+
+void do_draw_brush(SculptSession *ss, const BrushAction *a, const ListBase* active_verts)
+{
+       float area_normal[3];
+       ActiveData *node= active_verts->first;
+
+       calc_area_normal(area_normal, a, a->symm.out, active_verts);
+       
+       sculpt_axislock(area_normal);
+       
+       while(node){
+               float *co= ss->mvert[node->Index].co;
+               
+               const float val[3]= {co[0]+area_normal[0]*node->Fade*a->scale[0],
+                                    co[1]+area_normal[1]*node->Fade*a->scale[1],
+                                    co[2]+area_normal[2]*node->Fade*a->scale[2]};
+                                    
+               sculpt_clip(a, co, val);
+               
+               node= node->next;
+       }
+}
+
+/* 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.) */
+vec3f neighbor_average(SculptSession *ss, const int vert)
+{
+       int i, skip= -1, total=0;
+       IndexNode *node= ss->vertex_users[vert].first;
+       vec3f avg= {0,0,0};
+       char ncount= BLI_countlist(&ss->vertex_users[vert]);
+       MFace *f;
+               
+       /* Don't modify corner vertices */
+       if(ncount==1) {
+               VecCopyf(&avg.x, ss->mvert[vert].co);
+               return avg;
+       }
+
+       while(node){
+               f= &ss->mface[node->index];
+               
+               if(f->v4) {
+                       skip= (f->v1==vert?2:
+                              f->v2==vert?3:
+                              f->v3==vert?0:
+                              f->v4==vert?1:-1);
+               }
+
+               for(i=0; i<(f->v4?4:3); ++i) {
+                       if(i != skip && (ncount!=2 || BLI_countlist(&ss->vertex_users[(&f->v1)[i]]) <= 2)) {
+                               VecAddf(&avg.x, &avg.x, ss->mvert[(&f->v1)[i]].co);
+                               ++total;
+                       }
+               }
+
+               node= node->next;
+       }
+
+       if(total>0) {
+               avg.x/= total;
+               avg.y/= total;
+               avg.z/= total;
+       }
+       else
+               VecCopyf(&avg.x, ss->mvert[vert].co);
+
+       return avg;
+}
+
+void do_smooth_brush(SculptSession *ss, const BrushAction *a, const ListBase* active_verts)
+{
+       ActiveData *node= active_verts->first;
+
+       while(node){
+               float *co= ss->mvert[node->Index].co;
+               const vec3f avg= neighbor_average(ss, node->Index);
+               const float val[3]= {co[0]+(avg.x-co[0])*node->Fade,
+                                    co[1]+(avg.y-co[1])*node->Fade,
+                                    co[2]+(avg.z-co[2])*node->Fade};
+               sculpt_clip(a, co, val);
+               node= node->next;
+       }
+}
+
+void do_pinch_brush(SculptSession *ss, const BrushAction *a, const ListBase* active_verts)
+{
+       ActiveData *node= active_verts->first;
+
+       while(node) {
+               float *co= ss->mvert[node->Index].co;
+               const float val[3]= {co[0]+(a->symm.center_3d[0]-co[0])*node->Fade,
+                                    co[1]+(a->symm.center_3d[1]-co[1])*node->Fade,
+                                    co[2]+(a->symm.center_3d[2]-co[2])*node->Fade};
+               sculpt_clip(a, co, val);
+               node= node->next;
+       }
+}
+
+void do_grab_brush(SculptSession *ss, BrushAction *a)
+{
+       ActiveData *node= a->grab_active_verts[a->symm.index].first;
+       float add[3];
+       float grab_delta[3];
+       
+       VecCopyf(grab_delta, a->symm.grab_delta);
+       sculpt_axislock(grab_delta);
+       
+       while(node) {
+               float *co= ss->mvert[node->Index].co;
+               
+               VecCopyf(add, grab_delta);
+               VecMulf(add, node->Fade);
+               VecAddf(add, add, co);
+               sculpt_clip(a, co, add);
+
+               node= node->next;
+       }
+       
+}
+
+void do_layer_brush(SculptSession *ss, BrushAction *a, const ListBase *active_verts)
+{
+       float area_normal[3];
+       ActiveData *node= active_verts->first;
+       const float bstr= brush_strength(a);
+
+       calc_area_normal(area_normal, a, NULL, active_verts);
+
+       while(node){
+               float *disp= &a->layer_disps[node->Index];
+               
+               if((bstr > 0 && *disp < bstr) ||
+                 (bstr < 0 && *disp > bstr)) {
+                       float *co= ss->mvert[node->Index].co;
+                       
+                       *disp+= node->Fade;
+
+                       if(bstr < 0) {
+                               if(*disp < bstr)
+                                       *disp = bstr;
+                       } else {
+                               if(*disp > bstr)
+                                       *disp = bstr;
+                       }
+
+                       {
+                               const float val[3]= {a->mesh_store[node->Index].x+area_normal[0] * *disp*a->scale[0],
+                                                    a->mesh_store[node->Index].y+area_normal[1] * *disp*a->scale[1],
+                                                    a->mesh_store[node->Index].z+area_normal[2] * *disp*a->scale[2]};
+                               sculpt_clip(a, co, val);
+                       }
+               }
+
+               node= node->next;
+       }
+}
+
+void do_inflate_brush(SculptSession *ss, const BrushAction *a, const ListBase *active_verts)
+{
+       ActiveData *node= active_verts->first;
+       float add[3];
+       
+       while(node) {
+               float *co= ss->mvert[node->Index].co;
+               short *no= ss->mvert[node->Index].no;
+
+               add[0]= no[0]/ 32767.0f;
+               add[1]= no[1]/ 32767.0f;
+               add[2]= no[2]/ 32767.0f;
+               VecMulf(add, node->Fade);
+               add[0]*= a->scale[0];
+               add[1]*= a->scale[1];
+               add[2]*= a->scale[2];
+               VecAddf(add, add, co);
+               
+               sculpt_clip(a, co, add);
+
+               node= node->next;
+       }
+}
+
+void calc_flatten_center(SculptSession *ss, ActiveData *node, float co[3])
+{
+       ActiveData *outer[FLATTEN_SAMPLE_SIZE];
+       int i;
+       
+       for(i = 0; i < FLATTEN_SAMPLE_SIZE; ++i)
+               outer[i] = node;
+               
+       for(; node; node = node->next) {
+               for(i = 0; i < FLATTEN_SAMPLE_SIZE; ++i) {
+                       if(node->dist > outer[i]->dist) {
+                               outer[i] = node;
+                               break;
+                       }
+               }
+       }
+       
+       co[0] = co[1] = co[2] = 0.0f;
+       for(i = 0; i < FLATTEN_SAMPLE_SIZE; ++i)
+               VecAddf(co, co, ss->mvert[outer[i]->Index].co);
+       VecMulf(co, 1.0f / FLATTEN_SAMPLE_SIZE);
+}
+
+void do_flatten_brush(SculptSession *ss, const BrushAction *a, const ListBase *active_verts)
+{
+       ActiveData *node= active_verts->first;
+       /* area_normal and cntr define the plane towards which vertices are squashed */
+       float area_normal[3];
+       float cntr[3];
+
+       calc_area_normal(area_normal, a, a->symm.out, active_verts);
+       calc_flatten_center(ss, node, cntr);
+
+       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, node->Fade);
+               VecAddf(val, val, co);
+               
+               sculpt_clip(a, co, val);
+               
+               node= node->next;
+       }
+}
+
+/* Uses the brush curve control to find a strength value between 0 and 1 */
+float curve_strength(float p, const float len)
+{
+       SculptData *sd = NULL; /* XXX */
+       if(p > len) p= len;
+       return curvemapping_evaluateF(sd->cumap, 0, p/len);
+}
+
+/* Uses symm to selectively flip any axis of a coordinate. */
+void flip_coord(float co[3], const char symm)
+{
+       if(symm & SYMM_X)
+               co[0]= -co[0];
+       if(symm & SYMM_Y)
+               co[1]= -co[1];
+       if(symm & SYMM_Z)
+               co[2]= -co[2];
+}
+
+/* Use the warpfac field in MTex to store a rotation value for sculpt textures. Value is in degrees */
+float sculpt_tex_angle(void)
+{
+       SculptData *sd= sculpt_data();
+       if(sd->texact!=-1 && sd->mtex[sd->texact])
+               return sd->mtex[sd->texact]->warpfac;
+       return 0;
+}
+
+void set_tex_angle(const float f)
+{
+       SculptData *sd = sculpt_data();
+       if(sd->texact != -1 && sd->mtex[sd->texact])
+               sd->mtex[sd->texact]->warpfac = f;
+}
+       
+float to_rad(const float deg)
+{
+       return deg * (M_PI/180.0f);
+}
+
+float to_deg(const float rad)
+{
+       return rad * (180.0f/M_PI);
+}
+
+/* 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_w + 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 = TC_SIZE - 1;
+       float urat, vrat, uopp;
+
+       if(u < 0) u = 0;
+       else if(u >= TC_SIZE) u = tc_max;
+       if(v < 0) v = 0;
+       else if(v >= TC_SIZE) v = tc_max;
+
+       x = floor(u);
+       y = floor(v);
+       x2 = x + 1;
+       y2 = y + 1;
+
+       if(x2 > TC_SIZE) x2 = tc_max;
+       if(y2 > TC_SIZE) y2 = tc_max;
+       
+       urat = u - x;
+       vrat = v - y;
+       uopp = 1 - urat;
+               
+       return ((get_texcache_pixel(ss, x, y) * uopp +
+                get_texcache_pixel(ss, x2, y) * urat) * (1 - vrat) + 
+               (get_texcache_pixel(ss, x, y2) * uopp +
+                get_texcache_pixel(ss, x2, y2) * urat) * vrat) / 255.0;
+}
+
+/* Return a multiplier for brush strength on a particular vertex. */
+float tex_strength(BrushAction *a, float *point, const float len,const unsigned vindex)
+{
+       SculptData *sd= sculpt_data();
+       SculptSession *ss= sculpt_session();
+       float avg= 1;
+
+       if(sd->texact==-1 || !sd->mtex[sd->texact])
+               avg= 1;
+       else if(sd->texrept==SCULPTREPT_3D) {
+               /* Get strength by feeding the vertex location directly
+                  into a texture */
+               float jnk;
+               const float factor= 0.01;
+               MTex mtex;
+               memset(&mtex,0,sizeof(MTex));
+               mtex.tex= sd->mtex[sd->texact]->tex;
+               mtex.projx= 1;
+               mtex.projy= 2;
+               mtex.projz= 3;
+               VecCopyf(mtex.size, sd->mtex[sd->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);
+       }
+       else if(ss->texcache) {
+               const float bsize= a->radius * 2;
+               const float rot= to_rad(sculpt_tex_angle()) + a->anchored_rot;
+               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. */
+               VecCopyf(flip, point);
+               flip_coord(flip, a->symm.index);
+               projectf(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(sd->texrept==SCULPTREPT_TILE) {
+                       const int sx= (const int)sd->mtex[sd->texact]->size[0];
+                       const int sy= (const int)sd->texsep ? sd->mtex[sd->texact]->size[1] : sx;
+                       
+                       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, TC_SIZE*px/sx, TC_SIZE*py/sy);
+               } else {
+                       float fx= (point_2d[0] - a->mouse[0]) / bsize;
+                       float fy= (point_2d[1] - a->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;
+
+                       avg= get_texcache_pixel_bilinear(ss, fx * TC_SIZE, fy * TC_SIZE);
+               }
+       }
+
+       if(sd->texfade)
+               avg*= curve_strength(len, a->size_3d); /* Smooth curve */
+
+       return avg;
+}
+
+/* Mark area around the brush as damaged. projverts are marked if they are
+   inside the area and the damaged rectangle in 2D screen coordinates is 
+   added to damaged_rects. */
+void sculpt_add_damaged_rect(BrushAction *a)
+{
+       short p[2];
+       RectNode *rn= MEM_mallocN(sizeof(RectNode),"RectNode");
+       SculptSession *ss = sculpt_session();
+       const float radius = a->radius > a->prev_radius ? a->radius : a->prev_radius;
+       unsigned i;
+
+       /* Find center */
+       project(a->symm.center_3d, p);
+       rn->r.xmin= p[0] - radius;
+       rn->r.ymin= p[1] - radius;
+       rn->r.xmax= p[0] + radius;
+       rn->r.ymax= p[1] + radius;
+
+       BLI_addtail(&sculpt_session()->damaged_rects, rn);
+
+       /* Update insides */
+       for(i=0; i<ss->totvert; ++i) {
+               if(!ss->projverts[i].inside) {
+                       if(ss->projverts[i].co[0] > rn->r.xmin && ss->projverts[i].co[1] > rn->r.ymin &&
+                          ss->projverts[i].co[0] < rn->r.xmax && ss->projverts[i].co[1] < rn->r.ymax) {
+                               ss->projverts[i].inside= 1;
+                       }
+               }
+       }
+}
+
+/* Clears the depth buffer in each modified area. */
+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);
+       }
+}
+
+void do_brush_action(BrushAction *a)
+{
+       int i;
+       float av_dist;
+       ListBase active_verts={0,0};
+       ActiveData *adata= 0;
+       float *vert;
+       Mesh *me= NULL; /*XXX: get_mesh(OBACT); */
+       const float bstrength= brush_strength(a);
+       KeyBlock *keyblock= NULL; /*XXX: ob_get_keyblock(OBACT); */
+       SculptData *sd = sculpt_data();
+       SculptSession *ss = sculpt_session();
+
+       sculpt_add_damaged_rect(a);
+
+       /* Build a list of all vertices that are potentially within the brush's
+          area of influence. Only do this once for the grab brush. */
+       if((sd->brush_type != GRAB_BRUSH) || a->firsttime) {
+               for(i=0; i<ss->totvert; ++i) {
+                       /* Projverts.inside provides a rough bounding box */
+                       if(ss->multires || ss->projverts[i].inside) {
+                               //vert= ss->vertexcosnos ? &ss->vertexcosnos[i*6] : a->verts[i].co;
+                               vert= ss->mvert[i].co;
+                               av_dist= VecLenf(a->symm.center_3d, vert);
+                               if(av_dist < a->size_3d) {
+                                       adata= (ActiveData*)MEM_mallocN(sizeof(ActiveData), "ActiveData");
+
+                                       adata->Index = i;
+                                       /* Fade is used to store the final strength at which the brush
+                                          should modify a particular vertex. */
+                                       adata->Fade= tex_strength(a, vert, av_dist, i) * bstrength;
+                                       adata->dist = av_dist;
+
+                                       if(sd->brush_type == GRAB_BRUSH && a->firsttime)
+                                               BLI_addtail(&a->grab_active_verts[a->symm.index], adata);
+                                       else
+                                               BLI_addtail(&active_verts, adata);
+                               }
+                       }
+               }
+       }
+
+       /* Only act if some verts are inside the brush area */
+       if(active_verts.first || (sd->brush_type == GRAB_BRUSH && a->grab_active_verts[a->symm.index].first)) {
+               /* Apply one type of brush action */
+               switch(sd->brush_type){
+               case DRAW_BRUSH:
+                       do_draw_brush(ss, a, &active_verts);
+                       break;
+               case SMOOTH_BRUSH:
+                       do_smooth_brush(ss, a, &active_verts);
+                       break;
+               case PINCH_BRUSH:
+                       do_pinch_brush(ss, a, &active_verts);
+                       break;
+               case INFLATE_BRUSH:
+                       do_inflate_brush(ss, a, &active_verts);
+                       break;
+               case GRAB_BRUSH:
+                       do_grab_brush(ss, a);
+                       break;
+               case LAYER_BRUSH:
+                       do_layer_brush(ss, a, &active_verts);
+                       break;
+               case FLATTEN_BRUSH:
+                       do_flatten_brush(ss, a, &active_verts);
+                       break;
+               }
+       
+               /* Copy the modified vertices from mesh to the active key */
+               if(keyblock && !ss->multires) {
+                       float *co= keyblock->data;
+                       if(co) {
+                               if(sd->brush_type == GRAB_BRUSH)
+                                       adata = a->grab_active_verts[a->symm.index].first;
+                               else
+                                       adata = active_verts.first;
+
+                               for(; adata; adata= adata->next)
+                                       if(adata->Index < keyblock->totelem)
+                                               VecCopyf(&co[adata->Index*3], me->mvert[adata->Index].co);
+                       }
+               }
+
+               if(ss->vertexcosnos && !ss->multires)
+                       BLI_freelistN(&active_verts);
+               else {
+                       if(sd->brush_type != GRAB_BRUSH)
+                               addlisttolist(&ss->damaged_verts, &active_verts);
+               }
+       }
+}
+
+/* Flip all the editdata across the axis/axes specified by symm. Used to
+   calculate multiple modifications to the mesh when symmetry is enabled. */
+void calc_brushdata_symm(BrushAction *a, const char symm)
+{
+       flip_coord(a->symm.center_3d, symm);
+       flip_coord(a->symm.up, symm);
+       flip_coord(a->symm.right, symm);
+       flip_coord(a->symm.out, symm);
+       
+       a->symm.index= symm;
+
+       flip_coord(a->symm.grab_delta, symm);
+}
+
+void do_symmetrical_brush_actions(BrushAction *a, short co[2], short pr_co[2])
+{
+       const char symm = sculpt_data()->symm;
+       BrushActionSymm orig;
+       int i;
+
+       init_brushaction(a, co, pr_co);
+       orig = a->symm;
+       do_brush_action(a);
+
+       for(i = 1; i <= symm; ++i) {
+               if(symm & i && (symm != 5 || i != 3) && (symm != 6 || (i != 3 && i != 5))) {
+                       // Restore the original symmetry data
+                       a->symm = orig;
+
+                       calc_brushdata_symm(a, i);
+                       do_brush_action(a);
+               }
+       }
+
+       a->symm = orig;
+}
+
+void add_face_normal(vec3f *norm, MVert *mvert, const MFace* face, float *fn)
+{
+       vec3f c= {mvert[face->v1].co[0],mvert[face->v1].co[1],mvert[face->v1].co[2]};
+       vec3f b= {mvert[face->v2].co[0],mvert[face->v2].co[1],mvert[face->v2].co[2]};
+       vec3f a= {mvert[face->v3].co[0],mvert[face->v3].co[1],mvert[face->v3].co[2]};
+       vec3f s1, s2;
+       float final[3];
+
+       VecSubf(&s1.x,&a.x,&b.x);
+       VecSubf(&s2.x,&c.x,&b.x);
+
+       final[0] = s1.y * s2.z - s1.z * s2.y;
+       final[1] = s1.z * s2.x - s1.x * s2.z;
+       final[2] = s1.x * s2.y - s1.y * s2.x;
+
+       if(fn)
+               VecCopyf(fn, final);
+
+       norm->x+= final[0];
+       norm->y+= final[1];
+       norm->z+= final[2];
+}
+
+void update_damaged_vert(ListBase *lb, BrushAction *a)
+{
+       ActiveData *vert;
+       SculptSession *ss = sculpt_session();
+       
+       for(vert= lb->first; vert; vert= vert->next) {
+               vec3f norm= {0,0,0};            
+               IndexNode *face= sculpt_session()->vertex_users[vert->Index].first;
+
+               while(face){
+                       float *fn = NULL;
+                       if(ss->face_normals)
+                               fn = &ss->face_normals[face->index*3];
+                       add_face_normal(&norm, ss->mvert, &ss->mface[face->index], fn);
+                       face= face->next;
+               }
+               Normalize(&norm.x);
+               
+               ss->mvert[vert->Index].no[0]=norm.x*32767;
+               ss->mvert[vert->Index].no[1]=norm.y*32767;
+               ss->mvert[vert->Index].no[2]=norm.z*32767;
+       }
+}
+
+void calc_damaged_verts(ListBase *damaged_verts, BrushAction *a)
+{
+       int i;
+       
+       for(i=0; i<8; ++i)
+               update_damaged_vert(&a->grab_active_verts[i], a);
+
+       update_damaged_vert(damaged_verts, a);
+       BLI_freelistN(damaged_verts);
+       damaged_verts->first = damaged_verts->last = NULL;
+}
+
+void projverts_clear_inside(SculptSession *ss)
+{
+       int i;
+       for(i = 0; i < ss->totvert; ++i)
+               ss->projverts[i].inside = 0;
+}
+
+BrushData *sculptmode_brush(void)
+{
+       SculptData *sd= sculpt_data();
+
+       BrushData *bd = 
+               (sd->brush_type==DRAW_BRUSH ? &sd->drawbrush :
+                sd->brush_type==SMOOTH_BRUSH ? &sd->smoothbrush :
+                sd->brush_type==PINCH_BRUSH ? &sd->pinchbrush :
+                sd->brush_type==INFLATE_BRUSH ? &sd->inflatebrush :
+                sd->brush_type==GRAB_BRUSH ? &sd->grabbrush :
+                sd->brush_type==LAYER_BRUSH ? &sd->layerbrush :
+                sd->brush_type==FLATTEN_BRUSH ? &sd->flattenbrush : NULL);
+
+       if(!bd) {
+               /* XXX: sculptdata_init(G.scene); */
+               bd = &sd->drawbrush;
+       }
+
+       return bd;
+}
+
+void sculptmode_update_tex()
+{
+       SculptData *sd= sculpt_data();
+       SculptSession *ss= sculpt_session();
+       MTex *mtex = sd->mtex[sd->texact];
+       TexResult texres = {0};
+       float x, y, step=2.0/TC_SIZE, co[3];
+       int hasrgb, ix, iy;
+       
+       /* Skip Default brush shape and non-textures */
+       if(sd->texact == -1 || !sd->mtex[sd->texact]) return;
+
+       if(ss->texcache) {
+               MEM_freeN(ss->texcache);
+               ss->texcache= NULL;
+       }
+       
+       ss->texcache_w = ss->texcache_h = TC_SIZE;
+       ss->texcache = MEM_callocN(sizeof(int) * ss->texcache_w * ss->texcache_h, "Sculpt Texture cache");
+       
+       if(mtex && mtex->tex) {
+               BKE_image_get_ibuf(sd->mtex[sd->texact]->tex->ima, NULL);
+               
+               /*do normalized cannonical view coords for texture*/
+               for (y=-1.0, iy=0; iy<TC_SIZE; iy++, y += step) {
+                       for (x=-1.0, ix=0; ix<TC_SIZE; ix++, x += step) {
+                               co[0]= x;
+                               co[1]= y;
+                               co[2]= 0.0f;
+                               
+                               /* This is copied from displace modifier code */
+                               hasrgb = multitex_ext(mtex->tex, co, NULL, NULL, 1, &texres);
+                       
+                               /* if the texture gave an RGB value, we assume it didn't give a valid
+                                * intensity, so calculate one (formula from do_material_tex).
+                                * if the texture didn't give an RGB value, copy the intensity across
+                                */
+                               if(hasrgb & TEX_RGB)
+                                       texres.tin = (0.35 * texres.tr + 0.45 *
+                                                     texres.tg + 0.2 * texres.tb);
+
+                               texres.tin = texres.tin * 255.0;
+                               ((char*)ss->texcache)[(iy*TC_SIZE+ix)*4] = (char)texres.tin;
+                               ((char*)ss->texcache)[(iy*TC_SIZE+ix)*4+1] = (char)texres.tin;
+                               ((char*)ss->texcache)[(iy*TC_SIZE+ix)*4+2] = (char)texres.tin;
+                               ((char*)ss->texcache)[(iy*TC_SIZE+ix)*4+3] = (char)texres.tin;
+                       }
+               }
+       }
+}
+
+/* pr_mouse is only used for the grab brush, can be NULL otherwise */
+static void init_brushaction(BrushAction *a, short *mouse, short *pr_mouse)
+{
+       SculptData *sd = sculpt_data();
+       SculptSession *ss = sculpt_session();
+       Object *ob = NULL; /* XXX */
+       const float mouse_depth = get_depth(mouse[0], mouse[1]);
+       float brush_edge_loc[3], zero_loc[3], oldloc[3];
+       ModifierData *md;
+       int i;
+       const char flip = 0; /*XXX: (get_qual() == LR_SHIFTKEY); */
+       const char anchored = sculptmode_brush()->flag & SCULPT_BRUSH_ANCHORED;
+       short orig_mouse[2], dx=0, dy=0;
+
+       a->flip = flip;
+       a->symm.index = 0;
+
+       if(a->firsttime) 
+               a->depth = mouse_depth;
+       
+       /* Convert the location and size of the brush to
+          modelspace coords */
+       if(a->firsttime || !anchored) {
+               unproject(a->symm.center_3d, mouse[0], mouse[1], mouse_depth);
+               a->mouse[0] = mouse[0];
+               a->mouse[1] = mouse[1];
+       }
+       if(anchored) {
+               project(a->symm.center_3d, orig_mouse);
+               dx = mouse[0] - orig_mouse[0];
+               dy = mouse[1] - orig_mouse[1];
+       }
+       if(anchored) {
+               unproject(brush_edge_loc, mouse[0], mouse[1], a->depth);
+               a->anchored_rot = atan2(dy, dx);
+       }
+       else
+               unproject(brush_edge_loc, mouse[0] + brush_size(), mouse[1], mouse_depth);
+       a->size_3d = VecLenf(a->symm.center_3d, brush_edge_loc);
+
+       a->prev_radius = a->radius;
+
+       if(anchored)
+               a->radius = sqrt(dx*dx + dy*dy);
+       else
+               a->radius = brush_size();
+
+       /* Set the pivot to allow the model to rotate around the center of the brush */
+       if(get_depth(mouse[0],mouse[1]) < 1.0)
+               VecCopyf(sd->pivot, a->symm.center_3d);
+
+       /* Now project the Up, Right, and Out normals from view to model coords */
+       unproject(zero_loc, 0, 0, 0);
+       unproject(a->symm.up, 0, -1, 0);
+       unproject(a->symm.right, 1, 0, 0);
+       unproject(a->symm.out, 0, 0, -1);
+       VecSubf(a->symm.up, a->symm.up, zero_loc);
+       VecSubf(a->symm.right, a->symm.right, zero_loc);
+       VecSubf(a->symm.out, a->symm.out, zero_loc);
+       Normalize(a->symm.up);
+       Normalize(a->symm.right);
+       Normalize(a->symm.out);
+       
+       /* Initialize mirror modifier clipping */
+       for(i=0; i<3; ++i) {
+               a->clip[i]= 0;
+               a->cliptol[i]= 0;
+       }
+       for(md= ob->modifiers.first; md; md= md->next) {
+               if(md->type==eModifierType_Mirror && (md->mode & eModifierMode_Realtime)) {
+                       const MirrorModifierData *mmd = (MirrorModifierData*) md;
+                       
+                       if(mmd->flag & MOD_MIR_CLIPPING) {
+                               a->clip[mmd->axis]= 1;
+                               if(mmd->tolerance > a->cliptol[mmd->axis])
+                                       a->cliptol[mmd->axis] = mmd->tolerance;
+                       }
+               }
+       }
+
+       if(sd->brush_type == GRAB_BRUSH) {
+               float gcenter[3];
+
+               /* Find the delta */
+               unproject(gcenter, mouse[0], mouse[1], a->depth);
+               unproject(oldloc, pr_mouse[0], pr_mouse[1], a->depth);
+               VecSubf(a->symm.grab_delta, gcenter, oldloc);
+       }
+       else if(sd->brush_type == LAYER_BRUSH) {
+               if(!a->layer_disps)
+                       a->layer_disps= MEM_callocN(sizeof(float)*ss->totvert,"Layer disps");
+       }
+
+       if(sd->brush_type == LAYER_BRUSH || anchored) {
+               unsigned i;
+               if(!a->mesh_store) {
+                       a->mesh_store= MEM_mallocN(sizeof(vec3f) * ss->totvert, "Sculpt mesh store");
+                       for(i = 0; i < ss->totvert; ++i)
+                               VecCopyf(&a->mesh_store[i].x, ss->mvert[i].co);
+               }
+
+               if(anchored && a->layer_disps)
+                       memset(a->layer_disps, 0, sizeof(float) * ss->totvert);
+
+               if(anchored && !a->orig_norms) {
+                       a->orig_norms= MEM_mallocN(sizeof(short) * 3 * ss->totvert, "Sculpt orig norm");
+                       for(i = 0; i < ss->totvert; ++i) {
+                               a->orig_norms[i][0] = ss->mvert[i].no[0];
+                               a->orig_norms[i][1] = ss->mvert[i].no[1];
+                               a->orig_norms[i][2] = ss->mvert[i].no[2];
+                       }
+               }
+       }
+}
+void sculptmode_set_strength(const int delta)
+{
+       int val = sculptmode_brush()->strength + delta;
+       if(val < 1) val = 1;
+       if(val > 100) val = 100;
+       sculptmode_brush()->strength= val;
+}
+
+/* XXX: haven't brought in the radial control files, not sure where to put them. Note that all the paint modes should have access to radial control! */
+#if 0
+static void sculpt_radialcontrol_callback(const int mode, const int val)
+{
+       SculptSession *ss = sculpt_session();
+       BrushData *br = sculptmode_brush();
+
+       if(mode == RADIALCONTROL_SIZE)
+               br->size = val;
+       else if(mode == RADIALCONTROL_STRENGTH)
+               br->strength = val;
+       else if(mode == RADIALCONTROL_ROTATION)
+               set_tex_angle(val);
+
+       ss->radialcontrol = NULL;
+}
+
+/* Returns GL handle to brush texture */
+static GLuint sculpt_radialcontrol_calctex()
+{
+       SculptData *sd= sculpt_data();
+       SculptSession *ss= sculpt_session();
+       int i, j;
+       const int tsz = TC_SIZE;
+       float *texdata= MEM_mallocN(sizeof(float)*tsz*tsz, "Brush preview");
+       GLuint tex;
+
+       if(sd->texrept!=SCULPTREPT_3D)
+               sculptmode_update_tex();
+       for(i=0; i<tsz; ++i)
+               for(j=0; j<tsz; ++j) {
+                       float magn= sqrt(pow(i-tsz/2,2)+pow(j-tsz/2,2));
+                       if(sd->texfade)
+                               texdata[i*tsz+j]= curve_strength(magn,tsz/2);
+                       else
+                               texdata[i*tsz+j]= magn < tsz/2 ? 1 : 0;
+               }
+       if(sd->texact != -1 && ss->texcache) {
+               for(i=0; i<tsz; ++i)
+                       for(j=0; j<tsz; ++j) {
+                               const int col= ss->texcache[i*tsz+j];
+                               texdata[i*tsz+j]*= (((char*)&col)[0]+((char*)&col)[1]+((char*)&col)[2])/3.0f/255.0f;
+                       }
+       }
+               
+       glGenTextures(1, &tex);
+       glBindTexture(GL_TEXTURE_2D, tex);
+       glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, tsz, tsz, 0, GL_ALPHA, GL_FLOAT, texdata);
+       MEM_freeN(texdata);
+
+       return tex;
+}
+
+void sculpt_radialcontrol_start(int mode)
+{
+       SculptData *sd = sculpt_data();
+       SculptSession *ss = sculpt_session();
+       BrushData *br = sculptmode_brush();
+       int orig=1, max=100;
+
+       if(mode == RADIALCONTROL_SIZE) {
+               orig = br->size;
+               max = 200;
+       }
+       else if(mode == RADIALCONTROL_STRENGTH) {
+               orig = br->strength;
+               max = 100;
+       }
+       else if(mode == RADIALCONTROL_ROTATION) {
+               if(sd->texact!=-1 && sd->mtex[sd->texact]) {
+                       orig = sculpt_tex_angle();
+                       max = 360;
+               }
+               else
+                       mode = RADIALCONTROL_NONE;
+       }
+
+       if(mode != RADIALCONTROL_NONE) {
+               ss->radialcontrol= radialcontrol_start(mode, sculpt_radialcontrol_callback, orig, max,
+                                                      sculpt_radialcontrol_calctex());
+       }
+}
+#endif
+
+/* XXX: drawing code to go elsewhere!
+void sculpt_paint_brush(char clear)
+{
+       if(sculpt_data()->flags & SCULPT_DRAW_BRUSH) {
+               static short mvalo[2];
+               short mval[2];
+               const short rad= sculptmode_brush()->size;
+
+               getmouseco_areawin(mval);
+               
+               persp(PERSP_WIN);
+               if(clear)
+                       fdrawXORcirc(mval[0], mval[1], rad);
+               else
+                       draw_sel_circle(mval, mvalo, rad, rad, 0);
+               
+               mvalo[0]= mval[0];
+               mvalo[1]= mval[1];
+       }
+}
+*/
+
+void sculptmode_selectbrush_menu(void)
+{
+       /* XXX: I guess menus belong elsewhere too?
+
+       SculptData *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;
+
+               BIF_undo_push("Brush type");
+               
+               allqueue(REDRAWVIEW3D, 1);
+               allqueue(REDRAWBUTSEDIT, 1);
+       }*/
+}
+
+void sculptmode_update_all_projverts(float *vertcosnos)
+{
+       SculptSession *ss = sculpt_session();
+       unsigned i;
+
+       if(!ss->projverts)
+               ss->projverts = MEM_mallocN(sizeof(ProjVert)*ss->totvert,"ProjVerts");
+
+       for(i=0; i<ss->totvert; ++i) {
+               project(vertcosnos ? &vertcosnos[i * 6] : ss->mvert[i].co, ss->projverts[i].co);
+               ss->projverts[i].inside= 0;
+       }
+}
+
+/* Checks whether full update mode (slower) needs to be used to work with modifiers */
+char sculpt_modifiers_active(Object *ob)
+{
+       ModifierData *md;
+       
+       for(md= modifiers_getVirtualModifierList(ob); md; md= md->next) {
+               if(md->mode & eModifierMode_Realtime && md->type != eModifierType_Multires)
+                       return 1;
+       }
+       
+       return 0;
+}
+
+/* Sculpt mode handles multires differently from regular meshes, but only if
+   it's the last modifier on the stack and it is not on the first level */
+struct MultiresModifierData *sculpt_multires_active(Object *ob)
+{
+       ModifierData *md;
+       
+       for(md= modifiers_getVirtualModifierList(ob); md; md= md->next) {
+               if(md->type == eModifierType_Multires && !md->next) {
+                       MultiresModifierData *mmd = (MultiresModifierData*)md;
+                       if(mmd->lvl != 1)
+                               return mmd;
+               }
+       }
+
+       return NULL;
+}
+
+static void sculpt_update_mesh_elements(SculptSession *ss, Object *ob)
+{
+       if(sculpt_multires_active(ob)) {
+               DerivedMesh *dm = mesh_get_derived_final(NULL, ob, CD_MASK_BAREMESH); /* XXX scene=? */
+               ss->multires = 1;
+               ss->totvert = dm->getNumVerts(dm);
+               ss->totface = dm->getNumFaces(dm);
+               ss->mvert = dm->getVertDataArray(dm, CD_MVERT);
+               ss->mface = dm->getFaceDataArray(dm, CD_MFACE);
+               ss->face_normals = dm->getFaceDataArray(dm, CD_NORMAL);
+       }
+       else {
+               Mesh *me = get_mesh(ob);
+               ss->multires = 0;
+               ss->totvert = me->totvert;
+               ss->totface = me->totface;
+               ss->mvert = me->mvert;
+               ss->mface = me->mface;
+               ss->face_normals = NULL;
+       }
+}
+
+/* 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), &ss->mvert[0].co);
+       glNormalPointer(GL_SHORT, sizeof(MVert), &ss->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
+
+void sculptmode_correct_state(void)
+{
+       if(!sculpt_session())
+               sculpt_init_session();
+
+       glEnableClientState(GL_VERTEX_ARRAY);
+       glEnableClientState(GL_NORMAL_ARRAY);
+       
+       if(!sculpt_session()->vertex_users) calc_vertex_users();
+}
+
+void sculpt(void)
+{
+       SculptData *sd= sculpt_data();
+       SculptSession *ss= sculpt_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;
+
+       /* 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;
+               }
+       }*/
+       
+       if(!ss) {
+               sculpt_init_session();
+               ss= sd->session;
+       }
+
+       anchored = sculptmode_brush()->flag & SCULPT_BRUSH_ANCHORED;
+       smooth_stroke = (sd->flags & SCULPT_INPUT_SMOOTH) && (sd->brush_type != GRAB_BRUSH) && !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);
+       sculpt_update_mesh_elements(ss, ob);
+
+       /* Check that vertex users are up-to-date */
+       if(ob != active_ob || !ss->vertex_users || ss->vertex_users_size != ss->totvert) {
+               sculpt_vertexusers_free(ss);
+               calc_vertex_users();
+               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->texrept!=SCULPTREPT_3D)
+               sculptmode_update_tex();
+
+       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);
+
+       init_sculptmatrices();
+
+       if(modifier_calculations)
+               ss->vertexcosnos= mesh_get_mapped_verts_nors(NULL, ob); /* XXX: scene = ? */
+       sculptmode_update_all_projverts(ss->vertexcosnos);
+
+       /* Set scaling adjustment */
+       a->scale[0]= 1.0f / ob->size[0];
+       a->scale[1]= 1.0f / ob->size[1];
+       a->scale[2]= 1.0f / ob->size[2];
+
+       /* 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();
+
+       me = get_mesh(ob);
+
+       while (get_mbut() & mousebut) {
+               getmouseco_areawin(mouse);
+               /* If rake, and the mouse has moved over 10 pixels (euclidean) (prevents jitter) then get the new angle */
+               if (sd->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(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] ||
+                  sculptmode_brush()->flag & SCULPT_BRUSH_AIRBRUSH) {
+                       a->firsttime = firsttime;
+                       firsttime= 0;
+
+                       if(smooth_stroke)
+                               sculpt_stroke_add_point(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_type != GRAB_BRUSH) {
+                               if(anchored) {
+                                       /* Restore the mesh before continuing with anchored stroke */
+                                       if(a->mesh_store) {
+                                               for(i = 0; i < ss->totvert; ++i) {
+                                                       VecCopyf(ss->mvert[i].co, &a->mesh_store[i].x);
+                                                       ss->mvert[i].no[0] = a->orig_norms[i][0];
+                                                       ss->mvert[i].no[1] = a->orig_norms[i][1];
+                                                       ss->mvert[i].no[2] = a->orig_norms[i][2];
+                                               }
+                                       }
+                                       
+                                       do_symmetrical_brush_actions(a, mouse, NULL);
+                               }
+                               else {
+                                       if(smooth_stroke) {
+                                               sculpt_stroke_apply(a);
+                                       }
+                                       else if(sd->spacing==0 || spacing>sd->spacing) {
+                                               do_symmetrical_brush_actions(a, mouse, NULL);
+                                               spacing= 0;
+                                       }
+                               }
+                       }
+                       else {
+                               do_symmetrical_brush_actions(a, mouse, mvalo);
+                               unproject(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_type == GRAB_BRUSH || !(sd->flags & SCULPT_DRAW_FAST)) {
+                               calc_damaged_verts(&ss->damaged_verts, a);
+                               /*XXX: scrarea_do_windraw(curarea); */
+                               screen_swapbuffers();
+                       } else { /* Optimized drawing */
+                               calc_damaged_verts(&ss->damaged_verts, 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]);
+                               sculptmode_draw_mesh(1);
+                               glAccum(GL_LOAD, 1);
+
+                               projverts_clear_inside(ss);
+
+                               /* XXX: persp(PERSP_WIN); */
+                               glDisable(GL_DEPTH_TEST);
+                               
+                               /* Draw cursor */
+                               if(sculpt_data()->flags & SCULPT_DRAW_BRUSH)
+                                       fdrawXORcirc((float)mouse[0],(float)mouse[1],sculptmode_brush()->size);
+                               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 BIF_wait_for_statechange();
+       }
+
+       /* Set the rotation of the brush back to what it was before any rake */
+       set_tex_angle(offsetRot);
+       
+       if(smooth_stroke) {
+               sculpt_stroke_apply_all(a);
+               calc_damaged_verts(&ss->damaged_verts, 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();
+
+       if(mmd) {
+               if(mmd->undo_verts && mmd->undo_verts != ss->mvert)
+                       MEM_freeN(mmd->undo_verts);
+               
+               mmd->undo_verts = ss->mvert;
+               mmd->undo_verts_tot = ss->totvert;
+       }
+
+       /* XXX: sculpt_undo_push(G.scene->sculptdata.brush_type); */
+
+       /* XXX: if(G.vd->depths) G.vd->depths->damaged= 1;
+          allqueue(REDRAWVIEW3D, 0); */
+}
+
+static void sculpt_undo_push(const short brush_type)
+{
+       switch(brush_type) {
+       case DRAW_BRUSH:
+               BIF_undo_push("Draw Brush"); break;
+       case SMOOTH_BRUSH:
+               BIF_undo_push("Smooth Brush"); break;
+       case PINCH_BRUSH:
+               BIF_undo_push("Pinch Brush"); break;
+       case INFLATE_BRUSH:
+               BIF_undo_push("Inflate Brush"); break;
+       case GRAB_BRUSH:
+               BIF_undo_push("Grab Brush"); break;
+       case LAYER_BRUSH:
+               BIF_undo_push("Layer Brush"); break;
+       case FLATTEN_BRUSH:
+               BIF_undo_push("Flatten Brush"); break;
+       default:
+               BIF_undo_push("Sculpting"); break;
+       }
+}
+
+void set_sculptmode(void)
+{
+       if(G.f & G_SCULPTMODE) {
+               Object *ob = NULL; /*XXX: OBACT; */
+               Mesh *me= get_mesh(ob);
+
+               multires_force_update(ob);
+               
+               G.f &= ~G_SCULPTMODE;
+
+               /* XXX: sculptsession_free(G.scene); */
+               if(me && me->pv) 
+                       mesh_pmv_off(ob, me);
+       } 
+       else {
+               G.f |= G_SCULPTMODE;
+
+               /* Called here to sanity-check the brush */
+               sculptmode_brush();
+
+               sculpt_init_session();
+               
+               glEnableClientState(GL_VERTEX_ARRAY);
+               glEnableClientState(GL_NORMAL_ARRAY);
+       }
+       
+       active_ob= NULL;
+}
+
+/* Partial Mesh Visibility */
+
+/* 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); */
+}
+
+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); */
+
+       BIF_undo_push("Partial mesh hide");
+
+       waitcursor(0);
+}
diff --git a/source/blender/editors/sculpt/sculpt_intern.h b/source/blender/editors/sculpt/sculpt_intern.h
new file mode 100644 (file)
index 0000000..04112da
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * $Id: BDR_sculptmode.h 13396 2008-01-25 04:17:38Z nicholasbishop $
+ *
+ * ***** 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, 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 *****
+ */ 
+
+#ifndef BDR_SCULPTMODE_H
+#define BDR_SCULPTMODE_H
+
+#include "DNA_listBase.h"
+#include "DNA_vec_types.h"
+#include "BKE_sculpt.h"
+
+struct uiBlock;
+struct BrushAction;
+struct BrushData;
+struct IndexNode;
+struct KeyBlock;
+struct Mesh;
+struct Object;
+struct PartialVisibility;
+struct Scene;
+struct ScrArea;
+struct SculptData;
+struct SculptStroke;
+
+struct SculptSession *sculpt_session(void);
+struct SculptData *sculpt_data(void);
+
+/* Memory */
+void sculptmode_correct_state(void);
+
+/* Interface */
+void sculptmode_draw_interface_tools(struct uiBlock *block,unsigned short cx, unsigned short cy);
+void sculptmode_draw_interface_brush(struct uiBlock *block,unsigned short cx, unsigned short cy);
+void sculptmode_draw_interface_textures(struct uiBlock *block,unsigned short cx, unsigned short cy);
+void sculptmode_rem_tex(void*,void*);
+void sculptmode_selectbrush_menu(void);
+void sculptmode_draw_mesh(int);
+void sculpt_paint_brush(char clear);
+void sculpt_stroke_draw();
+void sculpt_radialcontrol_start(int mode);
+
+struct BrushData *sculptmode_brush(void);
+void do_symmetrical_brush_actions(struct BrushAction *a, short *, short *);
+
+void sculptmode_update_tex(void);
+char sculpt_modifiers_active(struct Object *ob);
+void sculpt(void);
+void set_sculptmode(void);
+
+/* Stroke */
+void sculpt_stroke_new(const int max);
+void sculpt_stroke_free();
+void sculpt_stroke_add_point(const short x, const short y);
+void sculpt_stroke_apply(struct BrushAction *);
+void sculpt_stroke_apply_all(struct BrushAction *);
+void sculpt_stroke_draw();
+
+
+/* Partial Mesh Visibility */
+void sculptmode_pmv(int mode);
+
+#endif
diff --git a/source/blender/editors/sculpt/stroke.c b/source/blender/editors/sculpt/stroke.c
new file mode 100644 (file)
index 0000000..482b2fc
--- /dev/null
@@ -0,0 +1,284 @@
+/*
+ * $Id$
+ *
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software  Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ * The Original Code is Copyright (C) 2007 by Nicholas Bishop
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): none yet.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ *
+ * Storage and manipulation of sculptmode brush strokes.
+ *
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_listBase.h"
+#include "DNA_scene_types.h"
+
+#include "BKE_sculpt.h"
+#include "BLI_blenlib.h"
+#include "BIF_gl.h"
+
+#include "sculpt_intern.h"
+
+#include <math.h>
+
+/* Temporary storage of input stroke control points */
+typedef struct StrokePoint {
+       struct StrokePoint *next, *prev;
+       short x, y;
+} StrokePoint;
+typedef struct SculptStroke {
+       short (*loc)[2];
+       int max;
+       int index;
+       float length;
+       ListBase final;
+       StrokePoint *final_mem;
+       float offset;
+} SculptStroke;
+
+void sculpt_stroke_new(const int max)
+{
+       SculptSession *ss = sculpt_session();
+
+       ss->stroke = MEM_callocN(sizeof(SculptStroke), "SculptStroke");
+       ss->stroke->loc = MEM_callocN(sizeof(short) * 4 * max, "SculptStroke.loc");
+       ss->stroke->max = max;
+       ss->stroke->index = -1;
+}
+
+void sculpt_stroke_free()
+{
+       SculptSession *ss = sculpt_session();
+       if(ss && ss->stroke) {
+               if(ss->stroke->loc) MEM_freeN(ss->stroke->loc);
+               if(ss->stroke->final_mem) MEM_freeN(ss->stroke->final_mem);
+
+               MEM_freeN(ss->stroke);
+               ss->stroke = NULL;
+       }
+}
+
+void sculpt_stroke_add_point(const short x, const short y)
+{
+       SculptStroke *stroke = sculpt_session()->stroke;
+       const int next = stroke->index + 1;
+
+       if(stroke->index == -1) {
+               stroke->loc[0][0] = x;
+               stroke->loc[0][1] = y;
+               stroke->index = 0;
+       }
+       else if(next < stroke->max) {
+               const int dx = x - stroke->loc[stroke->index][0];
+               const int dy = y - stroke->loc[stroke->index][1];
+               stroke->loc[next][0] = x;
+               stroke->loc[next][1] = y;
+               stroke->length += sqrt(dx*dx + dy*dy);
+               stroke->index = next;
+       }
+}
+
+void sculpt_stroke_smooth(SculptStroke *stroke)
+{
+       /* Apply smoothing (exclude the first and last points)*/
+       StrokePoint *p = stroke->final.first;
+       if(p && p->next && p->next->next) {
+               for(p = p->next->next; p && p->next && p->next->next; p = p->next) {
+                       p->x = p->prev->prev->x*0.1 + p->prev->x*0.2 + p->x*0.4 + p->next->x*0.2 + p->next->next->x*0.1;
+                       p->y = p->prev->prev->y*0.1 + p->prev->y*0.2 + p->y*0.4 + p->next->y*0.2 + p->next->next->y*0.1;
+               }
+       }       
+}
+
+static void sculpt_stroke_create_final()
+{
+       SculptStroke *stroke = sculpt_session()->stroke;
+
+       if(stroke) {
+               StrokePoint *p, *pnext;
+               int i;
+
+               /* Copy loc into final */
+               if(stroke->final_mem)
+                       MEM_freeN(stroke->final_mem);
+               stroke->final_mem = MEM_callocN(sizeof(StrokePoint) * (stroke->index + 1) * 2, "SculptStroke.final");
+               stroke->final.first = stroke->final.last = NULL;
+               for(i = 0; i <= stroke->index; ++i) {
+                       p = &stroke->final_mem[i];
+                       p->x = stroke->loc[i][0];
+                       p->y = stroke->loc[i][1];
+                       BLI_addtail(&stroke->final, p);
+               }
+
+               /* Remove shortest edges */
+               if(stroke->final.first) {
+                       for(p = ((StrokePoint*)stroke->final.first)->next; p && p->next; p = pnext) {
+                               const int dx = p->x - p->prev->x;
+                               const int dy = p->y - p->prev->y;
+                               const float len = sqrt(dx*dx + dy*dy);
+                               pnext = p->next;
+                               if(len < 10) {
+                                       BLI_remlink(&stroke->final, p);
+                               }
+                       }
+               }
+
+               sculpt_stroke_smooth(stroke);
+
+               /* Subdivide edges */
+               for(p = stroke->final.first; p && p->next; p = pnext) {
+                       StrokePoint *np = &stroke->final_mem[i++];
+
+                       pnext = p->next;
+                       np->x = (p->x + p->next->x) / 2;
+                       np->y = (p->y + p->next->y) / 2;
+                       BLI_insertlink(&stroke->final, p, np);
+               }
+
+               sculpt_stroke_smooth(stroke);
+       }
+}
+
+float sculpt_stroke_seglen(StrokePoint *p1, StrokePoint *p2)
+{
+       int dx = p2->x - p1->x;
+       int dy = p2->y - p1->y;
+       return sqrt(dx*dx + dy*dy);
+}
+
+float sculpt_stroke_final_length(SculptStroke *stroke)
+{
+       StrokePoint *p;
+       float len = 0;
+       for(p = stroke->final.first; p && p->next; ++p)
+               len += sculpt_stroke_seglen(p, p->next);
+       return len;
+}
+
+/* If partial is nonzero, cuts off apply after that length has been processed */
+static StrokePoint *sculpt_stroke_apply_generic(SculptStroke *stroke, struct BrushAction *a, const int partial)
+{
+       const int sdspace = sculpt_data()->spacing;
+       const short spacing = sdspace > 0 ? sdspace : 2;
+       const int dots = sculpt_stroke_final_length(stroke) / spacing;
+       int i;
+       StrokePoint *p = stroke->final.first;
+       float startloc = stroke->offset;
+
+       for(i = 0; i < dots && p && p->next; ++i) {
+               const float dotloc = spacing * i;
+               short co[2];
+               float len = sculpt_stroke_seglen(p, p->next);
+               float u, v;
+
+               /* Find edge containing dot */
+               while(dotloc > startloc + len && p && p->next && p->next->next) {
+                       p = p->next;
+                       startloc += len;
+                       len = sculpt_stroke_seglen(p, p->next);
+               }
+
+               if(!p || !p->next || dotloc > startloc + len)
+                       break;
+
+               if(partial && startloc > partial) {
+                       /* Calculate offset for next stroke segment */
+                       stroke->offset = startloc + len - dotloc;
+                       break;
+               }
+
+               u = (dotloc - startloc) / len;
+               v = 1 - u;
+                                       
+               co[0] = p->x*v + p->next->x*u;
+               co[1] = p->y*v + p->next->y*u;
+
+               do_symmetrical_brush_actions(a, co, NULL);
+       }
+
+       return p ? p->next : NULL;
+}
+
+void sculpt_stroke_apply(struct BrushAction *a)
+{
+       SculptStroke *stroke = sculpt_session()->stroke;
+       /* TODO: make these values user-modifiable? */
+       const int partial_len = 100;
+       const int min_len = 200;
+
+       if(stroke) {
+               sculpt_stroke_create_final();
+
+               if(sculpt_stroke_final_length(stroke) > min_len) {
+                       StrokePoint *p = sculpt_stroke_apply_generic(stroke, a, partial_len);
+
+                       /* Replace remaining values in stroke->loc with remaining stroke->final values */
+                       stroke->index = -1;
+                       stroke->length = 0;
+                       for(; p; p = p->next) {
+                               ++stroke->index;
+                               stroke->loc[stroke->index][0] = p->x;
+                               stroke->loc[stroke->index][1] = p->y;
+                               if(p->next) {
+                                       stroke->length += sculpt_stroke_seglen(p, p->next);
+                               }
+                       }
+               }
+       }
+}
+
+void sculpt_stroke_apply_all(struct BrushAction *a)
+{
+       SculptStroke *stroke = sculpt_session()->stroke;
+
+       sculpt_stroke_create_final();
+
+       if(stroke) {
+               sculpt_stroke_apply_generic(stroke, a, 0);
+       }
+}
+
+void sculpt_stroke_draw()
+{
+       SculptStroke *stroke = sculpt_session()->stroke;
+
+       if(stroke) {
+               StrokePoint *p;
+
+               /* Draws the original stroke */
+               /*glColor3f(1, 0, 0);           
+               glBegin(GL_LINE_STRIP);
+               for(i = 0; i <= stroke->index; ++i)
+                       glVertex2s(stroke->loc[i][0], stroke->loc[i][1]);
+               glEnd();*/
+
+               /* Draws the smoothed stroke */
+               glColor3f(0, 1, 0);
+               glBegin(GL_LINE_STRIP);
+               for(p = stroke->final.first; p; p = p->next)
+                       glVertex2s(p->x, p->y);
+               glEnd();
+       }
+}