operator renaming for more consistent word ordering (_add/_remmove shold be last...
[blender-staging.git] / source / blender / editors / physics / particle_object.c
1 /**
2  * $Id:
3  *
4  * ***** BEGIN GPL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version. 
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19  *
20  * The Original Code is Copyright (C) 2009 Blender Foundation.
21  * All rights reserved.
22  *
23  * Contributor(s): Blender Foundation
24  *
25  * ***** END GPL LICENSE BLOCK *****
26  */
27
28 #include <stdlib.h>
29 #include <string.h>
30
31 #include "MEM_guardedalloc.h"
32
33 #include "DNA_meshdata_types.h"
34 #include "DNA_modifier_types.h"
35 #include "DNA_object_types.h"
36 #include "DNA_particle_types.h"
37 #include "DNA_scene_types.h"
38 #include "DNA_windowmanager_types.h"
39
40 #include "BLI_math.h"
41 #include "BLI_listbase.h"
42
43 #include "BKE_context.h"
44 #include "BKE_depsgraph.h"
45 #include "BKE_DerivedMesh.h"
46 #include "BKE_cdderivedmesh.h"
47 #include "BKE_main.h"
48 #include "BKE_particle.h"
49 #include "BKE_pointcache.h"
50 #include "BKE_utildefines.h"
51
52 #include "RNA_access.h"
53 #include "RNA_define.h"
54
55 #include "WM_api.h"
56 #include "WM_types.h"
57
58 #include "ED_particle.h"
59
60 #include "physics_intern.h"
61
62 /********************** particle system slot operators *********************/
63
64 static int particle_system_add_exec(bContext *C, wmOperator *op)
65 {
66         Object *ob= CTX_data_pointer_get_type(C, "object", &RNA_Object).data;
67         Scene *scene = CTX_data_scene(C);
68
69         if(!scene || !ob)
70                 return OPERATOR_CANCELLED;
71
72         object_add_particle_system(scene, ob, NULL);
73         WM_event_add_notifier(C, NC_OBJECT|ND_DRAW, ob);
74         
75         return OPERATOR_FINISHED;
76 }
77
78 void OBJECT_OT_particle_system_add(wmOperatorType *ot)
79 {
80         /* identifiers */
81         ot->name= "Add Particle System Slot";
82         ot->idname= "OBJECT_OT_particle_system_add";
83         ot->description="Add a particle system.";
84         
85         /* api callbacks */
86         ot->exec= particle_system_add_exec;
87
88         /* flags */
89         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
90 }
91
92 static int particle_system_remove_exec(bContext *C, wmOperator *op)
93 {
94         Object *ob= CTX_data_pointer_get_type(C, "object", &RNA_Object).data;
95         Scene *scene = CTX_data_scene(C);
96         int mode_orig = ob->mode;
97         if(!scene || !ob)
98                 return OPERATOR_CANCELLED;
99
100         object_remove_particle_system(scene, ob);
101
102         /* possible this isn't the active object
103          * object_remove_particle_system() clears the mode on the last psys
104          * */
105         if(mode_orig & OB_MODE_PARTICLE_EDIT)
106                 if((ob->mode & OB_MODE_PARTICLE_EDIT)==0)
107                         if(scene->basact && scene->basact->object==ob)
108                                 WM_event_add_notifier(C, NC_SCENE|ND_MODE|NS_MODE_OBJECT, NULL);
109
110         WM_event_add_notifier(C, NC_OBJECT|ND_DRAW, ob);
111         
112         return OPERATOR_FINISHED;
113 }
114
115 void OBJECT_OT_particle_system_remove(wmOperatorType *ot)
116 {
117         /* identifiers */
118         ot->name= "Remove Particle System Slot";
119         ot->idname= "OBJECT_OT_particle_system_remove";
120         ot->description="Remove the selected particle system.";
121         
122         /* api callbacks */
123         ot->exec= particle_system_remove_exec;
124
125         /* flags */
126         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
127 }
128
129 /********************** new particle settings operator *********************/
130
131 static int psys_poll(bContext *C)
132 {
133         PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem);
134         return (ptr.data != NULL);
135 }
136
137 static int new_particle_settings_exec(bContext *C, wmOperator *op)
138 {
139         Scene *scene = CTX_data_scene(C);
140         Main *bmain= CTX_data_main(C);
141         ParticleSystem *psys;
142         ParticleSettings *part = NULL;
143         Object *ob;
144         PointerRNA ptr;
145
146         ptr = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem);
147
148         psys = ptr.data;
149
150         /* add or copy particle setting */
151         if(psys->part)
152                 part= psys_copy_settings(psys->part);
153         else
154                 part= psys_new_settings("ParticleSettings", bmain);
155
156         ob= ptr.id.data;
157
158         if(psys->part)
159                 psys->part->id.us--;
160
161         psys->part = part;
162
163         psys_check_boid_data(psys);
164
165         DAG_scene_sort(scene);
166         DAG_id_flush_update(&ob->id, OB_RECALC_DATA);
167
168         WM_event_add_notifier(C, NC_OBJECT|ND_DRAW, ob);
169         
170         return OPERATOR_FINISHED;
171 }
172
173 void PARTICLE_OT_new(wmOperatorType *ot)
174 {
175         /* identifiers */
176         ot->name= "New Particle Settings";
177         ot->idname= "PARTICLE_OT_new";
178         ot->description="Add new particle settings.";
179         
180         /* api callbacks */
181         ot->exec= new_particle_settings_exec;
182         ot->poll= psys_poll;
183
184         /* flags */
185         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
186 }
187
188 /********************** keyed particle target operators *********************/
189
190 static int new_particle_target_exec(bContext *C, wmOperator *op)
191 {
192         Scene *scene = CTX_data_scene(C);
193         PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem);
194         ParticleSystem *psys= ptr.data;
195         Object *ob = ptr.id.data;
196
197         ParticleTarget *pt;
198
199         if(!psys)
200                 return OPERATOR_CANCELLED;
201
202         pt = psys->targets.first;
203         for(; pt; pt=pt->next)
204                 pt->flag &= ~PTARGET_CURRENT;
205
206         pt = MEM_callocN(sizeof(ParticleTarget), "keyed particle target");
207
208         pt->flag |= PTARGET_CURRENT;
209         pt->psys = 1;
210
211         BLI_addtail(&psys->targets, pt);
212
213         DAG_scene_sort(scene);
214         DAG_id_flush_update(&ob->id, OB_RECALC_DATA);
215
216         WM_event_add_notifier(C, NC_OBJECT|ND_DRAW, ob);
217         
218         return OPERATOR_FINISHED;
219 }
220
221 void PARTICLE_OT_new_target(wmOperatorType *ot)
222 {
223         /* identifiers */
224         ot->name= "New Particle Target";
225         ot->idname= "PARTICLE_OT_new_target";
226         ot->description="Add a new particle target.";
227         
228         /* api callbacks */
229         ot->exec= new_particle_target_exec;
230
231         /* flags */
232         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
233 }
234
235 static int remove_particle_target_exec(bContext *C, wmOperator *op)
236 {
237         Scene *scene = CTX_data_scene(C);
238         PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem);
239         ParticleSystem *psys= ptr.data;
240         Object *ob = ptr.id.data;
241
242         ParticleTarget *pt;
243
244         if(!psys)
245                 return OPERATOR_CANCELLED;
246
247         pt = psys->targets.first;
248         for(; pt; pt=pt->next) {
249                 if(pt->flag & PTARGET_CURRENT) {
250                         BLI_remlink(&psys->targets, pt);
251                         MEM_freeN(pt);
252                         break;
253                 }
254
255         }
256         pt = psys->targets.last;
257
258         if(pt)
259                 pt->flag |= PTARGET_CURRENT;
260
261         DAG_scene_sort(scene);
262         DAG_id_flush_update(&ob->id, OB_RECALC_DATA);
263
264         WM_event_add_notifier(C, NC_OBJECT|ND_DRAW, ob);
265         
266         return OPERATOR_FINISHED;
267 }
268
269 void PARTICLE_OT_target_remove(wmOperatorType *ot)
270 {
271         /* identifiers */
272         ot->name= "Remove Particle Target";
273         ot->idname= "PARTICLE_OT_target_remove";
274         ot->description="Remove the selected particle target.";
275         
276         /* api callbacks */
277         ot->exec= remove_particle_target_exec;
278
279         /* flags */
280         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
281 }
282
283 /************************ move up particle target operator *********************/
284
285 static int target_move_up_exec(bContext *C, wmOperator *op)
286 {
287         PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem);
288         ParticleSystem *psys= ptr.data;
289         Object *ob = ptr.id.data;
290         ParticleTarget *pt;
291
292         if(!psys)
293                 return OPERATOR_CANCELLED;
294         
295         pt = psys->targets.first;
296         for(; pt; pt=pt->next) {
297                 if(pt->flag & PTARGET_CURRENT && pt->prev) {
298                         BLI_remlink(&psys->targets, pt);
299                         BLI_insertlink(&psys->targets, pt->prev->prev, pt);
300
301                         DAG_id_flush_update(&ob->id, OB_RECALC_DATA);
302                         WM_event_add_notifier(C, NC_OBJECT|ND_DRAW, ob);
303                         break;
304                 }
305         }
306         
307         return OPERATOR_FINISHED;
308 }
309
310 void PARTICLE_OT_target_move_up(wmOperatorType *ot)
311 {
312         ot->name= "Move Up Target";
313         ot->idname= "PARTICLE_OT_target_move_up";
314         ot->description= "Move particle target up in the list.";
315         
316         ot->exec= target_move_up_exec;
317         
318         /* flags */
319         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
320 }
321
322 /************************ move down particle target operator *********************/
323
324 static int target_move_down_exec(bContext *C, wmOperator *op)
325 {
326         PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem);
327         ParticleSystem *psys= ptr.data;
328         Object *ob = ptr.id.data;
329         ParticleTarget *pt;
330
331         if(!psys)
332                 return OPERATOR_CANCELLED;
333         pt = psys->targets.first;
334         for(; pt; pt=pt->next) {
335                 if(pt->flag & PTARGET_CURRENT && pt->next) {
336                         BLI_remlink(&psys->targets, pt);
337                         BLI_insertlink(&psys->targets, pt->next, pt);
338
339                         DAG_id_flush_update(&ob->id, OB_RECALC_DATA);
340                         WM_event_add_notifier(C, NC_OBJECT|ND_DRAW, ob);
341                         break;
342                 }
343         }
344         
345         return OPERATOR_FINISHED;
346 }
347
348 void PARTICLE_OT_target_move_down(wmOperatorType *ot)
349 {
350         ot->name= "Move Down Target";
351         ot->idname= "PARTICLE_OT_target_move_down";
352         ot->description= "Move particle target down in the list.";
353         
354         ot->exec= target_move_down_exec;
355         
356         /* flags */
357         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
358 }
359
360 /************************ move up particle dupliweight operator *********************/
361
362 static int dupliob_move_up_exec(bContext *C, wmOperator *op)
363 {
364         PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem);
365         ParticleSystem *psys= ptr.data;
366         ParticleSettings *part;
367         ParticleDupliWeight *dw;
368
369         if(!psys)
370                 return OPERATOR_CANCELLED;
371
372         part = psys->part;
373         for(dw=part->dupliweights.first; dw; dw=dw->next) {
374                 if(dw->flag & PART_DUPLIW_CURRENT && dw->prev) {
375                         BLI_remlink(&part->dupliweights, dw);
376                         BLI_insertlink(&part->dupliweights, dw->prev->prev, dw);
377
378                         WM_event_add_notifier(C, NC_OBJECT|ND_DRAW, NULL);
379                         break;
380                 }
381         }
382         
383         return OPERATOR_FINISHED;
384 }
385
386 void PARTICLE_OT_dupliob_move_up(wmOperatorType *ot)
387 {
388         ot->name= "Move Up Dupli Object";
389         ot->idname= "PARTICLE_OT_dupliob_move_up";
390         ot->description= "Move dupli object up in the list.";
391         
392         ot->exec= dupliob_move_up_exec;
393         
394         /* flags */
395         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
396 }
397
398 /********************** particle dupliweight operators *********************/
399
400 static int copy_particle_dupliob_exec(bContext *C, wmOperator *op)
401 {
402         PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem);
403         ParticleSystem *psys= ptr.data;
404         ParticleSettings *part;
405         ParticleDupliWeight *dw;
406
407         if(!psys)
408                 return OPERATOR_CANCELLED;
409         part = psys->part;
410         for(dw=part->dupliweights.first; dw; dw=dw->next) {
411                 if(dw->flag & PART_DUPLIW_CURRENT) {
412                         dw->flag &= ~PART_DUPLIW_CURRENT;
413                         dw = MEM_dupallocN(dw);
414                         dw->flag |= PART_DUPLIW_CURRENT;
415                         BLI_addhead(&part->dupliweights, dw);
416
417                         WM_event_add_notifier(C, NC_OBJECT|ND_DRAW, NULL);
418                         break;
419                 }
420         }
421         
422         return OPERATOR_FINISHED;
423 }
424
425 void PARTICLE_OT_dupliob_copy(wmOperatorType *ot)
426 {
427         /* identifiers */
428         ot->name= "Copy Particle Dupliob";
429         ot->idname= "PARTICLE_OT_dupliob_copy";
430         ot->description="Duplicate the current dupliobject.";
431         
432         /* api callbacks */
433         ot->exec= copy_particle_dupliob_exec;
434
435         /* flags */
436         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
437 }
438
439 static int remove_particle_dupliob_exec(bContext *C, wmOperator *op)
440 {
441         PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem);
442         ParticleSystem *psys= ptr.data;
443         ParticleSettings *part;
444         ParticleDupliWeight *dw;
445
446         if(!psys)
447                 return OPERATOR_CANCELLED;
448
449         part = psys->part;
450         for(dw=part->dupliweights.first; dw; dw=dw->next) {
451                 if(dw->flag & PART_DUPLIW_CURRENT) {
452                         BLI_remlink(&part->dupliweights, dw);
453                         MEM_freeN(dw);
454                         break;
455                 }
456
457         }
458         dw = part->dupliweights.last;
459
460         if(dw)
461                 dw->flag |= PART_DUPLIW_CURRENT;
462
463         WM_event_add_notifier(C, NC_OBJECT|ND_DRAW, NULL);
464         
465         return OPERATOR_FINISHED;
466 }
467
468 void PARTICLE_OT_dupliob_remove(wmOperatorType *ot)
469 {
470         /* identifiers */
471         ot->name= "Remove Particle Dupliobject";
472         ot->idname= "PARTICLE_OT_dupliob_remove";
473         ot->description="Remove the selected dupliobject.";
474         
475         /* api callbacks */
476         ot->exec= remove_particle_dupliob_exec;
477
478         /* flags */
479         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
480 }
481
482 /************************ move down particle dupliweight operator *********************/
483
484 static int dupliob_move_down_exec(bContext *C, wmOperator *op)
485 {
486         PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem);
487         ParticleSystem *psys= ptr.data;
488         ParticleSettings *part;
489         ParticleDupliWeight *dw;
490
491         if(!psys)
492                 return OPERATOR_CANCELLED;
493
494         part = psys->part;
495         for(dw=part->dupliweights.first; dw; dw=dw->next) {
496                 if(dw->flag & PART_DUPLIW_CURRENT && dw->next) {
497                         BLI_remlink(&part->dupliweights, dw);
498                         BLI_insertlink(&part->dupliweights, dw->next, dw);
499
500                         WM_event_add_notifier(C, NC_OBJECT|ND_DRAW, NULL);
501                         break;
502                 }
503         }
504         
505         return OPERATOR_FINISHED;
506 }
507
508 void PARTICLE_OT_dupliob_move_down(wmOperatorType *ot)
509 {
510         ot->name= "Move Down Dupli Object";
511         ot->idname= "PARTICLE_OT_dupliob_move_down";
512         ot->description= "Move dupli object down in the list.";
513         
514         ot->exec= dupliob_move_down_exec;
515         
516         /* flags */
517         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
518 }
519
520 /************************ connect/disconnect hair operators *********************/
521
522 static void disconnect_hair(Scene *scene, Object *ob, ParticleSystem *psys)
523 {
524         ParticleSystemModifierData *psmd = psys_get_modifier(ob,psys);
525         ParticleData *pa;
526         PTCacheEdit *edit;
527         PTCacheEditPoint *point;
528         PTCacheEditKey *ekey = NULL;
529         HairKey *key;
530         int i, k;
531         float hairmat[4][4];
532
533         if(!ob || !psys || psys->flag & PSYS_GLOBAL_HAIR)
534                 return;
535
536         if(!psys->part || psys->part->type != PART_HAIR)
537                 return;
538         
539         edit = psys->edit;
540         point= edit ? edit->points : NULL;
541
542         for(i=0, pa=psys->particles; i<psys->totpart; i++,pa++) {
543                 if(point) {
544                         ekey = point->keys;
545                         point++;
546                 }
547
548                 psys_mat_hair_to_global(ob, psmd->dm, psys->part->from, pa, hairmat);
549
550                 for(k=0,key=pa->hair; k<pa->totkey; k++,key++) {
551                         mul_m4_v3(hairmat,key->co);
552                         
553                         if(ekey) {
554                                 ekey->flag &= ~PEK_USE_WCO;
555                                 ekey++;
556                         }
557                 }
558         }
559
560         psys_free_path_cache(psys, psys->edit);
561
562         psys->flag |= PSYS_GLOBAL_HAIR;
563
564         PE_update_object(scene, ob, 0);
565 }
566
567 static int disconnect_hair_exec(bContext *C, wmOperator *op)
568 {
569         Scene *scene= CTX_data_scene(C);
570         Object *ob= CTX_data_pointer_get_type(C, "object", &RNA_Object).data;
571         PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem);
572         ParticleSystem *psys= NULL;
573         int all = RNA_boolean_get(op->ptr, "all");
574
575         if(!ob)
576                 return OPERATOR_CANCELLED;
577
578         if(all) {
579                 for(psys=ob->particlesystem.first; psys; psys=psys->next) {
580                         disconnect_hair(scene, ob, psys);
581                 }
582         }
583         else {
584                 psys = ptr.data;
585                 disconnect_hair(scene, ob, psys);
586         }
587
588         WM_event_add_notifier(C, NC_OBJECT|ND_DRAW, ob);
589
590         return OPERATOR_FINISHED;
591 }
592
593 void PARTICLE_OT_disconnect_hair(wmOperatorType *ot)
594 {
595         ot->name= "Disconnect Hair";
596         ot->description= "Disconnect hair from the emitter mesh.";
597         ot->idname= "PARTICLE_OT_disconnect_hair";
598         
599         ot->exec= disconnect_hair_exec;
600         
601         /* flags */
602         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
603
604         RNA_def_boolean(ot->srna, "all", 0, "All hair", "Disconnect all hair systems from the emitter mesh");
605 }
606
607 static void connect_hair(Scene *scene, Object *ob, ParticleSystem *psys)
608 {
609         ParticleSystemModifierData *psmd = psys_get_modifier(ob,psys);
610         ParticleData *pa;
611         PTCacheEdit *edit;
612         PTCacheEditPoint *point;
613         PTCacheEditKey *ekey = NULL;
614         HairKey *key;
615         BVHTreeFromMesh bvhtree;
616         BVHTreeNearest nearest;
617         MFace *mface;
618         DerivedMesh *dm = NULL;
619         int numverts;
620         int i, k;
621         float hairmat[4][4], imat[4][4];
622         float v[4][3], vec[3];
623
624         if(!psys || !psys->part || psys->part->type != PART_HAIR)
625                 return;
626         
627         edit= psys->edit;
628         point=  edit ? edit->points : NULL;
629         
630         if(psmd->dm->deformedOnly)
631                 dm= psmd->dm;
632         else
633                 dm= mesh_get_derived_deform(scene, ob, CD_MASK_BAREMESH);
634
635         numverts = dm->getNumVerts (dm);
636
637         memset( &bvhtree, 0, sizeof(bvhtree) );
638
639         /* convert to global coordinates */
640         for (i=0; i<numverts; i++)
641                 mul_m4_v3(ob->obmat, CDDM_get_vert(dm, i)->co);
642
643         bvhtree_from_mesh_faces(&bvhtree, dm, 0.0, 2, 6);
644
645         for(i=0, pa= psys->particles; i<psys->totpart; i++,pa++) {
646                 key = pa->hair;
647
648                 nearest.index = -1;
649                 nearest.dist = FLT_MAX;
650
651                 BLI_bvhtree_find_nearest(bvhtree.tree, key->co, &nearest, bvhtree.nearest_callback, &bvhtree);
652
653                 if(nearest.index == -1) {
654                         printf("No nearest point found for hair root!");
655                         continue;
656                 }
657
658                 mface = CDDM_get_face(dm,nearest.index);
659
660                 copy_v3_v3(v[0], CDDM_get_vert(dm,mface->v1)->co);
661                 copy_v3_v3(v[1], CDDM_get_vert(dm,mface->v2)->co);
662                 copy_v3_v3(v[2], CDDM_get_vert(dm,mface->v3)->co);
663                 if(mface->v4) {
664                         copy_v3_v3(v[3], CDDM_get_vert(dm,mface->v4)->co);
665                         interp_weights_poly_v3( pa->fuv,v, 4, nearest.co);
666                 }
667                 else
668                         interp_weights_poly_v3( pa->fuv,v, 3, nearest.co);
669
670                 pa->num = nearest.index;
671                 pa->num_dmcache = psys_particle_dm_face_lookup(ob,psmd->dm,pa->num,pa->fuv,NULL);
672                 
673                 psys_mat_hair_to_global(ob, psmd->dm, psys->part->from, pa, hairmat);
674                 invert_m4_m4(imat,hairmat);
675
676                 VECSUB(vec, nearest.co, key->co);
677
678                 if(point) {
679                         ekey = point->keys;
680                         point++;
681                 }
682
683                 for(k=0,key=pa->hair; k<pa->totkey; k++,key++) {
684                         VECADD(key->co, key->co, vec);
685                         mul_m4_v3(imat,key->co);
686
687                         if(ekey) {
688                                 ekey->flag |= PEK_USE_WCO;
689                                 ekey++;
690                         }
691                 }
692         }
693
694         free_bvhtree_from_mesh(&bvhtree);
695         if(!psmd->dm->deformedOnly)
696                 dm->release(dm);
697
698         psys_free_path_cache(psys, psys->edit);
699
700         psys->flag &= ~PSYS_GLOBAL_HAIR;
701
702         PE_update_object(scene, ob, 0);
703 }
704
705 static int connect_hair_exec(bContext *C, wmOperator *op)
706 {
707         Scene *scene= CTX_data_scene(C);
708         Object *ob= CTX_data_pointer_get_type(C, "object", &RNA_Object).data;
709         PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem);
710         ParticleSystem *psys= NULL;
711         int all = RNA_boolean_get(op->ptr, "all");
712
713         if(!ob)
714                 return OPERATOR_CANCELLED;
715
716         if(all) {
717                 for(psys=ob->particlesystem.first; psys; psys=psys->next) {
718                         connect_hair(scene, ob, psys);
719                 }
720         }
721         else {
722                 psys = ptr.data;
723                 connect_hair(scene, ob, psys);
724         }
725
726         WM_event_add_notifier(C, NC_OBJECT|ND_DRAW, ob);
727
728         return OPERATOR_FINISHED;
729 }
730
731 void PARTICLE_OT_connect_hair(wmOperatorType *ot)
732 {
733         ot->name= "Connect Hair";
734         ot->description= "Connect hair to the emitter mesh.";
735         ot->idname= "PARTICLE_OT_connect_hair";
736         
737         ot->exec= connect_hair_exec;
738         
739         /* flags */
740         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
741
742         RNA_def_boolean(ot->srna, "all", 0, "All hair", "Connect all hair systems to the emitter mesh");
743 }
744