merge with 2.5 at r22793
authorJoseph Eagar <joeedh@gmail.com>
Wed, 26 Aug 2009 10:27:04 +0000 (10:27 +0000)
committerJoseph Eagar <joeedh@gmail.com>
Wed, 26 Aug 2009 10:27:04 +0000 (10:27 +0000)
91 files changed:
1  2 
SConstruct
projectfiles_vc9/blender/BPY_python/BPY_python.vcproj
projectfiles_vc9/blender/blender.sln
projectfiles_vc9/blender/blender.vcproj
projectfiles_vc9/blender/blenkernel/BKE_blenkernel.vcproj
projectfiles_vc9/blender/editors/ED_editors.vcproj
source/blender/blenkernel/BKE_blender.h
source/blender/blenkernel/BKE_modifier.h
source/blender/blenkernel/BKE_multires.h
source/blender/blenkernel/BKE_utildefines.h
source/blender/blenkernel/SConscript
source/blender/blenkernel/intern/DerivedMesh.c
source/blender/blenkernel/intern/blender.c
source/blender/blenkernel/intern/cdderivedmesh.c
source/blender/blenkernel/intern/cloth.c
source/blender/blenkernel/intern/displist.c
source/blender/blenkernel/intern/modifier.c
source/blender/blenkernel/intern/multires.c
source/blender/blenkernel/intern/object.c
source/blender/blenkernel/intern/particle.c
source/blender/blenkernel/intern/particle_system.c
source/blender/blenkernel/intern/smoke.c
source/blender/blenkernel/intern/softbody.c
source/blender/blenlib/BLI_arithb.h
source/blender/blenlib/intern/arithb.c
source/blender/blenloader/intern/readfile.c
source/blender/blenloader/intern/writefile.c
source/blender/editors/include/ED_view3d.h
source/blender/editors/mesh/bmesh_select.c
source/blender/editors/mesh/bmesh_tools.c
source/blender/editors/mesh/editmesh.c
source/blender/editors/mesh/editmesh_add.c
source/blender/editors/mesh/editmesh_loop.c
source/blender/editors/mesh/editmesh_mods.c
source/blender/editors/mesh/mesh_layers.c
source/blender/editors/mesh/mesh_ops.c
source/blender/editors/object/object_edit.c
source/blender/editors/physics/editparticle.c
source/blender/editors/screen/Makefile
source/blender/editors/screen/SConscript
source/blender/editors/screen/screen_ops.c
source/blender/editors/sculpt_paint/paint_image.c
source/blender/editors/sculpt_paint/paint_utils.c
source/blender/editors/sculpt_paint/sculpt.c
source/blender/editors/space_buttons/buttons_ops.c
source/blender/editors/space_image/Makefile
source/blender/editors/space_image/SConscript
source/blender/editors/space_view3d/drawmesh.c
source/blender/editors/space_view3d/drawobject.c
source/blender/editors/space_view3d/view3d_buttons.c
source/blender/editors/space_view3d/view3d_header.c
source/blender/editors/space_view3d/view3d_ops.c
source/blender/editors/space_view3d/view3d_select.c
source/blender/editors/space_view3d/view3d_snap.c
source/blender/editors/transform/transform_conversions.c
source/blender/editors/transform/transform_generics.c
source/blender/editors/transform/transform_manipulator.c
source/blender/editors/transform/transform_ops.c
source/blender/editors/transform/transform_orientations.c
source/blender/makesdna/DNA_mesh_types.h
source/blender/makesdna/DNA_object_types.h
source/blender/makesrna/SConscript
source/blender/makesrna/intern/CMakeLists.txt
source/blender/makesrna/intern/SConscript
source/blender/makesrna/intern/rna_mesh.c
source/blender/makesrna/intern/rna_nla.c
source/blender/python/generic/BGL.c
source/blender/python/generic/BGL.h
source/blender/python/generic/Geometry.c
source/blender/python/generic/Geometry.h
source/blender/python/generic/Mathutils.c
source/blender/python/generic/Mathutils.h
source/blender/python/generic/bpy_internal_import.c
source/blender/python/generic/bpy_internal_import.h
source/blender/python/generic/euler.c
source/blender/python/generic/euler.h
source/blender/python/generic/matrix.c
source/blender/python/generic/quat.c
source/blender/python/generic/quat.h
source/blender/python/generic/vector.c
source/blender/python/generic/vector.h
source/blender/python/intern/bpy_interface.c
source/blender/render/intern/source/convertblender.c
source/blender/render/intern/source/shadeoutput.c
source/blender/render/intern/source/strand.c
source/blender/windowmanager/intern/wm.c
source/creator/creator.c
source/gameengine/Converter/BL_BlenderDataConversion.cpp
source/gameengine/Physics/Bullet/CcdPhysicsController.cpp
tools/Blender.py
tools/btools.py

diff --cc SConstruct
Simple merge
index dacced09d6569c6713ddcd51f3eebc82dcd19166,1bd132ea223b73fa334d3e71577ffb8fac0671e2..756b72082dcbf58125d5c732567bb989c3ee6a5e
@@@ -6,13 -6,13 +6,12 @@@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C
                {6E24BF09-9653-4166-A871-F65CC9E98A9B} = {6E24BF09-9653-4166-A871-F65CC9E98A9B}\r
                {A90C4918-4B21-4277-93BD-AF65F30951D9} = {A90C4918-4B21-4277-93BD-AF65F30951D9}\r
                {FB88301F-F725-401B-ACD7-D2ABBF333B71} = {FB88301F-F725-401B-ACD7-D2ABBF333B71}\r
-               {98330220-47A6-42E0-9DE4-AD0FF5D204D6} = {98330220-47A6-42E0-9DE4-AD0FF5D204D6}\r
                {BAAE3F2B-BCF8-4E84-B8BA-CFB2D64945FE} = {BAAE3F2B-BCF8-4E84-B8BA-CFB2D64945FE}\r
                {C66F722C-46BE-40C9-ABAE-2EAC7A697EB8} = {C66F722C-46BE-40C9-ABAE-2EAC7A697EB8}\r
 -              {D1A9312F-4557-4982-A0F4-4D08508235F4} = {D1A9312F-4557-4982-A0F4-4D08508235F4}\r
                {884D8731-654C-4C7F-9A75-8F37A305BE1E} = {884D8731-654C-4C7F-9A75-8F37A305BE1E}\r
+               {79D0B232-208C-F208-DA71-79B4AC088602} = {79D0B232-208C-F208-DA71-79B4AC088602}\r
                {E645CC32-4823-463E-82F0-46ADDE664018} = {E645CC32-4823-463E-82F0-46ADDE664018}\r
                {7495FE37-933A-4AC1-BB2A-B3FDB4DE4284} = {7495FE37-933A-4AC1-BB2A-B3FDB4DE4284}\r
-               {FAF46346-65CC-4DB2-85C4-B99826F79D0C} = {FAF46346-65CC-4DB2-85C4-B99826F79D0C}\r
                {51FB3D48-2467-4BFA-A321-D848252B437E} = {51FB3D48-2467-4BFA-A321-D848252B437E}\r
                {FFD3C64A-30E2-4BC7-BC8F-51818C320400} = {FFD3C64A-30E2-4BC7-BC8F-51818C320400}\r
                {31628053-825D-4C06-8A21-D13883489718} = {31628053-825D-4C06-8A21-D13883489718}\r
index 063f0ee96aaeda57db455f4df333787f36d82e4c,a331479cad1f587bdf901b9474da33ef8916e26f..d20a00cbfff69db8f1d4ee811fe19ee5accd5f39
@@@ -39,9 -40,9 +40,10 @@@ typedef struct MultiresSubsurf 
  } MultiresSubsurf;
  
  /* MultiresDM */
+ struct Object *MultiresDM_get_object(struct DerivedMesh *dm);
  struct Mesh *MultiresDM_get_mesh(struct DerivedMesh *dm);
 -struct DerivedMesh *MultiresDM_new(struct MultiresSubsurf *, struct DerivedMesh*, int, int, int);
 +struct DerivedMesh *MultiresDM_new(struct MultiresSubsurf *, struct DerivedMesh*, 
 +                                   int, int, int, int, int);
  void *MultiresDM_get_vertnorm(struct DerivedMesh *);
  void *MultiresDM_get_orco(struct DerivedMesh *);
  struct MVert *MultiresDM_get_subco(struct DerivedMesh *);
index 78732be210d95d7a5b7a2c683ab6f235b354b262,0c7922de6ff6132d20acc17e3a5d0ee6f2e8525a..e235a790a3449e8a412b2db06993bdb6bc3369e9
@@@ -10,8 -10,10 +10,11 @@@ incs += ' #/intern/iksolver/extern ../b
  incs += ' #/extern/bullet2/src'
  incs += ' #/intern/opennl/extern #/intern/bsp/extern'
  incs += ' ../gpu #/extern/glew/include'
 +incs += ' ../bmesh'
  incs += ' #/intern/smoke/extern'
+ incs += ' #/extern/lzo/minilzo'
+ incs += ' #/extern/lzma'
+ incs += ' #/intern/audaspace/intern'
  
  incs += ' ' + env['BF_OPENGL_INC']
  incs += ' ' + env['BF_ZLIB_INC']
index df8eb47831ee7408be59a8ee4fc36744cfd1f729,9ba5769843f76360929585b16f17933023ed9db9..2555bc6de27a8421581e95371fb1fc7a2726107b
@@@ -465,11 -467,13 +467,13 @@@ void multiresModifier_subdivide(Multire
                MultiresModifierData mmd_sub;
  
                orig = CDDM_from_mesh(me, NULL);
+               memset(&mmd_sub, 0, sizeof(MultiresModifierData));
                mmd_sub.lvl = mmd_sub.totlvl = mmd->lvl;
-               mrdm = multires_dm_create_from_derived(&mmd_sub, orig, me, 0, 0);
+               mmd_sub.simple = simple;
+               mrdm = multires_dm_create_from_derived(&mmd_sub, 1, orig, ob, 0, 0);
                totsubvert = mrdm->getNumVerts(mrdm);
                totsubedge = mrdm->getNumEdges(mrdm);
 -              totsubface = mrdm->getNumFaces(mrdm);
 +              totsubface = mrdm->getNumTessFaces(mrdm);
                orig->needsFree = 1;
                orig->release(orig);
                
@@@ -1197,8 -1203,8 +1203,8 @@@ static void multiresModifier_update(Der
  
                        final = multires_subdisp_pre(dm, totlvl - lvl, 0);
  
-                       multires_subdisp(orig, me, final, lvl, totlvl, dm->getNumVerts(dm), dm->getNumEdges(dm),
+                       multires_subdisp(orig, ob, final, lvl, totlvl, dm->getNumVerts(dm), dm->getNumEdges(dm),
 -                                       dm->getNumFaces(dm), 1);
 +                                       dm->getNumTessFaces(dm), 1);
  
                        subco_dm->release(subco_dm);
                        orig->release(orig);
index b461592f35f8b9528715f8aa6296dfc6aa2a04bd,223d48012df1f55d43da4bbe56313bcec9fa236e..888ba7c9a5ebd2c15bc82a5d815692343e040f90
@@@ -281,12 -212,23 +212,23 @@@ int smokeModifier_init (SmokeModifierDa
                // TODO: put in failsafe if res<=0 - dg
  
                // printf("res[0]: %d, res[1]: %d, res[2]: %d\n", smd->domain->res[0], smd->domain->res[1], smd->domain->res[2]);
                // dt max is 0.1
-               smd->domain->fluid = smoke_init(smd->domain->res, smd->domain->amplify, smd->domain->p0, smd->domain->p1, 2.5 / FPS);
+               smd->domain->fluid = smoke_init(smd->domain->res, smd->domain->p0, 0.1);
                smd->time = scene->r.cfra;
                smd->domain->firstframe = smd->time;
 -
 +              
+               if(!smd->domain->wt && (smd->domain->flags & MOD_SMOKE_HIGHRES))
+               {
+                       smd->domain->wt = smoke_turbulence_init(smd->domain->res,  smd->domain->amplify + 1, smd->domain->noise);
+                       smoke_turbulence_initBlenderRNA(smd->domain->wt, &smd->domain->strength);
+               }
+               if(!smd->domain->view3d)
+               {
+                       // TVox is for transparency
+                       smd->domain->view3d = MEM_callocN(sizeof(float)*smd->domain->res[0]*smd->domain->res[1]*smd->domain->res[2]*4, "Smoke_tVox");
+               }
                smoke_initBlenderRNA(smd->domain->fluid, &(smd->domain->alpha), &(smd->domain->beta));
  
                return 1;
                        // init collision points
                        SmokeCollSettings *scs = smd->coll;
                        MVert *mvert = dm->getVertArray(dm);
 -                      MFace *mface = dm->getFaceArray(dm);
 +                      MFace *mface = dm->getTessFaceArray(dm);
-                       size_t i = 0, divs = 0;
+                       size_t i = 0;
+                       int divs = 0;
                        int *tridivs = NULL;
                        float cell_len = 1.0 / 50.0; // for res = 50
                        size_t newdivs = 0;
@@@ -562,26 -522,20 +522,20 @@@ void smokeModifier_freeDomain(SmokeModi
        if(smd->domain)
        {
                // free visualisation buffers
-               if(smd->domain->bind)
-               {
-                       glDeleteTextures(smd->domain->max_textures, (GLuint *)smd->domain->bind);
-                       MEM_freeN(smd->domain->bind);
-               }
-               smd->domain->max_textures = 0; // unnecessary but let's be sure
-               if(smd->domain->tray)
-                       MEM_freeN(smd->domain->tray);
-               if(smd->domain->tvox)
-                       MEM_freeN(smd->domain->tvox);
-               if(smd->domain->traybig)
-                       MEM_freeN(smd->domain->traybig);
-               if(smd->domain->tvoxbig)
-                       MEM_freeN(smd->domain->tvoxbig);
+               if(smd->domain->view3d)
+                       MEM_freeN(smd->domain->view3d);
 -              
 +
                if(smd->domain->fluid)
-               {
                        smoke_free(smd->domain->fluid);
-               }
+               if(smd->domain->wt)
+                       smoke_turbulence_free(smd->domain->wt);
+               BKE_ptcache_free_list(&(smd->domain->ptcaches[0]));
+               smd->domain->point_cache[0] = NULL;
+               BKE_ptcache_free_list(&(smd->domain->ptcaches[1]));
+               smd->domain->point_cache[1] = NULL;
                MEM_freeN(smd->domain);
                smd->domain = NULL;
        }
@@@ -652,10 -600,35 +600,35 @@@ void smokeModifier_reset(struct SmokeMo
                                smoke_free(smd->domain->fluid);
                                smd->domain->fluid = NULL;
                        }
 -                      }
+                       if(smd->domain->wt)
+                       {
+                               smoke_turbulence_free(smd->domain->wt);
+                               smd->domain->wt = NULL;
++              }
+               
+                       smd->domain->point_cache[0]->flag &= ~PTCACHE_SIMULATION_VALID;
+                       smd->domain->point_cache[0]->flag |= PTCACHE_OUTDATED;
+                       smd->domain->point_cache[0]->simframe= 0;
+                       smd->domain->point_cache[0]->last_exact= 0;
+                       smd->domain->point_cache[1]->flag &= ~PTCACHE_SIMULATION_VALID;
+                       smd->domain->point_cache[1]->flag |= PTCACHE_OUTDATED;
+                       smd->domain->point_cache[1]->simframe= 0;
+                       smd->domain->point_cache[1]->last_exact= 0;
+                       // printf("reset_domain\n");
                }
                else if(smd->flow)
                {
-                                               
+                       /*
+                       if(smd->flow->bvh)
+                       {
+                               free_bvhtree_from_mesh(smd->flow->bvh);
+                               MEM_freeN(smd->flow->bvh);
 -                      }
++              }
+                       smd->flow->bvh = NULL;
+                       */
                }
                else if(smd->coll)
                {
@@@ -756,11 -739,11 +739,11 @@@ void smoke_simulate_domain(SmokeModifie
  
  void smokeModifier_do(SmokeModifierData *smd, Scene *scene, Object *ob, DerivedMesh *dm, int useRenderParams, int isFinalCalc)
  {     
 -              if(scene->r.cfra >= smd->time)
 -                      smokeModifier_init(smd, ob, scene, dm);
+       if((smd->type & MOD_SMOKE_TYPE_FLOW))
+       {
 +      if(scene->r.cfra >= smd->time)
 +              smokeModifier_init(smd, ob, scene, dm);
  
-       if((smd->type & MOD_SMOKE_TYPE_FLOW))
-       {
                if(scene->r.cfra > smd->time)
                {
                        // XXX TODO
        }
        else if(smd->type & MOD_SMOKE_TYPE_DOMAIN)
        {
+               PointCache *cache;
+               PTCacheID pid;
+               float timescale;
+               int cache_result = 0;
+               int startframe, endframe, framenr;
                SmokeDomainSettings *sds = smd->domain;
 -
+               float light[3] = {0.0,0.0,0.0};
+               int have_lamp = 0;
-               if(scene->r.cfra > smd->time)
 +              
+               // printf("smd->type & MOD_SMOKE_TYPE_DOMAIN\n");
+               framenr = scene->r.cfra;
+               cache = sds->point_cache[0];
+               BKE_ptcache_id_from_smoke(&pid, ob, smd);
+               BKE_ptcache_id_time(&pid, scene, framenr, &startframe, &endframe, &timescale);
+               /* handle continuous simulation with the play button */
+               if(BKE_ptcache_get_continue_physics()) 
                {
-                       GroupObject *go = NULL;
-                       Base *base = NULL;
-                       int cnt_domain = 0;
+                       cache->flag &= ~PTCACHE_SIMULATION_VALID;
+                       cache->simframe= 0;
+                       cache->last_exact= 0;
 -
 +                      
-                       tstart();
+                       if(!smokeModifier_init(smd, ob, scene, dm))
+                               return;
+                       if(!smd->domain->fluid)
+                               return;
  
-                       sds->viewsettings = 0; // reset view for new frame
+                       smoke_simulate_domain(smd, scene, ob, dm);
  
-                       // check for 2nd domain, if not there -> no groups are necessary
-                       for(base = scene->base.first; base; base= base->next) 
                        {
-                               Object *ob1= base->object;
+                               Base *base_tmp = NULL;
 -
 +                              
-                               if(ob1 && ob1 != ob)
+                               for(base_tmp = scene->base.first; base_tmp; base_tmp= base_tmp->next) 
                                {
-                                       ModifierData *tmd = modifiers_findByType(ob1, eModifierType_Smoke);
+                                       if(base_tmp->object->type == OB_LAMP) 
+                                       {
+                                               Lamp *la = (Lamp *)base_tmp->object->data;
 -                                              
 +
-                                       if(tmd && tmd->mode & (eModifierMode_Realtime | eModifierMode_Render))
+                                               if(la->type == LA_LOCAL)
 -                                              {
 +                                      {
-                                               SmokeModifierData *tsmd = (SmokeModifierData *)tmd;
+                                                       VECCOPY(light, base_tmp->object->obmat[3]);
+                                                       have_lamp = 1;
+                                                       break;
+                                               }
+                                       }
+                               }
+                       }
+                       smoke_prepare_View(smd, (float)framenr, light, have_lamp);
  
-                                               if((tsmd->type & MOD_SMOKE_TYPE_DOMAIN))
+                       return;
+               }
+               
+               if(framenr < startframe)
 -              {
 +                                              {
-                                                       cnt_domain++;
+                       cache->flag &= ~PTCACHE_SIMULATION_VALID;
+                       cache->simframe= 0;
+                       cache->last_exact= 0;
+                       // we got back in time, reset smoke in this case (TODO: use cache later)
+                       // smd->time = scene->r.cfra;
+                       // smokeModifier_reset(smd);
+                       return;
 -              }
 +                                              }
+               else if(framenr > endframe) 
+               {
+                       framenr = endframe;
 -              }
++                                      }
+               if(!(cache->flag & PTCACHE_SIMULATION_VALID))
+               {
+                       // printf("reseting\n");
+                       BKE_ptcache_id_reset(scene, &pid, PTCACHE_RESET_OUTDATED);
 -              }
++                              }
+       
+               if(!smokeModifier_init(smd, ob, scene, dm))
+                       return;
+               if(!smd->domain->fluid)
+                               return;
+               /* try to read from cache */
+               cache_result = BKE_ptcache_read_cache(&pid, (float)framenr, scene->r.frs_sec);
+               // printf("cache_result: %d\n", cache_result);
+               if(cache_result == PTCACHE_READ_EXACT) 
+               {
+                       SmokeDomainSettings *sds = smd->domain;
+                       cache->flag |= PTCACHE_SIMULATION_VALID;
+                       cache->simframe= framenr;
+                       sds->v3dnum = framenr;
+                       // printf("PTCACHE_READ_EXACT\n");
+                       return;
 -              }
++                      }
+               else if(cache_result==PTCACHE_READ_OLD) 
+               {
+                       BKE_ptcache_id_reset(scene, &pid, PTCACHE_RESET_FREE);
+                       // printf("PTCACHE_READ_OLD\n");
+                       cache->flag |= PTCACHE_SIMULATION_VALID;
+               }
+               else if(ob->id.lib || (cache->flag & PTCACHE_BAKED)) 
+               {
+                       /* if baked and nothing in cache, do nothing */
+                       cache->flag &= ~PTCACHE_SIMULATION_VALID;
+                       cache->simframe= 0;
+                       cache->last_exact= 0;
+                       // printf("PTCACHE_BAKED\n");
+                       return;
+               }
+               else if((cache_result==0) && (startframe!=framenr) && !(cache->flag & PTCACHE_SIMULATION_VALID))
+               {
+                       cache->flag &= ~PTCACHE_SIMULATION_VALID;
+                       cache->simframe= 0;
+                       cache->last_exact= 0;
+                       return;
+               }
+               
+               /* do simulation */
+               // low res
+               cache->flag |= PTCACHE_SIMULATION_VALID;
+               cache->simframe= framenr;
+               smoke_simulate_domain(smd, scene, ob, dm);
+               if(sds->wt)
+                       smoke_turbulence_step(sds->wt, sds->fluid);
+               {
+                       Base *base_tmp = NULL;
+                       for(base_tmp = scene->base.first; base_tmp; base_tmp= base_tmp->next) 
+                       {
+                               if(base_tmp->object->type == OB_LAMP) 
+                               {
+                                       Lamp *la = (Lamp *)base_tmp->object->data;
+                                       
+                                       if(la->type == LA_LOCAL)
+                                       {
+                                               VECCOPY(light, base_tmp->object->obmat[3]);
+                                               have_lamp = 1;
+                                               break;
                                        }
                                }
                        }
+               }
+               smoke_prepare_View(smd, (float)framenr, light, have_lamp);
+               BKE_ptcache_write_cache(&pid, framenr);
+               // printf("Writing cache_low, %d\n", framenr);
+       
+               tend();
+               // printf ( "Frame: %d, Time: %f\n", (int)smd->time, ( float ) tval() );
+       }
+ }
+ void smoke_simulate_domain(SmokeModifierData *smd, Scene *scene, Object *ob, DerivedMesh *dm)
+ {
+       GroupObject *go = NULL;
+       Base *base = NULL;
+       SmokeDomainSettings *sds = smd->domain;
+       
+       tstart();
+       
+       if(sds->flags & MOD_SMOKE_DISSOLVE)
+               smoke_dissolve(sds->fluid, sds->diss_speed, sds->flags & MOD_SMOKE_DISSOLVE_LOG);
  
 -      // do flows and fluids
 +                      // do flows and fluids
-                       if(sds->fluid_group || !cnt_domain)
+       if(1)
 -      {
 -              Object *otherobj = NULL;
 -              ModifierData *md = NULL;
 +                      {
 +                              Object *otherobj = NULL;
 +                              ModifierData *md = NULL;
  
-                               if(cnt_domain && !sds->fluid_group) // we use groups since we have 2 domains
+               if(sds->fluid_group) // we use groups since we have 2 domains
 -                      go = sds->fluid_group->gobject.first;
 -              else
 -                      base = scene->base.first;
 +                                      go = sds->fluid_group->gobject.first;
 +                              else
 +                                      base = scene->base.first;
  
 -              while(base || go)
 -              {
 -                      otherobj = NULL;
 +                              while(base || go)
 +                              {
 +                                      otherobj = NULL;
  
-                                       if(cnt_domain && !sds->fluid_group) 
+                       if(sds->fluid_group) 
 -                      {
 -                              if(go->ob)
 -                                      otherobj = go->ob;
 -                      }
 -                      else
 -                              otherobj = base->object;
 +                                      {
 +                                              if(go->ob)
 +                                                      otherobj = go->ob;
 +                                      }
 +                                      else
 +                                              otherobj = base->object;
  
 -                      if(!otherobj)
 -                      {
 +                                      if(!otherobj)
 +                                      {
-                                               if(cnt_domain && !sds->fluid_group)
+                               if(sds->fluid_group)
 -                                      go = go->next;
 -                              else
 -                                      base= base->next;
 +                                                      go = go->next;
 +                                              else
 +                                                      base= base->next;
  
 -                              continue;
 -                      }
 +                                              continue;
 +                                      }
  
 -                      md = modifiers_findByType(otherobj, eModifierType_Smoke);
 -                      
 -                      // check for active smoke modifier
 -                      if(md && md->mode & (eModifierMode_Realtime | eModifierMode_Render))
 -                      {
 -                              SmokeModifierData *smd2 = (SmokeModifierData *)md;
 -                              
 -                              // check for initialized smoke object
 -                              if((smd2->type & MOD_SMOKE_TYPE_FLOW) && smd2->flow)
 -                              {
 -                                      // we got nice flow object
 -                                      SmokeFlowSettings *sfs = smd2->flow;
 +                                      md = modifiers_findByType(otherobj, eModifierType_Smoke);
                                        
 -                                      if(sfs->psys && sfs->psys->part && sfs->psys->part->type==PART_EMITTER) // is particle system selected
 +                                      // check for active smoke modifier
 +                                      if(md && md->mode & (eModifierMode_Realtime | eModifierMode_Render))
                                        {
 -                                              ParticleSystem *psys = sfs->psys;
 -                                              ParticleSettings *part=psys->part;
 -                                              ParticleData *pa = NULL;
 -                                              int p = 0;
 -                                              float *density = smoke_get_density(sds->fluid);
 -                                              // float *bigdensity = smoke_turbulence_get_density(sds->wt);
 -                                              float *heat = smoke_get_heat(sds->fluid);
 -                                              float *velocity_x = smoke_get_velocity_x(sds->fluid);
 -                                              float *velocity_y = smoke_get_velocity_y(sds->fluid);
 -                                              float *velocity_z = smoke_get_velocity_z(sds->fluid);
 -                                              unsigned char *obstacle = smoke_get_obstacle(sds->fluid);
 -
 -                                              // debug printf("found flow psys\n");
 +                                              SmokeModifierData *smd2 = (SmokeModifierData *)md;
                                                
 -                                              // mostly copied from particle code
 -                                              for(p=0, pa=psys->particles; p<psys->totpart; p++, pa++)
 +                                              // check for initialized smoke object
 +                                              if((smd2->type & MOD_SMOKE_TYPE_FLOW) && smd2->flow)
                                                {
 -                                                      int cell[3];
 -                                                      size_t i = 0;
 -                                                      size_t index = 0;
 -                                                      int badcell = 0;
 -                                                      
 -                                                      if(pa->alive == PARS_KILLED) continue;
 -                                                      else if(pa->alive == PARS_UNBORN && (part->flag & PART_UNBORN)==0) continue;
 -                                                      else if(pa->alive == PARS_DEAD && (part->flag & PART_DIED)==0) continue;
 -                                                      else if(pa->flag & (PARS_UNEXIST+PARS_NO_DISP)) continue;
 -                                                      
 -                                                      // VECCOPY(pos, pa->state.co);
 -                                                      // Mat4MulVecfl (ob->imat, pos);
 +                                                      // we got nice flow object
 +                                                      SmokeFlowSettings *sfs = smd2->flow;
                                                        
 -                                                      // 1. get corresponding cell
 -                                                      get_cell(sds->p0, sds->res, sds->dx, pa->state.co, cell, 0);
 -                                              
 -                                                      // check if cell is valid (in the domain boundary)
 -                                                      for(i = 0; i < 3; i++)
 +                                                      if(sfs->psys && sfs->psys->part && sfs->psys->part->type==PART_EMITTER) // is particle system selected
                                                        {
 -                                                              if((cell[i] > sds->res[i] - 1) || (cell[i] < 0))
 +                                                              ParticleSystem *psys = sfs->psys;
 +                                                              ParticleSettings *part=psys->part;
 +                                                              ParticleData *pa = NULL;
 +                                                              int p = 0;
 +                                                              float *density = smoke_get_density(sds->fluid);
-                                                               float *bigdensity = smoke_get_bigdensity(sds->fluid);
++                                              // float *bigdensity = smoke_turbulence_get_density(sds->wt);
 +                                                              float *heat = smoke_get_heat(sds->fluid);
 +                                                              float *velocity_x = smoke_get_velocity_x(sds->fluid);
 +                                                              float *velocity_y = smoke_get_velocity_y(sds->fluid);
 +                                                              float *velocity_z = smoke_get_velocity_z(sds->fluid);
-                                                               int bigres[3];
++                                              unsigned char *obstacle = smoke_get_obstacle(sds->fluid);
 +
-                                                               smoke_get_bigres(smd->domain->fluid, bigres);
++                                              // debug printf("found flow psys\n");
 +                                                              
 +                                                              // mostly copied from particle code
 +                                                              for(p=0, pa=psys->particles; p<psys->totpart; p++, pa++)
                                                                {
 -                                                                      badcell = 1;
 -                                                                      break;
 -                                                              }
 -                                                      }
 +                                                                      int cell[3];
 +                                                                      size_t i = 0;
 +                                                                      size_t index = 0;
 +                                                                      int badcell = 0;
 +                                                                      
 +                                                                      if(pa->alive == PARS_KILLED) continue;
 +                                                                      else if(pa->alive == PARS_UNBORN && (part->flag & PART_UNBORN)==0) continue;
 +                                                                      else if(pa->alive == PARS_DEAD && (part->flag & PART_DIED)==0) continue;
 +                                                                      else if(pa->flag & (PARS_UNEXIST+PARS_NO_DISP)) continue;
 +                                                                      
 +                                                                      // VECCOPY(pos, pa->state.co);
 +                                                                      // Mat4MulVecfl (ob->imat, pos);
 +                                                                      
 +                                                                      // 1. get corresponding cell
-                                                                       get_cell(smd, pa->state.co, cell, 0);
++                                                      get_cell(sds->p0, sds->res, sds->dx, pa->state.co, cell, 0);
                                                                
 -                                                      if(badcell)
 -                                                              continue;
 -                                                      
 -                                                      // 2. set cell values (heat, density and velocity)
 -                                                      index = smoke_get_index(cell[0], sds->res[0], cell[1], sds->res[1], cell[2]);
 -                                                      
 +                                                                      // check if cell is valid (in the domain boundary)
 +                                                                      for(i = 0; i < 3; i++)
 +                                                                      {
 +                                                                              if((cell[i] > sds->res[i] - 1) || (cell[i] < 0))
 +                                                                              {
 +                                                                                      badcell = 1;
 +                                                                                      break;
 +                                                                              }
 +                                                                      }
 +                                                                              
 +                                                                      if(badcell)
 +                                                                              continue;
 +                                                                      
 +                                                                      // 2. set cell values (heat, density and velocity)
 +                                                                      index = smoke_get_index(cell[0], sds->res[0], cell[1], sds->res[1], cell[2]);
 +                                                                      
-                                                                       if(!(sfs->type & MOD_SMOKE_FLOW_TYPE_OUTFLOW)) // this is inflow
+                                                       if(!(sfs->type & MOD_SMOKE_FLOW_TYPE_OUTFLOW) && !(obstacle[index] & 2)) // this is inflow
 -                                                      {
 +                                                                      {
+                                                               // heat[index] += sfs->temp * 0.1;
+                                                               // density[index] += sfs->density * 0.1;
 -                                                              heat[index] = sfs->temp;
 -                                                              density[index] = sfs->density;
 +                                                                              heat[index] = sfs->temp;
 +                                                                              density[index] = sfs->density;
+                                                               /*
 -                                                              velocity_x[index] = pa->state.vel[0];
 -                                                              velocity_y[index] = pa->state.vel[1];
 -                                                              velocity_z[index] = pa->state.vel[2];
 +                                                                              velocity_x[index] = pa->state.vel[0];
 +                                                                              velocity_y[index] = pa->state.vel[1];
 +                                                                              velocity_z[index] = pa->state.vel[2];
-                                                                               // we need different handling for the high-res feature
-                                                                               if(bigdensity)
-                                                                               {
-                                                                                       // init all surrounding cells according to amplification, too
-                                                                                       int i, j, k;
-                                                                                       for(i = 0; i < smd->domain->amplify; i++)
-                                                                                               for(j = 0; j < smd->domain->amplify; j++)
-                                                                                                       for(k = 0; k < smd->domain->amplify; k++)
-                                                                                                       {
-                                                                                                               index = smoke_get_index(smd->domain->amplify * cell[0] + i, bigres[0], smd->domain->amplify * cell[1] + j, bigres[1], smd->domain->amplify * cell[2] + k);
-                                                                                                               bigdensity[index] = sfs->density;
+                                                               */
+                                                               obstacle[index] |= 2;
 -                                                      }
 +                                                                                                      }
-                                                                               }
-                                                                       }
-                                                                       else // outflow
+                                                       else if(sfs->type & MOD_SMOKE_FLOW_TYPE_OUTFLOW) // outflow
 -                                                      {
 -                                                              heat[index] = 0.f;
 -                                                              density[index] = 0.f;
 -                                                              velocity_x[index] = 0.f;
 -                                                              velocity_y[index] = 0.f;
 -                                                              velocity_z[index] = 0.f;
 -                                                      }
 -                                              }
 -                                      }
 +                                                                      {
 +                                                                              heat[index] = 0.f;
 +                                                                              density[index] = 0.f;
 +                                                                              velocity_x[index] = 0.f;
 +                                                                              velocity_y[index] = 0.f;
 +                                                                              velocity_z[index] = 0.f;
-                                                                               // we need different handling for the high-res feature
-                                                                               if(bigdensity)
-                                                                               {
-                                                                                       // init all surrounding cells according to amplification, too
-                                                                                       int i, j, k;
-                                                                                       for(i = 0; i < smd->domain->amplify; i++)
-                                                                                               for(j = 0; j < smd->domain->amplify; j++)
-                                                                                                       for(k = 0; k < smd->domain->amplify; k++)
-                                                                                                       {
-                                                                                                               index = smoke_get_index(smd->domain->amplify * cell[0] + i, bigres[0], smd->domain->amplify * cell[1] + j, bigres[1], smd->domain->amplify * cell[2] + k);
-                                                                                                               bigdensity[index] = 0.f;
 +                                                                                                      }
 +                                                                              }
 +                                                                      }
+                                       else
+                                       {
+                                               /*
+                                               for()
+                                               {
+                                                       // no psys
+                                                       BVHTreeNearest nearest;
+                                                       nearest.index = -1;
+                                                       nearest.dist = FLT_MAX;
+                                                       BLI_bvhtree_find_nearest(sfs->bvh->tree, pco, &nearest, sfs->bvh->nearest_callback, sfs->bvh);
+                                               }*/
 -                                      }
 -                              }       
 -                      }
 +                                                              }
 +                                                      }       
 +                                              }       
-                                       }
  
-                                       if(cnt_domain && !sds->fluid_group)
+                       if(sds->fluid_group)
 -                              go = go->next;
 -                      else
 -                              base= base->next;
 -              }
 -      }
 +                                              go = go->next;
 +                                      else
 +                                              base= base->next;
 +                              }
 +                      }
  
 -      // do effectors
 -      /*
 -      if(sds->eff_group)
 -      {
 -              for(go = sds->eff_group->gobject.first; go; go = go->next) 
 -              {
 -                      if(go->ob)
 +                      // do effectors
 +                      /*
 +                      if(sds->eff_group)
                        {
 -                              if(ob->pd)
 +                              for(go = sds->eff_group->gobject.first; go; go = go->next) 
                                {
 -                                      
 +                                      if(go->ob)
 +                                      {
 +                                              if(ob->pd)
 +                                              {
 +                                                      
 +                                              }
 +                                      }
                                }
                        }
 -              }
 -      }
 -      */
 +                      */
  
 -      // do collisions        
 +                      // do collisions        
