"flip -> pen_flip" missing rename on particle edit mode
[blender.git] / source / blender / editors / physics / particle_edit.c
index c1846c6a49347e1c9cfcc123cbc190843b3e42e4..132533fc12314c34918ada4e51de1a3f6114c3ae 100644 (file)
@@ -15,7 +15,7 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software Foundation,
- * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  *
  * The Original Code is Copyright (C) 2007 by Janne Karhu.
  * All rights reserved.
 #include "DNA_scene_types.h"
 #include "DNA_mesh_types.h"
 #include "DNA_meshdata_types.h"
-#include "DNA_modifier_types.h"
-#include "DNA_object_force.h"
-#include "DNA_object_types.h"
-#include "DNA_vec_types.h"
-#include "DNA_userdef_types.h"
 #include "DNA_view3d_types.h"
 #include "DNA_screen_types.h"
 #include "DNA_space_types.h"
-#include "DNA_windowmanager_types.h"
 
 #include "BKE_DerivedMesh.h"
 #include "BKE_depsgraph.h"
@@ -66,7 +60,6 @@
 #include "BLI_kdtree.h"
 #include "BLI_rand.h"
 
-#include "PIL_time.h"
 
 #include "BIF_gl.h"
 #include "BIF_glutil.h"
@@ -75,7 +68,6 @@
 #include "ED_particle.h"
 #include "ED_view3d.h"
 
-#include "UI_interface.h"
 #include "UI_resources.h"
 
 #include "WM_api.h"
@@ -132,7 +124,7 @@ int PE_hair_poll(bContext *C)
        return (edit && edit->psys);
 }
 
-int PE_poll_3dview(bContext *C)
+int PE_poll_view3d(bContext *C)
 {
        return PE_poll(C) && CTX_wm_area(C)->spacetype == SPACE_VIEW3D &&
                CTX_wm_region(C)->regiontype == RGN_TYPE_WINDOW;
@@ -168,7 +160,7 @@ void PE_free_ptcache_edit(PTCacheEdit *edit)
                edit->emitter_field= 0;
        }
 
-       psys_free_path_cache(NULL, edit);
+       psys_free_path_cache(edit->psys, edit);
 
        MEM_freeN(edit);
 }
@@ -191,10 +183,13 @@ int PE_start_edit(PTCacheEdit *edit)
 
 ParticleEditSettings *PE_settings(Scene *scene)
 {
-       return &scene->toolsettings->particle;
+       return scene->toolsettings ? &scene->toolsettings->particle : NULL;
 }
 
-/* always gets atleast the first particlesystem even if PSYS_CURRENT flag is not set */
+/* always gets atleast the first particlesystem even if PSYS_CURRENT flag is not set
+ *
+ * note: this function runs on poll, therefor it can runs many times a second
+ * keep it fast! */
 static PTCacheEdit *pe_get_current(Scene *scene, Object *ob, int create)
 {
        ParticleEditSettings *pset= PE_settings(scene);
@@ -202,13 +197,13 @@ static PTCacheEdit *pe_get_current(Scene *scene, Object *ob, int create)
        ListBase pidlist;
        PTCacheID *pid;
 
+       if(pset==NULL || ob==NULL)
+               return NULL;
+
        pset->scene = scene;
        pset->object = ob;
 
-       if(ob==NULL)
-               return NULL;
-
-       BKE_ptcache_ids_from_object(&pidlist, ob);
+       BKE_ptcache_ids_from_object(&pidlist, ob, NULL, 0);
 
        /* in the case of only one editable thing, set pset->edittype accordingly */
        if(pidlist.first && pidlist.first == pidlist.last) {
@@ -266,13 +261,8 @@ static PTCacheEdit *pe_get_current(Scene *scene, Object *ob, int create)
                }
        }
 
-       if(edit) {
+       if(edit)
                edit->pid = *pid;
-               
-               /* mesh may have changed since last entering editmode.
-                * note, this may have run before if the edit data was just created, so could avoid this and speed up a little */
-               recalc_emitter_field(ob, edit->psys);
-       }
 
        BLI_freelistN(&pidlist);
 
@@ -376,7 +366,8 @@ static void PE_set_view3d_data(bContext *C, PEData *data)
        PE_set_data(C, data);
 
        view3d_set_viewcontext(C, &data->vc);
-       view3d_get_transformation(data->vc.ar, data->vc.rv3d, data->ob, &data->mats);
+       /* note, the object argument means the modelview matrix does not account for the objects matrix, use viewmat rather then (obmat * viewmat) */
+       view3d_get_transformation(data->vc.ar, data->vc.rv3d, NULL, &data->mats);
 
        if((data->vc.v3d->drawtype>OB_WIRE) && (data->vc.v3d->flag & V3D_ZBUF_SELECT))
                view3d_validate_backbuf(&data->vc);
