merge with trunk at r27259 and commit of a patch by anthony jones to fix msvc (though...
[blender-staging.git] / source / blender / editors / physics / particle_edit.c
index 5ac843f796c738a1f688ecda1103bda622c8a889..71c18b353653cb3dfaa34e2dfd48c5f6656de595 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.
@@ -88,6 +88,7 @@
 
 static void PE_create_particle_edit(Scene *scene, Object *ob, PointCache *cache, ParticleSystem *psys);
 static void PTCacheUndo_clear(PTCacheEdit *edit);
+static void recalc_emitter_field(Object *ob, ParticleSystem *psys);
 
 #define KEY_K                                  PTCacheEditKey *key; int k
 #define POINT_P                                        PTCacheEditPoint *point; int p
@@ -190,10 +191,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);
@@ -201,12 +205,12 @@ 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);
 
        /* in the case of only one editable thing, set pset->edittype accordingly */
@@ -370,7 +374,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);
@@ -403,7 +408,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)
@@ -2362,6 +2368,52 @@ 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];
+       edit= psys->edit;
+
+       weight= (float)(brush->strength / 100.0f);
+
+       LOOP_SELECTED_POINTS {
+               ParticleData *pa= psys->particles + p;
+
+               LOOP_SELECTED_KEYS {
+                       hkey= pa->hair + k;
+                       hkey->weight= weight;
+               }
+       }
+
+       DAG_id_flush_update(&ob->id, OB_RECALC_DATA);
+       WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE_DATA, 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;
+}
+
 /************************ cursor drawing *******************************/
 
 static void brush_drawcursor(bContext *C, int x, int y, void *customdata)
@@ -2536,7 +2588,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 **************************/
@@ -2840,7 +2892,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);
@@ -2856,12 +2917,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;
 
@@ -2871,24 +2935,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((key->flag & PEK_SELECT) && !(key->flag & PEK_HIDE)) {
+                               VECADDFAC(kco, rootco, nor, length);
 
-                       VECADDFAC(kco, rootco, nor, length);
+                               /* blend between the current and straight position */
+                               VECSUB(dco, kco, co);
+                               VECADDFAC(co, co, dco, fac);
 
-                       /* 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);
+
+                               mul_v3_m4v3(key->co, imat, co);
+
+                               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)
@@ -3336,6 +3477,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 = (float)(brush->strength / 100.0f); /* note that this will never be zero */
+
+                                       foreach_mouse_hit_key(&data, brush_weight, selected);
+                               }
+
                                break;
                        }
                }
@@ -3903,8 +4061,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);
        }