-                       if(sds->coll_group || !cnt_domain)
+       if(1)
 -      {
 -              Object *otherobj = NULL;
 -              ModifierData *md = NULL;
 +                      {
 +                              Object *otherobj = NULL;
 +                              ModifierData *md = NULL;
  
-                               if(cnt_domain && !sds->coll_group) // we use groups since we have 2 domains
+               if(sds->coll_group) // we use groups since we have 2 domains
 -                      go = sds->coll_group->gobject.first;
 -              else
 -                      base = scene->base.first;
 +                                      go = sds->coll_group->gobject.first;
 +                              else
 +                                      base = scene->base.first;
  
 -              while(base || go)
 -              {
 -                      otherobj = NULL;
 +                              while(base || go)
 +                              {
 +                                      otherobj = NULL;
  
-                                       if(cnt_domain && !sds->coll_group) 
+                       if(sds->coll_group) 
 -                      {
 -                              if(go->ob)
 -                                      otherobj = go->ob;
 -                      }
 -                      else
 -                              otherobj = base->object;
 +                                      {
 +                                              if(go->ob)
 +                                                      otherobj = go->ob;
 +                                      }
 +                                      else
 +                                              otherobj = base->object;
  
 -                      if(!otherobj)
 -                      {
 +                                      if(!otherobj)
 +                                      {
-                                               if(cnt_domain && !sds->coll_group)
+                               if(sds->coll_group)
 -                                      go = go->next;
 -                              else
 -                                      base= base->next;
 +                                                      go = go->next;
 +                                              else
 +                                                      base= base->next;
  
 -                              continue;
 -                      }
 -      
 -                      md = modifiers_findByType(otherobj, eModifierType_Smoke);
 +                                              continue;
 +                                      }
                        
 -                      // check for active smoke modifier
 -                      if(md && md->mode & (eModifierMode_Realtime | eModifierMode_Render))
 -                      {
 -                              SmokeModifierData *smd2 = (SmokeModifierData *)md;
 +                                      md = modifiers_findByType(otherobj, eModifierType_Smoke);
 +                                      
 +                                      // check for active smoke modifier
 +                                      if(md && md->mode & (eModifierMode_Realtime | eModifierMode_Render))
 +                                      {
 +                                              SmokeModifierData *smd2 = (SmokeModifierData *)md;
  
 -                              if((smd2->type & MOD_SMOKE_TYPE_COLL) && smd2->coll)
 -                              {
 -                                      // we got nice collision object
 -                                      SmokeCollSettings *scs = smd2->coll;
 -                                      size_t i, j;
 -                                      unsigned char *obstacles = smoke_get_obstacle(smd->domain->fluid);
 +                                              if((smd2->type & MOD_SMOKE_TYPE_COLL) && smd2->coll)
 +                                              {
 +                                                      // we got nice collision object
 +                                                      SmokeCollSettings *scs = smd2->coll;
-                                                       int cell[3];
-                                                       size_t index = 0;
 +                                                      size_t i, j;
 +                                                      unsigned char *obstacles = smoke_get_obstacle(smd->domain->fluid);
  
-                                                       // int BLI_bvhtree_find_nearest(BVHTree *tree, const float *co, BVHTreeNearest *nearest, BVHTree_NearestPointCallback callback, void *userdata);
 -                                      for(i = 0; i < scs->numpoints; i++)
 -                                      {
 -                                              int badcell = 0;
 +                                                      for(i = 0; i < scs->numpoints; i++)
 +                                                      {
 +                                                              int badcell = 0;
+                                               size_t index = 0;
+                                               int cell[3];
  
 -                                              // 1. get corresponding cell
 +                                                              // 1. get corresponding cell
-                                                               get_cell(smd, &scs->points[3 * i], cell, 0);
+                                               get_cell(sds->p0, sds->res, sds->dx, &scs->points[3 * i], cell, 0);
 -                                      
 -                                              // check if cell is valid (in the domain boundary)
 -                                              for(j = 0; j < 3; j++)
 -                                                      if((cell[j] > sds->res[j] - 1) || (cell[j] < 0))
 -                                                      {
 -                                                              badcell = 1;
 -                                                              break;
 -                                                      }
 -                                                              
 -                                              if(badcell)
 -                                                      continue;
 -
 -                                              // 2. set cell values (heat, density and velocity)
 -                                              index = smoke_get_index(cell[0], sds->res[0], cell[1], sds->res[1], cell[2]);
 -                                              
 -                                              // printf("cell[0]: %d, cell[1]: %d, cell[2]: %d\n", cell[0], cell[1], cell[2]);
 -                                              // printf("res[0]: %d, res[1]: %d, res[2]: %d, index: %d\n\n", sds->res[0], sds->res[1], sds->res[2], index);
                                                        
 -                                              obstacles[index] = 1;
 +                                                              // check if cell is valid (in the domain boundary)
 +                                                              for(j = 0; j < 3; j++)
 +                                                                      if((cell[j] > sds->res[j] - 1) || (cell[j] < 0))
 +                                                                      {
 +                                                                              badcell = 1;
 +                                                                              break;
 +                                                                      }
 +                                                                              
 +                                                              if(badcell)
 +                                                                      continue;
 +
 +                                                              // 2. set cell values (heat, density and velocity)
 +                                                              index = smoke_get_index(cell[0], sds->res[0], cell[1], sds->res[1], cell[2]);
 +                                                              
 +                                                              // printf("cell[0]: %d, cell[1]: %d, cell[2]: %d\n", cell[0], cell[1], cell[2]);
 +                                                              // printf("res[0]: %d, res[1]: %d, res[2]: %d, index: %d\n\n", sds->res[0], sds->res[1], sds->res[2], index);
 +                                                                      
 +                                                              obstacles[index] = 1;
  
 -                                              // for moving gobstacles
 -                                              /*
 -                                              const LbmFloat maxVelVal = 0.1666;
 -                                              const LbmFloat maxusqr = maxVelVal*maxVelVal*3. *1.5;
 -
 -                                              LbmVec objvel = vec2L((mMOIVertices[n]-mMOIVerticesOld[n]) /dvec); { 
 -                                              const LbmFloat usqr = (objvel[0]*objvel[0]+objvel[1]*objvel[1]+objvel[2]*objvel[2])*1.5; 
 -                                              USQRMAXCHECK(usqr, objvel[0],objvel[1],objvel[2], mMaxVlen, mMxvx,mMxvy,mMxvz); 
 -                                              if(usqr>maxusqr) { 
 -                                                      // cutoff at maxVelVal 
 -                                                      for(int jj=0; jj<3; jj++) { 
 -                                                              if(objvel[jj]>0.) objvel[jj] =  maxVelVal;  
 -                                                              if(objvel[jj]<0.) objvel[jj] = -maxVelVal; 
 -                                                      } 
 -                                              } } 
 -
 -                                              const LbmFloat dp=dot(objvel, vec2L((*pNormals)[n]) ); 
 -                                              const LbmVec oldov=objvel; // debug
 -                                              objvel = vec2L((*pNormals)[n]) *dp;
 -                                              */
 +                                                              // for moving gobstacles
 +                                                              /*
 +                                                              const LbmFloat maxVelVal = 0.1666;
 +                                                              const LbmFloat maxusqr = maxVelVal*maxVelVal*3. *1.5;
 +
 +                                                              LbmVec objvel = vec2L((mMOIVertices[n]-mMOIVerticesOld[n]) /dvec); { 
 +                                                              const LbmFloat usqr = (objvel[0]*objvel[0]+objvel[1]*objvel[1]+objvel[2]*objvel[2])*1.5; 
 +                                                              USQRMAXCHECK(usqr, objvel[0],objvel[1],objvel[2], mMaxVlen, mMxvx,mMxvy,mMxvz); 
 +                                                              if(usqr>maxusqr) { 
 +                                                                      // cutoff at maxVelVal 
 +                                                                      for(int jj=0; jj<3; jj++) { 
 +                                                                              if(objvel[jj]>0.) objvel[jj] =  maxVelVal;  
 +                                                                              if(objvel[jj]<0.) objvel[jj] = -maxVelVal; 
 +                                                                      } 
 +                                                              } } 
 +
 +                                                              const LbmFloat dp=dot(objvel, vec2L((*pNormals)[n]) ); 
 +                                                              const LbmVec oldov=objvel; // debug
 +                                                              objvel = vec2L((*pNormals)[n]) *dp;
 +                                                              */
 +                                                      }
 +                                              }
                                        }
 -                              }
 -                      }
  
-                                       if(cnt_domain && !sds->coll_group)
+                       if(sds->coll_group)
 -                              go = go->next;
 -                      else
 -                              base= base->next;
 -              }
 -      }
 -      
 -      // set new time
 -      smd->time = scene->r.cfra;
 +                                              go = go->next;
 +                                      else
 +                                              base= base->next;
 +                              }
 +                      }
 +                      
 +                      // set new time
 +                      smd->time = scene->r.cfra;
  
 -      // simulate the actual smoke (c++ code in intern/smoke)
 +                      // simulate the actual smoke (c++ code in intern/smoke)
-                       smoke_step(sds->fluid);
-                       tend();
-                       printf ( "Frame: %d, Time: %f\n", (int)smd->time, ( float ) tval() );
+       smoke_step(sds->fluid, smd->time);
 -}
 +              }
-               else if(scene->r.cfra < smd->time)
+ static int calc_voxel_transp(float *input, int res[3], int *pixel, float *tRay)
 -{
 +              {
-                       // we got back in time, reset smoke in this case (TODO: use cache later)
-                       smd->time = scene->r.cfra;
-                       smokeModifier_reset(smd);
+       const size_t index = smoke_get_index(pixel[0], res[0], pixel[1], res[1], pixel[2]);
+       // T_ray *= T_vox
+       *tRay *= input[index*4];
+       return *tRay;
 -}
 +              }
-       }
- }
+ // forward decleration
+ void smoke_calc_transparency(float *result, float *p0, float *p1, int res[3], float dx, float *light, bresenham_callback cb);
  
  // update necessary information for 3dview
- void smoke_prepare_View(SmokeModifierData *smd, float *light)
+ void smoke_prepare_View(SmokeModifierData *smd, float framenr, float *light, int have_light)
  {
        float *density = NULL;
        int x, y, z;
        // update 3dview
        density = smoke_get_density(smd->domain->fluid);
        for(x = 0; x < smd->domain->res[0]; x++)
 -              for(y = 0; y < smd->domain->res[1]; y++)
 -                      for(z = 0; z < smd->domain->res[2]; z++)
 -                      {
 -                              size_t index;
 +                      for(y = 0; y < smd->domain->res[1]; y++)
 +                              for(z = 0; z < smd->domain->res[2]; z++)
 +                              {
 +                                      size_t index;
  
 -                              index = smoke_get_index(x, smd->domain->res[0], y, smd->domain->res[1], z);
 -                              // Transparency computation
 -                              // formula taken from "Visual Simulation of Smoke" / Fedkiw et al. pg. 4
 -                              // T_vox = exp(-C_ext * h)
 -                              // C_ext/sigma_t = density * C_ext
 +                                      index = smoke_get_index(x, smd->domain->res[0], y, smd->domain->res[1], z);
 +                                      // Transparency computation
 +                                      // formula taken from "Visual Simulation of Smoke" / Fedkiw et al. pg. 4
 +                                      // T_vox = exp(-C_ext * h)
 +                                      // C_ext/sigma_t = density * C_ext
-                                       smoke_set_tvox(smd, index, exp(-density[index] * 7.0 * smd->domain->dx));
-       }
-       smoke_calc_transparency(smd, light, 0);
- }
+                               smd->domain->view3d[index * 4] = smd->domain->view3d[index * 4 + 1] = 
+                               smd->domain->view3d[index * 4 + 2] = exp(-density[index] * 7.0 * smd->domain->dx);
+                               smd->domain->view3d[index * 4 + 3] = 1.0 - smd->domain->view3d[index * 4];
 -                              
 -                      }
 +
- // update necessary information for 3dview ("high res" option)
- void smoke_prepare_bigView(SmokeModifierData *smd, float *light)
- {
-       float *density = NULL;
-       size_t i = 0;
-       int bigres[3];
-       smoke_get_bigres(smd->domain->fluid, bigres);
-       if(!smd->domain->traybig)
-       {
-               // TRay is for self shadowing
-               smd->domain->traybig = MEM_callocN(sizeof(float)*bigres[0]*bigres[1]*bigres[2], "Smoke_tRayBig");
-       }
-       if(!smd->domain->tvoxbig)
-       {
-               // TVox is for tranaparency
-               smd->domain->tvoxbig = MEM_callocN(sizeof(float)*bigres[0]*bigres[1]*bigres[2], "Smoke_tVoxBig");
 +      }
  
-       density = smoke_get_bigdensity(smd->domain->fluid);
-       for (i = 0; i < bigres[0] * bigres[1] * bigres[2]; i++)
+       if(have_light)
        {
-               // Transparency computation
-               // formula taken from "Visual Simulation of Smoke" / Fedkiw et al. pg. 4
-               // T_vox = exp(-C_ext * h)
-               // C_ext/sigma_t = density * C_ext
-               smoke_set_bigtvox(smd, i, exp(-density[i] * 7.0 * smd->domain->dx / smd->domain->amplify) );
-       }
-       smoke_calc_transparency(smd, light, 1);
- }
+               smoke_calc_transparency(sds->view3d, sds->p0, sds->p1, sds->res, sds->dx, light, calc_voxel_transp);
  
- float smoke_get_tvox(SmokeModifierData *smd, size_t index)
+               cells = smd->domain->res[0]*smd->domain->res[1]*smd->domain->res[2];
+               for(i = 0; i < cells; i++)
 -              {
 +{
-       return smd->domain->tvox[index];
+                       smd->domain->view3d[i * 4] = smd->domain->view3d[i * 4 + 1] = 
+                       smd->domain->view3d[i * 4 + 2] = smd->domain->view3d[i * 4 + 1] * smd->domain->view3d[i * 4 + 0];
 -              }
 -      }
 +}
- void smoke_set_tvox(SmokeModifierData *smd, size_t index, float tvox)
- {
-       smd->domain->tvox[index] = tvox;
 +}
- float smoke_get_tray(SmokeModifierData *smd, size_t index)
- {
-       return smd->domain->tray[index];
- }
- void smoke_set_tray(SmokeModifierData *smd, size_t index, float transparency)
- {
-       smd->domain->tray[index] = transparency;
- }
- float smoke_get_bigtvox(SmokeModifierData *smd, size_t index)
- {
-       return smd->domain->tvoxbig[index];
- }
- void smoke_set_bigtvox(SmokeModifierData *smd, size_t index, float tvox)
- {
-       smd->domain->tvoxbig[index] = tvox;
- }
- float smoke_get_bigtray(SmokeModifierData *smd, size_t index)
- {
-       return smd->domain->traybig[index];
- }
- void smoke_set_bigtray(SmokeModifierData *smd, size_t index, float transparency)
- {
-       smd->domain->traybig[index] = transparency;
+       smd->domain->v3dnum = framenr;
  }
  
  long long smoke_get_mem_req(int xres, int yres, int zres, int amplify)