@@ -409,7 +400,8 @@ static int key_test_depth(PEData *data, float co[3])
        x+= (short)data->vc.ar->winrct.xmin;
        y+= (short)data->vc.ar->winrct.ymin;
 
-       view3d_validate_backbuf(&data->vc);
+       /* PE_set_view3d_data calls this. no need to call here */
+       /* view3d_validate_backbuf(&data->vc); */
        glReadPixels(x, y, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &depth);
 
        if((float)uz - 0.0001 > depth)
@@ -831,9 +823,12 @@ static void PE_apply_mirror(Object *ob, ParticleSystem *psys)
        edit= psys->edit;
        psmd= psys_get_modifier(ob, psys);
 
-       if(!edit->mirror_cache || !psmd->dm)
+       if(!psmd->dm)
                return;
 
+       if(!edit->mirror_cache)
+               PE_update_mirror_cache(ob, psys);
+
        /* we delay settings the PARS_EDIT_RECALC for mirrored particles
         * to avoid doing mirror twice */
        LOOP_POINTS {
@@ -902,13 +897,13 @@ static void pe_deflect_emitter(Scene *scene, Object *ob, PTCacheEdit *edit)
                                        if(dot<dist_1st) {
                                                normalize_v3(dvec);
                                                mul_v3_fl(dvec,dist_1st-dot);
-                                               add_v3_v3v3(key->co,key->co,dvec);
+                                               add_v3_v3(key->co, dvec);
                                        }
                                }
                                else {
                                        normalize_v3(dvec);
                                        mul_v3_fl(dvec,dist_1st-dot);
-                                       add_v3_v3v3(key->co,key->co,dvec);
+                                       add_v3_v3(key->co, dvec);
                                }
                                if(k==1)
                                        dist_1st*=1.3333f;
@@ -994,7 +989,7 @@ static void pe_iterate_lengths(Scene *scene, PTCacheEdit *edit)
                                }
 
                                if(k) {
-                                       add_v3_v3v3((key-1)->co,(key-1)->co,dv1);
+                                       add_v3_v3((key-1)->co, dv1);
                                }
 
                                VECADD(dv1,dv0,dv2);
@@ -1326,7 +1321,7 @@ static int select_all_exec(bContext *C, wmOperator *op)
        }
 
        PE_update_selection(scene, ob, 1);
-       WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE_SELECT, ob);
+       WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_SELECTED, ob);
 
        return OPERATOR_FINISHED;
 }
@@ -1376,7 +1371,7 @@ int PE_mouse_particles(bContext *C, short *mval, int extend)
        for_mouse_hit_keys(&data, toggle_key_select, 1);  /* nearest only */
 
        PE_update_selection(scene, ob, 1);
-       WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE_SELECT, data.ob);
+       WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_SELECTED, data.ob);
 
        return OPERATOR_FINISHED;
 }
@@ -1385,11 +1380,14 @@ int PE_mouse_particles(bContext *C, short *mval, int extend)
 
 static void select_root(PEData *data, int point_index)
 {
+       if (data->edit->points[point_index].flag & PEP_HIDE)
+               return;
+       
        data->edit->points[point_index].keys->flag |= PEK_SELECT;
        data->edit->points[point_index].flag |= PEP_EDIT_RECALC; /* redraw selection only */
 }
 
-static int select_first_exec(bContext *C, wmOperator *op)
+static int select_roots_exec(bContext *C, wmOperator *op)
 {
        PEData data;
 
@@ -1397,19 +1395,19 @@ static int select_first_exec(bContext *C, wmOperator *op)
        foreach_point(&data, select_root);
 
        PE_update_selection(data.scene, data.ob, 1);
-       WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE_SELECT, data.ob);
+       WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_SELECTED, data.ob);
 
        return OPERATOR_FINISHED;
 }
 
-void PARTICLE_OT_select_first(wmOperatorType *ot)
+void PARTICLE_OT_select_roots(wmOperatorType *ot)
 {
        /* identifiers */
-       ot->name= "Select First";
-       ot->idname= "PARTICLE_OT_select_first";
+       ot->name= "Select Roots";
+       ot->idname= "PARTICLE_OT_select_roots";
        
        /* api callbacks */
-       ot->exec= select_first_exec;
+       ot->exec= select_roots_exec;
        ot->poll= PE_poll;
 
        /* flags */
@@ -1421,11 +1419,15 @@ void PARTICLE_OT_select_first(wmOperatorType *ot)
 static void select_tip(PEData *data, int point_index)
 {
        PTCacheEditPoint *point = data->edit->points + point_index;
+       
+       if (point->flag & PEP_HIDE)
+               return;
+       
        point->keys[point->totkey - 1].flag |= PEK_SELECT;
        point->flag |= PEP_EDIT_RECALC; /* redraw selection only */
 }
 
-static int select_last_exec(bContext *C, wmOperator *op)
+static int select_tips_exec(bContext *C, wmOperator *op)
 {
        PEData data;
 
@@ -1433,19 +1435,19 @@ static int select_last_exec(bContext *C, wmOperator *op)
        foreach_point(&data, select_tip);
 
        PE_update_selection(data.scene, data.ob, 1);
-       WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE_SELECT, data.ob);
+       WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_SELECTED, data.ob);
 
        return OPERATOR_FINISHED;
 }
 
