d227591739b17269741105055481aba8e1fa5a86
[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_arithb.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);
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
97         if(!scene || !ob)
98                 return OPERATOR_CANCELLED;
99
100         object_remove_particle_system(scene, ob);
101         WM_event_add_notifier(C, NC_OBJECT|ND_DRAW, ob);
102         
103         return OPERATOR_FINISHED;
104 }
105
106 void OBJECT_OT_particle_system_remove(wmOperatorType *ot)
107 {
108         /* identifiers */
109         ot->name= "Remove Particle System Slot";
110         ot->idname= "OBJECT_OT_particle_system_remove";
111         ot->description="Remove the selected particle system.";
112         
113         /* api callbacks */
114         ot->exec= particle_system_remove_exec;
115
116         /* flags */
117         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
118 }
119
120 /********************** new particle settings operator *********************/
121
122 static int psys_poll(bContext *C)
123 {
124         PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem);
125         return (ptr.data != NULL);
126 }
127
128 static int new_particle_settings_exec(bContext *C, wmOperator *op)
129 {
130         Scene *scene = CTX_data_scene(C);
131         Main *bmain= CTX_data_main(C);
132         ParticleSystem *psys;
133         ParticleSettings *part = NULL;
134         Object *ob;
135         PointerRNA ptr;
136
137         ptr = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem);
138
139         psys = ptr.data;
140
141         /* add or copy particle setting */
142         if(psys->part)
143                 part= psys_copy_settings(psys->part);
144         else
145                 part= psys_new_settings("ParticleSettings", bmain);
146
147         ob= ptr.id.data;
148
149         if(psys->part)
150                 psys->part->id.us--;
151
152         psys->part = part;
153
154         psys_check_boid_data(psys);
155
156         DAG_scene_sort(scene);
157         DAG_id_flush_update(&ob->id, OB_RECALC_DATA);
158
159         WM_event_add_notifier(C, NC_OBJECT|ND_DRAW, ob);
160         
161         return OPERATOR_FINISHED;
162 }
163
164 void PARTICLE_OT_new(wmOperatorType *ot)
165 {
166         /* identifiers */
167         ot->name= "New Particle Settings";
168         ot->idname= "PARTICLE_OT_new";
169         ot->description="Add new particle settings.";
170         
171         /* api callbacks */
172         ot->exec= new_particle_settings_exec;
173         ot->poll= psys_poll;
174
175         /* flags */
176         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
177 }
178
179 /********************** keyed particle target operators *********************/
180
181 static int new_particle_target_exec(bContext *C, wmOperator *op)
182 {
183         Scene *scene = CTX_data_scene(C);
184         PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem);
185         ParticleSystem *psys= ptr.data;
186         Object *ob = ptr.id.data;
187
188         ParticleTarget *pt;
189
190         if(!psys)
191                 return OPERATOR_CANCELLED;
192
193         pt = psys->targets.first;
194         for(; pt; pt=pt->next)
195                 pt->flag &= ~PTARGET_CURRENT;
196
197         pt = MEM_callocN(sizeof(ParticleTarget), "keyed particle target");
198
199         pt->flag |= PTARGET_CURRENT;
200         pt->psys = 1;
201
202         BLI_addtail(&psys->targets, pt);
203
204         DAG_scene_sort(scene);
205         DAG_id_flush_update(&ob->id, OB_RECALC_DATA);
206
207         WM_event_add_notifier(C, NC_OBJECT|ND_DRAW, ob);
208         
209         return OPERATOR_FINISHED;
210 }
211
212 void PARTICLE_OT_new_target(wmOperatorType *ot)
213 {
214         /* identifiers */
215         ot->name= "New Particle Target";
216         ot->idname= "PARTICLE_OT_new_target";
217         ot->description="Add a new particle target.";
218         
219         /* api callbacks */
220         ot->exec= new_particle_target_exec;
221
222         /* flags */
223         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
224 }
225
226 static int remove_particle_target_exec(bContext *C, wmOperator *op)
227 {
228         Scene *scene = CTX_data_scene(C);
229         PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem);
230         ParticleSystem *psys= ptr.data;
231         Object *ob = ptr.id.data;
232
233         ParticleTarget *pt;
234
235         if(!psys)
236                 return OPERATOR_CANCELLED;
237
238         pt = psys->targets.first;
239         for(; pt; pt=pt->next) {
240                 if(pt->flag & PTARGET_CURRENT) {
241                         BLI_remlink(&psys->targets, pt);
242                         MEM_freeN(pt);
243                         break;
244                 }
245
246         }
247         pt = psys->targets.last;
248
249         if(pt)
250                 pt->flag |= PTARGET_CURRENT;
251
252         DAG_scene_sort(scene);
253         DAG_id_flush_update(&ob->id, OB_RECALC_DATA);
254
255         WM_event_add_notifier(C, NC_OBJECT|ND_DRAW, ob);
256         
257         return OPERATOR_FINISHED;
258 }
259
260 void PARTICLE_OT_remove_target(wmOperatorType *ot)
261 {
262         /* identifiers */
263         ot->name= "Remove Particle Target";
264         ot->idname= "PARTICLE_OT_remove_target";
265         ot->description="Remove the selected particle target.";
266         
267         /* api callbacks */
268         ot->exec= remove_particle_target_exec;
269
270         /* flags */
271         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
272 }
273
274 /************************ move up particle target operator *********************/
275
276 static int target_move_up_exec(bContext *C, wmOperator *op)
277 {
278         PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem);
279         ParticleSystem *psys= ptr.data;
280         Object *ob = ptr.id.data;
281         ParticleTarget *pt;
282
283         if(!psys)
284                 return OPERATOR_CANCELLED;
285         
286         pt = psys->targets.first;
287         for(; pt; pt=pt->next) {
288                 if(pt->flag & PTARGET_CURRENT && pt->prev) {
289                         BLI_remlink(&psys->targets, pt);
290                         BLI_insertlink(&psys->targets, pt->prev->prev, pt);
291
292                         DAG_id_flush_update(&ob->id, OB_RECALC_DATA);
293                         WM_event_add_notifier(C, NC_OBJECT|ND_DRAW, ob);
294                         break;
295                 }
296         }
297         
298         return OPERATOR_FINISHED;
299 }
300
301 void PARTICLE_OT_target_move_up(wmOperatorType *ot)
302 {
303         ot->name= "Move Up Target";
304         ot->idname= "PARTICLE_OT_target_move_up";
305         ot->description= "Move particle target up in the list.";
306         
307         ot->exec= target_move_up_exec;
308         
309         /* flags */
310         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
311 }
312
313 /************************ move down particle target operator *********************/
314
315 static int target_move_down_exec(bContext *C, wmOperator *op)
316 {
317         PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem);
318         ParticleSystem *psys= ptr.data;
319         Object *ob = ptr.id.data;
320         ParticleTarget *pt;
321
322         if(!psys)
323                 return OPERATOR_CANCELLED;
324         pt = psys->targets.first;
325         for(; pt; pt=pt->next) {
326                 if(pt->flag & PTARGET_CURRENT && pt->next) {
327                         BLI_remlink(&psys->targets, pt);
328                         BLI_insertlink(&psys->targets, pt->next, pt);
329
330                         DAG_id_flush_update(&ob->id, OB_RECALC_DATA);
331                         WM_event_add_notifier(C, NC_OBJECT|ND_DRAW, ob);
332                         break;
333                 }
334         }
335         
336         return OPERATOR_FINISHED;
337 }
338
339 void PARTICLE_OT_target_move_down(wmOperatorType *ot)
340 {
341         ot->name= "Move Down Target";
342         ot->idname= "PARTICLE_OT_target_move_down";
343         ot->description= "Move particle target down in the list.";
344         
345         ot->exec= target_move_down_exec;
346         
347         /* flags */
348         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
349 }
350
351 /************************ move up particle dupliweight operator *********************/
352
353 static int dupliob_move_up_exec(bContext *C, wmOperator *op)
354 {
355         PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem);
356         ParticleSystem *psys= ptr.data;
357         ParticleSettings *part;
358         ParticleDupliWeight *dw;
359
360         if(!psys)
361                 return OPERATOR_CANCELLED;
362
363         part = psys->part;
364         for(dw=part->dupliweights.first; dw; dw=dw->next) {
365                 if(dw->flag & PART_DUPLIW_CURRENT && dw->prev) {
366                         BLI_remlink(&part->dupliweights, dw);
367                         BLI_insertlink(&part->dupliweights, dw->prev->prev, dw);
368
369                         WM_event_add_notifier(C, NC_OBJECT|ND_DRAW, NULL);
370                         break;
371                 }
372         }
373         
374         return OPERATOR_FINISHED;
375 }
376
377 void PARTICLE_OT_dupliob_move_up(wmOperatorType *ot)
378 {
379         ot->name= "Move Up Dupli Object";
380         ot->idname= "PARTICLE_OT_dupliob_move_up";
381         ot->description= "Move dupli object up in the list.";
382         
383         ot->exec= dupliob_move_up_exec;
384         
385         /* flags */
386         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
387 }
388
389 /********************** particle dupliweight operators *********************/
390
391 static int copy_particle_dupliob_exec(bContext *C, wmOperator *op)
392 {
393         PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem);
394         ParticleSystem *psys= ptr.data;
395         ParticleSettings *part;
396         ParticleDupliWeight *dw;
397
398         if(!psys)
399                 return OPERATOR_CANCELLED;
400         part = psys->part;
401         for(dw=part->dupliweights.first; dw; dw=dw->next) {
402                 if(dw->flag & PART_DUPLIW_CURRENT) {
403                         dw->flag &= ~PART_DUPLIW_CURRENT;
404                         dw = MEM_dupallocN(dw);
405                         dw->flag |= PART_DUPLIW_CURRENT;
406                         BLI_addhead(&part->dupliweights, dw);
407
408                         WM_event_add_notifier(C, NC_OBJECT|ND_DRAW, NULL);
409                         break;
410                 }
411         }
412         
413         return OPERATOR_FINISHED;
414 }
415
416 void PARTICLE_OT_dupliob_copy(wmOperatorType *ot)
417 {
418         /* identifiers */
419         ot->name= "Copy Particle Dupliob";
420         ot->idname= "PARTICLE_OT_dupliob_copy";
421         ot->description="Duplicate the current dupliobject.";
422         
423         /* api callbacks */
424         ot->exec= copy_particle_dupliob_exec;
425
426         /* flags */
427         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
428 }
429
430 static int remove_particle_dupliob_exec(bContext *C, wmOperator *op)
431 {
432         PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem);
433         ParticleSystem *psys= ptr.data;
434         ParticleSettings *part;
435         ParticleDupliWeight *dw;
436
437         if(!psys)
438                 return OPERATOR_CANCELLED;
439
440         part = psys->part;
441         for(dw=part->dupliweights.first; dw; dw=dw->next) {
442                 if(dw->flag & PART_DUPLIW_CURRENT) {
443                         BLI_remlink(&part->dupliweights, dw);
444                         MEM_freeN(dw);
445                         break;
446                 }
447
448         }
449         dw = part->dupliweights.last;
450
451         if(dw)
452                 dw->flag |= PART_DUPLIW_CURRENT;
453
454         WM_event_add_notifier(C, NC_OBJECT|ND_DRAW, NULL);
455         
456         return OPERATOR_FINISHED;
457 }
458
459 void PARTICLE_OT_dupliob_remove(wmOperatorType *ot)
460 {
461         /* identifiers */
462         ot->name= "Remove Particle Dupliobject";
463         ot->idname= "PARTICLE_OT_dupliob_remove";
464         ot->description="Remove the selected dupliobject.";
465         
466         /* api callbacks */
467         ot->exec= remove_particle_dupliob_exec;
468
469         /* flags */
470         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
471 }
472
473 /************************ move down particle dupliweight operator *********************/
474
475 static int dupliob_move_down_exec(bContext *C, wmOperator *op)
476 {
477         PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem);
478         ParticleSystem *psys= ptr.data;
479         ParticleSettings *part;
480         ParticleDupliWeight *dw;
481
482         if(!psys)
483                 return OPERATOR_CANCELLED;
484
485         part = psys->part;
486         for(dw=part->dupliweights.first; dw; dw=dw->next) {
487                 if(dw->flag & PART_DUPLIW_CURRENT && dw->next) {
488                         BLI_remlink(&part->dupliweights, dw);
489                         BLI_insertlink(&part->dupliweights, dw->next, dw);
490
491                         WM_event_add_notifier(C, NC_OBJECT|ND_DRAW, NULL);
492                         break;
493                 }
494         }
495         
496         return OPERATOR_FINISHED;
497 }
498
499 void PARTICLE_OT_dupliob_move_down(wmOperatorType *ot)
500 {
501         ot->name= "Move Down Dupli Object";
502         ot->idname= "PARTICLE_OT_dupliob_move_down";
503         ot->description= "Move dupli object down in the list.";
504         
505         ot->exec= dupliob_move_down_exec;
506         
507         /* flags */
508         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
509 }
510
511 /************************ connect/disconnect hair operators *********************/
512
513 static void disconnect_hair(Scene *scene, Object *ob, ParticleSystem *psys)
514 {
515         ParticleSystemModifierData *psmd = psys_get_modifier(ob,psys);
516         ParticleData *pa;
517         PTCacheEdit *edit;
518         PTCacheEditPoint *point;
519         PTCacheEditKey *ekey = NULL;
520         HairKey *key;
521         int i, k;
522         float hairmat[4][4];
523
524         if(!ob || !psys || psys->flag & PSYS_GLOBAL_HAIR)
525                 return;
526
527         if(!psys->part || psys->part->type != PART_HAIR)
528                 return;
529         
530         edit = psys->edit;
531         point= edit ? edit->points : NULL;
532
533         for(i=0, pa=psys->particles; i<psys->totpart; i++,pa++) {
534                 if(point) {
535                         ekey = point->keys;
536                         point++;
537                 }
538
539                 psys_mat_hair_to_global(ob, psmd->dm, psys->part->from, pa, hairmat);
540
541                 for(k=0,key=pa->hair; k<pa->totkey; k++,key++) {
542                         Mat4MulVecfl(hairmat,key->co);
543                         
544                         if(ekey) {
545                                 ekey->flag &= ~PEK_USE_WCO;
546                                 ekey++;
547                         }
548                 }
549         }
550
551         psys_free_path_cache(psys, psys->edit);
552
553         psys->flag |= PSYS_GLOBAL_HAIR;
554
555         PE_update_object(scene, ob, 0);
556 }
557
558 static int disconnect_hair_exec(bContext *C, wmOperator *op)
559 {
560         Scene *scene= CTX_data_scene(C);
561         Object *ob= CTX_data_pointer_get_type(C, "object", &RNA_Object).data;
562         PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem);
563         ParticleSystem *psys= NULL;
564         int all = RNA_boolean_get(op->ptr, "all");
565
566         if(!ob)
567                 return OPERATOR_CANCELLED;
568
569         if(all) {
570                 for(psys=ob->particlesystem.first; psys; psys=psys->next) {
571                         disconnect_hair(scene, ob, psys);
572                 }
573         }
574         else {
575                 psys = ptr.data;
576                 disconnect_hair(scene, ob, psys);
577         }
578
579         WM_event_add_notifier(C, NC_OBJECT|ND_DRAW, ob);
580
581         return OPERATOR_FINISHED;
582 }
583
584 void PARTICLE_OT_disconnect_hair(wmOperatorType *ot)
585 {
586         ot->name= "Disconnect Hair";
587         ot->description= "Disconnect hair from the emitter mesh.";
588         ot->idname= "PARTICLE_OT_disconnect_hair";
589         
590         ot->exec= disconnect_hair_exec;
591         
592         /* flags */
593         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
594
595         RNA_def_boolean(ot->srna, "all", 0, "All hair", "Disconnect all hair systems from the emitter mesh");
596 }
597
598 static void connect_hair(Scene *scene, Object *ob, ParticleSystem *psys)
599 {
600         ParticleSystemModifierData *psmd = psys_get_modifier(ob,psys);
601         ParticleData *pa;
602         PTCacheEdit *edit;
603         PTCacheEditPoint *point;
604         PTCacheEditKey *ekey = NULL;
605         HairKey *key;
606         BVHTreeFromMesh bvhtree;
607         BVHTreeNearest nearest;
608         MFace *mface;
609         DerivedMesh *dm = NULL;
610         int numverts;
611         int i, k;
612         float hairmat[4][4], imat[4][4];
613         float v[4][3], vec[3];
614
615         if(!psys || !psys->part || psys->part->type != PART_HAIR)
616                 return;
617         
618         edit= psys->edit;
619         point=  edit ? edit->points : NULL;
620         
621         if(psmd->dm->deformedOnly)
622                 dm= psmd->dm;
623         else
624                 dm= mesh_get_derived_deform(scene, ob, CD_MASK_BAREMESH);
625
626         numverts = dm->getNumVerts (dm);
627
628         memset( &bvhtree, 0, sizeof(bvhtree) );
629
630         /* convert to global coordinates */
631         for (i=0; i<numverts; i++)
632                 Mat4MulVecfl (ob->obmat, CDDM_get_vert(dm, i)->co);
633
634         bvhtree_from_mesh_faces(&bvhtree, dm, 0.0, 2, 6);
635
636         for(i=0, pa= psys->particles; i<psys->totpart; i++,pa++) {
637                 key = pa->hair;
638
639                 nearest.index = -1;
640                 nearest.dist = FLT_MAX;
641
642                 BLI_bvhtree_find_nearest(bvhtree.tree, key->co, &nearest, bvhtree.nearest_callback, &bvhtree);
643
644                 if(nearest.index == -1) {
645                         printf("No nearest point found for hair root!");
646                         continue;
647                 }
648
649                 mface = CDDM_get_face(dm,nearest.index);
650
651                 VecCopyf(v[0], CDDM_get_vert(dm,mface->v1)->co);
652                 VecCopyf(v[1], CDDM_get_vert(dm,mface->v2)->co);
653                 VecCopyf(v[2], CDDM_get_vert(dm,mface->v3)->co);
654                 if(mface->v4) {
655                         VecCopyf(v[3], CDDM_get_vert(dm,mface->v4)->co);
656                         MeanValueWeights(v, 4, nearest.co, pa->fuv);
657                 }
658                 else
659                         MeanValueWeights(v, 3, nearest.co, pa->fuv);
660
661                 pa->num = nearest.index;
662                 pa->num_dmcache = psys_particle_dm_face_lookup(ob,psmd->dm,pa->num,pa->fuv,NULL);
663                 
664                 psys_mat_hair_to_global(ob, psmd->dm, psys->part->from, pa, hairmat);
665                 Mat4Invert(imat,hairmat);
666
667                 VECSUB(vec, nearest.co, key->co);
668
669                 if(point) {
670                         ekey = point->keys;
671                         point++;
672                 }
673
674                 for(k=0,key=pa->hair; k<pa->totkey; k++,key++) {
675                         VECADD(key->co, key->co, vec);
676                         Mat4MulVecfl(imat,key->co);
677
678                         if(ekey) {
679                                 ekey->flag |= PEK_USE_WCO;
680                                 ekey++;
681                         }
682                 }
683         }
684
685         free_bvhtree_from_mesh(&bvhtree);
686         if(!psmd->dm->deformedOnly)
687                 dm->release(dm);
688
689         psys_free_path_cache(psys, psys->edit);
690
691         psys->flag &= ~PSYS_GLOBAL_HAIR;
692
693         PE_update_object(scene, ob, 0);
694 }
695
696 static int connect_hair_exec(bContext *C, wmOperator *op)
697 {
698         Scene *scene= CTX_data_scene(C);
699         Object *ob= CTX_data_pointer_get_type(C, "object", &RNA_Object).data;
700         PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem);
701         ParticleSystem *psys= NULL;
702         int all = RNA_boolean_get(op->ptr, "all");
703
704         if(!ob)
705                 return OPERATOR_CANCELLED;
706
707         if(all) {
708                 for(psys=ob->particlesystem.first; psys; psys=psys->next) {
709                         connect_hair(scene, ob, psys);
710                 }
711         }
712         else {
713                 psys = ptr.data;
714                 connect_hair(scene, ob, psys);
715         }
716
717         WM_event_add_notifier(C, NC_OBJECT|ND_DRAW, ob);
718
719         return OPERATOR_FINISHED;
720 }
721
722 void PARTICLE_OT_connect_hair(wmOperatorType *ot)
723 {
724         ot->name= "Connect Hair";
725         ot->description= "Connect hair to the emitter mesh.";
726         ot->idname= "PARTICLE_OT_connect_hair";
727         
728         ot->exec= connect_hair_exec;
729         
730         /* flags */
731         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
732
733         RNA_def_boolean(ot->srna, "all", 0, "All hair", "Connect all hair systems to the emitter mesh");
734 }
735