Simple merge
index cd14b5c1e3344fdfaae293e6ae64ca30efcfa363,0000000000000000000000000000000000000000..8eeb209ad63e4188725883fd50cc3be827619740
mode 100644,000000..100644
--- /dev/null
@@@ -1,2065 -1,0 +1,2074 @@@
-               if(FACESEL_PAINT_TEST);
 +/**
 + * $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) 2004 Blender Foundation.
 + * All rights reserved.
 + *
 + * The Original Code is: all of this file.
 + *
 + * Contributor(s): none yet.
 + *
 + * ***** END GPL LICENSE BLOCK *****
 + */
 +
 +/*
 +
 +BMEditMesh_mods.c, UI level access, no geometry changes 
 +
 +*/
 +
 +#include <stdlib.h>
 +#include <string.h>
 +#include <math.h>
 +
 +#include "MEM_guardedalloc.h"
 +
 +#include "MTC_matrixops.h"
 +
 +#include "DNA_mesh_types.h"
 +#include "DNA_material_types.h"
 +#include "DNA_meshdata_types.h"
 +#include "DNA_modifier_types.h"
 +#include "DNA_object_types.h"
 +#include "DNA_texture_types.h"
 +#include "DNA_scene_types.h"
 +#include "DNA_screen_types.h"
 +#include "DNA_space_types.h"
 +#include "DNA_view3d_types.h"
 +
 +#include "BLI_blenlib.h"
 +#include "BLI_arithb.h"
 +#include "BLI_rand.h"
 +
 +#include "BKE_context.h"
 +#include "BKE_displist.h"
 +#include "BKE_depsgraph.h"
 +#include "BKE_DerivedMesh.h"
 +#include "BKE_customdata.h"
 +#include "BKE_global.h"
 +#include "BKE_mesh.h"
 +#include "BKE_material.h"
 +#include "BKE_texture.h"
 +#include "BKE_utildefines.h"
 +#include "BKE_report.h"
 +#include "BKE_tessmesh.h"
 +
 +#include "IMB_imbuf_types.h"
 +#include "IMB_imbuf.h"
 +
 +#include "RE_render_ext.h"  /* externtex */
 +
 +#include "WM_api.h"
 +#include "WM_types.h"
 +
 +#include "RNA_access.h"
 +#include "RNA_define.h"
 +
 +#include "ED_mesh.h"
 +#include "ED_screen.h"
 +#include "ED_view3d.h"
 +#include "bmesh.h"
 +
 +#include "BIF_gl.h"
 +#include "BIF_glutil.h"
 +
 +#include "mesh_intern.h"
 +
 +#include "BLO_sys_types.h" // for intptr_t support
 +
 +/* XXX */
 +static void waitcursor() {}
 +static int pupmenu() {return 0;}
 +
 +/* ****************************** MIRROR **************** */
 +
 +void EDBM_select_mirrored(Object *obedit, BMEditMesh *em)
 +{
 +#if 0 //BMESH_TODO
 +      if(em->selectmode & SCE_SELECT_VERTEX) {
 +              BMVert *eve, *v1;
 +              
 +              for(eve= em->verts.first; eve; eve= eve->next) {
 +                      if(eve->f & SELECT) {
 +                              v1= BMEditMesh_get_x_mirror_vert(obedit, em, eve->co);
 +                              if(v1) {
 +                                      eve->f &= ~SELECT;
 +                                      v1->f |= SELECT;
 +                              }
 +                      }
 +              }
 +      }
 +#endif
 +}
 +
 +void EDBM_automerge(int update) 
 +{
 +// XXX        int len;
 +      
 +//    if ((scene->automerge) &&
 +//            (obedit && obedit->type==OB_MESH) &&
 +//            (((Mesh*)obedit->data)->mr==NULL)
 +//      ) {
 +//            len = removedoublesflag(1, 1, scene->toolsettings->doublimit);
 +//            if (len) {
 +//                    em->totvert -= len; /* saves doing a countall */
 +//                    if (update) {
 +//                            DAG_object_flush_update(scene, obedit, OB_RECALC_DATA);
 +//                    }
 +//            }
 +//    }
 +}
 +
 +/* ****************************** SELECTION ROUTINES **************** */
 +
 +unsigned int bm_solidoffs=0, bm_wireoffs=0, bm_vertoffs=0;    /* set in drawobject.c ... for colorindices */
 +
 +/* facilities for border select and circle select */
 +static char *selbuf= NULL;
 +
 +/* opengl doesn't support concave... */
 +static void draw_triangulated(short mcords[][2], short tot)
 +{
 +      ListBase lb={NULL, NULL};
 +      DispList *dl;
 +      float *fp;
 +      int a;
 +      
 +      /* make displist */
 +      dl= MEM_callocN(sizeof(DispList), "poly disp");
 +      dl->type= DL_POLY;
 +      dl->parts= 1;
 +      dl->nr= tot;
 +      dl->verts= fp=  MEM_callocN(tot*3*sizeof(float), "poly verts");
 +      BLI_addtail(&lb, dl);
 +      
 +      for(a=0; a<tot; a++, fp+=3) {
 +              fp[0]= (float)mcords[a][0];
 +              fp[1]= (float)mcords[a][1];
 +      }
 +      
 +      /* do the fill */
 +      filldisplist(&lb, &lb);
 +
 +      /* do the draw */
 +      dl= lb.first;   /* filldisplist adds in head of list */
 +      if(dl->type==DL_INDEX3) {
 +              int *index;
 +              
 +              a= dl->parts;
 +              fp= dl->verts;
 +              index= dl->index;
 +              glBegin(GL_TRIANGLES);
 +              while(a--) {
 +                      glVertex3fv(fp+3*index[0]);
 +                      glVertex3fv(fp+3*index[1]);
 +                      glVertex3fv(fp+3*index[2]);
 +                      index+= 3;
 +              }
 +              glEnd();
 +      }
 +      
 +      freedisplist(&lb);
 +}
 +
 +
 +/* reads rect, and builds selection array for quick lookup */
 +/* returns if all is OK */
 +int EDBM_init_backbuf_border(ViewContext *vc, short xmin, short ymin, short xmax, short ymax)
 +{
 +      struct ImBuf *buf;
 +      unsigned int *dr;
 +      int a;
 +      
 +      if(vc->obedit==NULL || vc->v3d->drawtype<OB_SOLID || (vc->v3d->flag & V3D_ZBUF_SELECT)==0) return 0;
 +      
 +      buf= view3d_read_backbuf(vc, xmin, ymin, xmax, ymax);
 +      if(buf==NULL) return 0;
 +      if(bm_vertoffs==0) return 0;
 +
 +      dr = buf->rect;
 +      
 +      /* build selection lookup */
 +      selbuf= MEM_callocN(bm_vertoffs+1, "selbuf");
 +      
 +      a= (xmax-xmin+1)*(ymax-ymin+1);
 +      while(a--) {
 +              if(*dr>0 && *dr<=bm_vertoffs) 
 +                      selbuf[*dr]= 1;
 +              dr++;
 +      }
 +      IMB_freeImBuf(buf);
 +      return 1;
 +}
 +
 +int EDBM_check_backbuf(unsigned int index)
 +{
 +      if(selbuf==NULL) return 1;
 +      if(index>0 && index<=bm_vertoffs)
 +              return selbuf[index];
 +      return 0;
 +}
 +
 +void EDBM_free_backbuf(void)
 +{
 +      if(selbuf) MEM_freeN(selbuf);
 +      selbuf= NULL;
 +}
 +
 +/* mcords is a polygon mask
 +   - grab backbuffer,
 +   - draw with black in backbuffer, 
 +   - grab again and compare
 +   returns 'OK' 
 +*/
 +int EDBM_mask_init_backbuf_border(ViewContext *vc, short mcords[][2], short tot, short xmin, short ymin, short xmax, short ymax)
 +{
 +      unsigned int *dr, *drm;
 +      struct ImBuf *buf, *bufmask;
 +      int a;
 +      
 +      /* method in use for face selecting too */
 +      if(vc->obedit==NULL) {
-               if(FACESEL_PAINT_TEST);
++              if(paint_facesel_test(vc->obact));
 +              else return 0;
 +      }
 +      else if(vc->v3d->drawtype<OB_SOLID || (vc->v3d->flag & V3D_ZBUF_SELECT)==0) return 0;
 +
 +      buf= view3d_read_backbuf(vc, xmin, ymin, xmax, ymax);
 +      if(buf==NULL) return 0;
 +      if(bm_vertoffs==0) return 0;
 +
 +      dr = buf->rect;
 +
 +      /* draw the mask */
 +      glDisable(GL_DEPTH_TEST);
 +      
 +      glColor3ub(0, 0, 0);
 +      
 +      /* yah, opengl doesn't do concave... tsk! */
 +      ED_region_pixelspace(vc->ar);
 +      draw_triangulated(mcords, tot); 
 +      
 +      glBegin(GL_LINE_LOOP);  /* for zero sized masks, lines */
 +      for(a=0; a<tot; a++) glVertex2s(mcords[a][0], mcords[a][1]);
 +      glEnd();
 +      
 +      glFinish();     /* to be sure readpixels sees mask */
 +      
 +      /* grab mask */
 +      bufmask= view3d_read_backbuf(vc, xmin, ymin, xmax, ymax);
 +      drm = bufmask->rect;
 +      if(bufmask==NULL) return 0; /* only when mem alloc fails, go crash somewhere else! */
 +      
 +      /* build selection lookup */
 +      selbuf= MEM_callocN(bm_vertoffs+1, "selbuf");
 +      
 +      a= (xmax-xmin+1)*(ymax-ymin+1);
 +      while(a--) {
 +              if(*dr>0 && *dr<=bm_vertoffs && *drm==0) selbuf[*dr]= 1;
 +              dr++; drm++;
 +      }
 +      IMB_freeImBuf(buf);
 +      IMB_freeImBuf(bufmask);
 +      return 1;
 +      
 +}
 +
 +/* circle shaped sample area */
 +int EDBM_init_backbuf_circle(ViewContext *vc, short xs, short ys, short rads)
 +{
 +      struct ImBuf *buf;
 +      unsigned int *dr;
 +      short xmin, ymin, xmax, ymax, xc, yc;
 +      int radsq;
 +      
 +      /* method in use for face selecting too */
 +      if(vc->obedit==NULL) {
-               BMIter iter;
++              if(paint_facesel_test(vc->obact));
 +              else return 0;
 +      }
 +      else if(vc->v3d->drawtype<OB_SOLID || (vc->v3d->flag & V3D_ZBUF_SELECT)==0) return 0;
 +      
 +      xmin= xs-rads; xmax= xs+rads;
 +      ymin= ys-rads; ymax= ys+rads;
 +      buf= view3d_read_backbuf(vc, xmin, ymin, xmax, ymax);
 +      if(bm_vertoffs==0) return 0;
 +      if(buf==NULL) return 0;
 +
 +      dr = buf->rect;
 +      
 +      /* build selection lookup */
 +      selbuf= MEM_callocN(bm_vertoffs+1, "selbuf");
 +      radsq= rads*rads;
 +      for(yc= -rads; yc<=rads; yc++) {
 +              for(xc= -rads; xc<=rads; xc++, dr++) {
 +                      if(xc*xc + yc*yc < radsq) {
 +                              if(*dr>0 && *dr<=bm_vertoffs) selbuf[*dr]= 1;
 +                      }
 +              }
 +      }
 +
 +      IMB_freeImBuf(buf);
 +      return 1;
 +      
 +}
 +
 +static void findnearestvert__doClosest(void *userData, BMVert *eve, int x, int y, int index)
 +{
 +      struct { short mval[2], pass, select, strict; int dist, lastIndex, closestIndex; BMVert *closest; } *data = userData;
 +
 +      if (data->pass==0) {
 +              if (index<=data->lastIndex)
 +                      return;
 +      } else {
 +              if (index>data->lastIndex)
 +                      return;
 +      }
 +
 +      if (data->dist>3) {
 +              int temp = abs(data->mval[0] - x) + abs(data->mval[1]- y);
 +              if (BM_TestHFlag(eve, BM_SELECT) == data->select) {
 +                      if (data->strict == 1)
 +                              return;
 +                      else
 +                              temp += 5;
 +              }
 +
 +              if (temp<data->dist) {
 +                      data->dist = temp;
 +                      data->closest = eve;
 +                      data->closestIndex = index;
 +              }
 +      }
 +}
 +
 +
 +
 +
 +static unsigned int findnearestvert__backbufIndextest(void *handle, unsigned int index)
 +{
 +      BMEditMesh *em= (BMEditMesh *)handle;
 +      BMIter iter;
 +      BMVert *eve = BMIter_AtIndex(em->bm, BM_VERTS_OF_MESH, NULL, index-1);
 +
 +      if(eve && BM_TestHFlag(eve, BM_SELECT)) return 0;
 +      return 1; 
 +}
 +/**
 + * findnearestvert
 + * 
 + * dist (in/out): minimal distance to the nearest and at the end, actual distance
 + * sel: selection bias
 + *            if SELECT, selected vertice are given a 5 pixel bias to make them farter than unselect verts
 + *            if 0, unselected vertice are given the bias
 + * strict: if 1, the vertice corresponding to the sel parameter are ignored and not just biased 
 + */
 +BMVert *EDBM_findnearestvert(ViewContext *vc, int *dist, short sel, short strict)
 +{
 +      if(vc->v3d->drawtype>OB_WIRE && (vc->v3d->flag & V3D_ZBUF_SELECT)){
 +              int distance;
 +              unsigned int index;
 +              BMVert *eve;
 +              
 +              if(strict) index = view3d_sample_backbuf_rect(vc, vc->mval, 50, bm_wireoffs, 0xFFFFFF, &distance, strict, vc->em, findnearestvert__backbufIndextest); 
 +              else index = view3d_sample_backbuf_rect(vc, vc->mval, 50, bm_wireoffs, 0xFFFFFF, &distance, 0, NULL, NULL); 
 +              
 +              eve = BMIter_AtIndex(vc->em->bm, BM_VERTS_OF_MESH, NULL, index-1);
 +              
 +              if(eve && distance < *dist) {
 +                      *dist = distance;
 +                      return eve;
 +              } else {
 +                      return NULL;
 +              }
 +                      
 +      }
 +      else {
 +              struct { short mval[2], pass, select, strict; int dist, lastIndex, closestIndex; BMVert *closest; } data;
 +              static int lastSelectedIndex=0;
 +              static BMVert *lastSelected=NULL;
 +              
 +              if (lastSelected && BMIter_AtIndex(vc->em->bm, BM_VERTS_OF_MESH, NULL, lastSelectedIndex)!=lastSelected) {
 +                      lastSelectedIndex = 0;
 +                      lastSelected = NULL;
 +              }
 +
 +              data.lastIndex = lastSelectedIndex;
 +              data.mval[0] = vc->mval[0];
 +              data.mval[1] = vc->mval[1];
 +              data.select = sel;
 +              data.dist = *dist;
 +              data.strict = strict;
 +              data.closest = NULL;
 +              data.closestIndex = 0;
 +
 +              data.pass = 0;
 +              mesh_foreachScreenVert(vc, findnearestvert__doClosest, &data, 1);
 +
 +              if (data.dist>3) {
 +                      data.pass = 1;
 +                      mesh_foreachScreenVert(vc, findnearestvert__doClosest, &data, 1);
 +              }
 +
 +              *dist = data.dist;
 +              lastSelected = data.closest;
 +              lastSelectedIndex = data.closestIndex;
 +
 +              return data.closest;
 +      }
 +}
 +
 +/* returns labda for closest distance v1 to line-piece v2-v3 */
 +static float labda_PdistVL2Dfl( float *v1, float *v2, float *v3) 
 +{
 +      float rc[2], len;
 +      
 +      rc[0]= v3[0]-v2[0];
 +      rc[1]= v3[1]-v2[1];
 +      len= rc[0]*rc[0]+ rc[1]*rc[1];
 +      if(len==0.0f)
 +              return 0.0f;
 +      
 +      return ( rc[0]*(v1[0]-v2[0]) + rc[1]*(v1[1]-v2[1]) )/len;
 +}
 +
 +/* note; uses v3d, so needs active 3d window */
 +static void findnearestedge__doClosest(void *userData, BMEdge *eed, int x0, int y0, int x1, int y1, int index)
 +{
 +      struct { ViewContext vc; float mval[2]; int dist; BMEdge *closest; } *data = userData;
 +      float v1[2], v2[2];
 +      int distance;
 +              
 +      v1[0] = x0;
 +      v1[1] = y0;
 +      v2[0] = x1;
 +      v2[1] = y1;
 +              
 +      distance= PdistVL2Dfl(data->mval, v1, v2);
 +              
 +      if(BM_TestHFlag(eed, BM_SELECT)) distance+=5;
 +      if(distance < data->dist) {
 +              if(data->vc.rv3d->rflag & RV3D_CLIPPING) {
 +                      float labda= labda_PdistVL2Dfl(data->mval, v1, v2);
 +                      float vec[3];
 +
 +                      vec[0]= eed->v1->co[0] + labda*(eed->v2->co[0] - eed->v1->co[0]);
 +                      vec[1]= eed->v1->co[1] + labda*(eed->v2->co[1] - eed->v1->co[1]);
 +                      vec[2]= eed->v1->co[2] + labda*(eed->v2->co[2] - eed->v1->co[2]);
 +                      Mat4MulVecfl(data->vc.obedit->obmat, vec);
 +
 +                      if(view3d_test_clipping(data->vc.rv3d, vec)==0) {
 +                              data->dist = distance;
 +                              data->closest = eed;
 +                      }
 +              }
 +              else {
 +                      data->dist = distance;
 +                      data->closest = eed;
 +              }
 +      }
 +}
 +BMEdge *EDBM_findnearestedge(ViewContext *vc, int *dist)
 +{
 +
 +      if(vc->v3d->drawtype>OB_WIRE && (vc->v3d->flag & V3D_ZBUF_SELECT)) {
 +              int distance;
 +              unsigned int index = view3d_sample_backbuf_rect(vc, vc->mval, 50, bm_solidoffs, bm_wireoffs, &distance,0, NULL, NULL);
 +              BMEdge *eed = BMIter_AtIndex(vc->em->bm, BM_EDGES_OF_MESH, NULL, index-1);
 +
 +              if (eed && distance<*dist) {
 +                      *dist = distance;
 +                      return eed;
 +              } else {
 +                      return NULL;
 +              }
 +      }
 +      else {
 +              struct { ViewContext vc; float mval[2]; int dist; BMEdge *closest; } data;
 +
 +              data.vc= *vc;
 +              data.mval[0] = vc->mval[0];
 +              data.mval[1] = vc->mval[1];
 +              data.dist = *dist;
 +              data.closest = NULL;
 +
 +              mesh_foreachScreenEdge(vc, findnearestedge__doClosest, &data, 2);
 +
 +              *dist = data.dist;
 +              return data.closest;
 +      }
 +}
 +
 +static void findnearestface__getDistance(void *userData, BMFace *efa, int x, int y, int index)
 +{
 +      struct { short mval[2]; int dist; BMFace *toFace; } *data = userData;
 +
 +      if (efa==data->toFace) {
 +              int temp = abs(data->mval[0]-x) + abs(data->mval[1]-y);
 +
 +              if (temp<data->dist)
 +                      data->dist = temp;
 +      }
 +}
 +static void findnearestface__doClosest(void *userData, BMFace *efa, int x, int y, int index)
 +{
 +      struct { short mval[2], pass; int dist, lastIndex, closestIndex; BMFace *closest; } *data = userData;
 +
 +      if (data->pass==0) {
 +              if (index<=data->lastIndex)
 +                      return;
 +      } else {
 +              if (index>data->lastIndex)
 +                      return;
 +      }
 +
 +      if (data->dist>3) {
 +              int temp = abs(data->mval[0]-x) + abs(data->mval[1]-y);
 +
 +              if (temp<data->dist) {
 +                      data->dist = temp;
 +                      data->closest = efa;
 +                      data->closestIndex = index;
 +              }
 +      }
 +}
 +static BMFace *EDBM_findnearestface(ViewContext *vc, int *dist)
 +{
 +
 +      if(vc->v3d->drawtype>OB_WIRE && (vc->v3d->flag & V3D_ZBUF_SELECT)) {
 +              unsigned int index = view3d_sample_backbuf(vc, vc->mval[0], vc->mval[1]);
 +              BMFace *efa = BMIter_AtIndex(vc->em->bm, BM_FACES_OF_MESH, NULL, index-1);
 +
 +              if (efa) {
 +                      struct { short mval[2]; int dist; BMFace *toFace; } data;
 +
 +                      data.mval[0] = vc->mval[0];
 +                      data.mval[1] = vc->mval[1];
 +                      data.dist = 0x7FFF;             /* largest short */
 +                      data.toFace = efa;
 +
 +                      mesh_foreachScreenFace(vc, findnearestface__getDistance, &data);
 +
 +                      if(vc->em->selectmode == SCE_SELECT_FACE || data.dist<*dist) {  /* only faces, no dist check */
 +                              *dist= data.dist;
 +                              return efa;
 +                      }
 +              }
 +              
 +              return NULL;
 +      }
 +      else {
 +              struct { short mval[2], pass; int dist, lastIndex, closestIndex; BMFace *closest; } data;
 +              static int lastSelectedIndex=0;
 +              static BMFace *lastSelected=NULL;
 +
 +              if (lastSelected && BMIter_AtIndex(vc->em->bm, BM_FACES_OF_MESH, NULL, lastSelectedIndex)!=lastSelected) {
 +                      lastSelectedIndex = 0;
 +                      lastSelected = NULL;
 +              }
 +
 +              data.lastIndex = lastSelectedIndex;
 +              data.mval[0] = vc->mval[0];
 +              data.mval[1] = vc->mval[1];
 +              data.dist = *dist;
 +              data.closest = NULL;
 +              data.closestIndex = 0;
 +
 +              data.pass = 0;
 +              mesh_foreachScreenFace(vc, findnearestface__doClosest, &data);
 +
 +              if (data.dist>3) {
 +                      data.pass = 1;
 +                      mesh_foreachScreenFace(vc, findnearestface__doClosest, &data);
 +              }
 +
 +              *dist = data.dist;
 +              lastSelected = data.closest;
 +              lastSelectedIndex = data.closestIndex;
 +
 +              return data.closest;
 +      }
 +}
 +
 +/* best distance based on screen coords. 
 +   use em->selectmode to define how to use 
 +   selected vertices and edges get disadvantage
 +   return 1 if found one
 +*/
 +static int unified_findnearest(ViewContext *vc, BMVert **eve, BMEdge **eed, BMFace **efa) 
 +{
 +      BMEditMesh *em= vc->em;
 +      int dist= 75;
 +      
 +      *eve= NULL;
 +      *eed= NULL;
 +      *efa= NULL;
 +      
 +      /* no afterqueue (yet), so we check it now, otherwise the em_xxxofs indices are bad */
 +      view3d_validate_backbuf(vc);
 +      
 +      if(em->selectmode & SCE_SELECT_VERTEX)
 +              *eve= EDBM_findnearestvert(vc, &dist, BM_SELECT, 0);
 +      if(em->selectmode & SCE_SELECT_FACE)
 +              *efa= EDBM_findnearestface(vc, &dist);
 +
 +      dist-= 20;      /* since edges select lines, we give dots advantage of 20 pix */
 +      if(em->selectmode & SCE_SELECT_EDGE)
 +              *eed= EDBM_findnearestedge(vc, &dist);
 +
 +      /* return only one of 3 pointers, for frontbuffer redraws */
 +      if(*eed) {
 +              *efa= NULL; *eve= NULL;
 +      }
 +      else if(*efa) {
 +              *eve= NULL;
 +      }
 +      
 +      return (*eve || *eed || *efa);
 +}
 +
 +/* ****************  SIMILAR "group" SELECTS. FACE, EDGE AND VERTEX ************** */
 +
 +/* selects new faces/edges/verts based on the existing selection */
 +
 +/* FACES GROUP */
 +
 +static EnumPropertyItem prop_simface_types[] = {
 +      {SIMFACE_MATERIAL, "MATERIAL", 0, "Material", ""},
 +      {SIMFACE_IMAGE, "IMAGE", 0, "Image", ""},
 +      {SIMFACE_AREA, "AREA", 0, "Area", ""},
 +      {SIMFACE_PERIMETER, "PERIMETER", 0, "Perimeter", ""},
 +      {SIMFACE_NORMAL, "NORMAL", 0, "Normal", ""},
 +      {SIMFACE_COPLANAR, "COPLANAR", 0, "Co-planar", ""},
 +      {0, NULL, 0, NULL, NULL}
 +};
 +
 +static int similar_face_select_exec(bContext *C, wmOperator *op)
 +{
 +      Scene *scene = CTX_data_scene(C);
 +      Object *ob = CTX_data_edit_object(C);
 +      BMEditMesh *em = ((Mesh*)ob->data)->edit_btmesh;
 +      BMOperator bmop;
 +
 +      /* get the type from RNA */
 +      int type = RNA_enum_get(op->ptr, "type");
 +
 +      float thresh = scene->toolsettings->select_thresh;
 +
 +      /* initialize the bmop using EDBM api, which does various ui error reporting and other stuff */
 +      EDBM_InitOpf(em, &bmop, op, "similarfaces faces=%hf type=%d thresh=%f", BM_SELECT, type, thresh);
 +
 +      /* execute the operator */
 +      BMO_Exec_Op(em->bm, &bmop);
 +
 +      /* clear the existing selection */
 +      EDBM_clear_flag_all(em, BM_SELECT);
 +
 +      /* select the output */
 +      BMO_HeaderFlag_Buffer(em->bm, &bmop, "faceout", BM_SELECT, BM_ALL);
 +
 +      /* finish the operator */
 +      if( !EDBM_FinishOp(em, &bmop, op, 1) )
 +              return OPERATOR_CANCELLED;
 +
 +      /* dependencies graph and notification stuff */
 +      DAG_object_flush_update(scene, ob, OB_RECALC_DATA);
 +      WM_event_add_notifier(C, NC_OBJECT | ND_GEOM_SELECT, ob);
 +
 +      /* we succeeded */
 +      return OPERATOR_FINISHED;
 +}     
 +
 +/* ***************************************************** */
 +
 +/* EDGE GROUP */
 +
 +#define SIMEDGE_LENGTH                101
 +#define SIMEDGE_DIR                   102
 +#define SIMEDGE_FACE          103
 +#define SIMEDGE_FACE_ANGLE    104
 +#define SIMEDGE_CREASE                105
 +#define SIMEDGE_SEAM          106
 +#define SIMEDGE_SHARP         107
 +
 +static EnumPropertyItem prop_simedge_types[] = {
 +      {SIMEDGE_LENGTH, "LENGTH", 0, "Length", ""},
 +      {SIMEDGE_DIR, "DIR", 0, "Direction", ""},
 +      {SIMEDGE_FACE, "FACE", 0, "Amount of Vertices in Face", ""},
 +      {SIMEDGE_FACE_ANGLE, "FACE_ANGLE", 0, "Face Angles", ""},
 +      {SIMEDGE_CREASE, "CREASE", 0, "Crease", ""},
 +      {SIMEDGE_SEAM, "SEAM", 0, "Seam", ""},
 +      {SIMEDGE_SHARP, "SHARP", 0, "Sharpness", ""},
 +      {0, NULL, 0, NULL, NULL}
 +};
 +
 +static int similar_edge_select__internal(Scene *scene, EditMesh *em, int mode)
 +{
 +#if 0
 +      EditEdge *eed, *base_eed=NULL;
 +      unsigned int selcount=0; /* count how many new edges we select*/
 +      
 +      /*count how many visible selected edges there are,
 +      so we can return when there are none left */
 +      unsigned int deselcount=0;
 +      
 +      short ok=0;
 +      float thresh= scene->toolsettings->select_thresh;
 +      
 +      for(eed= em->edges.first; eed; eed= eed->next) {
 +              if (!eed->h) {
 +                      if (eed->f & SELECT) {
 +                              eed->f1=1;
 +                              ok=1;
 +                      } else {
 +                              eed->f1=0;
 +                              deselcount++;
 +                      }
 +                      /* set all eed->tmp.l to 0 we use it later.
 +                      for counting face users*/
 +                      eed->tmp.l=0;
 +                      eed->f2=0; /* only for mode SIMEDGE_FACE_ANGLE, edge animations */
 +              }
 +      }
 +      
 +      if (!ok || !deselcount) /* no data selected OR no more data to select*/
 +              return 0;
 +      
 +      if (mode==SIMEDGE_LENGTH) { /*store length*/
 +              for(eed= em->edges.first; eed; eed= eed->next) {
 +                      if (!eed->h) /* dont calc data for hidden edges*/
 +                              eed->tmp.fp= VecLenf(eed->v1->co, eed->v2->co);
 +              }
 +      } else if (mode==SIMEDGE_FACE) { /*store face users*/
 +              EditFace *efa;
 +              /* cound how many faces each edge uses use tmp->l */
 +              for(efa= em->faces.first; efa; efa= efa->next) {
 +                      efa->e1->tmp.l++;
 +                      efa->e2->tmp.l++;
 +                      efa->e3->tmp.l++;
 +                      if (efa->e4) efa->e4->tmp.l++;
 +              }
 +      } else if (mode==SIMEDGE_FACE_ANGLE) { /*store edge angles */
 +              EditFace *efa;
 +              int j;
 +              /* cound how many faces each edge uses use tmp.l */
 +              for(efa= em->faces.first; efa; efa= efa->next) {
 +                      /* here we use the edges temp data to assign a face
 +                      if a face has alredy been assigned (eed->f2==1)
 +                      we calculate the angle between the current face and
 +                      the edges previously found face.
 +                      store the angle in eed->tmp.fp (loosing the face eed->tmp.f)
 +                      but tagging eed->f2==2, so we know not to look at it again.
 +                      This only works for edges that connect to 2 faces. but its good enough
 +                      */
 +                      
 +                      /* se we can loop through face edges*/
 +                      j=0;
 +                      eed= efa->e1;
 +                      while (j<4) {
 +                              if (j==1) eed= efa->e2;
 +                              else if (j==2) eed= efa->e3;
 +                              else if (j==3) {
 +                                      eed= efa->e4;
 +                                      if (!eed)
 +                                              break;
 +                              } /* done looping */
 +                              
 +                              if (!eed->h) { /* dont calc data for hidden edges*/
 +                                      if (eed->f2==2)
 +                                              break;
 +                                      else if (eed->f2==0) /* first access, assign the face */
 +                                              eed->tmp.f= efa;
 +                                      else if (eed->f2==1) /* second, we assign the angle*/
 +                                              eed->tmp.fp= VecAngle2(eed->tmp.f->n, efa->n)/180;
 +                                      eed->f2++; /* f2==0 no face assigned. f2==1 one face found. f2==2 angle calculated.*/
 +                              }
 +                              j++;
 +                      }
 +              }
 +      }
 +      
 +      for(base_eed= em->edges.first; base_eed; base_eed= base_eed->next) {
 +              if (base_eed->f1) {
 +                      if (mode==SIMEDGE_LENGTH) { /* same length */
 +                              for(eed= em->edges.first; eed; eed= eed->next) {
 +                                      if (
 +                                              !(eed->f & SELECT) &&
 +                                              !eed->h &&
 +                                              SCALE_CMP(base_eed->tmp.fp, eed->tmp.fp)
 +                                      ) {
 +                                              EM_select_edge(eed, 1);
 +                                              selcount++;
 +                                              deselcount--;
 +                                              if (!deselcount) /*have we selected all posible faces?, if so return*/
 +                                                      return selcount;
 +                                      }
 +                              }
 +                      } else if (mode==SIMEDGE_DIR) { /* same direction */
 +                              float base_dir[3], dir[3], angle;
 +                              VecSubf(base_dir, base_eed->v1->co, base_eed->v2->co);
 +                              for(eed= em->edges.first; eed; eed= eed->next) {
 +                                      if (!(eed->f & SELECT) && !eed->h) {
 +                                              VecSubf(dir, eed->v1->co, eed->v2->co);
 +                                              angle= VecAngle2(base_dir, dir);
 +                                              
 +                                              if (angle>90) /* use the smallest angle between the edges */
 +                                                      angle= fabs(angle-180.0f);
 +                                              
 +                                              if (angle/90.0<=thresh) {
 +                                                      EM_select_edge(eed, 1);
 +                                                      selcount++;
 +                                                      deselcount--;
 +                                                      if (!deselcount) /*have we selected all posible faces?, if so return*/
 +                                                              return selcount;
 +                                              }
 +                                      }
 +                              }
 +                      } else if (mode==SIMEDGE_FACE) { /* face users */                               
 +                              for(eed= em->edges.first; eed; eed= eed->next) {
 +                                      if (
 +                                              !(eed->f & SELECT) &&
 +                                              !eed->h &&
 +                                              base_eed->tmp.l==eed->tmp.l
 +                                      ) {
 +                                              EM_select_edge(eed, 1);
 +                                              selcount++;
 +                                              deselcount--;
 +                                              if (!deselcount) /*have we selected all posible faces?, if so return*/
 +                                                      return selcount;
 +                                      }
 +                              }
 +                      } else if (mode==SIMEDGE_FACE_ANGLE && base_eed->f2==2) { /* edge angles, f2==2 means the edge has an angle. */                         
 +                              for(eed= em->edges.first; eed; eed= eed->next) {
 +                                      if (
 +                                              !(eed->f & SELECT) &&
 +                                              !eed->h &&
 +                                              eed->f2==2 &&
 +                                              (fabs(base_eed->tmp.fp-eed->tmp.fp)<=thresh)
 +                                      ) {
 +                                              EM_select_edge(eed, 1);
 +                                              selcount++;
 +                                              deselcount--;
 +                                              if (!deselcount) /*have we selected all posible faces?, if so return*/
 +                                                      return selcount;
 +                                      }
 +                              }
 +                      } else if (mode==SIMEDGE_CREASE) { /* edge crease */
 +                              for(eed= em->edges.first; eed; eed= eed->next) {
 +                                      if (
 +                                              !(eed->f & SELECT) &&
 +                                              !eed->h &&
 +                                              (fabs(base_eed->crease-eed->crease) <= thresh)
 +                                      ) {
 +                                              EM_select_edge(eed, 1);
 +                                              selcount++;
 +                                              deselcount--;
 +                                              if (!deselcount) /*have we selected all posible faces?, if so return*/
 +                                                      return selcount;
 +                                      }
 +                              }
 +                      } else if (mode==SIMEDGE_SEAM) { /* edge seam */
 +                              for(eed= em->edges.first; eed; eed= eed->next) {
 +                                      if (
 +                                              !(eed->f & SELECT) &&
 +                                              !eed->h &&
 +                                              (eed->seam == base_eed->seam)
 +                                      ) {
 +                                              EM_select_edge(eed, 1);
 +                                              selcount++;
 +                                              deselcount--;
 +                                              if (!deselcount) /*have we selected all posible faces?, if so return*/
 +                                                      return selcount;
 +                                      }
 +                              }
 +                      } else if (mode==SIMEDGE_SHARP) { /* edge sharp */
 +                              for(eed= em->edges.first; eed; eed= eed->next) {
 +                                      if (
 +                                              !(eed->f & SELECT) &&
 +                                              !eed->h &&
 +                                              (eed->sharp == base_eed->sharp)
 +                                      ) {
 +                                              EM_select_edge(eed, 1);
 +                                              selcount++;
 +                                              deselcount--;
 +                                              if (!deselcount) /*have we selected all posible faces?, if so return*/
 +                                                      return selcount;
 +                                      }
 +                              }
 +                      }
 +              }
 +      }       
 +      return selcount;
 +#endif
 +}
 +/* wrap the above function but do selection flushing edge to face */
 +static int similar_edge_select_exec(bContext *C, wmOperator *op)
 +{
 +#if 0
 +      Scene *scene= CTX_data_scene(C);
 +      Object *obedit= CTX_data_edit_object(C);
 +      Mesh *me= obedit->data;
 +      EditMesh *em= BKE_mesh_get_editmesh(me); 
 +
 +      int selcount = similar_edge_select__internal(scene, em, RNA_int_get(op->ptr, "type"));
 +      
 +      if (selcount) {
 +              /* here was an edge-mode only select flush case, has to be generalized */
 +              EM_selectmode_flush(em);
 +              WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, obedit);
 +              BKE_mesh_end_editmesh(me, em);
 +              return OPERATOR_FINISHED;
 +      }
 +      
 +      BKE_mesh_end_editmesh(me, em);
 +      return OPERATOR_CANCELLED;
 +#endif
 +}
 +
 +/* ********************************* */
 +
 +/*
 +VERT GROUP
 + mode 1: same normal
 + mode 2: same number of face users
 + mode 3: same vertex groups
 +*/
 +
 +#define SIMVERT_NORMAL        0
 +#define SIMVERT_FACE  1
 +#define SIMVERT_VGROUP        2
 +
 +static EnumPropertyItem prop_simvertex_types[] = {
 +      {SIMVERT_NORMAL, "NORMAL", 0, "Normal", ""},
 +      {SIMVERT_FACE, "FACE", 0, "Amount of Vertices in Face", ""},
 +      {SIMVERT_VGROUP, "VGROUP", 0, "Vertex Groups", ""},
 +      {0, NULL, 0, NULL, NULL}
 +};
 +
 +
 +static int similar_vert_select_exec(bContext *C, wmOperator *op)
 +{
 +#if 0
 +      Scene *scene= CTX_data_scene(C);
 +      Object *obedit= CTX_data_edit_object(C);
 +      Mesh *me= obedit->data;
 +      EditMesh *em= BKE_mesh_get_editmesh(me); 
 +      EditVert *eve, *base_eve=NULL;
 +      unsigned int selcount=0; /* count how many new edges we select*/
 +      
 +      /*count how many visible selected edges there are,
 +      so we can return when there are none left */
 +      unsigned int deselcount=0;
 +      int mode= RNA_enum_get(op->ptr, "type");
 +      
 +      short ok=0;
 +      float thresh= scene->toolsettings->select_thresh;
 +      
 +      for(eve= em->verts.first; eve; eve= eve->next) {
 +              if (!eve->h) {
 +                      if (eve->f & SELECT) {
 +                              eve->f1=1;
 +                              ok=1;
 +                      } else {
 +                              eve->f1=0;
 +                              deselcount++;
 +                      }
 +                      /* set all eve->tmp.l to 0 we use them later.*/
 +                      eve->tmp.l=0;
 +              }
 +              
 +      }
 +      
 +      if (!ok || !deselcount) { /* no data selected OR no more data to select*/
 +              BKE_mesh_end_editmesh(me, em);
 +              return 0;
 +      }
 +      
 +      if(mode == SIMVERT_FACE) {
 +              /* store face users */
 +              EditFace *efa;
 +              
 +              /* count how many faces each edge uses use tmp->l */
 +              for(efa= em->faces.first; efa; efa= efa->next) {
 +                      efa->v1->tmp.l++;
 +                      efa->v2->tmp.l++;
 +                      efa->v3->tmp.l++;
 +                      if (efa->v4) efa->v4->tmp.l++;
 +              }
 +      }
 +      
 +      
 +      for(base_eve= em->verts.first; base_eve; base_eve= base_eve->next) {
 +              if (base_eve->f1) {
 +                              
 +                      if(mode == SIMVERT_NORMAL) {
 +                              float angle;
 +                              for(eve= em->verts.first; eve; eve= eve->next) {
 +                                      if (!(eve->f & SELECT) && !eve->h) {
 +                                              angle= VecAngle2(base_eve->no, eve->no);
 +                                              if (angle/180.0<=thresh) {
 +                                                      eve->f |= SELECT;
 +                                                      selcount++;
 +                                                      deselcount--;
 +                                                      if (!deselcount) {/*have we selected all posible faces?, if so return*/
 +                                                              BKE_mesh_end_editmesh(me, em);
 +                                                              return selcount;
 +                                                      }
 +                                              }
 +                                      }
 +                              }
 +                      }
 +                      else if(mode == SIMVERT_FACE) {
 +                              for(eve= em->verts.first; eve; eve= eve->next) {
 +                                      if (
 +                                              !(eve->f & SELECT) &&
 +                                              !eve->h &&
 +                                              base_eve->tmp.l==eve->tmp.l
 +                                      ) {
 +                                              eve->f |= SELECT;
 +                                              selcount++;
 +                                              deselcount--;
 +                                              if (!deselcount) {/*have we selected all posible faces?, if so return*/
 +                                                      BKE_mesh_end_editmesh(me, em);
 +                                                      return selcount;
 +                                              }
 +                                      }
 +                              }
 +                      } 
 +                      else if(mode == SIMVERT_VGROUP) {
 +                              MDeformVert *dvert, *base_dvert;
 +                              short i, j; /* weight index */
 +
 +                              base_dvert= CustomData_em_get(&em->vdata, base_eve->data,
 +                                      CD_MDEFORMVERT);
 +
 +                              if (!base_dvert || base_dvert->totweight == 0) {
 +                                      BKE_mesh_end_editmesh(me, em);
 +                                      return selcount;
 +                              }
 +                              
 +                              for(eve= em->verts.first; eve; eve= eve->next) {
 +                                      dvert= CustomData_em_get(&em->vdata, eve->data,
 +                                              CD_MDEFORMVERT);
 +
 +                                      if (dvert && !(eve->f & SELECT) && !eve->h && dvert->totweight) {
 +                                              /* do the extra check for selection in the following if, so were not
 +                                              checking verts that may be alredy selected */
 +                                              for (i=0; base_dvert->totweight >i && !(eve->f & SELECT); i++) { 
 +                                                      for (j=0; dvert->totweight >j; j++) {
 +                                                              if (base_dvert->dw[i].def_nr==dvert->dw[j].def_nr) {
 +                                                                      eve->f |= SELECT;
 +                                                                      selcount++;
 +                                                                      deselcount--;
 +                                                                      if (!deselcount) { /*have we selected all posible faces?, if so return*/
 +                                                                              BKE_mesh_end_editmesh(me, em);
 +                                                                              return selcount;
 +                                                                      }
 +                                                                      break;
 +                                                              }
 +                                                      }
 +                                              }
 +                                      }
 +                              }
 +                      }
 +              }
 +      } /* end basevert loop */
 +
 +      if(selcount) {
 +              WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, obedit);
 +              BKE_mesh_end_editmesh(me, em);
 +              return OPERATOR_FINISHED;
 +      }
 +
 +      BKE_mesh_end_editmesh(me, em);
 +#endif
 +      return OPERATOR_CANCELLED;
 +}
 +
 +static int select_similar_exec(bContext *C, wmOperator *op)
 +{
 +      int type= RNA_enum_get(op->ptr, "type");
 +
 +      if(type < 100)
 +              return similar_vert_select_exec(C, op);
 +      else if(type < 200)
 +              return similar_edge_select_exec(C, op);
 +      else
 +              return similar_face_select_exec(C, op);
 +}
 +
 +static EnumPropertyItem *select_similar_type_itemf(bContext *C, PointerRNA *ptr, int *free)
 +{
 +      Object *obedit;
 +      EnumPropertyItem *item= NULL;
 +      int totitem= 0;
 +      
 +      if(C==NULL) {
 +              /* needed for doc generation */
 +              RNA_enum_items_add(&item, &totitem, prop_simvertex_types);
 +              RNA_enum_items_add(&item, &totitem, prop_simedge_types);
 +              RNA_enum_items_add(&item, &totitem, prop_simface_types);
 +              RNA_enum_item_end(&item, &totitem);
 +              *free= 1;
 +              
 +              return item;
 +      }
 +      
 +      obedit= CTX_data_edit_object(C);
 +      
 +      if(obedit && obedit->type == OB_MESH) {
 +              BMEditMesh *em= ((Mesh*)obedit->data)->edit_btmesh; 
 +
 +              if(em->selectmode & SCE_SELECT_VERTEX)
 +                      RNA_enum_items_add(&item, &totitem, prop_simvertex_types);
 +              else if(em->selectmode & SCE_SELECT_EDGE)
 +                      RNA_enum_items_add(&item, &totitem, prop_simedge_types);
 +              else if(em->selectmode & SCE_SELECT_FACE)
 +                      RNA_enum_items_add(&item, &totitem, prop_simface_types);
 +              RNA_enum_item_end(&item, &totitem);
 +
 +              *free= 1;
 +
 +              return item;
 +      }
 +      
 +      return NULL;
 +}
 +
 +void MESH_OT_select_similar(wmOperatorType *ot)
 +{
 +      PropertyRNA *prop;
 +
 +      /* identifiers */
 +      ot->name= "Select Similar";
 +      ot->idname= "MESH_OT_select_similar";
 +      
 +      /* api callbacks */
 +      ot->invoke= WM_menu_invoke;
 +      ot->exec= select_similar_exec;
 +      ot->poll= ED_operator_editmesh;
++      ot->description= "Select similar vertices, edges or faces by property types.";
 +      
 +      /* flags */
 +      ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
 +      
 +      /* properties */
 +      prop= RNA_def_enum(ot->srna, "type", prop_simvertex_types, 0, "Type", "");
 +      RNA_def_enum_funcs(prop, select_similar_type_itemf);
 +}
 +
 +/* ***************************************************** */
 +
 +/* ****************  LOOP SELECTS *************** */
 +/*faceloop_select, edgeloop_select, and edgering_select, are left
 +  here for reference purposes temporarily, but have all been replaced
 +  by uses of walker_select.*/
 +
 +static void walker_select(BMEditMesh *em, int walkercode, void *start, int select)
 +{
 +      BMesh *bm = em->bm;
 +      BMHeader *h;
 +      BMWalker walker;
 +
 +      BMW_Init(&walker, bm, walkercode, 0, 0);
 +      h = BMW_Begin(&walker, start);
 +      for (; h; h=BMW_Step(&walker)) {
 +              BM_Select(bm, h, select);
 +      }
 +      BMW_End(&walker);
 +}
 +
 +#if 0
 +/* selects quads in loop direction of indicated edge */
 +/* only flush over edges with valence <= 2 */
 +void faceloop_select(EditMesh *em, EditEdge *startedge, int select)
 +{
 +      EditEdge *eed;
 +      EditFace *efa;
 +      int looking= 1;
 +      
 +      /* in eed->f1 we put the valence (amount of faces in edge) */
 +      /* in eed->f2 we put tagged flag as correct loop */
 +      /* in efa->f1 we put tagged flag as correct to select */
 +
 +      for(eed= em->edges.first; eed; eed= eed->next) {
 +              eed->f1= 0;
 +              eed->f2= 0;
 +      }
 +      for(efa= em->faces.first; efa; efa= efa->next) {
 +              efa->f1= 0;
 +              if(efa->h==0) {
 +                      efa->e1->f1++;
 +                      efa->e2->f1++;
 +                      efa->e3->f1++;
 +                      if(efa->e4) efa->e4->f1++;
 +              }
 +      }
 +      
 +      /* tag startedge OK*/
 +      startedge->f2= 1;
 +      
 +      while(looking) {
 +              looking= 0;
 +              
 +              for(efa= em->faces.first; efa; efa= efa->next) {
 +                      if(efa->h==0 && efa->e4 && efa->f1==0) {        /* not done quad */
 +                              if(efa->e1->f1<=2 && efa->e2->f1<=2 && efa->e3->f1<=2 && efa->e4->f1<=2) { /* valence ok */
 +
 +                                      /* if edge tagged, select opposing edge and mark face ok */
 +                                      if(efa->e1->f2) {
 +                                              efa->e3->f2= 1;
 +                                              efa->f1= 1;
 +                                              looking= 1;
 +                                      }
 +                                      else if(efa->e2->f2) {
 +                                              efa->e4->f2= 1;
 +                                              efa->f1= 1;
 +                                              looking= 1;
 +                                      }
 +                                      if(efa->e3->f2) {
 +                                              efa->e1->f2= 1;
 +                                              efa->f1= 1;
 +                                              looking= 1;
 +                                      }
 +                                      if(efa->e4->f2) {
 +                                              efa->e2->f2= 1;
 +                                              efa->f1= 1;
 +                                              looking= 1;
 +                                      }
 +                              }
 +                      }
 +              }
 +      }
 +      
 +      /* (de)select the faces */
 +      if(select!=2) {
 +              for(efa= em->faces.first; efa; efa= efa->next) {
 +                      if(efa->f1) EM_select_face(efa, select);
 +              }
 +      }
 +}
 +#endif
 +
 +
 +/* selects or deselects edges that:
 +- if edges has 2 faces:
 +      - has vertices with valence of 4
 +      - not shares face with previous edge
 +- if edge has 1 face:
 +      - has vertices with valence 4
 +      - not shares face with previous edge
 +      - but also only 1 face
 +- if edge no face:
 +      - has vertices with valence 2
 +*/
 +
 +/* 
 +   Almostly exactly the same code as faceloop select
 +*/
 +static void edgering_select(BMEditMesh *em, BMEdge *startedge, int select)
 +{
 +#if 0 //BMESH_TODO
 +      BMEdge *eed;
 +      BMFace *efa;
 +      int looking= 1;
 +      
 +      /* in eed->f1 we put the valence (amount of faces in edge) */
 +      /* in eed->f2 we put tagged flag as correct loop */
 +      /* in efa->f1 we put tagged flag as correct to select */
 +
 +      for(eed= em->edges.first; eed; eed= eed->next) {
 +              eed->f1= 0;
 +              eed->f2= 0;
 +      }
 +      for(efa= em->faces.first; efa; efa= efa->next) {
 +              efa->f1= 0;
 +              if(efa->h==0) {
 +                      efa->e1->f1++;
 +                      efa->e2->f1++;
 +                      efa->e3->f1++;
 +                      if(efa->e4) efa->e4->f1++;
 +              }
 +      }
 +      
 +      /* tag startedge OK */
 +      startedge->f2= 1;
 +      
 +      while(looking) {
 +              looking= 0;
 +              
 +              for(efa= em->faces.first; efa; efa= efa->next) {
 +                      if(efa->e4 && efa->f1==0 && !efa->h) {  /* not done quad */
 +                              if(efa->e1->f1<=2 && efa->e2->f1<=2 && efa->e3->f1<=2 && efa->e4->f1<=2) { /* valence ok */
 +
 +                                      /* if edge tagged, select opposing edge and mark face ok */
 +                                      if(efa->e1->f2) {
 +                                              efa->e3->f2= 1;
 +                                              efa->f1= 1;
 +                                              looking= 1;
 +                                      }
 +                                      else if(efa->e2->f2) {
 +                                              efa->e4->f2= 1;
 +                                              efa->f1= 1;
 +                                              looking= 1;
 +                                      }
 +                                      if(efa->e3->f2) {
 +                                              efa->e1->f2= 1;
 +                                              efa->f1= 1;
 +                                              looking= 1;
 +                                      }
 +                                      if(efa->e4->f2) {
 +                                              efa->e2->f2= 1;
 +                                              efa->f1= 1;
 +                                              looking= 1;
 +                                      }
 +                              }
 +                      }
 +              }
 +      }
 +      
 +      /* (de)select the edges */
 +      for(eed= em->edges.first; eed; eed= eed->next) {
 +              if(eed->f2) EM_select_edge(eed, select);
 +      }
 +#endif
 +}
 +
 +static int loop_multiselect(bContext *C, wmOperator *op)
 +{
 +#if 0 //BMESH_TODO
 +      Object *obedit= CTX_data_edit_object(C);
 +      BMEditMesh *em= EM_GetBMEditMesh(((Mesh *)obedit->data));
 +      BMEdge *eed;
 +      BMEdge **edarray;
 +      int edindex, edfirstcount;
 +      int looptype= RNA_boolean_get(op->ptr, "ring");
 +      
 +      /* sets em->totedgesel */
 +      EM_nedges_selected(em);
 +      
 +      edarray = MEM_mallocN(sizeof(BMEdge*)*em->totedgesel,"edge array");
 +      edindex = 0;
 +      edfirstcount = em->totedgesel;
 +      
 +      for(eed=em->edges.first; eed; eed=eed->next){
 +              if(eed->f&SELECT){
 +                      edarray[edindex] = eed;
 +                      edindex += 1;
 +              }
 +      }
 +      
 +      if(looptype){
 +              for(edindex = 0; edindex < edfirstcount; edindex +=1){
 +                      eed = edarray[edindex];
 +                      edgering_select(em, eed,SELECT);
 +              }
 +              EM_selectmode_flush(em);
 +      }
 +      else{
 +              for(edindex = 0; edindex < edfirstcount; edindex +=1){
 +                      eed = edarray[edindex];
 +                      edgeloop_select(em, eed,SELECT);
 +              }
 +              EM_selectmode_flush(em);
 +      }
 +      MEM_freeN(edarray);
 +//    if (EM_texFaceCheck())
 +      
 +      WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, obedit);
 +
 +      EM_EndBMEditMesh(obedit->data, em);
 +#endif
 +      return OPERATOR_FINISHED;       
 +}
 +
 +void MESH_OT_loop_multi_select(wmOperatorType *ot)
 +{
 +      /* identifiers */
 +      ot->name= "Multi Select Loops";
 +      ot->idname= "MESH_OT_loop_multi_select";
 +      
 +      /* api callbacks */
 +      ot->exec= loop_multiselect;
 +      ot->poll= ED_operator_editmesh;
++      ot->description= "Select a loop of connected edges by connection type.";
 +      
 +      /* flags */
 +      ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
 +      
 +      /* properties */
 +      RNA_def_boolean(ot->srna, "ring", 0, "Ring", "");
 +}
 +
 +              
 +/* ***************** MAIN MOUSE SELECTION ************** */
 +
 +
 +/* ***************** loop select (non modal) ************** */
 +
 +static void mouse_mesh_loop(bContext *C, short mval[2], short extend, short ring)
 +{
 +      ViewContext vc;
 +      BMEditMesh *em;
 +      BMEdge *eed;
 +      int select= 1;
 +      int dist= 50;
 +      
 +      em_setup_viewcontext(C, &vc);
 +      vc.mval[0]= mval[0];
 +      vc.mval[1]= mval[1];
 +      em= vc.em;
 +      
 +      eed= EDBM_findnearestedge(&vc, &dist);
 +      if(eed) {
 +              if(extend==0) EDBM_clear_flag_all(em, BM_SELECT);
 +      
 +              if(BM_TestHFlag(em, BM_SELECT)==0) select=1;
 +              else if(extend) select=0;
 +
 +              if(em->selectmode & SCE_SELECT_FACE) {
 +                      walker_select(em, BMW_FACELOOP, eed, select);
 +              }
 +              else if(em->selectmode & SCE_SELECT_EDGE) {
 +                      if(ring)
 +                              walker_select(em, BMW_EDGERING, eed, select);
 +                      else
 +                              walker_select(em, BMW_LOOP, eed, select);
 +              }
 +              else if(em->selectmode & SCE_SELECT_VERTEX) {
 +                      if(ring)
 +                              walker_select(em, BMW_EDGERING, eed, select);
 +                      else 
 +                              walker_select(em, BMW_LOOP, eed, select);
 +              }
 +
 +              EDBM_selectmode_flush(em);
 +//                    if (EM_texFaceCheck())
 +              
 +              WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, vc.obedit);
 +      }
 +}
 +
 +static int mesh_select_loop_invoke(bContext *C, wmOperator *op, wmEvent *event)
 +{
 +      
 +      view3d_operator_needs_opengl(C);
 +      
 +      mouse_mesh_loop(C, event->mval, RNA_boolean_get(op->ptr, "extend"),
 +                                      RNA_boolean_get(op->ptr, "ring"));
 +      
 +      /* cannot do tweaks for as long this keymap is after transform map */
 +      return OPERATOR_FINISHED;
 +}
 +
 +void MESH_OT_loop_select(wmOperatorType *ot)
 +{
 +      /* identifiers */
 +      ot->name= "Loop Select";
 +      ot->idname= "MESH_OT_loop_select";
 +      
 +      /* api callbacks */
 +      ot->invoke= mesh_select_loop_invoke;
 +      ot->poll= ED_operator_editmesh;
++      ot->description= "Select a loop of connected edges.";
 +      
 +      /* flags */
 +      ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
 +      
 +      /* properties */
 +      RNA_def_boolean(ot->srna, "extend", 0, "Extend Select", "");
 +      RNA_def_boolean(ot->srna, "ring", 0, "Select Ring", "");
 +}
 +
 +/* ******************* mesh shortest path select, uses prev-selected edge ****************** */
 +
 +/* since you want to create paths with multiple selects, it doesn't have extend option */
 +static void mouse_mesh_shortest_path(bContext *C, short mval[2])
 +{
 +#if 0 //BMESH_TODO
 +      ViewContext vc;
 +      BMEditMesh *em;
 +      BMEdge *eed;
 +      int dist= 50;
 +      
 +      em_setup_viewcontext(C, &vc);
 +      vc.mval[0]= mval[0];
 +      vc.mval[1]= mval[1];
 +      em= vc.em;
 +      
 +      eed= findnearestedge(&vc, &dist);
 +      if(eed) {
 +              Mesh *me= vc.obedit->data;
 +              int path = 0;
 +              
 +              if (em->bm->selected.last) {
 +                      EditSelection *ese = em->bm->selected.last;
 +                      
 +                      if(ese && ese->type == BMEdge) {
 +                              BMEdge *eed_act;
 +                              eed_act = (BMEdge*)ese->data;
 +                              if (eed_act != eed) {
 +                                      if (edgetag_shortest_path(vc.scene, em, eed_act, eed)) {
 +                                              EM_remove_selection(em, eed_act, BMEdge);
 +                                              path = 1;
 +                                      }
 +                              }
 +                      }
 +              }
 +              if (path==0) {
 +                      int act = (edgetag_context_check(vc.scene, eed)==0);
 +                      edgetag_context_set(vc.scene, eed, act); /* switch the edge option */
 +              }
 +              
 +              EM_selectmode_flush(em);
 +
 +              /* even if this is selected it may not be in the selection list */
 +              if(edgetag_context_check(vc.scene, eed)==0)
 +                      EDBM_remove_selection(em, eed);
 +              else
 +                      EDBM_store_selection(em, eed);
 +      
 +              /* force drawmode for mesh */
 +              switch (vc.scene->toolsettings->edge_mode) {
 +                      
 +                      case EDGE_MODE_TAG_SEAM:
 +                              me->drawflag |= ME_DRAWSEAMS;
 +                              break;
 +                      case EDGE_MODE_TAG_SHARP:
 +                              me->drawflag |= ME_DRAWSHARP;
 +                              break;
 +                      case EDGE_MODE_TAG_CREASE:      
 +                              me->drawflag |= ME_DRAWCREASES;
 +                              break;
 +                      case EDGE_MODE_TAG_BEVEL:
 +                              me->drawflag |= ME_DRAWBWEIGHTS;
 +                              break;
 +              }
 +              
 +              DAG_object_flush_update(vc.scene, vc.obedit, OB_RECALC_DATA);
 +      
 +              WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, vc.obedit);
 +      }
 +#endif
 +}
 +
 +
 +static int mesh_shortest_path_select_invoke(bContext *C, wmOperator *op, wmEvent *event)
 +{
 +      
 +      view3d_operator_needs_opengl(C);
 +
 +      mouse_mesh_shortest_path(C, event->mval);
 +      
 +      return OPERATOR_FINISHED;
 +}
 +      
 +void MESH_OT_select_shortest_path(wmOperatorType *ot)
 +{
 +      /* identifiers */
 +      ot->name= "Shortest Path Select";
 +      ot->idname= "MESH_OT_select_shortest_path";
 +      
 +      /* api callbacks */
 +      ot->invoke= mesh_shortest_path_select_invoke;
 +      ot->poll= ED_operator_editmesh;
++      ot->description= "Select shortest path between two selections.";
 +      
 +      /* flags */
 +      ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
 +      
 +      /* properties */
 +      RNA_def_boolean(ot->srna, "extend", 0, "Extend Select", "");
 +}
 +
 +
 +/* ************************************************** */
 +/* here actual select happens */
 +/* gets called via generic mouse select operator */
 +void mouse_mesh(bContext *C, short mval[2], short extend)
 +{
 +      ViewContext vc;
 +      BMVert *eve = NULL;
 +      BMEdge *eed = NULL;
 +      BMFace *efa = NULL;
 +      
 +      /* setup view context for argument to callbacks */
 +      em_setup_viewcontext(C, &vc);
 +      vc.mval[0]= mval[0];
 +      vc.mval[1]= mval[1];
 +      
 +      if(unified_findnearest(&vc, &eve, &eed, &efa)) {
 +              
 +              if(extend==0) EDBM_clear_flag_all(vc.em, BM_SELECT);
 +              
 +              if(efa) {
 +                      /* set the last selected face */
 +                      EDBM_set_actFace(vc.em, efa);
 +                      
 +                      if(!BM_TestHFlag(efa, BM_SELECT)) {
 +                              EDBM_store_selection(vc.em, efa);
 +                              BM_Select(vc.em->bm, efa, 1);
 +                      }
 +                      else if(extend) {
 +                              EDBM_remove_selection(vc.em, efa);
 +                              BM_Select(vc.em->bm, efa, 0);
 +                      }
 +              }
 +              else if(eed) {
 +                      if(!BM_TestHFlag(eed, BM_SELECT)) {
 +                              EDBM_store_selection(vc.em, eed);
 +                              BM_Select(vc.em->bm, eed, 1);
 +                      }
 +                      else if(extend) {
 +                              EDBM_remove_selection(vc.em, eed);
 +                              BM_Select(vc.em->bm, eed, 0);
 +                      }
 +              }
 +              else if(eve) {
 +                      if(!BM_TestHFlag(eve, BM_SELECT)) {
 +                              EDBM_store_selection(vc.em, eve);
 +                              BM_Select(vc.em->bm, eve, 1);
 +                      }
 +                      else if(extend){ 
 +                              EDBM_remove_selection(vc.em, eve);
 +                              BM_Select(vc.em->bm, eve, 0);
 +                      }
 +              }
 +              
 +              EDBM_selectmode_flush(vc.em);
 +                
 +//            if (EM_texFaceCheck()) {
 +
 +              if (efa && efa->mat_nr != vc.obedit->actcol-1) {
 +                      vc.obedit->actcol= efa->mat_nr+1;
 +                      vc.em->mat_nr= efa->mat_nr;
 +//                    BIF_preview_changed(ID_MA);
 +              }
 +      }
 +
 +      WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, vc.obedit);
 +}
 +
 +static void EDBM_strip_selections(BMEditMesh *em)
 +{
 +      BMEditSelection *ese, *nextese;
 +
 +      if(!(em->selectmode & SCE_SELECT_VERTEX)){
 +              ese = em->bm->selected.first;
 +              while(ese){
 +                      nextese = ese->next; 
 +                      if(ese->type == BM_VERT) BLI_freelinkN(&(em->bm->selected),ese);
 +                      ese = nextese;
 +              }
 +      }
 +      if(!(em->selectmode & SCE_SELECT_EDGE)){
 +              ese=em->bm->selected.first;
 +              while(ese){
 +                      nextese = ese->next;
 +                      if(ese->type == BM_EDGE) BLI_freelinkN(&(em->bm->selected), ese);
 +                      ese = nextese;
 +              }
 +      }
 +      if(!(em->selectmode & SCE_SELECT_FACE)){
 +              ese=em->bm->selected.first;
 +              while(ese){
 +                      nextese = ese->next;
 +                      if(ese->type == BM_FACE) BLI_freelinkN(&(em->bm->selected), ese);
 +                      ese = nextese;
 +              }
 +      }
 +}
 +
 +/* when switching select mode, makes sure selection is consistant for editing */
 +/* also for paranoia checks to make sure edge or face mode works */
 +void EDBM_selectmode_set(BMEditMesh *em)
 +{
 +      BMVert *eve;
 +      BMEdge *eed;
 +      BMFace *efa;
 +      BMIter iter;
 +      
 +      em->bm->selectmode = em->selectmode;
 +
 +      EDBM_strip_selections(em); /*strip BMEditSelections from em->selected that are not relevant to new mode*/
 +      
 +      if(em->selectmode & SCE_SELECT_VERTEX) {
-               /*eed = BMIter_New(&iter, em->bm, BM_EDGES_OF_MESH, NULL);
++              /*BMIter iter;
 +              
++              eed = BMIter_New(&iter, em->bm, BM_EDGES_OF_MESH, NULL);
 +              for ( ; eed; eed=BMIter_Step(&iter)) BM_Select(em->bm, eed, 0);
 +              
 +              efa = BMIter_New(&iter, em->bm, BM_FACES_OF_MESH, NULL);
 +              for ( ; efa; efa=BMIter_Step(&iter)) BM_Select(em->bm, efa, 0);*/
 +
 +              EDBM_selectmode_flush(em);
 +      }
 +      else if(em->selectmode & SCE_SELECT_EDGE) {
 +              /* deselect vertices, and select again based on edge select */
 +              eve = BMIter_New(&iter, em->bm, BM_VERTS_OF_MESH, NULL);
 +              for ( ; eve; eve=BMIter_Step(&iter)) BM_Select(em->bm, eve, 0);
 +              
 +              eed = BMIter_New(&iter, em->bm, BM_EDGES_OF_MESH, NULL);
 +              for ( ; eed; eed=BMIter_Step(&iter)) {
 +                      if (BM_TestHFlag(eed, BM_SELECT))
 +                              BM_Select(em->bm, eed, 1);
 +              }
 +              
 +              /* selects faces based on edge status */
 +              EDBM_selectmode_flush(em);
 +      }
 +      else if(em->selectmode & SCE_SELECT_FACE) {
 +              /* deselect eges, and select again based on face select */
 +              eed = BMIter_New(&iter, em->bm, BM_EDGES_OF_MESH, NULL);
 +              for ( ; eed; eed=BMIter_Step(&iter)) BM_Select(em->bm, eed, 0);
 +              
 +              efa = BMIter_New(&iter, em->bm, BM_FACES_OF_MESH, NULL);
 +              for ( ; efa; efa=BMIter_Step(&iter)) {
 +                      if (BM_TestHFlag(efa, BM_SELECT))
 +                              BM_Select(em->bm, efa, 1);
 +              }
 +      }
 +}
 +
 +void EDBM_convertsel(BMEditMesh *em, short oldmode, short selectmode)
 +{
 +      BMVert *eve;
 +      BMEdge *eed;
 +      BMFace *efa;
 +      BMIter iter;
 +
 +      /*have to find out what the selectionmode was previously*/
 +      if(oldmode == SCE_SELECT_VERTEX) {
 +              if(selectmode == SCE_SELECT_EDGE) {
 +                      /*select all edges associated with every selected vertex*/
 +                      eed = BMIter_New(&iter, em->bm, BM_EDGES_OF_MESH, NULL);
 +                      for ( ; eed; eed=BMIter_Step(&iter)) {
 +                              if(BM_TestHFlag(eed->v1, BM_SELECT)) BM_Select(em->bm, eed, 1);
 +                              else if(BM_TestHFlag(eed->v2, BM_SELECT)) BM_Select(em->bm, eed, 1);
 +                      }
 +              }               
 +              else if(selectmode == SCE_SELECT_FACE) {
 +                      BMIter liter;
 +                      BMLoop *l;
 +
 +                      /*select all faces associated with every selected vertex*/
 +                      efa = BMIter_New(&iter, em->bm, BM_FACES_OF_MESH, NULL);
 +                      for ( ; efa; efa=BMIter_Step(&iter)) {
 +                              l = BMIter_New(&liter, em->bm, BM_LOOPS_OF_FACE, efa);
 +                              for (; l; l=BMIter_Step(&liter)) {
 +                                      if (BM_TestHFlag(l->v, BM_SELECT)) {
 +                                              BM_Select(em->bm, efa, 1);
 +                                              break;
 +                                      }
 +                              }
 +                      }
 +              }
 +      }
 +      
 +      if(oldmode == SCE_SELECT_EDGE){
 +              if(selectmode == SCE_SELECT_FACE) {
 +                      BMIter liter;
 +                      BMLoop *l;
 +
 +                      /*select all faces associated with every selected vertex*/
 +                      efa = BMIter_New(&iter, em->bm, BM_FACES_OF_MESH, NULL);
 +                      for ( ; efa; efa=BMIter_Step(&iter)) {
 +                              l = BMIter_New(&liter, em->bm, BM_LOOPS_OF_FACE, efa);
 +                              for (; l; l=BMIter_Step(&liter)) {
 +                                      if (BM_TestHFlag(l->v, BM_SELECT)) {
 +                                              BM_Select(em->bm, efa, 1);
 +                                              break;
 +                                      }
 +                              }
 +                      }
 +              }
 +      }
 +}
 +
 +
 +void EDBM_select_swap(BMEditMesh *em) /* exported for UV */
 +{
 +      BMIter iter;
 +      BMVert *eve;
 +      BMEdge *eed;
 +      BMFace *efa;
 +      
 +      if(em->bm->selectmode & SCE_SELECT_VERTEX) {
 +              BM_ITER(eve, &iter, em->bm, BM_VERTS_OF_MESH, NULL) {
 +                      if (BM_TestHFlag(eve, BM_HIDDEN))
 +                              continue;
 +                      BM_Select(em->bm, eve, !BM_TestHFlag(eve, BM_SELECT));
 +              }
 +      }
 +      else if(em->selectmode & SCE_SELECT_EDGE) {
 +              BM_ITER(eed, &iter, em->bm, BM_EDGES_OF_MESH, NULL) {
 +                      if (BM_TestHFlag(eed, BM_HIDDEN))
 +                              continue;
 +                      BM_Select(em->bm, eed, !BM_TestHFlag(eed, BM_SELECT));
 +              }
 +      }
 +      else {
 +              BM_ITER(efa, &iter, em->bm, BM_FACES_OF_MESH, NULL) {
 +                      if (BM_TestHFlag(efa, BM_HIDDEN))
 +                              continue;
 +                      BM_Select(em->bm, efa, !BM_TestHFlag(efa, BM_SELECT));
 +              }
 +
 +      }
 +//    if (EM_texFaceCheck())
 +}
 +
 +static int select_inverse_mesh_exec(bContext *C, wmOperator *op)
 +{
 +      Object *obedit= CTX_data_edit_object(C);
 +      BMEditMesh *em= ((Mesh *)obedit->data)->edit_btmesh;
 +      
 +      EDBM_select_swap(em);
 +      
 +      WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, obedit);
 +
 +      return OPERATOR_FINISHED;       
 +}
 +
 +void MESH_OT_select_inverse(wmOperatorType *ot)
 +{
 +      /* identifiers */
 +      ot->name= "Select Inverse";
 +      ot->idname= "MESH_OT_select_inverse";
++      ot->description= "Select inverse of (un)selected vertices, edges or faces.";
 +      
 +      /* api callbacks */
 +      ot->exec= select_inverse_mesh_exec;
 +      ot->poll= ED_operator_editmesh;
 +      
 +      /* flags */
 +      ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
 +}
 +
 +
 +static int select_linked_pick_invoke(bContext *C, wmOperator *op, wmEvent *event)
 +{
 +      Object *obedit= CTX_data_edit_object(C);
 +      ViewContext vc;
 +      BMWalker walker;
 +      BMEditMesh *em;
 +      BMVert *eve;
 +      BMEdge *e, *eed;
 +      BMFace *efa;
 +      short done=1, toggle=0;
 +      int sel= !RNA_boolean_get(op->ptr, "deselect");
 +      int limit= RNA_boolean_get(op->ptr, "limit");
 +      
 +      /* unified_finednearest needs ogl */
 +      view3d_operator_needs_opengl(C);
 +      
 +      /* setup view context for argument to callbacks */
 +      em_setup_viewcontext(C, &vc);
 +      em = vc.em;
 +
 +      if(vc.em->bm->totedge==0)
 +              return OPERATOR_CANCELLED;
 +      
 +      vc.mval[0]= event->mval[0];
 +      vc.mval[1]= event->mval[1];
 +      
 +      /* return warning! */
 +
 +      /*if(limit) {
 +              int retval= select_linked_limited_invoke(&vc, 0, sel);
 +              WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, obedit);
 +              return retval;
 +      }*/
 +      
 +      if( unified_findnearest(&vc, &eve, &eed, &efa)==0 ) {
 +              WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, obedit);
 +      
 +              return OPERATOR_CANCELLED;
 +      }
 +      
 +      if (efa) {
 +              eed = efa->loopbase->e;
 +      } else if (!eed) {
 +              if (!eve || !eve->edge)
 +                      return OPERATOR_CANCELLED;
 +              
 +              eed = eve->edge;
 +      }
 +
 +      BMW_Init(&walker, em->bm, BMW_SHELL, 0, 0);
 +      e = BMW_Begin(&walker, eed->v1);
 +      for (; e; e=BMW_Step(&walker)) {
 +                      BM_Select(em->bm, e->v1, sel);
 +                      BM_Select(em->bm, e->v2, sel);
 +      }
 +      BMW_End(&walker);
 +      EDBM_select_flush(em, SCE_SELECT_VERTEX);
 +
 +      WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, obedit);
 +      return OPERATOR_FINISHED;       
 +}
 +
 +void MESH_OT_select_linked_pick(wmOperatorType *ot)
 +{
 +      /* identifiers */
 +      ot->name= "Select Linked";
 +      ot->idname= "MESH_OT_select_linked_pick";
 +      
 +      /* api callbacks */
 +      ot->invoke= select_linked_pick_invoke;
 +      ot->poll= ED_operator_editmesh;
++      ot->description= "select/deselect all vertices linked to the edge under the mouse cursor.";
 +      
 +      /* flags */
 +      ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
 +      
 +      RNA_def_boolean(ot->srna, "deselect", 0, "Deselect", "");
 +      RNA_def_boolean(ot->srna, "limit", 0, "Limit by Seams", "");
 +}
 +
 +
 +static int select_linked_exec(bContext *C, wmOperator *op)
 +{
 +      Object *obedit= CTX_data_edit_object(C);
 +      BMEditMesh *em= ((Mesh*)obedit->data)->edit_btmesh;
 +      V_DECLARE(verts);
 +      BMVert **verts = NULL;
 +      BMIter iter;
 +      BMVert *v;
 +      BMEdge *e;
 +      BMWalker walker;
 +      int i, tot;
 +
 +      tot = 0;
 +              BM_ITER_SELECT(v, &iter, em->bm, BM_VERTS_OF_MESH, NULL)
 +              if (BM_TestHFlag(v, BM_SELECT)) {
 +                      V_GROW(verts);
 +                      verts[tot++] = v;
 +              }
 +      }
 +
 +      BMW_Init(&walker, em->bm, BMW_SHELL, 0, 0);
 +      for (i=0; i<tot; i++) {
 +              e = BMW_Begin(&walker, verts[i]);
 +              for (; e; e=BMW_Step(&walker)) {
 +                      BM_Select(em->bm, e->v1, 1);
 +                      BM_Select(em->bm, e->v2, 1);
 +              }
 +      }
 +      BMW_End(&walker);
 +      EDBM_select_flush(em, SCE_SELECT_VERTEX);
 +
 +      V_FREE(verts);
 +      WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, obedit);
 +
 +      return OPERATOR_FINISHED;       
 +}
 +
 +void MESH_OT_select_linked(wmOperatorType *ot)
 +{
 +      /* identifiers */
 +      ot->name= "Select Linked All";
 +      ot->idname= "MESH_OT_select_linked";
 +      
 +      /* api callbacks */
 +      ot->exec= select_linked_exec;
 +      ot->poll= ED_operator_editmesh;
++      ot->description= "Select all vertices linked to the active mesh.";
 +      
 +      /* flags */
 +      ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
 +      
 +      RNA_def_boolean(ot->srna, "limit", 0, "Limit by Seams", "");
 +}
 +
 +/* ******************** **************** */
 +
 +static int select_more(bContext *C, wmOperator *op)
 +{
 +      Object *obedit= CTX_data_edit_object(C);
 +      BMEditMesh *em= (((Mesh *)obedit->data))->edit_btmesh;
 +      BMOperator bmop;
 +      int usefaces = em->selectmode > SCE_SELECT_EDGE;
 +
 +      EDBM_InitOpf(em, &bmop, op, "regionextend geom=%hvef constrict=%d usefaces=%d", 
 +                   BM_SELECT, 0, usefaces);
 +
 +      BMO_Exec_Op(em->bm, &bmop);
 +      BMO_HeaderFlag_Buffer(em->bm, &bmop, "geomout", BM_SELECT, BM_ALL);
 +
 +      EDBM_selectmode_flush(em);
 +
 +      if (!EDBM_FinishOp(em, &bmop, op, 1))
 +              return OPERATOR_CANCELLED;
 +
 +      WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, obedit);
 +      return OPERATOR_FINISHED;
 +}
 +
 +void MESH_OT_select_more(wmOperatorType *ot)
 +{
 +      /* identifiers */
 +      ot->name= "Select More";
 +      ot->idname= "MESH_OT_select_more";
++      ot->description= "Select more vertices, edges or faces connected to initial selection.";
 +
 +      /* api callbacks */
 +      ot->exec= select_more;
 +      ot->poll= ED_operator_editmesh;
 +      
 +      /* flags */
 +      ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
 +}
 +
 +static int select_less(bContext *C, wmOperator *op)
 +{
 +      Object *obedit= CTX_data_edit_object(C);
 +      BMEditMesh *em= (((Mesh *)obedit->data))->edit_btmesh;
 +      BMOperator bmop;
 +      int usefaces = em->selectmode > SCE_SELECT_EDGE;
 +
 +      EDBM_InitOpf(em, &bmop, op, "regionextend geom=%hvef constrict=%d usefaces=%d", 
 +                   BM_SELECT, 1, usefaces);
 +
 +      BMO_Exec_Op(em->bm, &bmop);
 +      BMO_UnHeaderFlag_Buffer(em->bm, &bmop, "geomout", BM_SELECT, BM_ALL);
 +
 +      EDBM_selectmode_flush(em);
 +
 +      if (!EDBM_FinishOp(em, &bmop, op, 1))
 +              return OPERATOR_CANCELLED;
 +
 +      WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, obedit);
 +      return OPERATOR_FINISHED;
 +}
 +
 +void MESH_OT_select_less(wmOperatorType *ot)
 +{
 +      /* identifiers */
 +      ot->name= "Select Less";
 +      ot->idname= "MESH_OT_select_less";
++      ot->description= "Deselect vertices, edges or faces at the boundary of each selection region.";
 +
 +      /* api callbacks */
 +      ot->exec= select_less;
 +      ot->poll= ED_operator_editmesh;
 +      
 +      /* flags */
 +      ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
 +}