-void PARTICLE_OT_select_last(wmOperatorType *ot)
+void PARTICLE_OT_select_tips(wmOperatorType *ot)
 {
        /* identifiers */
-       ot->name= "Select Last";
-       ot->idname= "PARTICLE_OT_select_last";
+       ot->name= "Select Tips";
+       ot->idname= "PARTICLE_OT_select_tips";
        
        /* api callbacks */
-       ot->exec= select_last_exec;
+       ot->exec= select_tips_exec;
        ot->poll= PE_poll;
 
        /* flags */
@@ -1473,7 +1475,7 @@ static int select_linked_exec(bContext *C, wmOperator *op)
 
        for_mouse_hit_keys(&data, select_keys, 1);  /* nearest only */
        PE_update_selection(data.scene, data.ob, 1);
-       WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE_SELECT, data.ob);
+       WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_SELECTED, data.ob);
 
        return OPERATOR_FINISHED;
 }
@@ -1499,7 +1501,7 @@ void PARTICLE_OT_select_linked(wmOperatorType *ot)
        /* api callbacks */
        ot->exec= select_linked_exec;
        ot->invoke= select_linked_invoke;
-       ot->poll= PE_poll_3dview;
+       ot->poll= PE_poll_view3d;
 
        /* flags */
        ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
@@ -1539,7 +1541,7 @@ int PE_border_select(bContext *C, rcti *rect, int select, int extend)
        for_mouse_hit_keys(&data, select_key, 0);
 
        PE_update_selection(scene, ob, 1);
-       WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE_SELECT, ob);
+       WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_SELECTED, ob);
 
        return OPERATOR_FINISHED;
 }
@@ -1564,7 +1566,7 @@ int PE_circle_select(bContext *C, int selecting, short *mval, float rad)
        for_mouse_hit_keys(&data, select_key, 0);
 
        PE_update_selection(scene, ob, 1);
-       WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE_SELECT, ob);
+       WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_SELECTED, ob);
 
        return OPERATOR_FINISHED;
 }
@@ -1630,7 +1632,7 @@ int PE_lasso_select(bContext *C, short mcords[][2], short moves, short select)
        }
 
        PE_update_selection(scene, ob, 1);
-       WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE_SELECT, ob);
+       WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_SELECTED, ob);
 
        return OPERATOR_FINISHED;
 }
@@ -1664,7 +1666,7 @@ static int hide_exec(bContext *C, wmOperator *op)
        }
 
        PE_update_selection(scene, ob, 1);
-       WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE_SELECT, ob);
+       WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_SELECTED, ob);
 
        return OPERATOR_FINISHED;
 }
@@ -1706,7 +1708,7 @@ static int reveal_exec(bContext *C, wmOperator *op)
        }
 
        PE_update_selection(scene, ob, 1);
-       WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE_SELECT, ob);
+       WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_SELECTED, ob);
 
        return OPERATOR_FINISHED;
 }
@@ -1764,7 +1766,7 @@ static int select_less_exec(bContext *C, wmOperator *op)
        foreach_point(&data, select_less_keys);
 
        PE_update_selection(data.scene, data.ob, 1);
-       WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE_SELECT, data.ob);
+       WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_SELECTED, data.ob);
 
        return OPERATOR_FINISHED;
 }
@@ -1825,7 +1827,7 @@ static int select_more_exec(bContext *C, wmOperator *op)
        foreach_point(&data, select_more_keys);
 
        PE_update_selection(data.scene, data.ob, 1);
-       WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE_SELECT, data.ob);
+       WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_SELECTED, data.ob);
 
        return OPERATOR_FINISHED;
 }
@@ -1862,7 +1864,7 @@ static int select_inverse_exec(bContext *C, wmOperator *op)
        }
 
        PE_update_selection(data.scene, data.ob, 1);
-       WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE_SELECT, data.ob);
+       WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_SELECTED, data.ob);
 
        return OPERATOR_FINISHED;
 }
@@ -1953,7 +1955,7 @@ static int rekey_exec(bContext *C, wmOperator *op)
        
        recalc_lengths(data.edit);
        PE_update_object(data.scene, data.ob, 1);
-       WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE_DATA, data.ob);
+       WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_EDITED, data.ob);
 
        return OPERATOR_FINISHED;
 }
@@ -2096,6 +2098,7 @@ static void remove_tagged_keys(Scene *scene, Object *ob, ParticleSystem *psys)
        ParticleData *pa;
        HairKey *hkey, *nhkey, *new_hkeys=0;
        POINT_P; KEY_K;
+       PTCacheEditKey *nkey, *new_keys;
        ParticleSystemModifierData *psmd;
        short new_totkey;
 
@@ -2131,9 +2134,10 @@ static void remove_tagged_keys(Scene *scene, Object *ob, ParticleSystem *psys)
                }
 
                if(new_totkey != pa->totkey) {
-                       hkey= pa->hair;
                        nhkey= new_hkeys= MEM_callocN(new_totkey*sizeof(HairKey), "HairKeys");
+                       nkey= new_keys= MEM_callocN(new_totkey*sizeof(PTCacheEditKey), "particle edit keys");
 
+                       hkey= pa->hair;
                        LOOP_KEYS {
                                while(key->flag & PEK_TAG && hkey < pa->hair + pa->totkey) {
                                        key++;
@@ -2142,29 +2146,36 @@ static void remove_tagged_keys(Scene *scene, Object *ob, ParticleSystem *psys)
 
                                if(hkey < pa->hair + pa->totkey) {
                                        VECCOPY(nhkey->co, hkey->co);
+                                       nhkey->editflag = hkey->editflag;
                                        nhkey->time= hkey->time;
                                        nhkey->weight= hkey->weight;
+                                       
+                                       nkey->co= nhkey->co;
+                                       nkey->time= &nhkey->time;
+                                       /* these can be copied from old edit keys */
+                                       nkey->flag = key->flag;
+                                       nkey->ftime = key->ftime;
+                                       nkey->length = key->length;
+                                       VECCOPY(nkey->world_co, key->world_co);
                                }
-                               hkey++;
+                               nkey++;
                                nhkey++;
+                               hkey++;
                        }
+
                        if(pa->hair)
                                MEM_freeN(pa->hair);
+
+                       if(point->keys)
+                               MEM_freeN(point->keys);
                        
                        pa->hair= new_hkeys;
+                       point->keys= new_keys;
 
                        point->totkey= pa->totkey= new_totkey;
 
-                       if(point->keys)
-                               MEM_freeN(point->keys);
-                       key= point->keys= MEM_callocN(new_totkey*sizeof(PTCacheEditKey), "particle edit keys");
-
-                       hkey = pa->hair;
-                       LOOP_KEYS {
-                               key->co= hkey->co;
-                               key->time= &hkey->time;
-                               hkey++;
-                       }
+                       /* flag for recalculating length */
+                       point->flag |= PEP_EDIT_RECALC;
                }
        }
 }
@@ -2258,7 +2269,7 @@ static int subdivide_exec(bContext *C, wmOperator *op)
        
        recalc_lengths(data.edit);
        PE_update_object(data.scene, data.ob, 1);
-       WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE_DATA, data.ob);
+       WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_EDITED, data.ob);
 
        return OPERATOR_FINISHED;
 }
@@ -2346,7 +2357,7 @@ static int remove_doubles_exec(bContext *C, wmOperator *op)
        BKE_reportf(op->reports, RPT_INFO, "Remove %d double particles.", totremoved);
 
        DAG_id_flush_update(&ob->id, OB_RECALC_DATA);
-       WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE_DATA, ob);
+       WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_EDITED, ob);
 
        return OPERATOR_FINISHED;
 }
@@ -2368,6 +2379,55 @@ void PARTICLE_OT_remove_doubles(wmOperatorType *ot)
        RNA_def_float(ot->srna, "threshold", 0.0002f, 0.0f, FLT_MAX, "Threshold", "Threshold distance withing which particles are removed", 0.00001f, 0.1f);
 }
 