index 5e5452c5cc94bf62255bc6132c8171fc49b029b5,0000000000000000000000000000000000000000..85d48a7c605bf8512b3962e230eb49dc9c560e49
mode 100644,000000..100644
--- /dev/null
@@@ -1,2042 -1,0 +1,2061 @@@
-       
 + /* $Id: bmesh_tools.c
 + *
 + * ***** 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) 2004 by Blender Foundation.
 + * All rights reserved.
 + *
 + * The Original Code is: all of this file.
 + *
 + * Contributor(s): Joseph Eagar
 + *
 + * ***** END GPL LICENSE BLOCK *****
 + */
 +#include <stdlib.h>
 +#include <stdarg.h>
 +#include <string.h>
 +#include <math.h>
 +#include <float.h>
 +
 +#include "MEM_guardedalloc.h"
 +#include "PIL_time.h"
 +
 +#include "BLO_sys_types.h" // for intptr_t support
 +
 +#include "DNA_mesh_types.h"
 +#include "DNA_material_types.h"
 +#include "DNA_meshdata_types.h"
 +#include "DNA_modifier_types.h"
 +#include "DNA_object_types.h"
 +#include "DNA_scene_types.h"
 +#include "DNA_screen_types.h"
 +#include "DNA_view3d_types.h"
 +#include "DNA_key_types.h"
 +#include "DNA_windowmanager_types.h"
 +
 +#include "RNA_types.h"
 +#include "RNA_define.h"
 +#include "RNA_access.h"
 +
 +#include "BLI_blenlib.h"
 +#include "BLI_arithb.h"
 +#include "BLI_editVert.h"
 +#include "BLI_rand.h"
 +#include "BLI_ghash.h"
 +#include "BLI_linklist.h"
 +#include "BLI_heap.h"
 +
 +#include "BKE_context.h"
 +#include "BKE_customdata.h"
 +#include "BKE_depsgraph.h"
 +#include "BKE_global.h"
 +#include "BKE_library.h"
 +#include "BKE_mesh.h"
 +#include "BKE_object.h"
 +#include "BKE_utildefines.h"
 +#include "BKE_bmesh.h"
 +#include "BKE_report.h"
 +#include "BKE_tessmesh.h"
 +
 +#include "BIF_gl.h"
 +#include "BIF_glutil.h"
 +
 +#include "WM_api.h"
 +#include "WM_types.h"
 +
 +#include "ED_mesh.h"
 +#include "ED_view3d.h"
 +#include "ED_util.h"
 +#include "ED_screen.h"
 +#include "ED_transform.h"
 +
 +#include "UI_interface.h"
 +
 +#include "mesh_intern.h"
 +#include "bmesh.h"
 +
 +static void add_normal_aligned(float *nor, float *add)
 +{
 +      if( INPR(nor, add) < -0.9999f)
 +              VecSubf(nor, nor, add);
 +      else
 +              VecAddf(nor, nor, add);
 +}
 +
 +
 +static int subdivide_exec(bContext *C, wmOperator *op)
 +{
 +      Scene *scene = CTX_data_scene(C);
 +      Object *obedit= CTX_data_edit_object(C);
 +      BMEditMesh *em= ((Mesh *)obedit->data)->edit_btmesh;
 +      int cuts= RNA_int_get(op->ptr,"number_cuts");
 +      float smooth= 0.292f*RNA_float_get(op->ptr, "smoothness");
 +      float fractal= RNA_float_get(op->ptr, "fractal")/100;
 +      int flag= 0;
 +
 +      if(smooth != 0.0f)
 +              flag |= B_SMOOTH;
 +      if(fractal != 0.0f)
 +              flag |= B_FRACTAL;
 +
 +      BM_esubdivideflag(obedit, em->bm, BM_SELECT, 
 +                        smooth, fractal, 
 +                        scene->toolsettings->editbutflag|flag, 
 +                        cuts, 0, RNA_enum_get(op->ptr, "quadcorner"), 
 +                        RNA_boolean_get(op->ptr, "tess_single_edge"),
 +                        RNA_boolean_get(op->ptr, "gridfill"));
 +
 +      DAG_object_flush_update(scene, obedit, OB_RECALC_DATA);
 +      WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, obedit);
 +
 +      return OPERATOR_FINISHED;
 +}
 +
 +/* Note, these values must match delete_mesh() event values */
 +static EnumPropertyItem prop_mesh_cornervert_types[] = {
 +      {SUBD_INNERVERT,     "INNERVERT", 0,      "Inner Vert", ""},
 +      {SUBD_PATH,          "PATH", 0,           "Path", ""},
 +      {SUBD_STRAIGHT_CUT,  "STRAIGHT_CUT", 0,   "Straight Cut", ""},
 +      {SUBD_FAN,           "FAN", 0,            "Fan", ""},
 +      {0, NULL, 0, NULL, NULL}
 +};
 +
 +void MESH_OT_subdivide(wmOperatorType *ot)
 +{
 +      /* identifiers */
 +      ot->name= "Subdivide";
++      ot->description= "Subdivide selected edges.";
 +      ot->idname= "MESH_OT_subdivide";
 +
 +      /* api callbacks */
 +      ot->exec= subdivide_exec;
 +      ot->poll= ED_operator_editmesh;
 +
 +      /* flags */
 +      ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
 +
 +      /* properties */
 +      RNA_def_int(ot->srna, "number_cuts", 1, 1, 20, "Number of Cuts", "", 1, INT_MAX);
 +      RNA_def_float(ot->srna, "fractal", 0.0, 0.0f, FLT_MAX, "Fractal", "Fractal randomness factor.", 0.0f, 1000.0f);
 +      RNA_def_float(ot->srna, "smoothness", 0.0f, 0.0f, 1000.0f, "Smoothness", "Smoothness factor.", 0.0f, FLT_MAX);
 +
 +      /*props */
 +      RNA_def_enum(ot->srna, "quadcorner", prop_mesh_cornervert_types, SUBD_STRAIGHT_CUT, "Quad Corner Type", "Method used for subdividing two adjacent edges in a quad");
 +      RNA_def_boolean(ot->srna, "tess_single_edge", 0, "Tesselate Single Edge", "Adds triangles to single edges belonging to triangles or quads");
 +      RNA_def_boolean(ot->srna, "gridfill", 1, "Grid Fill", "Fill Fully Selected Triangles and Quads With A Grid");
 +}
 +
 +/* individual face extrude */
 +/* will use vertex normals for extrusion directions, so *nor is unaffected */
 +short EDBM_Extrude_face_indiv(BMEditMesh *em, wmOperator *op, short flag, float *nor) 
 +{
 +      BMOIter siter;
 +      BMIter liter;
 +      BMFace *f;
 +      BMLoop *l;
 +      BMOperator bmop;
 +
 +      EDBM_InitOpf(em, &bmop, op, "extrude_face_indiv faces=%hf", flag);
 +
 +      /*deselect original verts*/
 +      EDBM_clear_flag_all(em, BM_SELECT);
 +
 +      BMO_Exec_Op(em->bm, &bmop);
 +      
 +      BMO_ITER(f, &siter, em->bm, &bmop, "faceout", BM_FACE) {
 +              BM_Select(em->bm, f, 1);
 +
 +              /*set face vertex normals to face normal*/
 +              BM_ITER(l, &liter, em->bm, BM_LOOPS_OF_FACE, f) {
 +                      VECCOPY(l->v->no, f->no);
 +              }
 +      }
 +
 +      if (!EDBM_FinishOp(em, &bmop, op, 1)) return 0;
 +
 +      return 's'; // s is shrink/fatten
 +}
 +
 +#if 0
 +short EDBM_Extrude_face_indiv(BMEditMesh *em, wmOperator *op, short flag, float *nor) 
 +      EditVert *eve, *v1, *v2, *v3, *v4;
 +      EditEdge *eed;
 +      EditFace *efa, *nextfa;
 +      
 +      if(em==NULL) return 0;
 +      
 +      /* selected edges with 1 or more selected face become faces */
 +      /* selected faces each makes new faces */
 +      /* always remove old faces, keeps volumes manifold */
 +      /* select the new extrusion, deselect old */
 +      
 +      /* step 1; init, count faces in edges */
 +      recalc_editnormals(em);
 +      
 +      for(eve= em->verts.first; eve; eve= eve->next) eve->f1= 0;      // new select flag
 +
 +      for(eed= em->edges.first; eed; eed= eed->next) {
 +              eed->f2= 0; // amount of unselected faces
 +      }
 +      for(efa= em->faces.first; efa; efa= efa->next) {
 +              if(efa->f & SELECT);
 +              else {
 +                      efa->e1->f2++;
 +                      efa->e2->f2++;
 +                      efa->e3->f2++;
 +                      if(efa->e4) efa->e4->f2++;
 +              }
 +      }
 +
 +      /* step 2: make new faces from faces */
 +      for(efa= em->faces.last; efa; efa= efa->prev) {
 +              if(efa->f & SELECT) {
 +                      v1= addvertlist(em, efa->v1->co, efa->v1);
 +                      v2= addvertlist(em, efa->v2->co, efa->v2);
 +                      v3= addvertlist(em, efa->v3->co, efa->v3);
 +                      
 +                      v1->f1= v2->f1= v3->f1= 1;
 +                      VECCOPY(v1->no, efa->n);
 +                      VECCOPY(v2->no, efa->n);
 +                      VECCOPY(v3->no, efa->n);
 +                      if(efa->v4) {
 +                              v4= addvertlist(em, efa->v4->co, efa->v4); 
 +                              v4->f1= 1;
 +                              VECCOPY(v4->no, efa->n);
 +                      }
 +                      else v4= NULL;
 +                      
 +                      /* side faces, clockwise */
 +                      addfacelist(em, efa->v2, v2, v1, efa->v1, efa, NULL);
 +                      addfacelist(em, efa->v3, v3, v2, efa->v2, efa, NULL);
 +                      if(efa->v4) {
 +                              addfacelist(em, efa->v4, v4, v3, efa->v3, efa, NULL);
 +                              addfacelist(em, efa->v1, v1, v4, efa->v4, efa, NULL);
 +                      }
 +                      else {
 +                              addfacelist(em, efa->v1, v1, v3, efa->v3, efa, NULL);
 +                      }
 +                      /* top face */
 +                      addfacelist(em, v1, v2, v3, v4, efa, NULL);
 +              }
 +      }
 +      
 +      /* step 3: remove old faces */
 +      efa= em->faces.first;
 +      while(efa) {
 +              nextfa= efa->next;
 +              if(efa->f & SELECT) {
 +                      BLI_remlink(&em->faces, efa);
 +                      free_editface(em, efa);
 +              }
 +              efa= nextfa;
 +      }
 +
 +      /* step 4: redo selection */
 +      EM_clear_flag_all(em, SELECT);
 +      
 +      for(eve= em->verts.first; eve; eve= eve->next) {
 +              if(eve->f1)  eve->f |= SELECT;
 +      }
 +      
 +      EM_select_flush(em);
 +      
 +      return 'n';
 +}
 +#endif
 +
 +/* extrudes individual edges */
 +short EDBM_Extrude_edges_indiv(BMEditMesh *em, wmOperator *op, short flag, float *nor) 
 +{
 +      BMOperator bmop;
 +
 +      EDBM_InitOpf(em, &bmop, op, "extrude_edge_only edges=%he", flag);
 +
 +      /*deselect original verts*/
 +      EDBM_clear_flag_all(em, BM_SELECT);
 +
 +      BMO_Exec_Op(em->bm, &bmop);
 +      BMO_HeaderFlag_Buffer(em->bm, &bmop, "geomout", BM_SELECT, BM_VERT|BM_EDGE);
 +
 +      if (!EDBM_FinishOp(em, &bmop, op, 1)) return 0;
 +
 +      return 'n'; // n is normal grab
 +}
 +
 +#if 0
 +/* nor is filled with constraint vector */
 +short EDBM_Extrude_edges_indiv(BMEditMesh *em, short flag, float *nor) 
 +{
 +      EditVert *eve;
 +      EditEdge *eed;
 +      EditFace *efa;
 +      
 +      for(eve= em->verts.first; eve; eve= eve->next) eve->tmp.v = NULL;
 +      for(eed= em->edges.first; eed; eed= eed->next) {
 +              eed->tmp.f = NULL;
 +              eed->f2= ((eed->f & flag)!=0);
 +      }
 +      
 +      set_edge_directions_f2(em, 2);
 +
 +      /* sample for next loop */
 +      for(efa= em->faces.first; efa; efa= efa->next) {
 +              efa->e1->tmp.f = efa;
 +              efa->e2->tmp.f = efa;
 +              efa->e3->tmp.f = efa;
 +              if(efa->e4) efa->e4->tmp.f = efa;
 +      }
 +      /* make the faces */
 +      for(eed= em->edges.first; eed; eed= eed->next) {
 +              if(eed->f & flag) {
 +                      if(eed->v1->tmp.v == NULL)
 +                              eed->v1->tmp.v = addvertlist(em, eed->v1->co, eed->v1);
 +                      if(eed->v2->tmp.v == NULL)
 +                              eed->v2->tmp.v = addvertlist(em, eed->v2->co, eed->v2);
 +
 +                      if(eed->dir==1) 
 +                              addfacelist(em, eed->v1, eed->v2, 
 +                                                      eed->v2->tmp.v, eed->v1->tmp.v, 
 +                                                      eed->tmp.f, NULL);
 +                      else 
 +                              addfacelist(em, eed->v2, eed->v1, 
 +                                                      eed->v1->tmp.v, eed->v2->tmp.v, 
 +                                                      eed->tmp.f, NULL);
 +
 +                      /* for transform */
 +                      if(eed->tmp.f) {
 +                              efa = eed->tmp.f;
 +                              if (efa->f & SELECT) add_normal_aligned(nor, efa->n);
 +                      }
 +              }
 +      }
 +      Normalize(nor);
 +      
 +      /* set correct selection */
 +      EM_clear_flag_all(em, SELECT);
 +      for(eve= em->verts.last; eve; eve= eve->prev) {
 +              if(eve->tmp.v) {
 +                      eve->tmp.v->f |= flag;
 +              }
 +      }
 +
 +      for(eed= em->edges.first; eed; eed= eed->next) {
 +              if(eed->v1->f & eed->v2->f & flag) eed->f |= flag;
 +      }
 +      
 +      if(nor[0]==0.0 && nor[1]==0.0 && nor[2]==0.0) return 'g'; // g is grab
 +      return 'n';  // n is for normal constraint
 +}
 +#endif
 +
 +/* extrudes individual vertices */
 +short EDBM_Extrude_verts_indiv(BMEditMesh *em, wmOperator *op, short flag, float *nor) 
 +{
 +      BMOperator bmop;
 +
 +      EDBM_InitOpf(em, &bmop, op, "extrude_vert_indiv verts=%hv", flag);
 +
 +      /*deselect original verts*/
 +      BMO_UnHeaderFlag_Buffer(em->bm, &bmop, "verts", BM_SELECT, BM_VERT);
 +
 +      BMO_Exec_Op(em->bm, &bmop);
 +      BMO_HeaderFlag_Buffer(em->bm, &bmop, "vertout", BM_SELECT, BM_VERT);
 +
 +      if (!EDBM_FinishOp(em, &bmop, op, 1)) return 0;
 +
 +      return 'g'; // g is grab
 +}
 +
 +short EDBM_Extrude_edge(Object *obedit, BMEditMesh *em, int flag, float *nor)
 +{
 +      BMesh *bm = em->bm;
 +      BMIter iter;
 +      BMOIter siter;
 +      BMOperator extop;
 +      BMVert *vert;
 +      BMEdge *edge;
 +      BMFace *f;
 +      ModifierData *md;
 +      BMHeader *el;
 +      
 +      BMO_Init_Op(&extop, "extrudefaceregion");
 +      BMO_HeaderFlag_To_Slot(bm, &extop, "edgefacein",
 +                             flag, BM_VERT|BM_EDGE|BM_FACE);
 +
 +      BM_ITER(vert, &iter, bm, BM_VERTS_OF_MESH, NULL) {
 +              BM_Select(bm, vert, 0);
 +      }
 +
 +      BM_ITER(edge, &iter, bm, BM_EDGES_OF_MESH, NULL) {
 +              BM_Select(bm, edge, 0);
 +      }
 +
 +      BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) {
 +              BM_Select(bm, f, 0);
 +      }
 +
 +      /* If a mirror modifier with clipping is on, we need to adjust some 
 +       * of the cases above to handle edges on the line of symmetry.
 +       */
 +      md = obedit->modifiers.first;
 +      for (; md; md=md->next) {
 +              if (md->type==eModifierType_Mirror) {
 +                      MirrorModifierData *mmd = (MirrorModifierData*) md;     
 +              
 +                      if(mmd->flag & MOD_MIR_CLIPPING) {
 +                              float mtx[4][4];
 +                              if (mmd->mirror_ob) {
 +                                      float imtx[4][4];
 +                                      Mat4Invert(imtx, mmd->mirror_ob->obmat);
 +                                      Mat4MulMat4(mtx, obedit->obmat, imtx);
 +                              }
 +
 +                              for (edge=BMIter_New(&iter,bm,BM_EDGES_OF_MESH,NULL);
 +                                   edge; edge=BMIter_Step(&iter))
 +                              {
 +                                      if(edge->head.flag & flag) {
 +                                              float co1[3], co2[3];
 +
 +                                              VecCopyf(co1, edge->v1->co);
 +                                              VecCopyf(co2, edge->v2->co);
 +
 +                                              if (mmd->mirror_ob) {
 +                                                      VecMat4MulVecfl(co1, mtx, co1);
 +                                                      VecMat4MulVecfl(co2, mtx, co2);
 +                                              }
 +
 +                                              if (mmd->flag & MOD_MIR_AXIS_X)
 +                                                      if ( (fabs(co1[0]) < mmd->tolerance) &&
 +                                                               (fabs(co2[0]) < mmd->tolerance) )
 +                                                              BMO_Insert_MapPointer(bm, &extop, "exclude", edge, NULL);
 +
 +                                              if (mmd->flag & MOD_MIR_AXIS_Y)
 +                                                      if ( (fabs(co1[1]) < mmd->tolerance) &&
 +                                                               (fabs(co2[1]) < mmd->tolerance) )
 +                                                              BMO_Insert_MapPointer(bm, &extop, "exclude", edge, NULL);
 +
 +                                              if (mmd->flag & MOD_MIR_AXIS_Z)
 +                                                      if ( (fabs(co1[2]) < mmd->tolerance) &&
 +                                                               (fabs(co2[2]) < mmd->tolerance) )
 +                                                              BMO_Insert_MapPointer(bm, &extop, "exclude", edge, NULL);
 +                                      }
 +                              }
 +                      }
 +              }
 +      }
 +
 +      BMO_Exec_Op(bm, &extop);
 +
 +      nor[0] = nor[1] = nor[2] = 0.0f;
 +      
 +      BMO_ITER(el, &siter, bm, &extop, "geomout", BM_ALL) {
 +              BM_Select(bm, el, 1);
 +
 +              if (el->type == BM_FACE) {
 +                      f = (BMFace*)el;
 +                      add_normal_aligned(nor, f->no);
 +              };
 +      }
 +
 +      Normalize(nor);
 +
 +      BMO_Finish_Op(bm, &extop);
 +
 +      if(nor[0]==0.0 && nor[1]==0.0 && nor[2]==0.0) return 'g'; // grab
 +      return 'n'; // normal constraint 
 +
 +}
 +short EDBM_Extrude_vert(Object *obedit, BMEditMesh *em, short flag, float *nor)
 +{
 +              BMIter iter;
 +              BMEdge *eed;
 +              
 +              /*ensure vert flags are consistent for edge selections*/
 +              eed = BMIter_New(&iter, em->bm, BM_EDGES_OF_MESH, NULL);
 +              for ( ; eed; eed=BMIter_Step(&iter)) {
 +                      if (BM_TestHFlag(eed, flag)) {
 +                              if (flag != BM_SELECT) {
 +                                      BM_SetHFlag(eed->v1, flag);
 +                                      BM_SetHFlag(eed->v2, flag);
 +                              } else {
 +                                      BM_Select(em->bm, eed->v1, 1);
 +                                      BM_Select(em->bm, eed->v2, 1);
 +                              }
 +                      } else {
 +                              if (BM_TestHFlag(eed->v1, flag) && BM_TestHFlag(eed->v2, flag)) {
 +                                      if (flag != BM_SELECT)
 +                                              BM_SetHFlag(eed, flag);
 +                                      else BM_Select(em->bm, eed, 1);
 +                              }
 +                      }
 +              }
 +
 +              return EDBM_Extrude_edge(obedit, em, flag, nor);
 +
 +}
 +
 +static int extrude_repeat_mesh(bContext *C, wmOperator *op)
 +{
 +      Object *obedit= CTX_data_edit_object(C);
 +      Scene *scene = CTX_data_scene(C);
 +      BMEditMesh *em = ((Mesh *)obedit->data)->edit_btmesh;
 +      RegionView3D *rv3d = CTX_wm_region_view3d(C);           
 +              
 +      int steps = RNA_int_get(op->ptr,"steps");
 +      
 +      float offs = RNA_float_get(op->ptr,"offset");
 +      float dvec[3], tmat[3][3], bmat[3][3], nor[3]= {0.0, 0.0, 0.0};
 +      short a;
 +
 +      /* dvec */
 +      dvec[0]= rv3d->persinv[2][0];
 +      dvec[1]= rv3d->persinv[2][1];
 +      dvec[2]= rv3d->persinv[2][2];
 +      Normalize(dvec);
 +      dvec[0]*= offs;
 +      dvec[1]*= offs;
 +      dvec[2]*= offs;
 +
 +      /* base correction */
 +      Mat3CpyMat4(bmat, obedit->obmat);
 +      Mat3Inv(tmat, bmat);
 +      Mat3MulVecfl(tmat, dvec);
 +
 +      for(a=0; a<steps; a++) {
 +              EDBM_Extrude_edge(obedit, em, BM_SELECT, nor);
 +              //BMO_CallOpf(em->bm, "extrudefaceregion edgefacein=%hef", BM_SELECT);
 +              BMO_CallOpf(em->bm, "translate vec=%v verts=%hv", (float*)dvec, BM_SELECT);
 +              //extrudeflag(obedit, em, SELECT, nor);
 +              //translateflag(em, SELECT, dvec);
 +      }
 +      
 +      EDBM_RecalcNormals(em);
 +
 +      WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, obedit);
 +
 +      DAG_object_flush_update(scene, obedit, OB_RECALC_DATA);
 +      return OPERATOR_FINISHED;
 +}
 +
 +void MESH_OT_extrude_repeat(wmOperatorType *ot)
 +{
 +      /* identifiers */
 +      ot->name= "Extrude Repeat Mesh";
++      ot->description= "Extrude selected vertices, edges or faces repeatedly.";
 +      ot->idname= "MESH_OT_extrude_repeat";
 +      
 +      /* api callbacks */
 +      ot->exec= extrude_repeat_mesh;
 +      ot->poll= ED_operator_editmesh;
 +      
 +      /* flags */
 +      ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
 +      
 +      /* props */
 +      RNA_def_float(ot->srna, "offset", 2.0f, 0.0f, 100.0f, "Offset", "", 0.0f, FLT_MAX);
 +      RNA_def_int(ot->srna, "steps", 10, 0, 180, "Steps", "", 0, INT_MAX);
 +}
 +
 +/* generic extern called extruder */
 +int EDBM_Extrude_Mesh(Object *obedit, BMEditMesh *em, wmOperator *op, float *norin)
 +{
 +      Scene *scene= NULL;             // XXX CTX!
 +      short nr, transmode= 0;
 +      float stacknor[3] = {0.0f, 0.0f, 0.0f};
 +      float *nor = norin ? norin : stacknor;
 +
 +      nor[0] = nor[1] = nor[2] = 0.0f;
 +
 +      if(em->selectmode & SCE_SELECT_VERTEX) {
 +              if(em->bm->totvertsel==0) nr= 0;
 +              else if(em->bm->totvertsel==1) nr= 4;
 +              else if(em->bm->totedgesel==0) nr= 4;
 +              else if(em->bm->totfacesel==0) 
 +                      nr= 3; // pupmenu("Extrude %t|Only Edges%x3|Only Vertices%x4");
 +              else if(em->bm->totfacesel==1)
 +                      nr= 1; // pupmenu("Extrude %t|Region %x1|Only Edges%x3|Only Vertices%x4");
 +              else 
 +                      nr= 1; // pupmenu("Extrude %t|Region %x1||Individual Faces %x2|Only Edges%x3|Only Vertices%x4");
 +      }
 +      else if(em->selectmode & SCE_SELECT_EDGE) {
 +              if (em->bm->totedgesel==0) nr = 0;
 +              
 +              nr = 1;
 +              /*else if (em->totedgesel==1) nr = 3;
 +              else if(em->totfacesel==0) nr = 3;
 +              else if(em->totfacesel==1)
 +                      nr= 1; // pupmenu("Extrude %t|Region %x1|Only Edges%x3");
 +              else
 +                      nr= 1; // pupmenu("Extrude %t|Region %x1||Individual Faces %x2|Only Edges%x3");
 +              */
 +      }
 +      else {
 +              if (em->bm->totfacesel == 0) nr = 0;
 +              else if (em->bm->totfacesel == 1) nr = 1;
 +              else
 +                      nr= 1; // pupmenu("Extrude %t|Region %x1||Individual Faces %x2");
 +      }
 +
 +      if(nr<1) return 'g';
 +
 +      if(nr==1 && em->selectmode & SCE_SELECT_VERTEX) 
 +              transmode= EDBM_Extrude_vert(obedit, em, SELECT, nor);
 +      else if (nr == 1) transmode= EDBM_Extrude_edge(obedit, em, SELECT, nor);
 +      else if(nr==4) transmode= EDBM_Extrude_verts_indiv(em, op, SELECT, nor);
 +      else if(nr==3) transmode= EDBM_Extrude_edges_indiv(em, op, SELECT, nor);
 +      else transmode= EDBM_Extrude_face_indiv(em, op, SELECT, nor);
 +      
 +      if(transmode==0) {
 +              BKE_report(op->reports, RPT_ERROR, "Not a valid selection for extrude");
 +      }
 +      else {
 +              
 +                      /* We need to force immediate calculation here because 
 +                      * transform may use derived objects (which are now stale).
 +                      *
 +                      * This shouldn't be necessary, derived queries should be
 +                      * automatically building this data if invalid. Or something.
 +                      */
 +//            DAG_object_flush_update(scene, obedit, OB_RECALC_DATA); 
 +              object_handle_update(scene, obedit);
 +
 +              /* individual faces? */
 +//            BIF_TransformSetUndo("Extrude");
 +              if(nr==2) {
 +//                    initTransform(TFM_SHRINKFATTEN, CTX_NO_PET|CTX_NO_MIRROR);
 +//                    Transform();
 +              }
 +              else {
 +//                    initTransform(TFM_TRANSLATION, CTX_NO_PET|CTX_NO_MIRROR);
 +                      if(transmode=='n') {
 +                              Mat4MulVecfl(obedit->obmat, nor);
 +                              VecSubf(nor, nor, obedit->obmat[3]);
 +//                            BIF_setSingleAxisConstraint(nor, "along normal");
 +                      }
 +//                    Transform();
 +              }
 +      }
 +      
 +      return transmode;
 +}
 +
 +/* extrude without transform */
 +static int mesh_extrude_region_exec(bContext *C, wmOperator *op)
 +{
 +      Object *obedit= CTX_data_edit_object(C);
 +      BMEditMesh *em= ((Mesh*)obedit->data)->edit_btmesh;
 +      
 +      EDBM_Extrude_Mesh(obedit, em, op, NULL);
 +      
 +      WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, obedit);
 +      
 +      return OPERATOR_FINISHED;       
 +}
 +
 +static int mesh_extrude_region_invoke(bContext *C, wmOperator *op, wmEvent *event)
 +{
 +      Scene *scene= CTX_data_scene(C);
 +      Object *obedit= CTX_data_edit_object(C);
 +      BMEditMesh *em= ((Mesh*)obedit->data)->edit_btmesh;
 +      float nor[3];
 +      int constraint_axis[3] = {0, 0, 1};
 +      int tmode;
 +
 +      tmode = EDBM_Extrude_edge(obedit, em, BM_SELECT, nor);
 +
 +      DAG_object_flush_update(scene, obedit, OB_RECALC_DATA);
 +      WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, obedit);
 +
 +      RNA_enum_set(op->ptr, "proportional", 0);
 +      RNA_boolean_set(op->ptr, "mirror", 0);
 +
 +      if (tmode == 'n') {
 +              RNA_enum_set(op->ptr, "constraint_orientation", V3D_MANIP_NORMAL);
 +              RNA_boolean_set_array(op->ptr, "constraint_axis", constraint_axis);
 +      }
 +      WM_operator_name_call(C, "TFM_OT_translate", WM_OP_INVOKE_REGION_WIN, op->ptr);
 +
 +      return OPERATOR_FINISHED;
 +}
 +
 +void MESH_OT_extrude_region(wmOperatorType *ot)
 +{
 +      /* identifiers */
 +      ot->name= "Extrude Region";
 +      ot->idname= "MESH_OT_extrude_region";
 +      
 +      /* api callbacks */
 +      ot->invoke= mesh_extrude_region_invoke;
 +      ot->exec= mesh_extrude_region_exec;
 +      ot->poll= ED_operator_editmesh;
 +      
 +      /* flags */
 +      ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
 +
 +      /* to give to transform */
 +      Properties_Proportional(ot);
 +      Properties_Constraints(ot);
 +      RNA_def_boolean(ot->srna, "mirror", 0, "Mirror Editing", "");
 +}
 +
 +static int mesh_extrude_verts_exec(bContext *C, wmOperator *op)
 +{
 +      Object *obedit= CTX_data_edit_object(C);
 +      BMEditMesh *em= ((Mesh*)obedit->data)->edit_btmesh;
 +      float nor[3];
 +
 +      EDBM_Extrude_verts_indiv(em, op, BM_SELECT, nor);
 +      
 +      WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, obedit);
 +      
 +      return OPERATOR_FINISHED;       
 +}
 +
 +static int mesh_extrude_verts_invoke(bContext *C, wmOperator *op, wmEvent *event)
 +{
 +      Scene *scene= CTX_data_scene(C);
 +      Object *obedit= CTX_data_edit_object(C);
 +      BMEditMesh *em= ((Mesh*)obedit->data)->edit_btmesh;
 +      float nor[3];
 +      int constraint_axis[3] = {0, 0, 1};
 +      int tmode;
 +
 +      tmode = EDBM_Extrude_verts_indiv(em, op, BM_SELECT, nor);
 +
 +      DAG_object_flush_update(scene, obedit, OB_RECALC_DATA);
 +      WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, obedit);
 +
 +      RNA_enum_set(op->ptr, "proportional", 0);
 +      RNA_boolean_set(op->ptr, "mirror", 0);
 +
 +      if (tmode == 'n') {
 +              RNA_enum_set(op->ptr, "constraint_orientation", V3D_MANIP_NORMAL);
 +              RNA_boolean_set_array(op->ptr, "constraint_axis", constraint_axis);
 +      }
 +      WM_operator_name_call(C, "TFM_OT_translate", WM_OP_INVOKE_REGION_WIN, op->ptr);
 +
 +      return OPERATOR_FINISHED;
 +}
 +
 +void MESH_OT_extrude_verts_indiv(wmOperatorType *ot)
 +{
 +      /* identifiers */
 +      ot->name= "Extrude Only Vertices";
 +      ot->idname= "MESH_OT_extrude_verts_indiv";
 +      
 +      /* api callbacks */
 +      ot->invoke= mesh_extrude_verts_invoke;
 +      ot->exec= mesh_extrude_verts_exec;
 +      ot->poll= ED_operator_editmesh;
 +      
 +      /* flags */
 +      ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
 +
 +      /* to give to transform */
 +      Properties_Proportional(ot);
 +      Properties_Constraints(ot);
 +      RNA_def_boolean(ot->srna, "mirror", 0, "Mirror Editing", "");
 +}
 +
 +static int mesh_extrude_edges_exec(bContext *C, wmOperator *op)
 +{
 +      Object *obedit= CTX_data_edit_object(C);
 +      BMEditMesh *em= ((Mesh*)obedit->data)->edit_btmesh;
 +      float nor[3];
 +
 +      EDBM_Extrude_edges_indiv(em, op, BM_SELECT, nor);
 +      
 +      WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, obedit);
 +      
 +      return OPERATOR_FINISHED;       
 +}
 +
 +static int mesh_extrude_edges_invoke(bContext *C, wmOperator *op, wmEvent *event)
 +{
 +      Scene *scene= CTX_data_scene(C);
 +      Object *obedit= CTX_data_edit_object(C);
 +      BMEditMesh *em= ((Mesh*)obedit->data)->edit_btmesh;
 +      float nor[3];
 +      int constraint_axis[3] = {0, 0, 1};
 +      int tmode;
 +
 +      tmode = EDBM_Extrude_edges_indiv(em, op, BM_SELECT, nor);
 +
 +      DAG_object_flush_update(scene, obedit, OB_RECALC_DATA);
 +      WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, obedit);
 +
 +      RNA_enum_set(op->ptr, "proportional", 0);
 +      RNA_boolean_set(op->ptr, "mirror", 0);
 +
 +      /*if (tmode == 'n') {
 +              RNA_enum_set(op->ptr, "constraint_orientation", V3D_MANIP_NORMAL);
 +              RNA_boolean_set_array(op->ptr, "constraint_axis", constraint_axis);
 +      }*/
 +      WM_operator_name_call(C, "TFM_OT_translate", WM_OP_INVOKE_REGION_WIN, op->ptr);
 +
 +      return OPERATOR_FINISHED;
 +}
 +
 +void MESH_OT_extrude_edges_indiv(wmOperatorType *ot)
 +{
 +      /* identifiers */
 +      ot->name= "Extrude Only Edges";
 +      ot->idname= "MESH_OT_extrude_edges_indiv";
 +      
 +      /* api callbacks */
 +      ot->invoke= mesh_extrude_edges_invoke;
 +      ot->exec= mesh_extrude_edges_exec;
 +      ot->poll= ED_operator_editmesh;
 +      
 +      /* flags */
 +      ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
 +
 +      /* to give to transform */
 +      Properties_Proportional(ot);
 +      Properties_Constraints(ot);
 +      RNA_def_boolean(ot->srna, "mirror", 0, "Mirror Editing", "");
 +}
 +
 +static int mesh_extrude_faces_exec(bContext *C, wmOperator *op)
 +{
 +      Object *obedit= CTX_data_edit_object(C);
 +      BMEditMesh *em= ((Mesh*)obedit->data)->edit_btmesh;
 +      float nor[3];
 +
 +      EDBM_Extrude_face_indiv(em, op, BM_SELECT, nor);
 +      
 +      WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, obedit);
 +      
 +      return OPERATOR_FINISHED;       
 +}
 +
 +static int mesh_extrude_faces_invoke(bContext *C, wmOperator *op, wmEvent *event)
 +{
 +      Scene *scene= CTX_data_scene(C);
 +      Object *obedit= CTX_data_edit_object(C);
 +      BMEditMesh *em= ((Mesh*)obedit->data)->edit_btmesh;
 +      float nor[3];
 +      int constraint_axis[3] = {0, 0, 1};
 +      int tmode;
 +
 +      tmode = EDBM_Extrude_face_indiv(em, op, BM_SELECT, nor);
 +
 +      DAG_object_flush_update(scene, obedit, OB_RECALC_DATA);
 +      WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, obedit);
 +
 +      RNA_enum_set(op->ptr, "proportional", 0);
 +      RNA_boolean_set(op->ptr, "mirror", 0);
 +      
 +      if (tmode == 's') {
 +              WM_operator_name_call(C, "TFM_OT_shrink_fatten", WM_OP_INVOKE_REGION_WIN, op->ptr);
 +      } else {
 +              if (tmode == 'n') {
 +                      RNA_enum_set(op->ptr, "constraint_orientation", V3D_MANIP_NORMAL);
 +                      RNA_boolean_set_array(op->ptr, "constraint_axis", constraint_axis);
 +              }
 +              WM_operator_name_call(C, "TFM_OT_translate", WM_OP_INVOKE_REGION_WIN, op->ptr);
 +      }
 +      return OPERATOR_FINISHED;
 +}
 +
 +void MESH_OT_extrude_faces_indiv(wmOperatorType *ot)
 +{
 +      /* identifiers */
 +      ot->name= "Extrude Individual Faces";
 +      ot->idname= "MESH_OT_extrude_faces_indiv";
 +      
 +      /* api callbacks */
 +      ot->invoke= mesh_extrude_faces_invoke;
 +      ot->exec= mesh_extrude_faces_exec;
 +      ot->poll= ED_operator_editmesh;
 +      
 +      /* flags */
 +      ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
 +
 +      /* to give to transform */
 +      Properties_Proportional(ot);
 +      Properties_Constraints(ot);
 +      RNA_def_boolean(ot->srna, "mirror", 0, "Mirror Editing", "");
 +}
 +
 +int extrude_menu_invoke(bContext *C, wmOperator *op, wmEvent *event)
 +{
 +      Object *obedit= CTX_data_edit_object(C);
 +      BMEditMesh *em= ((Mesh*)obedit->data)->edit_btmesh;
 +      uiPopupMenu *pup;
 +      uiLayout *layout;
 +
 +      if(em->selectmode & SCE_SELECT_VERTEX) {
 +              if(em->bm->totvertsel==0) {
 +                      return OPERATOR_CANCELLED;
 +              } else if(em->bm->totvertsel==1) {
 +                      WM_operator_name_call(C, "MESH_OT_extrude_verts_indiv", WM_OP_INVOKE_REGION_WIN, op->ptr);
 +              } else if(em->bm->totedgesel==0) {
 +                      WM_operator_name_call(C, "MESH_OT_extrude_verts_indiv", WM_OP_INVOKE_REGION_WIN, op->ptr);
 +              } else if(em->bm->totfacesel==0) {
 +                      // pupmenu("Extrude %t|Only Edges%x3|Only Vertices%x4");
 +                      pup= uiPupMenuBegin(C, "Extrude", 0);
 +                      layout= uiPupMenuLayout(pup);
 +                      uiLayoutSetOperatorContext(layout, WM_OP_INVOKE_REGION_WIN);
 +                      
 +                      uiItemO(layout, "Only Edges", 0, "MESH_OT_extrude_edges_indiv");
 +                      uiItemO(layout, "Only Verts", 0, "MESH_OT_extrude_verts_indiv");
 +                      
 +                      uiPupMenuEnd(C, pup);
 +              } else if(em->bm->totfacesel==1) {
 +                      // pupmenu("Extrude %t|Region %x1|Only Edges%x3|Only Vertices%x4");
 +                      pup= uiPupMenuBegin(C, "Extrude", 0);
 +                      layout= uiPupMenuLayout(pup);
 +                      uiLayoutSetOperatorContext(layout, WM_OP_INVOKE_REGION_WIN);
 +                      
 +                      uiItemO(layout, "Region", 0, "MESH_OT_extrude_region");
 +                      uiItemO(layout, "Only Edges", 0, "MESH_OT_extrude_edges_indiv");
 +                      uiItemO(layout, "Only Verts", 0, "MESH_OT_extrude_verts_indiv");
 +                      
 +                      uiPupMenuEnd(C, pup);
 +              } else  {
 +                      // pupmenu("Extrude %t|Region %x1||Individual Faces %x2|Only Edges%x3|Only Vertices%x4");
 +                      pup= uiPupMenuBegin(C, "Extrude", 0);
 +                      layout= uiPupMenuLayout(pup);
 +                      uiLayoutSetOperatorContext(layout, WM_OP_INVOKE_REGION_WIN);
 +                      
 +                      uiItemO(layout, "Region", 0, "MESH_OT_extrude_region");
 +                      uiItemO(layout, "Individual Faces", 0, "MESH_OT_extrude_faces_indiv");
 +                      uiItemO(layout, "Only Edges", 0, "MESH_OT_extrude_edges_indiv");
 +                      uiItemO(layout, "Only Verts", 0, "MESH_OT_extrude_verts_indiv");
 +                      
 +                      uiPupMenuEnd(C, pup);
 +              }
 +      } else if (em->selectmode & SCE_SELECT_EDGE) {
 +              if (em->bm->totedge==0)
 +                      return OPERATOR_CANCELLED;
 +              else if (em->bm->totedgesel==1)
 +                      WM_operator_name_call(C, "MESH_OT_extrude_edges_indiv", WM_OP_INVOKE_REGION_WIN, op->ptr);
 +              else if (em->bm->totfacesel==0) {
 +                      WM_operator_name_call(C, "MESH_OT_extrude_edges_indiv", WM_OP_INVOKE_REGION_WIN, op->ptr);
 +              } else if (em->bm->totfacesel==1) {
 +                      // pupmenu("Extrude %t|Region %x1|Only Edges%x3");
 +                      pup= uiPupMenuBegin(C, "Extrude", 0);
 +                      layout= uiPupMenuLayout(pup);
 +                      uiLayoutSetOperatorContext(layout, WM_OP_INVOKE_REGION_WIN);
 +                      
 +                      uiItemO(layout, "Region", 0, "MESH_OT_extrude_region");
 +                      uiItemO(layout, "Only Edges", 0, "MESH_OT_extrude_edges_indiv");
 +                      
 +                      uiPupMenuEnd(C, pup);
 +              } else {
 +                      // pupmenu("Extrude %t|Region %x1||Individual Faces %x2|Only Edges%x3");
 +                      pup= uiPupMenuBegin(C, "Extrude", 0);
 +                      layout= uiPupMenuLayout(pup);
 +                      uiLayoutSetOperatorContext(layout, WM_OP_INVOKE_REGION_WIN);
 +                      
 +                      uiItemO(layout, "Region", 0, "MESH_OT_extrude_region");
 +                      uiItemO(layout, "Individual Faces", 0, "MESH_OT_extrude_faces_indiv");
 +                      uiItemO(layout, "Only Edges", 0, "MESH_OT_extrude_edges_indiv");
 +                      
 +                      uiPupMenuEnd(C, pup);
 +              }
 +
 +      } else if (em->selectmode & SCE_SELECT_FACE) {
 +              if (em->bm->totfacesel==0)
 +                      return OPERATOR_CANCELLED;
 +              else if (em->bm->totfacesel==1)
 +                      WM_operator_name_call(C, "MESH_OT_extrude_region", WM_OP_INVOKE_REGION_WIN, op->ptr);
 +              else {
 +                      // pupmenu("Extrude %t|Region %x1||Individual Faces %x2");
 +                      pup= uiPupMenuBegin(C, "Extrude", 0);
 +                      layout= uiPupMenuLayout(pup);
 +                      uiLayoutSetOperatorContext(layout, WM_OP_INVOKE_REGION_WIN);
 +                      
 +                      uiItemO(layout, "Region", 0, "MESH_OT_extrude_region");
 +                      uiItemO(layout, "Individual Faces", 0, "MESH_OT_extrude_faces_indiv");
 +                      
 +                      uiPupMenuEnd(C, pup);
 +              }
 +      }
 +
 +      return OPERATOR_CANCELLED;
 +}
 +
 +void MESH_OT_extrude(wmOperatorType *ot)
 +{
 +      /* identifiers */
 +      ot->name= "Extrude";
++      ot->description= "Extrude selected vertices, edges or faces.";
 +      ot->idname= "MESH_OT_extrude";
 +      
 +      /* api callbacks */
 +      ot->invoke= extrude_menu_invoke;
 +      ot->poll= ED_operator_editmesh;
 +}
 +
 +/* ******************** (de)select all operator **************** */
 +
 +void EDBM_toggle_select_all(BMEditMesh *em) /* exported for UV */
 +{
 +      if(em->bm->totvertsel || em->bm->totedgesel || em->bm->totfacesel)
 +              EDBM_clear_flag_all(em, SELECT);
 +      else 
 +              EDBM_set_flag_all(em, SELECT);
 +}
 +
 +static int toggle_select_all_exec(bContext *C, wmOperator *op)
 +{
 +      Object *obedit= CTX_data_edit_object(C);
 +      BMEditMesh *em= ((Mesh *)obedit->data)->edit_btmesh;
 +      
 +      EDBM_toggle_select_all(em);
 +      
 +      WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, obedit);
 +
 +      return OPERATOR_FINISHED;
 +}
 +
 +void MESH_OT_select_all_toggle(wmOperatorType *ot)
 +{
 +      /* identifiers */
 +      ot->name= "Select/Deselect All";
 +      ot->idname= "MESH_OT_select_all_toggle";
++      ot->description= "(de)select all vertices, edges or faces.";
 +      
 +      /* api callbacks */
 +      ot->exec= toggle_select_all_exec;
 +      ot->poll= ED_operator_editmesh;
 +      
 +      /* flags */
 +      ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
 +}
 +
 +/* *************** add-click-mesh (extrude) operator ************** */
 +
 +static int dupli_extrude_cursor(bContext *C, wmOperator *op, wmEvent *event)
 +{
 +      ViewContext vc;
 +      BMVert *v1;
 +      BMIter iter;
 +      float min[3], max[3];
 +      int done= 0;
 +      
 +      em_setup_viewcontext(C, &vc);
 +      
 +      INIT_MINMAX(min, max);
 +      
 +      BM_ITER_SELECT(v1, &iter, vc.em->bm, BM_VERTS_OF_MESH, NULL)
 +              DO_MINMAX(v1->co, min, max);
 +              done= 1;
 +      }
 +
 +      /* call extrude? */
 +      if(done) {
 +              BMEdge *eed;
 +              float vec[3], cent[3], mat[3][3];
 +              float nor[3]= {0.0, 0.0, 0.0};
 +              
 +              /* check for edges that are half selected, use for rotation */
 +              done= 0;
 +              BM_ITER(eed, &iter, vc.em->bm, BM_EDGES_OF_MESH, NULL) {
 +                      if (BM_TestHFlag(eed->v1, BM_SELECT) ^ BM_TestHFlag(eed->v2, BM_SELECT)) {
 +                              if(BM_TestHFlag(eed->v1, BM_SELECT)) 
 +                                      VecSubf(vec, eed->v1->co, eed->v2->co);
 +                              else 
 +                                      VecSubf(vec, eed->v2->co, eed->v1->co);
 +                              VecAddf(nor, nor, vec);
 +                              done= 1;
 +                      }
 +              }
 +              if(done) Normalize(nor);
 +              
 +              /* center */
 +              VecAddf(cent, min, max);
 +              VecMulf(cent, 0.5f);
 +              VECCOPY(min, cent);
 +              
 +              Mat4MulVecfl(vc.obedit->obmat, min);    // view space
 +              view3d_get_view_aligned_coordinate(&vc, min, event->mval);
 +              Mat4Invert(vc.obedit->imat, vc.obedit->obmat); 
 +              Mat4MulVecfl(vc.obedit->imat, min); // back in object space
 +              
 +              VecSubf(min, min, cent);
 +              
 +              /* calculate rotation */
 +              Mat3One(mat);
 +              if(done) {
 +                      float dot;
 +                      
 +                      VECCOPY(vec, min);
 +                      Normalize(vec);
 +                      dot= INPR(vec, nor);
 +
 +                      if( fabs(dot)<0.999) {
 +                              float cross[3], si, q1[4];
 +                              
 +                              Crossf(cross, nor, vec);
 +                              Normalize(cross);
 +                              dot= 0.5f*saacos(dot);
 +                              si= (float)sin(dot);
 +                              q1[0]= (float)cos(dot);
 +                              q1[1]= cross[0]*si;
 +                              q1[2]= cross[1]*si;
 +                              q1[3]= cross[2]*si;
 +                              
 +                              QuatToMat3(q1, mat);
 +                      }
 +              }
 +              
 +
 +              EDBM_Extrude_edge(vc.obedit, vc.em, SELECT, nor);
 +              EDBM_CallOpf(vc.em, op, "rotate verts=%hv cent=%v mat=%m3",
 +                      BM_SELECT, cent, mat);
 +              EDBM_CallOpf(vc.em, op, "translate verts=%hv vec=%v",
 +                      BM_SELECT, min);
 +      }
 +      else {
 +              float *curs= give_cursor(vc.scene, vc.v3d);
 +              BMOperator bmop;
 +              BMOIter oiter;
 +              
 +              VECCOPY(min, curs);
 +
 +              view3d_get_view_aligned_coordinate(&vc, min, event->mval);
 +              Mat4Invert(vc.obedit->imat, vc.obedit->obmat); 
 +              Mat4MulVecfl(vc.obedit->imat, min); // back in object space
 +              
 +              EDBM_InitOpf(vc.em, &bmop, op, "makevert co=%v", min);
 +              BMO_Exec_Op(vc.em->bm, &bmop);
 +
 +              BMO_ITER(v1, &oiter, vc.em->bm, &bmop, "newvertout", BM_VERT) {
 +                      BM_Select(vc.em->bm, v1, 1);
 +              }
 +
 +              if (!EDBM_FinishOp(vc.em, &bmop, op, 1))
 +                      return OPERATOR_CANCELLED;
 +      }
 +
 +      //retopo_do_all();
 +      WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, vc.obedit); 
 +      DAG_object_flush_update(vc.scene, vc.obedit, OB_RECALC_DATA);
 +      
 +      return OPERATOR_FINISHED;
 +}
 +
 +void MESH_OT_dupli_extrude_cursor(wmOperatorType *ot)
 +{
 +      /* identifiers */
 +      ot->name= "Duplicate or Extrude at 3D Cursor";
 +      ot->idname= "MESH_OT_dupli_extrude_cursor";
 +      
 +      /* api callbacks */
 +      ot->invoke= dupli_extrude_cursor;
++      ot->description= "Duplicate and extrude selected vertices, edges or faces towards the mouse cursor.";
 +      ot->poll= ED_operator_editmesh;
 +      
 +      /* flags */
 +      ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
 +}
 +
 +static int delete_mesh(Object *obedit, wmOperator *op, int event, Scene *scene)
 +{
 +      BMEditMesh *bem = ((Mesh*)obedit->data)->edit_btmesh;
 +      
 +      if(event<1) return OPERATOR_CANCELLED;
 +
 +      if(event==10 ) {
 +              //"Erase Vertices";
 +
 +              if (!EDBM_CallOpf(bem, op, "del geom=%hv context=%i", BM_SELECT, DEL_VERTS))
 +                      return OPERATOR_CANCELLED;
 +      } 
 +      else if(event==11) {
 +              //"Edge Loop"
 +              if (!EDBM_CallOpf(bem, op, "dissolveedgeloop edges=%he", BM_SELECT))
 +                      return OPERATOR_CANCELLED;
 +      }
 +      else if(event==7) {
 +              //"Dissolve"
 +              if (bem->selectmode & SCE_SELECT_FACE) {
 +                      if (!EDBM_CallOpf(bem, op, "dissolvefaces faces=%hf",BM_SELECT))
 +                              return OPERATOR_CANCELLED;
 +              } else if (bem->selectmode & SCE_SELECT_EDGE) {
 +                      if (!EDBM_CallOpf(bem, op, "dissolveedges edges=%he",BM_SELECT))
 +                              return OPERATOR_CANCELLED;
 +              } else if (bem->selectmode & SCE_SELECT_VERTEX) {
 +                      if (!EDBM_CallOpf(bem, op, "dissolveverts verts=%hv",BM_SELECT))
 +                              return OPERATOR_CANCELLED;
 +              }
 +      }
 +      else if(event==4) {
 +              //Edges and Faces
 +              if (!EDBM_CallOpf(bem, op, "del geom=%hef context=%i", BM_SELECT, DEL_EDGESFACES))
 +                      return OPERATOR_CANCELLED;
 +      } 
 +      else if(event==1) {
 +              //"Erase Edges"
 +              if (!EDBM_CallOpf(bem, op, "del geom=%he context=%i", BM_SELECT, DEL_EDGES))
 +                      return OPERATOR_CANCELLED;
 +      }
 +      else if(event==2) {
 +              //"Erase Faces";
 +              if (!EDBM_CallOpf(bem, op, "del geom=%hf context=%i", BM_SELECT, DEL_FACES))
 +                      return OPERATOR_CANCELLED;
 +      }
 +      else if(event==5) {
 +              //"Erase Only Faces";
 +              if (!EDBM_CallOpf(bem, op, "del geom=%hf context=%d",
 +                                BM_SELECT, DEL_ONLYFACES))
 +                      return OPERATOR_CANCELLED;
 +      }
 +      
 +      DAG_object_flush_update(scene, obedit, OB_RECALC_DATA);
 +
 +      return OPERATOR_FINISHED;
 +}
 +
 +/* Note, these values must match delete_mesh() event values */
 +static EnumPropertyItem prop_mesh_delete_types[] = {
 +      {7, "DISSOLVE",         0, "Dissolve", ""},
 +      {10,"VERT",             0, "Vertices", ""},
 +      {1, "EDGE",             0, "Edges", ""},
 +      {2, "FACE",             0, "Faces", ""},
 +      {11, "EDGE_LOOP", 0, "Edge Loop", ""},
 +      {4, "EDGE_FACE", 0, "Edges & Faces", ""},
 +      {5, "ONLY_FACE", 0, "Only Faces", ""},
 +      {0, NULL, 0, NULL, NULL}
 +};
 +
 +static int delete_mesh_exec(bContext *C, wmOperator *op)
 +{
 +      Object *obedit= CTX_data_edit_object(C);
 +      Scene *scene = CTX_data_scene(C);
 +
 +      delete_mesh(obedit, op, RNA_enum_get(op->ptr, "type"), scene);
 +      
 +      WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_DATA|ND_GEOM_SELECT, obedit);
 +      
 +      return OPERATOR_FINISHED;
 +}
 +
 +void MESH_OT_delete(wmOperatorType *ot)
 +{
 +      /* identifiers */
 +      ot->name= "Delete";
++      ot->description= "Delete selected vertices, edges or faces.";
 +      ot->idname= "MESH_OT_delete";
 +      
 +      /* api callbacks */
 +      ot->invoke= WM_menu_invoke;
 +      ot->exec= delete_mesh_exec;
 +      
 +      ot->poll= ED_operator_editmesh;
 +      
 +      /* flags */
 +      ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
 +      
 +      /*props */
 +      RNA_def_enum(ot->srna, "type", prop_mesh_delete_types, 10, "Type", "Method used for deleting mesh data");
 +}
 +
 +
 +static int addedgeface_mesh_exec(bContext *C, wmOperator *op)
 +{
 +      BMOperator bmop;
 +      Object *obedit= CTX_data_edit_object(C);
 +      BMEditMesh *em= ((Mesh *)obedit->data)->edit_btmesh;
 +      
 +      if (!EDBM_InitOpf(em, &bmop, op, "contextual_create geom=%hfev", BM_SELECT))
 +              return OPERATOR_CANCELLED;
 +      
 +      BMO_Exec_Op(em->bm, &bmop);
 +      BMO_HeaderFlag_Buffer(em->bm, &bmop, "faceout", BM_SELECT, BM_FACE);
 +
 +      if (!EDBM_FinishOp(em, &bmop, op, 1))
 +              return OPERATOR_CANCELLED;
 +
 +      WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, obedit);
 +      DAG_object_flush_update(CTX_data_scene(C), obedit, OB_RECALC_DATA);     
 +      
 +      return OPERATOR_FINISHED;
 +}
 +
 +void MESH_OT_edge_face_add(wmOperatorType *ot)
 +{
 +      /* identifiers */
 +      ot->name= "Make Edge/Face";
++      ot->description= "Add an edge or face to selected.";
 +      ot->idname= "MESH_OT_edge_face_add";
 +      
 +      /* api callbacks */
 +      ot->exec= addedgeface_mesh_exec;
 +      ot->poll= ED_operator_editmesh;
 +      
 +      /* flags */
 +      ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
 +      
 +}
 +
 +static EnumPropertyItem prop_mesh_edit_types[] = {
 +      {1, "VERT", 0, "Vertices", ""},
 +      {2, "EDGE", 0, "Edges", ""},
 +      {3, "FACE", 0, "Faces", ""},
 +      {0, NULL, 0, NULL, NULL}
 +};
 +
 +static int mesh_selection_type_exec(bContext *C, wmOperator *op)
 +{             
 +      
 +      Object *obedit= CTX_data_edit_object(C);
 +      BMEditMesh *em= ((Mesh *)obedit->data)->edit_btmesh;
 +      int type = RNA_enum_get(op->ptr,"type");
 +
 +      switch (type) {
 +              case 1:
 +                      em->selectmode = SCE_SELECT_VERTEX;
 +                      break;
 +              case 2:
 +                      em->selectmode = SCE_SELECT_EDGE;
 +                      break;
 +              case 3:
 +                      em->selectmode = SCE_SELECT_FACE;
 +                      break;
 +      }
 +
 +      EDBM_selectmode_set(em);
 +      CTX_data_scene(C)->toolsettings->selectmode = em->selectmode;
 +
 +      WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, obedit);
 +      
 +      return OPERATOR_FINISHED;
 +}
 +
 +void MESH_OT_selection_type(wmOperatorType *ot)
 +{
 +      /* identifiers */
 +      ot->name= "Selection Mode";
++      ot->description= "Set the selection mode type.";
 +      ot->idname= "MESH_OT_selection_type";
 +      
 +      /* api callbacks */
 +      ot->invoke= WM_menu_invoke;
 +      ot->exec= mesh_selection_type_exec;
 +      
 +      ot->poll= ED_operator_editmesh;
 +      
 +      /* flags */
 +      ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
 +      
 +      /* props */
 +      RNA_def_enum(ot->srna, "type", prop_mesh_edit_types, 0, "Type", "Set the mesh selection type");
 +      RNA_def_boolean(ot->srna, "inclusive", 0, "Inclusive", "Selects geometry around selected geometry, occording to selection mode");       
 +}
 +
 +/* ************************* SEAMS AND EDGES **************** */
 +
 +static int editbmesh_mark_seam(bContext *C, wmOperator *op)
 +{
 +      Scene *scene = CTX_data_scene(C);
 +      Object *obedit= CTX_data_edit_object(C);
 +      Mesh *me= ((Mesh *)obedit->data);
 +      BMEditMesh *em= ((Mesh *)obedit->data)->edit_btmesh;
 +      BMesh *bm = em->bm;
 +      BMEdge *eed;
 +      BMIter iter;
 +      int clear = RNA_boolean_get(op->ptr, "clear");
 +      
 +      /* auto-enable seams drawing */
 +      if(clear==0) {
 +              me->drawflag |= ME_DRAWSEAMS;
 +      }
 +
 +      if(clear) {
 +              BM_ITER_SELECT(eed, &iter, bm, BM_EDGES_OF_MESH, NULL)
 +                      BM_ClearHFlag(eed, BM_SEAM);
 +              }
 +      }
 +      else {
 +              BM_ITER_SELECT(eed, &iter, bm, BM_EDGES_OF_MESH, NULL)
 +                      BM_SetHFlag(eed, BM_SEAM);
 +              }
 +      }
 +
 +      DAG_object_flush_update(scene, obedit, OB_RECALC_DATA);
 +      WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, obedit);
 +
 +      return OPERATOR_FINISHED;
 +}
 +
 +void MESH_OT_mark_seam(wmOperatorType *ot)
 +{
 +      /* identifiers */
 +      ot->name= "Mark Seam";
 +      ot->idname= "MESH_OT_mark_seam";
++      ot->description= "(un)mark selected edges as a seam.";
 +      
 +      /* api callbacks */
 +      ot->exec= editbmesh_mark_seam;
 +      ot->poll= ED_operator_editmesh;
 +      
 +      /* flags */
 +      ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
 +      
 +      RNA_def_boolean(ot->srna, "clear", 0, "Clear", "");
 +}
 +
 +static int editbmesh_mark_sharp(bContext *C, wmOperator *op)
 +{
 +      Scene *scene = CTX_data_scene(C);
 +      Object *obedit= CTX_data_edit_object(C);
 +      Mesh *me= ((Mesh *)obedit->data);
 +      BMEditMesh *em= ((Mesh *)obedit->data)->edit_btmesh;
 +      BMesh *bm = em->bm;
 +      BMEdge *eed;
 +      BMIter iter;
 +      int clear = RNA_boolean_get(op->ptr, "clear");
 +
 +      /* auto-enable sharp edge drawing */
 +      if(clear == 0) {
 +              me->drawflag |= ME_DRAWSHARP;
 +      }
 +
 +      if(!clear) {
 +              BM_ITER_SELECT(eed, &iter, bm, BM_EDGES_OF_MESH, NULL)
 +                      BM_SetHFlag(eed, BM_SHARP);
 +              }
 +      } else {
 +              BM_ITER_SELECT(eed, &iter, bm, BM_EDGES_OF_MESH, NULL)
 +                      BM_ClearHFlag(eed, BM_SHARP);
 +              }
 +      }
 +
 +
 +      DAG_object_flush_update(scene, obedit, OB_RECALC_DATA);
 +      WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, obedit);
 +
 +      return OPERATOR_FINISHED;
 +}
 +
 +void MESH_OT_mark_sharp(wmOperatorType *ot)
 +{
 +      /* identifiers */
 +      ot->name= "Mark Sharp";
 +      ot->idname= "MESH_OT_mark_sharp";
++      ot->description= "(un)mark selected edges as sharp.";
 +      
 +      /* api callbacks */
 +      ot->exec= editbmesh_mark_sharp;
 +      ot->poll= ED_operator_editmesh;
 +      
 +      /* flags */
 +      ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
 +      
 +      RNA_def_boolean(ot->srna, "clear", 0, "Clear", "");
 +}
 +
 +
 +static int editbmesh_vert_connect(bContext *C, wmOperator *op)
 +{
 +      Scene *scene = CTX_data_scene(C);
 +      Object *obedit= CTX_data_edit_object(C);
 +      Mesh *me= ((Mesh *)obedit->data);
 +      BMEditMesh *em= ((Mesh *)obedit->data)->edit_btmesh;
 +      BMesh *bm = em->bm;
 +      BMOperator bmop;
 +      int len = 0;
 +      
 +      BMO_InitOpf(bm, &bmop, "connectverts verts=%hv", BM_SELECT);
 +      BMO_Exec_Op(bm, &bmop);
 +      len = BMO_GetSlot(&bmop, "edgeout")->len;
 +      BMO_Finish_Op(bm, &bmop);
 +      
 +      DAG_object_flush_update(scene, obedit, OB_RECALC_DATA);
 +      WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, obedit);
 +
 +      return len ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
 +}
 +
 +void MESH_OT_vert_connect(wmOperatorType *ot)
 +{
 +      /* identifiers */
 +      ot->name= "Vertex Connect";
 +      ot->idname= "MESH_OT_vert_connect";
 +      
 +      /* api callbacks */
 +      ot->exec= editbmesh_vert_connect;
 +      ot->poll= ED_operator_editmesh;
 +      
 +      /* flags */
 +      ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
 +}
 +
 +static int editbmesh_edge_split(bContext *C, wmOperator *op)
 +{
 +      Scene *scene = CTX_data_scene(C);
 +      Object *obedit= CTX_data_edit_object(C);
 +      Mesh *me= ((Mesh *)obedit->data);
 +      BMEditMesh *em= ((Mesh *)obedit->data)->edit_btmesh;
 +      BMesh *bm = em->bm;
 +      BMOperator bmop;
 +      int len = 0;
 +      
 +      BMO_InitOpf(bm, &bmop, "edgesplit edges=%he numcuts=%d", BM_SELECT, RNA_int_get(op->ptr,"number_cuts"));
 +      BMO_Exec_Op(bm, &bmop);
 +      len = BMO_GetSlot(&bmop, "outsplit")->len;
 +      BMO_Finish_Op(bm, &bmop);
 +      
 +      DAG_object_flush_update(scene, obedit, OB_RECALC_DATA);
 +      WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, obedit);
 +
 +      return len ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
 +}
 +
 +void MESH_OT_edge_split(wmOperatorType *ot)
 +{
 +      /* identifiers */
 +      ot->name= "Edge Split";
 +      ot->idname= "MESH_OT_edge_split";
 +      
 +      /* api callbacks */
 +      ot->exec= editbmesh_edge_split;
 +      ot->poll= ED_operator_editmesh;
 +      
 +      /* flags */
 +      ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
 +
 +      RNA_def_int(ot->srna, "number_cuts", 1, 1, 10, "Number of Cuts", "", 1, INT_MAX);
 +}
 +
 +/****************** add duplicate operator ***************/
 +
 +static int mesh_duplicate_exec(bContext *C, wmOperator *op)
 +{
 +      Scene *scene= CTX_data_scene(C);
 +      Object *ob= CTX_data_edit_object(C);
 +      BMEditMesh *em= ((Mesh*)ob->data)->edit_btmesh;
 +      BMOperator bmop;
 +
 +      EDBM_InitOpf(em, &bmop, op, "dupe geom=%hvef", BM_SELECT);
 +      
 +      BMO_Exec_Op(em->bm, &bmop);
 +      EDBM_clear_flag_all(em, BM_SELECT);
 +
 +      BMO_HeaderFlag_Buffer(em->bm, &bmop, "newout", BM_SELECT, BM_ALL);
 +
 +      if (!EDBM_FinishOp(em, &bmop, op, 1))
 +              return OPERATOR_CANCELLED;
 +
 +      DAG_object_flush_update(scene, ob, OB_RECALC_DATA);
 +      WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, ob);
 +      
 +      return OPERATOR_FINISHED;
 +}
 +
 +static int mesh_duplicate_invoke(bContext *C, wmOperator *op, wmEvent *event)
 +{
 +      WM_cursor_wait(1);
 +      mesh_duplicate_exec(C, op);
 +      WM_cursor_wait(0);
 +      
 +      return OPERATOR_FINISHED;
 +}
 +
 +void MESH_OT_duplicate(wmOperatorType *ot)
 +{
 +      /* identifiers */
 +      ot->name= "Duplicate";
++      ot->description= "Duplicate selected vertices, edges or faces.";
 +      ot->idname= "MESH_OT_duplicate";
 +      
 +      /* api callbacks */
 +      ot->invoke= mesh_duplicate_invoke;
 +      ot->exec= mesh_duplicate_exec;
 +      
 +      ot->poll= ED_operator_editmesh;
 +      
 +      /* to give to transform */
 +      RNA_def_int(ot->srna, "mode", TFM_TRANSLATION, 0, INT_MAX, "Mode", "", 0, INT_MAX);
 +}
 +
 +static int flip_normals(bContext *C, wmOperator *op)
 +{
 +      Scene *scene = CTX_data_scene(C);
 +      Object *obedit= CTX_data_edit_object(C);
 +      BMEditMesh *em= (((Mesh *)obedit->data))->edit_btmesh;
 +      
 +      if (!EDBM_CallOpf(em, op, "reversefaces facaes=%hf", BM_SELECT))
 +              return OPERATOR_CANCELLED;
 +      
 +      DAG_object_flush_update(scene, obedit, OB_RECALC_DATA);
 +      WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, obedit);
 +
 +      return OPERATOR_FINISHED;
 +}
 +
 +void MESH_OT_flip_normals(wmOperatorType *ot)
 +{
 +      /* identifiers */
 +      ot->name= "Flip Normals";
++      ot->description= "Flip the direction of selected face's vertex and face normals";
 +      ot->idname= "MESH_OT_flip_normals";
 +      
 +      /* api callbacks */
 +      ot->exec= flip_normals;
 +      ot->poll= ED_operator_editmesh;
 +      
 +      /* flags */
 +      ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
 +}
 +
 +#define DIRECTION_CW  1
 +#define DIRECTION_CCW 2
 +
 +static const EnumPropertyItem direction_items[]= {
 +      {DIRECTION_CW, "CW", 0, "Clockwise", ""},
 +      {DIRECTION_CCW, "CCW", 0, "Counter Clockwise", ""},
 +      {0, NULL, 0, NULL, NULL}};
 +
 +/* only accepts 1 selected edge, or 2 selected faces */
 +static int edge_rotate_selected(bContext *C, wmOperator *op)
 +{
 +      Scene *scene= CTX_data_scene(C);
 +      Object *obedit= CTX_data_edit_object(C);
 +      BMEditMesh *em= ((Mesh *)obedit->data)->edit_btmesh;
 +      BMOperator bmop;
 +      BMOIter siter;
 +      BMEdge *eed;
 +      BMIter iter;
 +      int ccw = RNA_int_get(op->ptr, "direction") == 1; // direction == 2 when clockwise and ==1 for counter CW.
 +      short edgeCount = 0;
 +      
 +      if (!(em->bm->totfacesel == 2 || em->bm->totedgesel == 1)) {
 +              BKE_report(op->reports, RPT_ERROR, "Select one edge or two adjacent faces");
 +              return OPERATOR_CANCELLED;
 +      }
 +
 +      /*first see if we have two adjacent faces*/
 +      BM_ITER(eed, &iter, em->bm, BM_EDGES_OF_MESH, NULL) {
 +              if (BM_Edge_FaceCount(eed) == 2) {
 +                      if ((BM_TestHFlag(eed->loop->f, BM_SELECT) && BM_TestHFlag(((BMLoop*)eed->loop->radial.next->data)->f, BM_SELECT))
 +                           && !(BM_TestHFlag(eed->loop->f, BM_HIDDEN) || BM_TestHFlag(((BMLoop*)eed->loop->radial.next->data)->f, BM_HIDDEN)))
 +                      {
 +                              break;
 +                      }
 +              }
 +      }
 +      
 +      /*ok, we don't have two adjacent faces, but we do have two selected ones.
 +        that's an error condition.*/
 +      if (!eed && em->bm->totfacesel == 2) {
 +              BKE_report(op->reports, RPT_ERROR, "Select one edge or two adjacent faces");
 +              return OPERATOR_CANCELLED;
 +      }
 +
 +      if (!eed) {
 +              BM_ITER_SELECT(eed, &iter, em->bm, BM_EDGES_OF_MESH, NULL)
 +                      if (BM_TestHFlag(eed, BM_SELECT))
 +                              break;
 +              }
 +      }
 +
 +      /*this should never happen*/
 +      if (!eed)
 +              return OPERATOR_CANCELLED;
 +      
 +      EDBM_InitOpf(em, &bmop, op, "edgerotate edges=%e ccw=%d", eed, ccw);
 +      BMO_Exec_Op(em->bm, &bmop);
 +
 +      BMO_HeaderFlag_Buffer(em->bm, &bmop, "edgeout", BM_SELECT, BM_EDGE);
 +
 +      if (!EDBM_FinishOp(em, &bmop, op, 1))
 +              return OPERATOR_CANCELLED;
 +
 +      DAG_object_flush_update(scene, obedit, OB_RECALC_DATA);
 +      WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, obedit);
 +
 +      return OPERATOR_FINISHED;
 +}
 +
 +void MESH_OT_edge_rotate(wmOperatorType *ot)
 +{
 +      /* identifiers */
 +      ot->name= "Rotate Selected Edge";
++      ot->description= "Rotate selected edge or adjoining faces.";
 +      ot->idname= "MESH_OT_edge_rotate";
 +
 +      /* api callbacks */
 +      ot->exec= edge_rotate_selected;
 +      ot->poll= ED_operator_editmesh;
 +
 +      /* flags */
 +      ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
 +
 +      /* props */
 +      RNA_def_enum(ot->srna, "direction", direction_items, DIRECTION_CW, "direction", "direction to rotate edge around.");
 +}
 +
 +/* swap is 0 or 1, if 1 it hides not selected */
 +void EDBM_hide_mesh(BMEditMesh *em, int swap)
 +{
 +      BMIter iter;
 +      BMHeader *h;
 +      int itermode;
 +
 +      if(em==NULL) return;
 +      
 +      if (em->selectmode & SCE_SELECT_VERTEX)
 +              itermode = BM_VERTS_OF_MESH;
 +      else if (em->selectmode & SCE_SELECT_EDGE)
 +              itermode = BM_EDGES_OF_MESH;
 +      else
 +              itermode = BM_FACES_OF_MESH;
 +
 +      BM_ITER(h, &iter, em->bm, itermode, NULL) {
 +              if (BM_TestHFlag(h, BM_SELECT) ^ swap)
 +                      BM_Hide(em->bm, h, 1);
 +      }
 +
 +      /*original hide flushing comment (OUTDATED): 
 +        hide happens on least dominant select mode, and flushes up, not down! (helps preventing errors in subsurf) */
 +      /*  - vertex hidden, always means edge is hidden too
 +              - edge hidden, always means face is hidden too
 +              - face hidden, only set face hide
 +              - then only flush back down what's absolute hidden
 +      */
 +
 +}
 +
 +static int hide_mesh_exec(bContext *C, wmOperator *op)
 +{
 +      Object *obedit= CTX_data_edit_object(C);
 +      Scene *scene = CTX_data_scene(C);
 +      BMEditMesh *em= (((Mesh *)obedit->data))->edit_btmesh;
 +      
 +      EDBM_hide_mesh(em, RNA_boolean_get(op->ptr, "unselected"));
 +              
 +      WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, obedit);
 +      DAG_object_flush_update(scene, obedit, OB_RECALC_DATA); 
 +
 +      return OPERATOR_FINISHED;       
 +}
 +
 +void MESH_OT_hide(wmOperatorType *ot)
 +{
 +      /* identifiers */
 +      ot->name= "Hide Selection";
 +      ot->idname= "MESH_OT_hide";
 +      
 +      /* api callbacks */
 +      ot->exec= hide_mesh_exec;
 +      ot->poll= ED_operator_editmesh;
++       ot->description= "Hide (un)selected vertices, edges or faces.";
++
 +      /* flags */
 +      ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
 +      
 +      /* props */
 +      RNA_def_boolean(ot->srna, "unselected", 0, "Unselected", "Hide unselected rather than selected.");
 +}
 +
 +
 +void EDBM_reveal_mesh(BMEditMesh *em)
 +{
 +      BMIter iter;
 +      BMHeader *ele;
 +      int i, types[3] = {BM_VERTS_OF_MESH, BM_EDGES_OF_MESH, BM_FACES_OF_MESH};
 +      int sels[3] = {1, !(em->selectmode & SCE_SELECT_VERTEX), !(em->selectmode & SCE_SELECT_VERTEX | SCE_SELECT_EDGE)};
 +
 +      for (i=0; i<3; i++) {
 +              BM_ITER(ele, &iter, em->bm, types[i], NULL) {
 +                      if (BM_TestHFlag(ele, BM_HIDDEN)) {
 +                              BM_Hide(em->bm, ele, 0);
 +
 +                              if (sels[i])
 +                                      BM_Select(em->bm, ele, 1);
 +                      }
 +              }
 +      }
 +
 +      EDBM_selectmode_flush(em);
 +}
 +
 +static int reveal_mesh_exec(bContext *C, wmOperator *op)
 +{
 +      Object *obedit= CTX_data_edit_object(C);
 +      Scene *scene = CTX_data_scene(C);
 +      BMEditMesh *em= (((Mesh *)obedit->data))->edit_btmesh;
 +      
 +      EDBM_reveal_mesh(em);
 +
 +      WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, obedit);
 +      DAG_object_flush_update(scene, obedit, OB_RECALC_DATA); 
 +
 +      return OPERATOR_FINISHED;       
 +}
 +
 +void MESH_OT_reveal(wmOperatorType *ot)
 +{
 +      /* identifiers */
 +      ot->name= "Reveal Hidden";
 +      ot->idname= "MESH_OT_reveal";
++      ot->description= "Reveal all hidden vertices, edges and faces.";
 +      
 +      /* api callbacks */
 +      ot->exec= reveal_mesh_exec;
 +      ot->poll= ED_operator_editmesh;
 +      
 +      /* flags */
 +      ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
 +}
 +
 +static int normals_make_consistent_exec(bContext *C, wmOperator *op)
 +{
 +      Scene *scene = CTX_data_scene(C);
 +      Object *obedit= CTX_data_edit_object(C);
 +      BMEditMesh *em= ((Mesh *)obedit->data)->edit_btmesh;
 +      
 +      if (!EDBM_CallOpf(em, op, "righthandfaces faces=%hf", BM_SELECT))
 +              return OPERATOR_CANCELLED;
 +      
 +      if (RNA_boolean_get(op->ptr, "inside"))
 +              EDBM_CallOpf(em, op, "reversefaces faces=%hf", BM_SELECT);
 +
 +      DAG_object_flush_update(scene, obedit, OB_RECALC_DATA);
 +      WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, obedit); //TODO is this needed ?
 +
 +      return OPERATOR_FINISHED;       
 +}
 +
 +void MESH_OT_normals_make_consistent(wmOperatorType *ot)
 +{
 +      /* identifiers */
 +      ot->name= "Make Normals Consistent";
++      ot->description= "Make face and vertex normals point either outside or inside the mesh";
 +      ot->idname= "MESH_OT_normals_make_consistent";
 +      
 +      /* api callbacks */
 +      ot->exec= normals_make_consistent_exec;
 +      ot->poll= ED_operator_editmesh;
 +      
 +      /* flags */
 +      ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
 +      
 +      RNA_def_boolean(ot->srna, "inside", 0, "Inside", "");
 +}
 +
 +
 +
 +static int do_smooth_vertex(bContext *C, wmOperator *op)
 +{
 +      Scene *scene = CTX_data_scene(C);
 +      Object *obedit= CTX_data_edit_object(C);
 +      BMEditMesh *em= ((Mesh *)obedit->data)->edit_btmesh;
 +      ModifierData *md;
 +      int mirrx=0, mirry=0, mirrz=0;
 +      int i, repeat;
 +
 +      /* if there is a mirror modifier with clipping, flag the verts that
 +       * are within tolerance of the plane(s) of reflection 
 +       */
 +      for(md=obedit->modifiers.first; md; md=md->next) {
 +              if(md->type==eModifierType_Mirror) {
 +                      MirrorModifierData *mmd = (MirrorModifierData*) md;     
 +              
 +                      if(mmd->flag & MOD_MIR_CLIPPING) {
 +                              if (mmd->flag & MOD_MIR_AXIS_X)
 +                                      mirrx = 1;
 +                              if (mmd->flag & MOD_MIR_AXIS_Y)
 +                                      mirry = 1;
 +                              if (mmd->flag & MOD_MIR_AXIS_Z)
 +                                      mirrz = 1;
 +                      }
 +              }
 +      }
 +
 +      repeat = RNA_int_get(op->ptr,"repeat");
 +      if (!repeat)
 +              repeat = 1;
 +      
 +      for (i=0; i<repeat; i++) {
 +              if (!EDBM_CallOpf(em, op, "vertexsmooth verts=%hv mirror_clip_x=%d mirror_clip_y=%d mirror_clip_z=%d",
 +                                BM_SELECT, mirrx, mirry, mirrz))
 +              {
 +                      return OPERATOR_CANCELLED;
 +              }
 +      }
 +
 +      //BMESH_TODO: need to handle the x-axis editing option here properly.
 +      //should probably make a helper function for that? I dunno.
 +
 +      DAG_object_flush_update(scene, obedit, OB_RECALC_DATA);
 +      WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, obedit); //TODO is this needed ?
 +
 +      return OPERATOR_FINISHED;
 +}     
 +      
 +void MESH_OT_vertices_smooth(wmOperatorType *ot)
 +{
 +      /* identifiers */
 +      ot->name= "Smooth Vertex";
++      ot->description= "Flatten angles of selected vertices.";
 +      ot->idname= "MESH_OT_vertices_smooth";
 +      
 +      /* api callbacks */
 +      ot->exec= do_smooth_vertex;
 +      ot->poll= ED_operator_editmesh;
 +      
 +      /* flags */
 +      ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
 +
 +      RNA_def_int(ot->srna, "repeat", 1, 1, 100, "Number of times to smooth the mesh", "", 1, INT_MAX);
 +}
 +
 +
 +static int bm_test_exec(bContext *C, wmOperator *op)
 +{
 +      Scene *scene = CTX_data_scene(C);
 +      Object *obedit= CTX_data_edit_object(C);
 +      BMEditMesh *em= ((Mesh *)obedit->data)->edit_btmesh;
 +#if 1
 +      if (!EDBM_CallOpf(em, op, "collapse edges=%he", BM_SELECT))
 +              return OPERATOR_CANCELLED;
 +
 +#else //uv island walker test
 +      BMIter iter, liter;
 +      BMFace *f;
 +      BMLoop *l, *l2;
 +      MLoopUV *luv;
 +      BMWalker walker;
 +      int i=0;
 +
 +      BM_ITER(f, &iter, em->bm, BM_FACES_OF_MESH, NULL) {
 +              BM_ITER(l, &liter, em->bm, BM_LOOPS_OF_FACE, f) {
 +                      luv = CustomData_bmesh_get(&em->bm->ldata, l->head.data, CD_MLOOPUV);
 +              }
 +      }
 +
 +      BMW_Init(&walker, em->bm, BMW_UVISLAND, 0);
 +
 +      BM_ITER(f, &iter, em->bm, BM_FACES_OF_MESH, NULL) {
 +              BM_ITER(l, &liter, em->bm, BM_LOOPS_OF_FACE, f) {
 +                      luv = CustomData_bmesh_get(&em->bm->ldata, l->head.data, CD_MLOOPUV);
 +                      if (luv->flag & MLOOPUV_VERTSEL) {
 +                              l2 = BMW_Begin(&walker, l);
 +                              for (; l2; l2=BMW_Step(&walker)) {
 +                                      luv = CustomData_bmesh_get(&em->bm->ldata, l2->head.data, CD_MLOOPUV);
 +                                      luv->flag |= MLOOPUV_VERTSEL;
 +                              }                               
 +                      }
 +              }
 +      }
 +
 +      BMW_End(&walker);
 +#endif
 +      DAG_object_flush_update(scene, obedit, OB_RECALC_DATA);
 +      WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, obedit); //TODO is this needed ?
 +
 +      return OPERATOR_FINISHED;
 +}     
 +      
 +void MESH_OT_bm_test(wmOperatorType *ot)
 +{
 +      /* identifiers */
 +      ot->name= "BMesh Test Operator";
 +      ot->idname= "MESH_OT_bm_test";
 +      
 +      /* api callbacks */
 +      ot->exec= bm_test_exec;
 +      ot->poll= ED_operator_editmesh;
 +      
 +      /* flags */
 +      ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
 +
 +      //RNA_def_int(ot->srna, "repeat", 1, 1, 100, "Number of times to smooth the mesh", "", 1, INT_MAX);
 +}
 +
 +/********************** Smooth/Solid Operators *************************/
 +
 +void mesh_set_smooth_faces(BMEditMesh *em, short smooth)
 +{
 +      BMIter iter;
 +      BMFace *efa;
 +
 +      if(em==NULL) return;
 +      
 +      BM_ITER(efa, &iter, em->bm, BM_FACES_OF_MESH, NULL) {
 +              if (BM_TestHFlag(efa, BM_SELECT)) {
 +                      if (smooth)
 +                              BM_SetHFlag(efa, BM_SMOOTH);
 +                      else
 +                              BM_ClearHFlag(efa, BM_SMOOTH);
 +              }
 +      }
 +}
 +
 +static int mesh_faces_shade_smooth_exec(bContext *C, wmOperator *op)
 +{
 +      Scene *scene= CTX_data_scene(C);
 +      Object *obedit= CTX_data_edit_object(C);
 +      BMEditMesh *em= ((Mesh *)obedit->data)->edit_btmesh;
 +
 +      mesh_set_smooth_faces(em, 1);
 +
 +      DAG_object_flush_update(scene, obedit, OB_RECALC_DATA);
 +      WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, obedit);
 +
 +      return OPERATOR_FINISHED;
 +}
 +
 +void MESH_OT_faces_shade_smooth(wmOperatorType *ot)
 +{
 +      /* identifiers */
 +      ot->name= "Shade Smooth";
++       ot->description= "Display faces smooth (using vertex normals).";
 +      ot->idname= "MESH_OT_faces_shade_smooth";
 +
 +      /* api callbacks */
 +      ot->exec= mesh_faces_shade_smooth_exec;
 +      ot->poll= ED_operator_editmesh;
 +
 +      /* flags */
 +      ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
 +}
 +
 +static int mesh_faces_shade_flat_exec(bContext *C, wmOperator *op)
 +{
 +      Scene *scene = CTX_data_scene(C);
 +      Object *obedit= CTX_data_edit_object(C);
 +      BMEditMesh *em= ((Mesh *)obedit->data)->edit_btmesh;
 +
 +      mesh_set_smooth_faces(em, 0);
 +
 +      DAG_object_flush_update(scene, obedit, OB_RECALC_DATA);
 +      WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, obedit);
 +
 +      return OPERATOR_FINISHED;
 +}
 +
 +void MESH_OT_faces_shade_flat(wmOperatorType *ot)
 +{
 +      /* identifiers */
 +      ot->name= "Shade Flat";
++      ot->description= "Display faces flat.";
 +      ot->idname= "MESH_OT_faces_shade_flat";
 +
 +      /* api callbacks */
 +      ot->exec= mesh_faces_shade_flat_exec;
 +      ot->poll= ED_operator_editmesh;
 +
 +      /* flags */
 +      ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
 +}