+
+static int weight_set_exec(bContext *C, wmOperator *op)
+{
+       Scene *scene= CTX_data_scene(C);
+       ParticleEditSettings *pset= PE_settings(scene);
+       Object *ob= CTX_data_active_object(C);
+       PTCacheEdit *edit= PE_get_current(scene, ob);
+       ParticleSystem *psys = edit->psys;
+       POINT_P;
+       KEY_K;
+       HairKey *hkey;
+       float weight;
+       ParticleBrushData *brush= &pset->brush[pset->brushtype];
+    float factor= RNA_float_get(op->ptr, "factor");
+
+       weight= brush->strength;
+       edit= psys->edit;
+
+       LOOP_SELECTED_POINTS {
+               ParticleData *pa= psys->particles + p;
+
+               LOOP_SELECTED_KEYS {
+                       hkey= pa->hair + k;
+                       hkey->weight= interpf(weight, hkey->weight, factor);
+               }
+       }
+
+       DAG_id_flush_update(&ob->id, OB_RECALC_DATA);
+       WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_EDITED, ob);
+
+       return OPERATOR_FINISHED;
+}
+
+void PARTICLE_OT_weight_set(wmOperatorType *ot)
+{
+       /* identifiers */
+       ot->name= "Weight Set";
+       ot->idname= "PARTICLE_OT_weight_set";
+
+       /* api callbacks */
+       ot->exec= weight_set_exec;
+       ot->poll= PE_poll;
+
+       /* flags */
+       ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+    
+    RNA_def_float(ot->srna, "factor", 1, 0, 1, "Factor", "", 0, 1);
+}
+
 /************************ cursor drawing *******************************/
 
 static void brush_drawcursor(bContext *C, int x, int y, void *customdata)
@@ -2405,7 +2465,7 @@ static void toggle_particle_cursor(bContext *C, int enable)
                pset->paintcursor = NULL;
        }
        else if(enable)
-               pset->paintcursor= WM_paint_cursor_activate(CTX_wm_manager(C), PE_poll_3dview, brush_drawcursor, NULL);
+               pset->paintcursor= WM_paint_cursor_activate(CTX_wm_manager(C), PE_poll_view3d, brush_drawcursor, NULL);
 }
 
 /********************* radial control operator *********************/
@@ -2461,6 +2521,8 @@ static int brush_radial_control_exec(bContext *C, wmOperator *op)
        else if(mode == WM_RADIALCONTROL_STRENGTH)
                brush->strength= new_value;
 
+       WM_event_add_notifier(C, NC_WINDOW, NULL);
+
        return OPERATOR_FINISHED;
 }
 
@@ -2522,7 +2584,7 @@ static int delete_exec(bContext *C, wmOperator *op)
        }
 
        DAG_id_flush_update(&data.ob->id, OB_RECALC_DATA);
-       WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE_DATA, data.ob);
+       WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_EDITED, data.ob);
 
        return OPERATOR_FINISHED;
 }
@@ -2542,7 +2604,7 @@ void PARTICLE_OT_delete(wmOperatorType *ot)
        ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
 
        /* properties */
-       RNA_def_enum(ot->srna, "type", delete_type_items, DEL_PARTICLE, "Type", "Delete a full particle or only keys.");
+       ot->prop= RNA_def_enum(ot->srna, "type", delete_type_items, DEL_PARTICLE, "Type", "Delete a full particle or only keys.");
 }
 
 /*************************** mirror operator **************************/
@@ -2681,7 +2743,7 @@ static int mirror_exec(bContext *C, wmOperator *op)
        PE_mirror_x(scene, ob, 0);
 
        update_world_cos(ob, edit);
-       WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE_DATA, ob);
+       WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_EDITED, ob);
        DAG_id_flush_update(&ob->id, OB_RECALC_DATA);
 
        return OPERATOR_FINISHED;
@@ -2846,7 +2908,16 @@ static void brush_puff(PEData *data, int point_index)
        PTCacheEditPoint *point = edit->points + point_index;
        KEY_K;
        float mat[4][4], imat[4][4];