index ff0814661ebd84cb6a5cfff6434a16e01a6eca2b,19078d2f6ff5da8274b8d22943dd1a6f1bca39ab..fbeb1636114d4c4373da4415ed535e1ca0845c4e
@@@ -119,7 -116,129 +119,6 @@@ static short icoface[20][3] = 
        {10,9,11}
  };
  
 -/* *************** add-click-mesh (extrude) operator ************** */
 -
 -static int dupli_extrude_cursor(bContext *C, wmOperator *op, wmEvent *event)
 -{
 -      ViewContext vc;
 -      EditVert *eve, *v1;
 -      float min[3], max[3];
 -      int done= 0;
 -      
 -      em_setup_viewcontext(C, &vc);
 -      
 -      INIT_MINMAX(min, max);
 -      
 -      for(v1= vc.em->verts.first;v1; v1=v1->next) {
 -              if(v1->f & SELECT) {
 -                      DO_MINMAX(v1->co, min, max);
 -                      done= 1;
 -              }
 -      }
 -
 -      /* call extrude? */
 -      if(done) {
 -              EditEdge *eed;
 -              float vec[3], cent[3], mat[3][3];
 -              float nor[3]= {0.0, 0.0, 0.0};
 -              
 -              /* check for edges that are half selected, use for rotation */
 -              done= 0;
 -              for(eed= vc.em->edges.first; eed; eed= eed->next) {
 -                      if( (eed->v1->f & SELECT)+(eed->v2->f & SELECT) == SELECT ) {
 -                              if(eed->v1->f & SELECT) VecSubf(vec, eed->v1->co, eed->v2->co);
 -                              else VecSubf(vec, eed->v2->co, eed->v1->co);
 -                              VecAddf(nor, nor, vec);
 -                              done= 1;
 -                      }
 -              }
 -              if(done) Normalize(nor);
 -              
 -              /* center */
 -              VecAddf(cent, min, max);
 -              VecMulf(cent, 0.5f);
 -              VECCOPY(min, cent);
 -              
 -              Mat4MulVecfl(vc.obedit->obmat, min);    // view space
 -              view3d_get_view_aligned_coordinate(&vc, min, event->mval);
 -              Mat4Invert(vc.obedit->imat, vc.obedit->obmat); 
 -              Mat4MulVecfl(vc.obedit->imat, min); // back in object space
 -              
 -              VecSubf(min, min, cent);
 -              
 -              /* calculate rotation */
 -              Mat3One(mat);
 -              if(done) {
 -                      float dot;
 -                      
 -                      VECCOPY(vec, min);
 -                      Normalize(vec);
 -                      dot= INPR(vec, nor);
 -
 -                      if( fabs(dot)<0.999) {
 -                              float cross[3], si, q1[4];
 -                              
 -                              Crossf(cross, nor, vec);
 -                              Normalize(cross);
 -                              dot= 0.5f*saacos(dot);
 -                              si= (float)sin(dot);
 -                              q1[0]= (float)cos(dot);
 -                              q1[1]= cross[0]*si;
 -                              q1[2]= cross[1]*si;
 -                              q1[3]= cross[2]*si;
 -                              
 -                              QuatToMat3(q1, mat);
 -                      }
 -              }
 -              
 -              extrudeflag(vc.obedit, vc.em, SELECT, nor);
 -              rotateflag(vc.em, SELECT, cent, mat);
 -              translateflag(vc.em, SELECT, min);
 -              
 -              recalc_editnormals(vc.em);
 -      }
 -      else {
 -              float mat[3][3],imat[3][3];
 -              float *curs= give_cursor(vc.scene, vc.v3d);
 -              
 -              VECCOPY(min, curs);
 -              view3d_get_view_aligned_coordinate(&vc, min, event->mval);
 -              
 -              eve= addvertlist(vc.em, 0, NULL);
 -
 -              Mat3CpyMat4(mat, vc.obedit->obmat);
 -              Mat3Inv(imat, mat);
 -              
 -              VECCOPY(eve->co, min);
 -              Mat3MulVecfl(imat, eve->co);
 -              VecSubf(eve->co, eve->co, vc.obedit->obmat[3]);
 -              
 -              eve->f= SELECT;
 -      }
 -      
 -      //retopo_do_all();
 -      WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, vc.obedit); 
 -      DAG_object_flush_update(vc.scene, vc.obedit, OB_RECALC_DATA);
 -      
 -      return OPERATOR_FINISHED;
 -}
 -
 -void MESH_OT_dupli_extrude_cursor(wmOperatorType *ot)
 -{
 -      /* identifiers */
 -      ot->name= "Duplicate or Extrude at 3D Cursor";
 -      ot->description= "Duplicate and extrude selected vertices, edges or faces towards 3D Cursor.";
 -      ot->idname= "MESH_OT_dupli_extrude_cursor";
 -      
 -      /* api callbacks */
 -      ot->invoke= dupli_extrude_cursor;
 -      ot->poll= ED_operator_editmesh;
 -      
 -      /* flags */
 -      ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
 -}
 -
--
  /* ********************** */
  
  /* selected faces get hidden edges */
@@@ -707,10 -783,41 +708,11 @@@ static void addedgeface_mesh(Mesh *me, 
                
                recalc_editnormals(em);
        }
 -      }
  
 -static int addedgeface_mesh_exec(bContext *C, wmOperator *op)
 -{
 -      Object *obedit= CTX_data_edit_object(C);
 -      EditMesh *em= BKE_mesh_get_editmesh(((Mesh *)obedit->data));
 -      
 -      addedgeface_mesh(em, op);
 -      
 -      WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, obedit);
 -      
 -      DAG_object_flush_update(CTX_data_scene(C), obedit, OB_RECALC_DATA);     
 -      
 -      BKE_mesh_end_editmesh(obedit->data, em);
 -      return OPERATOR_FINISHED;
 +      BKE_mesh_end_editmesh(me, em);
  }
  
 -void MESH_OT_edge_face_add(wmOperatorType *ot)
 -{
 -      /* identifiers */
 -      ot->name= "Make Edge/Face";
 -      ot->description= "Add an edge or face to selected.";
 -      ot->idname= "MESH_OT_edge_face_add";
 -      
 -      /* api callbacks */
 -      ot->exec= addedgeface_mesh_exec;
 -      ot->poll= ED_operator_editmesh;
 -      
 -      /* flags */
 -      ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
 -      
 -}
 -
 -
  /* ************************ primitives ******************* */
  
  // HACK: these can also be found in cmoview.tga.c, but are here so that they can be found by linker