-       float lastco[3], rootco[3] = {0.0f, 0.0f, 0.0f}, co[3], nor[3], kco[3], dco[3], fac=0.0f, length=0.0f;
+
+       float lastco[3], rootco[3] = {0.0f, 0.0f, 0.0f}, co[3], nor[3], kco[3], dco[3], ofs[3] = {0.0f, 0.0f, 0.0f}, fac=0.0f, length=0.0f;
+       int puff_volume = 0;
+       int change= 0;
+
+       {
+               ParticleEditSettings *pset= PE_settings(data->scene);
+               ParticleBrushData *brush= &pset->brush[pset->brushtype];
+               puff_volume = brush->flag & PE_BRUSH_DATA_PUFF_VOLUME;
+       }
 
        if(psys && !(psys->flag & PSYS_GLOBAL_HAIR)) {
                psys_mat_hair_to_global(data->ob, data->dm, psys->part->from, psys->particles + point_index, mat);
@@ -2862,12 +2933,15 @@ static void brush_puff(PEData *data, int point_index)
                        /* find root coordinate and normal on emitter */
                        VECCOPY(co, key->co);
                        mul_m4_v3(mat, co);
+                       mul_v3_m4v3(kco, data->ob->imat, co); /* use 'kco' as the object space version of worldspace 'co', ob->imat is set before calling */
 
-                       point_index= BLI_kdtree_find_nearest(edit->emitter_field, co, NULL, NULL);
+                       point_index= BLI_kdtree_find_nearest(edit->emitter_field, kco, NULL, NULL);
                        if(point_index == -1) return;
 
                        VECCOPY(rootco, co);
                        copy_v3_v3(nor, &edit->emitter_cosnos[point_index*6+3]);
+                       mul_mat3_m4_v3(data->ob->obmat, nor); /* normal into worldspace */
+
                        normalize_v3(nor);
                        length= 0.0f;
 
@@ -2877,24 +2951,101 @@ static void brush_puff(PEData *data, int point_index)
                                fac= -fac;
                }
                else {
-                       /* compute position as if hair was standing up straight */
+                       /* compute position as if hair was standing up straight.
+                        * */
                        VECCOPY(lastco, co);
                        VECCOPY(co, key->co);
                        mul_m4_v3(mat, co);
                        length += len_v3v3(lastco, co);
+                       if((data->select==0 || (key->flag & PEK_SELECT)) && !(key->flag & PEK_HIDE)) {
+                               VECADDFAC(kco, rootco, nor, length);
+
+                               /* blend between the current and straight position */
+                               VECSUB(dco, kco, co);
+                               VECADDFAC(co, co, dco, fac);
+
+                               /* re-use dco to compare before and after translation and add to the offset  */
+                               VECCOPY(dco, key->co);
 
-                       VECADDFAC(kco, rootco, nor, length);
+                               mul_v3_m4v3(key->co, imat, co);
 
-                       /* blend between the current and straight position */
-                       VECSUB(dco, kco, co);
-                       VECADDFAC(co, co, dco, fac);
+                               if(puff_volume) {
+                                       /* accumulate the total distance moved to apply to unselected
+                                        * keys that come after */
+                                       ofs[0] += key->co[0] - dco[0];
+                                       ofs[1] += key->co[1] - dco[1];
+                                       ofs[2] += key->co[2] - dco[2];
+                               }
+                               change = 1;
+                       }
+                       else {
 
-                       VECCOPY(key->co, co);
-                       mul_m4_v3(imat, key->co);
+                               if(puff_volume) {
+#if 0
+                                       /* this is simple but looks bad, adds annoying kinks */
+                                       add_v3_v3(key->co, ofs);
+#else
+                                       /* translate (not rotate) the rest of the hair if its not selected  */
+                                       if(ofs[0] || ofs[1] || ofs[2]) {
+#if 0                                  /* kindof works but looks worse then whats below */
+
+                                               /* Move the unselected point on a vector based on the
+                                                * hair direction and the offset */
+                                               float c1[3], c2[3];
+                                               VECSUB(dco, lastco, co);
+                                               mul_mat3_m4_v3(imat, dco); /* into particle space */
+
+                                               /* move the point allong a vector perpendicular to the
+                                                * hairs direction, reduces odd kinks, */
+                                               cross_v3_v3v3(c1, ofs, dco);
+                                               cross_v3_v3v3(c2, c1, dco);
+                                               normalize_v3(c2);
+                                               mul_v3_fl(c2, len_v3(ofs));
+                                               add_v3_v3(key->co, c2);
+#else
+                                               /* Move the unselected point on a vector based on the
+                                                * the normal of the closest geometry */
+                                               float oco[3], onor[3];
+                                               VECCOPY(oco, key->co);
+                                               mul_m4_v3(mat, oco);
+                                               mul_v3_m4v3(kco, data->ob->imat, oco); /* use 'kco' as the object space version of worldspace 'co', ob->imat is set before calling */
+
+                                               point_index= BLI_kdtree_find_nearest(edit->emitter_field, kco, NULL, NULL);
+                                               if(point_index != -1) {
+                                                       copy_v3_v3(onor, &edit->emitter_cosnos[point_index*6+3]);
+                                                       mul_mat3_m4_v3(data->ob->obmat, onor); /* normal into worldspace */
+                                                       mul_mat3_m4_v3(imat, onor); /* worldspace into particle space */
+                                                       normalize_v3(onor);
+
+
+                                                       mul_v3_fl(onor, len_v3(ofs));
+                                                       add_v3_v3(key->co, onor);
+                                               }
+#endif
+                                       }
+#endif
+                               }
+                       }
                }
        }
 
-       point->flag |= PEP_EDIT_RECALC;
+       if(change)
+               point->flag |= PEP_EDIT_RECALC;
+}
+
+
+static void brush_weight(PEData *data, float mat[][4], float imat[][4], int point_index, int key_index, PTCacheEditKey *key)
+{
+       /* roots have full weight allways */
+       if(key_index) {
+               PTCacheEdit *edit = data->edit;
+               ParticleSystem *psys = edit->psys;
+
+               ParticleData *pa= psys->particles + point_index;
+               pa->hair[key_index].weight = data->weightfac;
+
+               (data->edit->points + point_index)->flag |= PEP_EDIT_RECALC;
+       }
 }
 
 static void brush_smooth_get(PEData *data, float mat[][4], float imat[][4], int point_index, int key_index, PTCacheEditKey *key)