index 06870915e1d69bfd8db3c90eac4be7e81db9ac93,f2c5b7fb727b709dcc79eb03a0eb4e80fb4ec7fb..b38f35ed529cfb13241cfc68dce9eae63744f328
@@@ -1029,42 -1987,284 +1030,41 @@@ static int loop_multiselect(bContext *C
                        edgeloop_select(em, eed,SELECT);
                }
                EM_selectmode_flush(em);
 -      }
 -      MEM_freeN(edarray);
 -//    if (EM_texFaceCheck())
 -      
 -      WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, obedit);
 -
 -      BKE_mesh_end_editmesh(obedit->data, em);
 -      return OPERATOR_FINISHED;       
 -}
 -
 -void MESH_OT_loop_multi_select(wmOperatorType *ot)
 -{
 -      /* identifiers */
 -      ot->name= "Multi Select Loops";
 -      ot->description= "Select a loop of connected edges by connection type.";
 -      ot->idname= "MESH_OT_loop_multi_select";
 -      
 -      /* api callbacks */
 -      ot->exec= loop_multiselect;
 -      ot->poll= ED_operator_editmesh;
 -      
 -      /* flags */
 -      ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
 -      
 -      /* properties */
 -      RNA_def_boolean(ot->srna, "ring", 0, "Ring", "");
 -}
 -
 -              
 -/* ***************** MAIN MOUSE SELECTION ************** */
 -
 -
 -/* ***************** loop select (non modal) ************** */
 -
 -static void mouse_mesh_loop(bContext *C, short mval[2], short extend, short ring)
 -{
 -      ViewContext vc;
 -      EditMesh *em;
 -      EditEdge *eed;
 -      int select= 1;
 -      int dist= 50;
 -      
 -      em_setup_viewcontext(C, &vc);
 -      vc.mval[0]= mval[0];
 -      vc.mval[1]= mval[1];
 -      em= vc.em;
 -      
 -      eed= findnearestedge(&vc, &dist);
 -      if(eed) {
 -              if(extend==0) EM_clear_flag_all(em, SELECT);
 -      
 -              if((eed->f & SELECT)==0) select=1;
 -              else if(extend) select=0;
 -
 -              if(em->selectmode & SCE_SELECT_FACE) {
 -                      faceloop_select(em, eed, select);
 -              }
 -              else if(em->selectmode & SCE_SELECT_EDGE) {
 -                      if(ring)
 -                              edgering_select(em, eed, select);
 -                      else
 -                              edgeloop_select(em, eed, select);
 -              }
 -              else if(em->selectmode & SCE_SELECT_VERTEX) {
 -                      if(ring)
 -                              edgering_select(em, eed, select);
 -                      else 
 -                              edgeloop_select(em, eed, select);
 -              }
 -
 -              EM_selectmode_flush(em);
 -//                    if (EM_texFaceCheck())
 -              
 -              WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, vc.obedit);
 -      }
 -}
 -
 -static int mesh_select_loop_invoke(bContext *C, wmOperator *op, wmEvent *event)
 -{
 -      
 -      view3d_operator_needs_opengl(C);
 -      
 -      mouse_mesh_loop(C, event->mval, RNA_boolean_get(op->ptr, "extend"),
 -                                      RNA_boolean_get(op->ptr, "ring"));
 -      
 -      /* cannot do tweaks for as long this keymap is after transform map */
 -      return OPERATOR_FINISHED;
 -}
 -
 -void MESH_OT_loop_select(wmOperatorType *ot)
 -{
 -      /* identifiers */
 -      ot->name= "Loop Select";
 -      ot->description= "Select a loop of connected edges.";
 -      ot->idname= "MESH_OT_loop_select";
 -      
 -      /* api callbacks */
 -      ot->invoke= mesh_select_loop_invoke;
 -      ot->poll= ED_operator_editmesh;
 -      
 -      /* flags */
 -      ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
 -      
 -      /* properties */
 -      RNA_def_boolean(ot->srna, "extend", 0, "Extend Select", "");
 -      RNA_def_boolean(ot->srna, "ring", 0, "Select Ring", "");
 -}
 -
 -/* ******************* mesh shortest path select, uses prev-selected edge ****************** */
 -
 -/* since you want to create paths with multiple selects, it doesn't have extend option */
 -static void mouse_mesh_shortest_path(bContext *C, short mval[2])
 -{
 -      ViewContext vc;
 -      EditMesh *em;
 -      EditEdge *eed;
 -      int dist= 50;
 -      
 -      em_setup_viewcontext(C, &vc);
 -      vc.mval[0]= mval[0];
 -      vc.mval[1]= mval[1];
 -      em= vc.em;
 -      
 -      eed= findnearestedge(&vc, &dist);
 -      if(eed) {
 -              Mesh *me= vc.obedit->data;
 -              int path = 0;
 -              
 -              if (em->selected.last) {
 -                      EditSelection *ese = em->selected.last;
 -                      
 -                      if(ese && ese->type == EDITEDGE) {
 -                              EditEdge *eed_act;
 -                              eed_act = (EditEdge*)ese->data;
 -                              if (eed_act != eed) {
 -                                      if (edgetag_shortest_path(vc.scene, em, eed_act, eed)) {
 -                                              EM_remove_selection(em, eed_act, EDITEDGE);
 -                                              path = 1;
 -                                      }
 -                              }
 -                      }
 -              }
 -              if (path==0) {
 -                      int act = (edgetag_context_check(vc.scene, eed)==0);
 -                      edgetag_context_set(vc.scene, eed, act); /* switch the edge option */
 -              }
 -              
 -              EM_selectmode_flush(em);
 -
 -              /* even if this is selected it may not be in the selection list */
 -              if(edgetag_context_check(vc.scene, eed)==0)
 -                      EM_remove_selection(em, eed, EDITEDGE);
 -              else
 -                      EM_store_selection(em, eed, EDITEDGE);
 -      
 -              /* force drawmode for mesh */
 -              switch (vc.scene->toolsettings->edge_mode) {
 -                      
 -                      case EDGE_MODE_TAG_SEAM:
 -                              me->drawflag |= ME_DRAWSEAMS;
 -                              break;
 -                      case EDGE_MODE_TAG_SHARP:
 -                              me->drawflag |= ME_DRAWSHARP;
 -                              break;
 -                      case EDGE_MODE_TAG_CREASE:      
 -                              me->drawflag |= ME_DRAWCREASES;
 -                              break;
 -                      case EDGE_MODE_TAG_BEVEL:
 -                              me->drawflag |= ME_DRAWBWEIGHTS;
 -                              break;
 -              }
 -              
 -              DAG_object_flush_update(vc.scene, vc.obedit, OB_RECALC_DATA);
 -      
 -              WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, vc.obedit);
 -      }
 -}
 -
 -
 -static int mesh_shortest_path_select_invoke(bContext *C, wmOperator *op, wmEvent *event)
 -{
 +      }
 +      MEM_freeN(edarray);
 +//    if (EM_texFaceCheck())
        
 -      view3d_operator_needs_opengl(C);
 +      WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, obedit);
  
 -      mouse_mesh_shortest_path(C, event->mval);
 -      
 -      return OPERATOR_FINISHED;
 +      BKE_mesh_end_editmesh(obedit->data, em);
 +      return OPERATOR_FINISHED;       
  }
 -      
 -void MESH_OT_select_shortest_path(wmOperatorType *ot)
 +
 +#if 0 //moved to bmeshutils_mods.c
 +void MESH_OT_loop_multi_select(wmOperatorType *ot)
  {
        /* identifiers */
 -      ot->name= "Shortest Path Select";
 -      ot->description= "Select shortest path between two selections.";
 -      ot->idname= "MESH_OT_select_shortest_path";
 +      ot->name= "Multi Select Loops";
++      ot->description= "Select a loop of connected edges by connection type.";
 +      ot->idname= "MESH_OT_loop_multi_select";
        
        /* api callbacks */
 -      ot->invoke= mesh_shortest_path_select_invoke;
 +      ot->exec= loop_multiselect;
        ot->poll= ED_operator_editmesh;
        
        /* flags */
        ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
        
        /* properties */
 -      RNA_def_boolean(ot->srna, "extend", 0, "Extend Select", "");
 +      RNA_def_boolean(ot->srna, "ring", 0, "Ring", "");
  }
 +#endif
 +              
 +/* ***************** MAIN MOUSE SELECTION ************** */
  
  
- /* ******************* mesh shortest path select, uses prev-selected edge ****************** */
  /* ************************************************** */
  
 -
 -/* here actual select happens */
 -/* gets called via generic mouse select operator */
 -void mouse_mesh(bContext *C, short mval[2], short extend)
 -{
 -      ViewContext vc;
 -      EditVert *eve;
 -      EditEdge *eed;
 -      EditFace *efa;
 -      
 -      /* setup view context for argument to callbacks */
 -      em_setup_viewcontext(C, &vc);
 -      vc.mval[0]= mval[0];
 -      vc.mval[1]= mval[1];
 -      
 -      if(unified_findnearest(&vc, &eve, &eed, &efa)) {
 -              
 -              if(extend==0) EM_clear_flag_all(vc.em, SELECT);
 -              
 -              if(efa) {
 -                      /* set the last selected face */
 -                      EM_set_actFace(vc.em, efa);
 -                      
 -                      if( (efa->f & SELECT)==0 ) {
 -                              EM_store_selection(vc.em, efa, EDITFACE);
 -                              EM_select_face_fgon(vc.em, efa, 1);
 -                      }
 -                      else if(extend) {
 -                              EM_remove_selection(vc.em, efa, EDITFACE);
 -                              EM_select_face_fgon(vc.em, efa, 0);
 -                      }
 -              }
 -              else if(eed) {
 -                      if((eed->f & SELECT)==0) {
 -                              EM_store_selection(vc.em, eed, EDITEDGE);
 -                              EM_select_edge(eed, 1);
 -                      }
 -                      else if(extend) {
 -                              EM_remove_selection(vc.em, eed, EDITEDGE);
 -                              EM_select_edge(eed, 0);
 -                      }
 -              }
 -              else if(eve) {
 -                      if((eve->f & SELECT)==0) {
 -                              eve->f |= SELECT;
 -                              EM_store_selection(vc.em, eve, EDITVERT);
 -                      }
 -                      else if(extend){ 
 -                              EM_remove_selection(vc.em, eve, EDITVERT);
 -                              eve->f &= ~SELECT;
 -                      }
 -              }
 -              
 -              EM_selectmode_flush(vc.em);
 -                
 -//            if (EM_texFaceCheck()) {
 -
 -              if (efa && efa->mat_nr != vc.obedit->actcol-1) {
 -                      vc.obedit->actcol= efa->mat_nr+1;
 -                      vc.em->mat_nr= efa->mat_nr;
 -//                    BIF_preview_changed(ID_MA);
 -              }
 -      }
 -
 -      WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, vc.obedit);
 -      
 -}
 -
  /* *********** select linked ************* */
  
  /* for use with selectconnected_delimit_mesh only! */
@@@ -1652,17 -3369,18 +1655,18 @@@ static int select_non_manifold_exec(bCo
        WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, obedit);
  
        BKE_mesh_end_editmesh(obedit->data, em);
 -      return OPERATOR_FINISHED;
 +      return OPERATOR_FINISHED;       
  }
  
 -void MESH_OT_select_more(wmOperatorType *ot)
 +void MESH_OT_select_non_manifold(wmOperatorType *ot)
  {
        /* identifiers */
 -      ot->name= "Select More";
 -      ot->description= "Select more vertices, edges or faces connected to initial selection.";
 -      ot->idname= "MESH_OT_select_more";
 -
 +      ot->name= "Select Non Manifold";
++      ot->description= "Select all non-manifold vertices or edges.";
 +      ot->idname= "MESH_OT_select_non_manifold";
 +      
        /* api callbacks */
 -      ot->exec= select_more;
 +      ot->exec= select_non_manifold_exec;
        ot->poll= ED_operator_editmesh;
    &n