@@ -3185,7 +3336,7 @@ static void brush_edit_apply(bContext *C, wmOperator *op, PointerRNA *itemptr)
        RNA_float_get_array(itemptr, "mouse", mousef);
        mouse[0] = mousef[0];
        mouse[1] = mousef[1];
-       flip= RNA_boolean_get(itemptr, "flip");
+       flip= RNA_boolean_get(itemptr, "pen_flip");
 
        if(bedit->first) {
                bedit->lastmouse[0]= mouse[0];
@@ -3221,7 +3372,7 @@ static void brush_edit_apply(bContext *C, wmOperator *op, PointerRNA *itemptr)
                                data.mval= mval;
                                data.rad= (float)brush->size;
 
-                               data.combfac= (float)(brush->strength - 50) / 50.0f;
+                               data.combfac= (brush->strength - 0.5f) * 2.0f;
                                if(data.combfac < 0.0f)
                                        data.combfac= 1.0f - 9.0f * data.combfac;
                                else
@@ -3243,7 +3394,7 @@ static void brush_edit_apply(bContext *C, wmOperator *op, PointerRNA *itemptr)
                                        PE_set_view3d_data(C, &data);
                                        data.mval= mval;
                                        data.rad= (float)brush->size;
-                                       data.cutfac= (float)(brush->strength / 100.0f);
+                                       data.cutfac= brush->strength;
 
                                        if(selected)
                                                foreach_selected_point(&data, brush_cut);
@@ -3267,7 +3418,7 @@ static void brush_edit_apply(bContext *C, wmOperator *op, PointerRNA *itemptr)
                                data.mval= mval;
                                
                                data.rad= (float)brush->size;
-                               data.growfac= (float)brush->strength / 5000.0f;
+                               data.growfac= brush->strength / 50.0f;
 
                                if(brush->invert ^ flip)
                                        data.growfac= 1.0f - data.growfac;
@@ -3289,8 +3440,9 @@ static void brush_edit_apply(bContext *C, wmOperator *op, PointerRNA *itemptr)
                                        data.dm= psmd->dm;
                                        data.mval= mval;
                                        data.rad= (float)brush->size;
+                                       data.select= selected;
 
-                                       data.pufffac= (float)(brush->strength - 50) / 50.0f;
+                                       data.pufffac= (brush->strength - 0.5f) * 2.0f;
                                        if(data.pufffac < 0.0f)
                                                data.pufffac= 1.0f - 9.0f * data.pufffac;
                                        else
@@ -3311,7 +3463,7 @@ static void brush_edit_apply(bContext *C, wmOperator *op, PointerRNA *itemptr)
                                        PE_set_view3d_data(C, &data);
                                        data.mval= mval;
 
-                                       added= brush_add(&data, brush->strength);
+                                       added= brush_add(&data, brush->count);
 
                                        if(pset->flag & PE_KEEP_LENGTHS)
                                                recalc_lengths(edit);
@@ -3331,7 +3483,7 @@ static void brush_edit_apply(bContext *C, wmOperator *op, PointerRNA *itemptr)
                                data.vec[0]= data.vec[1]= data.vec[2]= 0.0f;
                                data.tot= 0;
 
-                               data.smoothfac= (float)(brush->strength / 100.0f);
+                               data.smoothfac= brush->strength;
 
                                invert_m4_m4(ob->imat, ob->obmat);
 
@@ -3342,6 +3494,23 @@ static void brush_edit_apply(bContext *C, wmOperator *op, PointerRNA *itemptr)
                                        foreach_mouse_hit_key(&data, brush_smooth_do, selected);
                                }
 
+                               break;
+                       }
+                       case PE_BRUSH_WEIGHT:
+                       {
+                               PEData data;
+                               PE_set_view3d_data(C, &data);
+
+                               if(edit->psys) {
+                                       data.dm= psmd->dm;
+                                       data.mval= mval;
+                                       data.rad= (float)brush->size;
+
+                                       data.weightfac = brush->strength; /* note that this will never be zero */
+
+                                       foreach_mouse_hit_key(&data, brush_weight, selected);
+                               }
+
                                break;
                        }
                }
@@ -3359,7 +3528,7 @@ static void brush_edit_apply(bContext *C, wmOperator *op, PointerRNA *itemptr)
                else
                        PE_update_object(scene, ob, 1);
 
-               WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE_SELECT, ob);
+               WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_EDITED, ob);
                
                bedit->lastmouse[0]= mouse[0];
                bedit->lastmouse[1]= mouse[1];
@@ -3404,7 +3573,7 @@ static void brush_edit_apply_event(bContext *C, wmOperator *op, wmEvent *event)
        RNA_collection_add(op->ptr, "stroke", &itemptr);
 
        RNA_float_set_array(&itemptr, "mouse", mouse);
-       RNA_boolean_set(&itemptr, "flip", event->shift != 0); // XXX hardcoded
+       RNA_boolean_set(&itemptr, "pen_flip", event->shift != 0); // XXX hardcoded
 
        /* apply */
        brush_edit_apply(C, op, &itemptr);
@@ -3456,7 +3625,7 @@ void PARTICLE_OT_brush_edit(wmOperatorType *ot)
        ot->invoke= brush_edit_invoke;
        ot->modal= brush_edit_modal;
        ot->cancel= brush_edit_cancel;
-       ot->poll= PE_poll_3dview;
+       ot->poll= PE_poll_view3d;
 
        /* flags */
        ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO|OPTYPE_BLOCKING;
@@ -3513,6 +3682,8 @@ static void make_PTCacheUndo(PTCacheEdit *edit, PTCacheUndo *undo)
                for(; pm; pm=pm->next) {
                        for(i=0; i<BPHYS_TOT_DATA; i++)
                                pm->data[i] = MEM_dupallocN(pm->data[i]);
+
+                       pm->index_array = MEM_dupallocN(pm->index_array);
                }
        }
 
@@ -3587,6 +3758,8 @@ static void get_PTCacheUndo(PTCacheEdit *edit, PTCacheUndo *undo)
                        for(i=0; i<BPHYS_TOT_DATA; i++)
                                pm->data[i] = MEM_dupallocN(pm->data[i]);
 
+                       pm->index_array = MEM_dupallocN(pm->index_array);
+
                        BKE_ptcache_mem_init_pointers(pm);
 
                        LOOP_POINTS {
@@ -3851,12 +4024,20 @@ static void PE_create_particle_edit(Scene *scene, Object *ob, PointCache *cache,
 
                                LOOP_POINTS {
                                        if(psys) {
-                                               pa = psys->particles + p;
-                                               if((pm->next && pm->next->frame < pa->time)
-                                                       || (pm->prev && pm->prev->frame >= pa->dietime)) {
-                                                               BKE_ptcache_mem_incr_pointers(pm);
+                                               if(pm->index_array) {
+                                                       if(pm->index_array[p])
+                                                               BKE_ptcache_mem_seek_pointers(p, pm);
+                                                       else
                                                                continue;
-                                                       }
+                                               }
+                                               else {
+                                                       pa = psys->particles + p;
+                                                       if((pm->next && pm->next->frame < pa->time)
+                                                               || (pm->prev && pm->prev->frame >= pa->dietime)) {
+                                                                       BKE_ptcache_mem_incr_pointers(pm);
+                                                                       continue;
+                                                               }
+                                               }
                                        }
 
                                        if(!point->totkey) {
@@ -3909,8 +4090,15 @@ static int particle_edit_toggle_exec(bContext *C, wmOperator *op)
        Object *ob= CTX_data_active_object(C);
 
        if(!(ob->mode & OB_MODE_PARTICLE_EDIT)) {
+               PTCacheEdit *edit;
                ob->mode |= OB_MODE_PARTICLE_EDIT;
-               PE_create_current(scene, ob);
+               edit= PE_create_current(scene, ob);
+       
+               /* mesh may have changed since last entering editmode.
+                * note, this may have run before if the edit data was just created, so could avoid this and speed up a little */
+               if(edit && edit->psys)
+                       recalc_emitter_field(ob, edit->psys);
+               
                toggle_particle_cursor(C, 1);
                WM_event_add_notifier(C, NC_SCENE|ND_MODE|NS_MODE_PARTICLE, NULL);
        }
@@ -3959,10 +4147,17 @@ static int clear_edited_exec(bContext *C, wmOperator *op)
                        psys->flag &= ~PSYS_EDITED;
 
                        psys_reset(psys, PSYS_RESET_DEPSGRAPH);
-                       WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE_DATA, ob);
+                       WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_EDITED, ob);
                        DAG_id_flush_update(&ob->id, OB_RECALC_DATA);
                }
        }
+       else { /* some operation might have protected hair from editing so let's clear the flag */
+               psys->recalc |= PSYS_RECALC_RESET;
+               psys->flag &= ~PSYS_GLOBAL_HAIR;
+               psys->flag &= ~PSYS_EDITED;
+               WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_EDITED, ob);
+               DAG_id_flush_update(&ob->id, OB_RECALC_DATA);
+       }
 
        return OPERATOR_FINISHED;
 }