4 * ***** BEGIN GPL LICENSE BLOCK *****
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.
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.
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 * The Original Code is Copyright (C) 2007 by Janne Karhu.
21 * All rights reserved.
23 * The Original Code is: all of this file.
25 * Contributor(s): none yet.
27 * ***** END GPL LICENSE BLOCK *****
34 #include "MEM_guardedalloc.h"
36 #include "DNA_scene_types.h"
37 #include "DNA_mesh_types.h"
38 #include "DNA_meshdata_types.h"
39 #include "DNA_view3d_types.h"
40 #include "DNA_screen_types.h"
41 #include "DNA_space_types.h"
43 #include "BKE_DerivedMesh.h"
44 #include "BKE_depsgraph.h"
46 #include "BKE_context.h"
47 #include "BKE_global.h"
48 #include "BKE_object.h"
50 #include "BKE_modifier.h"
51 #include "BKE_particle.h"
52 #include "BKE_report.h"
53 #include "BKE_scene.h"
54 #include "BKE_utildefines.h"
55 #include "BKE_pointcache.h"
58 #include "BLI_blenlib.h"
59 #include "BLI_dynstr.h"
60 #include "BLI_kdtree.h"
65 #include "BIF_glutil.h"
68 #include "ED_particle.h"
69 #include "ED_view3d.h"
71 #include "UI_resources.h"
76 #include "RNA_access.h"
77 #include "RNA_define.h"
79 #include "physics_intern.h"
81 static void PE_create_particle_edit(Scene *scene, Object *ob, PointCache *cache, ParticleSystem *psys);
82 static void PTCacheUndo_clear(PTCacheEdit *edit);
83 static void recalc_emitter_field(Object *ob, ParticleSystem *psys);
85 #define KEY_K PTCacheEditKey *key; int k
86 #define POINT_P PTCacheEditPoint *point; int p
87 #define LOOP_POINTS for(p=0, point=edit->points; p<edit->totpoint; p++, point++)
88 #define LOOP_VISIBLE_POINTS for(p=0, point=edit->points; p<edit->totpoint; p++, point++) if(!(point->flag & PEP_HIDE))
89 #define LOOP_SELECTED_POINTS for(p=0, point=edit->points; p<edit->totpoint; p++, point++) if(point_is_selected(point))
90 #define LOOP_UNSELECTED_POINTS for(p=0, point=edit->points; p<edit->totpoint; p++, point++) if(!point_is_selected(point))
91 #define LOOP_EDITED_POINTS for(p=0, point=edit->points; p<edit->totpoint; p++, point++) if(point->flag & PEP_EDIT_RECALC)
92 #define LOOP_TAGGED_POINTS for(p=0, point=edit->points; p<edit->totpoint; p++, point++) if(point->flag & PEP_TAG)
93 #define LOOP_KEYS for(k=0, key=point->keys; k<point->totkey; k++, key++)
94 #define LOOP_VISIBLE_KEYS for(k=0, key=point->keys; k<point->totkey; k++, key++) if(!(key->flag & PEK_HIDE))
95 #define LOOP_SELECTED_KEYS for(k=0, key=point->keys; k<point->totkey; k++, key++) if((key->flag & PEK_SELECT) && !(key->flag & PEK_HIDE))
96 #define LOOP_TAGGED_KEYS for(k=0, key=point->keys; k<point->totkey; k++, key++) if(key->flag & PEK_TAG)
98 #define KEY_WCO (key->flag & PEK_USE_WCO ? key->world_co : key->co)
100 /**************************** utilities *******************************/
102 int PE_poll(bContext *C)
104 Scene *scene= CTX_data_scene(C);
105 Object *ob= CTX_data_active_object(C);
107 if(!scene || !ob || !(ob->mode & OB_MODE_PARTICLE_EDIT))
110 return (PE_get_current(scene, ob) != NULL);
113 int PE_hair_poll(bContext *C)
115 Scene *scene= CTX_data_scene(C);
116 Object *ob= CTX_data_active_object(C);
119 if(!scene || !ob || !(ob->mode & OB_MODE_PARTICLE_EDIT))
122 edit= PE_get_current(scene, ob);
124 return (edit && edit->psys);
127 int PE_poll_view3d(bContext *C)
129 return PE_poll(C) && CTX_wm_area(C)->spacetype == SPACE_VIEW3D &&
130 CTX_wm_region(C)->regiontype == RGN_TYPE_WINDOW;
133 void PE_free_ptcache_edit(PTCacheEdit *edit)
139 PTCacheUndo_clear(edit);
144 MEM_freeN(point->keys);
147 MEM_freeN(edit->points);
150 if(edit->mirror_cache)
151 MEM_freeN(edit->mirror_cache);
153 if(edit->emitter_cosnos) {
154 MEM_freeN(edit->emitter_cosnos);
155 edit->emitter_cosnos= 0;
158 if(edit->emitter_field) {
159 BLI_kdtree_free(edit->emitter_field);
160 edit->emitter_field= 0;
163 psys_free_path_cache(edit->psys, edit);
168 /************************************************/
169 /* Edit Mode Helpers */
170 /************************************************/
172 int PE_start_edit(PTCacheEdit *edit)
177 edit->psys->flag |= PSYS_EDITED;
184 ParticleEditSettings *PE_settings(Scene *scene)
186 return scene->toolsettings ? &scene->toolsettings->particle : NULL;
189 /* always gets atleast the first particlesystem even if PSYS_CURRENT flag is not set
191 * note: this function runs on poll, therefor it can runs many times a second
193 static PTCacheEdit *pe_get_current(Scene *scene, Object *ob, int create)
195 ParticleEditSettings *pset= PE_settings(scene);
196 PTCacheEdit *edit = NULL;
200 if(pset==NULL || ob==NULL)
206 BKE_ptcache_ids_from_object(&pidlist, ob, NULL, 0);
208 /* in the case of only one editable thing, set pset->edittype accordingly */
209 if(pidlist.first && pidlist.first == pidlist.last) {
212 case PTCACHE_TYPE_PARTICLES:
213 pset->edittype = PE_TYPE_PARTICLES;
215 case PTCACHE_TYPE_SOFTBODY:
216 pset->edittype = PE_TYPE_SOFTBODY;
218 case PTCACHE_TYPE_CLOTH:
219 pset->edittype = PE_TYPE_CLOTH;
224 for(pid=pidlist.first; pid; pid=pid->next) {
225 if(pset->edittype == PE_TYPE_PARTICLES && pid->type == PTCACHE_TYPE_PARTICLES) {
226 ParticleSystem *psys = pid->calldata;
228 if(psys->flag & PSYS_CURRENT) {
229 if(psys->part && psys->part->type == PART_HAIR) {
230 if(psys->flag & PSYS_HAIR_DYNAMICS && psys->pointcache->flag & PTCACHE_BAKED) {
231 if(create && !psys->pointcache->edit)
232 PE_create_particle_edit(scene, ob, pid->cache, NULL);
233 edit = pid->cache->edit;
236 if(create && !psys->edit && psys->flag & PSYS_HAIR_DONE)
237 PE_create_particle_edit(scene, ob, NULL, psys);
242 if(create && pid->cache->flag & PTCACHE_BAKED && !pid->cache->edit)
243 PE_create_particle_edit(scene, ob, pid->cache, psys);
244 edit = pid->cache->edit;
250 else if(pset->edittype == PE_TYPE_SOFTBODY && pid->type == PTCACHE_TYPE_SOFTBODY) {
251 if(create && pid->cache->flag & PTCACHE_BAKED && !pid->cache->edit)
252 PE_create_particle_edit(scene, ob, pid->cache, NULL);
253 edit = pid->cache->edit;
256 else if(pset->edittype == PE_TYPE_CLOTH && pid->type == PTCACHE_TYPE_CLOTH) {
257 if(create && pid->cache->flag & PTCACHE_BAKED && !pid->cache->edit)
258 PE_create_particle_edit(scene, ob, pid->cache, NULL);
259 edit = pid->cache->edit;
267 BLI_freelistN(&pidlist);
272 PTCacheEdit *PE_get_current(Scene *scene, Object *ob)
274 return pe_get_current(scene, ob, 0);
277 PTCacheEdit *PE_create_current(Scene *scene, Object *ob)
279 return pe_get_current(scene, ob, 1);
282 void PE_current_changed(Scene *scene, Object *ob)
284 if(ob->mode == OB_MODE_PARTICLE_EDIT)
285 PE_create_current(scene, ob);
288 void PE_hide_keys_time(Scene *scene, PTCacheEdit *edit, float cfra)
290 ParticleEditSettings *pset=PE_settings(scene);
294 if(pset->flag & PE_FADE_TIME && pset->selectmode==SCE_SELECT_POINT) {
297 if(fabs(cfra-*key->time) < pset->fade_frames)
298 key->flag &= ~PEK_HIDE;
300 key->flag |= PEK_HIDE;
301 //key->flag &= ~PEK_SELECT;
309 key->flag &= ~PEK_HIDE;
315 static int pe_x_mirror(Object *ob)
317 if(ob->type == OB_MESH)
318 return (((Mesh*)ob->data)->editflag & ME_EDIT_MIRROR_X);
323 /****************** common struct passed to callbacks ******************/
325 typedef struct PEData {
355 static void PE_set_data(bContext *C, PEData *data)
357 memset(data, 0, sizeof(*data));
359 data->scene= CTX_data_scene(C);
360 data->ob= CTX_data_active_object(C);
361 data->edit= PE_get_current(data->scene, data->ob);
364 static void PE_set_view3d_data(bContext *C, PEData *data)
366 PE_set_data(C, data);
368 view3d_set_viewcontext(C, &data->vc);
369 /* note, the object argument means the modelview matrix does not account for the objects matrix, use viewmat rather then (obmat * viewmat) */
370 view3d_get_transformation(data->vc.ar, data->vc.rv3d, NULL, &data->mats);
372 if((data->vc.v3d->drawtype>OB_WIRE) && (data->vc.v3d->flag & V3D_ZBUF_SELECT))
373 view3d_validate_backbuf(&data->vc);
376 /*************************** selection utilities *******************************/
378 static int key_test_depth(PEData *data, float co[3])
380 View3D *v3d= data->vc.v3d;
386 if((v3d->drawtype<=OB_WIRE) || (v3d->flag & V3D_ZBUF_SELECT)==0)
389 project_short(data->vc.ar, co, wco);
391 if(wco[0] == IS_CLIPPED)
394 gluProject(co[0],co[1],co[2], data->mats.modelview, data->mats.projection,
395 (GLint *)data->mats.viewport, &ux, &uy, &uz);
400 x+= (short)data->vc.ar->winrct.xmin;
401 y+= (short)data->vc.ar->winrct.ymin;
403 /* PE_set_view3d_data calls this. no need to call here */
404 /* view3d_validate_backbuf(&data->vc); */
405 glReadPixels(x, y, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &depth);
407 if((float)uz - 0.0001 > depth)
413 static int key_inside_circle(PEData *data, float rad, float co[3], float *distance)
418 project_short(data->vc.ar, co, sco);
420 if(sco[0] == IS_CLIPPED)
423 dx= data->mval[0] - sco[0];
424 dy= data->mval[1] - sco[1];
425 dist= sqrt(dx*dx + dy*dy);
430 if(key_test_depth(data, co)) {
440 static int key_inside_rect(PEData *data, float co[3])
444 project_short(data->vc.ar, co,sco);
446 if(sco[0] == IS_CLIPPED)
449 if(sco[0] > data->rect->xmin && sco[0] < data->rect->xmax &&
450 sco[1] > data->rect->ymin && sco[1] < data->rect->ymax)
451 return key_test_depth(data, co);
456 static int key_inside_test(PEData *data, float co[3])
459 return key_inside_circle(data, data->rad, co, NULL);
461 return key_inside_rect(data, co);
464 static int point_is_selected(PTCacheEditPoint *point)
469 if(point->flag & PEP_HIDE)
481 /*************************** iterators *******************************/
483 typedef void (*ForPointFunc)(PEData *data, int point_index);
484 typedef void (*ForKeyFunc)(PEData *data, int point_index, int key_index);
485 typedef void (*ForKeyMatFunc)(PEData *data, float mat[][4], float imat[][4], int point_index, int key_index, PTCacheEditKey *key);
487 static void for_mouse_hit_keys(PEData *data, ForKeyFunc func, int nearest)
489 ParticleEditSettings *pset= PE_settings(data->scene);
490 PTCacheEdit *edit= data->edit;
492 int nearest_point, nearest_key;
493 float dist= data->rad;
495 /* in path select mode we have no keys */
496 if(pset->selectmode==SCE_SELECT_PATH)
502 LOOP_VISIBLE_POINTS {
503 if(pset->selectmode == SCE_SELECT_END) {
504 /* only do end keys */
505 key= point->keys + point->totkey-1;
508 if(key_inside_circle(data, dist, KEY_WCO, &dist)) {
510 nearest_key= point->totkey-1;
513 else if(key_inside_test(data, KEY_WCO))
514 func(data, p, point->totkey-1);
520 if(key_inside_circle(data, dist, KEY_WCO, &dist)) {
525 else if(key_inside_test(data, KEY_WCO))
531 /* do nearest only */
532 if(nearest && nearest_point > -1)
533 func(data, nearest_point, nearest_key);
536 static void foreach_mouse_hit_point(PEData *data, ForPointFunc func, int selected)
538 ParticleEditSettings *pset= PE_settings(data->scene);
539 PTCacheEdit *edit= data->edit;
542 /* all is selected in path mode */
543 if(pset->selectmode==SCE_SELECT_PATH)
546 LOOP_VISIBLE_POINTS {
547 if(pset->selectmode==SCE_SELECT_END) {
548 /* only do end keys */
549 key= point->keys + point->totkey - 1;
551 if(selected==0 || key->flag & PEK_SELECT)
552 if(key_inside_circle(data, data->rad, KEY_WCO, &data->dist))
558 if(selected==0 || key->flag & PEK_SELECT) {
559 if(key_inside_circle(data, data->rad, KEY_WCO, &data->dist)) {
569 static void foreach_mouse_hit_key(PEData *data, ForKeyMatFunc func, int selected)
571 PTCacheEdit *edit = data->edit;
572 ParticleSystem *psys = edit->psys;
573 ParticleSystemModifierData *psmd = NULL;
574 ParticleEditSettings *pset= PE_settings(data->scene);
576 float mat[4][4], imat[4][4];
579 psmd= psys_get_modifier(data->ob, edit->psys);
581 /* all is selected in path mode */
582 if(pset->selectmode==SCE_SELECT_PATH)
588 LOOP_VISIBLE_POINTS {
589 if(pset->selectmode==SCE_SELECT_END) {
590 /* only do end keys */
591 key= point->keys + point->totkey-1;
593 if(selected==0 || key->flag & PEK_SELECT) {
594 if(key_inside_circle(data, data->rad, KEY_WCO, &data->dist)) {
595 if(edit->psys && !(edit->psys->flag & PSYS_GLOBAL_HAIR)) {
596 psys_mat_hair_to_global(data->ob, psmd->dm, psys->part->from, psys->particles + p, mat);
597 invert_m4_m4(imat,mat);
600 func(data, mat, imat, p, point->totkey-1, key);
607 if(selected==0 || key->flag & PEK_SELECT) {
608 if(key_inside_circle(data, data->rad, KEY_WCO, &data->dist)) {
609 if(edit->psys && !(edit->psys->flag & PSYS_GLOBAL_HAIR)) {
610 psys_mat_hair_to_global(data->ob, psmd->dm, psys->part->from, psys->particles + p, mat);
611 invert_m4_m4(imat,mat);
614 func(data, mat, imat, p, k, key);
622 static void foreach_selected_point(PEData *data, ForPointFunc func)
624 PTCacheEdit *edit = data->edit;
627 LOOP_SELECTED_POINTS {
632 static void foreach_selected_key(PEData *data, ForKeyFunc func)
634 PTCacheEdit *edit = data->edit;
637 LOOP_VISIBLE_POINTS {
644 static void foreach_point(PEData *data, ForPointFunc func)
646 PTCacheEdit *edit = data->edit;
654 static int count_selected_keys(Scene *scene, PTCacheEdit *edit)
656 ParticleEditSettings *pset= PE_settings(scene);
660 LOOP_VISIBLE_POINTS {
661 if(pset->selectmode==SCE_SELECT_POINT) {
666 else if(pset->selectmode==SCE_SELECT_END) {
667 key = point->keys + point->totkey - 1;
668 if(key->flag & PEK_SELECT)
676 /************************************************/
677 /* Particle Edit Mirroring */
678 /************************************************/
680 static void PE_update_mirror_cache(Object *ob, ParticleSystem *psys)
683 ParticleSystemModifierData *psmd;
685 KDTreeNearest nearest;
688 float mat[4][4], co[3];
692 psmd= psys_get_modifier(ob, psys);
693 totpart= psys->totpart;
698 tree= BLI_kdtree_new(totpart);
700 /* insert particles into kd tree */
703 psys_mat_hair_to_orco(ob, psmd->dm, psys->part->from, pa, mat);
704 VECCOPY(co, key->co);
706 BLI_kdtree_insert(tree, p, co, NULL);
709 BLI_kdtree_balance(tree);
711 /* lookup particles and set in mirror cache */
712 if(!edit->mirror_cache)
713 edit->mirror_cache= MEM_callocN(sizeof(int)*totpart, "PE mirror cache");
717 psys_mat_hair_to_orco(ob, psmd->dm, psys->part->from, pa, mat);
718 VECCOPY(co, key->co);
722 index= BLI_kdtree_find_nearest(tree, co, NULL, &nearest);
724 /* this needs a custom threshold still, duplicated for editmode mirror */
725 if(index != -1 && index != p && (nearest.dist <= 0.0002f))
726 edit->mirror_cache[p]= index;
728 edit->mirror_cache[p]= -1;
731 /* make sure mirrors are in two directions */
733 if(edit->mirror_cache[p]) {
734 index= edit->mirror_cache[p];
735 if(edit->mirror_cache[index] != p)
736 edit->mirror_cache[p]= -1;
740 BLI_kdtree_free(tree);
743 static void PE_mirror_particle(Object *ob, DerivedMesh *dm, ParticleSystem *psys, ParticleData *pa, ParticleData *mpa)
745 HairKey *hkey, *mhkey;
746 PTCacheEditPoint *point, *mpoint;
747 PTCacheEditKey *key, *mkey;
749 float mat[4][4], mmat[4][4], immat[4][4];
753 i= pa - psys->particles;
755 /* find mirrored particle if needed */
757 if(!edit->mirror_cache)
758 PE_update_mirror_cache(ob, psys);
760 mi= edit->mirror_cache[i];
763 mpa= psys->particles + mi;
766 mi= mpa - psys->particles;
768 point = edit->points + i;
769 mpoint = edit->points + mi;
771 /* make sure they have the same amount of keys */
772 if(pa->totkey != mpa->totkey) {
773 if(mpa->hair) MEM_freeN(mpa->hair);
774 if(mpoint->keys) MEM_freeN(mpoint->keys);
776 mpa->hair= MEM_dupallocN(pa->hair);
777 mpoint->keys= MEM_dupallocN(point->keys);
778 mpoint->totkey= point->totkey;
782 for(k=0; k<mpa->totkey; k++, mkey++, mhkey++) {
784 mkey->time= &mhkey->time;
785 mkey->flag &= PEK_SELECT;
789 /* mirror positions and tags */
790 psys_mat_hair_to_orco(ob, dm, psys->part->from, pa, mat);
791 psys_mat_hair_to_orco(ob, dm, psys->part->from, mpa, mmat);
792 invert_m4_m4(immat, mmat);
798 for(k=0; k<pa->totkey; k++, hkey++, mhkey++, key++, mkey++) {
799 VECCOPY(mhkey->co, hkey->co);
800 mul_m4_v3(mat, mhkey->co);
801 mhkey->co[0]= -mhkey->co[0];
802 mul_m4_v3(immat, mhkey->co);
804 if(key->flag & PEK_TAG)
805 mkey->flag |= PEK_TAG;
808 if(point->flag & PEP_TAG)
809 mpoint->flag |= PEP_TAG;
810 if(point->flag & PEP_EDIT_RECALC)
811 mpoint->flag |= PEP_EDIT_RECALC;
814 static void PE_apply_mirror(Object *ob, ParticleSystem *psys)
817 ParticleSystemModifierData *psmd;
824 psmd= psys_get_modifier(ob, psys);
829 if(!edit->mirror_cache)
830 PE_update_mirror_cache(ob, psys);
832 /* we delay settings the PARS_EDIT_RECALC for mirrored particles
833 * to avoid doing mirror twice */
835 if(point->flag & PEP_EDIT_RECALC) {
836 PE_mirror_particle(ob, psmd->dm, psys, psys->particles + p, NULL);
838 if(edit->mirror_cache[p] != -1)
839 edit->points[edit->mirror_cache[p]].flag &= ~PEP_EDIT_RECALC;
844 if(point->flag & PEP_EDIT_RECALC)
845 if(edit->mirror_cache[p] != -1)
846 edit->points[edit->mirror_cache[p]].flag |= PEP_EDIT_RECALC;
850 /************************************************/
851 /* Edit Calculation */
852 /************************************************/
853 /* tries to stop edited particles from going through the emitter's surface */
854 static void pe_deflect_emitter(Scene *scene, Object *ob, PTCacheEdit *edit)
856 ParticleEditSettings *pset= PE_settings(scene);
857 ParticleSystem *psys;
858 ParticleSystemModifierData *psmd;
861 float *vec, *nor, dvec[3], dot, dist_1st=0.0f;
862 float hairimat[4][4], hairmat[4][4];
864 if(edit==NULL || edit->psys==NULL || (pset->flag & PE_DEFLECT_EMITTER)==0 || (edit->psys->flag & PSYS_GLOBAL_HAIR))
868 psmd = psys_get_modifier(ob,psys);
874 psys_mat_hair_to_object(ob, psmd->dm, psys->part->from, psys->particles + p, hairmat);
877 mul_m4_v3(hairmat, key->co);
882 dist_1st = len_v3v3((key+1)->co, key->co);
883 dist_1st *= 0.75f * pset->emitterdist;
886 index= BLI_kdtree_find_nearest(edit->emitter_field,key->co,NULL,NULL);
888 vec=edit->emitter_cosnos +index*6;
891 sub_v3_v3v3(dvec, key->co, vec);
893 dot=dot_v3v3(dvec,nor);
899 mul_v3_fl(dvec,dist_1st-dot);
900 add_v3_v3(key->co, dvec);
905 mul_v3_fl(dvec,dist_1st-dot);
906 add_v3_v3(key->co, dvec);
913 invert_m4_m4(hairimat,hairmat);
916 mul_m4_v3(hairimat, key->co);
920 /* force set distances between neighbouring keys */
921 void PE_apply_lengths(Scene *scene, PTCacheEdit *edit)
924 ParticleEditSettings *pset=PE_settings(scene);
928 if(edit==0 || (pset->flag & PE_KEEP_LENGTHS)==0)
931 if(edit->psys && edit->psys->flag & PSYS_GLOBAL_HAIR)
937 sub_v3_v3v3(dv1, key->co, (key - 1)->co);
939 mul_v3_fl(dv1, (key - 1)->length);
940 add_v3_v3v3(key->co, (key - 1)->co, dv1);
945 /* try to find a nice solution to keep distances between neighbouring keys */
946 static void pe_iterate_lengths(Scene *scene, PTCacheEdit *edit)
948 ParticleEditSettings *pset=PE_settings(scene);
953 float dv0[3]= {0.0f, 0.0f, 0.0f};
954 float dv1[3]= {0.0f, 0.0f, 0.0f};
955 float dv2[3]= {0.0f, 0.0f, 0.0f};
957 if(edit==0 || (pset->flag & PE_KEEP_LENGTHS)==0)
960 if(edit->psys && edit->psys->flag & PSYS_GLOBAL_HAIR)
964 for(j=1; j<point->totkey; j++) {
965 float mul= 1.0f / (float)point->totkey;
967 if(pset->flag & PE_LOCK_FIRST) {
968 key= point->keys + 1;
970 dv1[0]= dv1[1]= dv1[2]= 0.0;
975 dv0[0]= dv0[1]= dv0[2]= 0.0;
978 for(; k<point->totkey; k++, key++) {
980 sub_v3_v3v3(dv0, (key - 1)->co, key->co);
981 tlen= normalize_v3(dv0);
982 mul_v3_fl(dv0, (mul * (tlen - (key - 1)->length)));
985 if(k < point->totkey - 1) {
986 sub_v3_v3v3(dv2, (key + 1)->co, key->co);
987 tlen= normalize_v3(dv2);
988 mul_v3_fl(dv2, mul * (tlen - key->length));
992 add_v3_v3((key-1)->co, dv1);
1000 /* set current distances to be kept between neighbouting keys */
1001 static void recalc_lengths(PTCacheEdit *edit)
1008 LOOP_EDITED_POINTS {
1010 for(k=0; k<point->totkey-1; k++, key++) {
1011 key->length= len_v3v3(key->co, (key + 1)->co);
1016 /* calculate a tree for finding nearest emitter's vertice */
1017 static void recalc_emitter_field(Object *ob, ParticleSystem *psys)
1019 DerivedMesh *dm=psys_get_modifier(ob,psys)->dm;
1020 PTCacheEdit *edit= psys->edit;
1024 int i, totface, totvert;
1029 if(edit->emitter_cosnos)
1030 MEM_freeN(edit->emitter_cosnos);
1032 BLI_kdtree_free(edit->emitter_field);
1034 totface=dm->getNumFaces(dm);
1035 totvert=dm->getNumVerts(dm);
1037 edit->emitter_cosnos=MEM_callocN(totface*6*sizeof(float),"emitter cosnos");
1039 edit->emitter_field= BLI_kdtree_new(totface);
1041 vec=edit->emitter_cosnos;
1044 mvert=dm->getVertDataArray(dm,CD_MVERT);
1045 for(i=0; i<totface; i++, vec+=6, nor+=6) {
1046 mface=dm->getFaceData(dm,i,CD_MFACE);
1048 mvert=dm->getVertData(dm,mface->v1,CD_MVERT);
1049 VECCOPY(vec,mvert->co);
1050 VECCOPY(nor,mvert->no);
1052 mvert=dm->getVertData(dm,mface->v2,CD_MVERT);
1053 VECADD(vec,vec,mvert->co);
1054 VECADD(nor,nor,mvert->no);
1056 mvert=dm->getVertData(dm,mface->v3,CD_MVERT);
1057 VECADD(vec,vec,mvert->co);
1058 VECADD(nor,nor,mvert->no);
1061 mvert=dm->getVertData(dm,mface->v4,CD_MVERT);
1062 VECADD(vec,vec,mvert->co);
1063 VECADD(nor,nor,mvert->no);
1065 mul_v3_fl(vec,0.25);
1068 mul_v3_fl(vec,0.3333f);
1072 BLI_kdtree_insert(edit->emitter_field, i, vec, NULL);
1075 BLI_kdtree_balance(edit->emitter_field);
1078 static void PE_update_selection(Scene *scene, Object *ob, int useflag)
1080 PTCacheEdit *edit= PE_get_current(scene, ob);
1084 /* flag all particles to be updated if not using flag */
1087 point->flag |= PEP_EDIT_RECALC;
1089 /* flush edit key flag to hair key flag to preserve selection
1091 if(edit->psys) LOOP_POINTS {
1092 hkey = edit->psys->particles[p].hair;
1094 hkey->editflag= key->flag;
1099 psys_cache_edit_paths(scene, ob, edit, CFRA);
1102 /* disable update flag */
1104 point->flag &= ~PEP_EDIT_RECALC;
1107 static void update_world_cos(Object *ob, PTCacheEdit *edit)
1109 ParticleSystem *psys = edit->psys;
1110 ParticleSystemModifierData *psmd= psys_get_modifier(ob, psys);
1112 float hairmat[4][4];
1114 if(psys==0 || psys->edit==0 || psmd->dm==NULL)
1118 if(!(psys->flag & PSYS_GLOBAL_HAIR))
1119 psys_mat_hair_to_global(ob, psmd->dm, psys->part->from, psys->particles+p, hairmat);
1122 VECCOPY(key->world_co,key->co);
1123 if(!(psys->flag & PSYS_GLOBAL_HAIR))
1124 mul_m4_v3(hairmat, key->world_co);
1128 static void update_velocities(Object *ob, PTCacheEdit *edit)
1130 /*TODO: get frs_sec properly */
1131 float vec1[3], vec2[3], frs_sec, dfra;
1134 /* hair doesn't use velocities */
1135 if(edit->psys || !edit->points || !edit->points->keys->vel)
1138 frs_sec = edit->pid.flag & PTCACHE_VEL_PER_SEC ? 25.0f : 1.0f;
1140 LOOP_EDITED_POINTS {
1143 dfra = *(key+1)->time - *key->time;
1148 VECSUB(key->vel, (key+1)->co, key->co);
1150 if(point->totkey>2) {
1151 VECSUB(vec1, (key+1)->co, (key+2)->co);
1152 project_v3_v3v3(vec2, vec1, key->vel);
1153 VECSUB(vec2, vec1, vec2);
1154 VECADDFAC(key->vel, key->vel, vec2, 0.5f);
1157 else if(k==point->totkey-1) {
1158 dfra = *key->time - *(key-1)->time;
1163 VECSUB(key->vel, key->co, (key-1)->co);
1165 if(point->totkey>2) {
1166 VECSUB(vec1, (key-2)->co, (key-1)->co);
1167 project_v3_v3v3(vec2, vec1, key->vel);
1168 VECSUB(vec2, vec1, vec2);
1169 VECADDFAC(key->vel, key->vel, vec2, 0.5f);
1173 dfra = *(key+1)->time - *(key-1)->time;
1178 VECSUB(key->vel, (key+1)->co, (key-1)->co);
1180 mul_v3_fl(key->vel, frs_sec/dfra);
1185 void PE_update_object(Scene *scene, Object *ob, int useflag)
1187 /* use this to do partial particle updates, not usable when adding or
1188 removing, then a full redo is necessary and calling this may crash */
1189 ParticleEditSettings *pset= PE_settings(scene);
1190 PTCacheEdit *edit = PE_get_current(scene, ob);
1196 /* flag all particles to be updated if not using flag */
1199 point->flag |= PEP_EDIT_RECALC;
1202 /* do post process on particle edit keys */
1203 pe_iterate_lengths(scene, edit);
1204 pe_deflect_emitter(scene, ob, edit);
1205 PE_apply_lengths(scene, edit);
1207 PE_apply_mirror(ob,edit->psys);
1209 update_world_cos(ob, edit);
1210 if(pset->flag & PE_AUTO_VELOCITY)
1211 update_velocities(ob, edit);
1212 PE_hide_keys_time(scene, edit, CFRA);
1214 /* regenerate path caches */
1215 psys_cache_edit_paths(scene, ob, edit, CFRA);
1217 /* disable update flag */
1219 point->flag &= ~PEP_EDIT_RECALC;
1223 edit->psys->flag &= ~PSYS_HAIR_UPDATED;
1226 /************************************************/
1227 /* Edit Selections */
1228 /************************************************/
1230 /*-----selection callbacks-----*/
1232 static void select_key(PEData *data, int point_index, int key_index)
1234 PTCacheEdit *edit = data->edit;
1235 PTCacheEditPoint *point = edit->points + point_index;
1236 PTCacheEditKey *key = point->keys + key_index;
1239 key->flag |= PEK_SELECT;
1241 key->flag &= ~PEK_SELECT;
1243 point->flag |= PEP_EDIT_RECALC;
1246 static void select_keys(PEData *data, int point_index, int key_index)
1248 PTCacheEdit *edit = data->edit;
1249 PTCacheEditPoint *point = edit->points + point_index;
1254 key->flag |= PEK_SELECT;
1256 key->flag &= ~PEK_SELECT;
1259 point->flag |= PEP_EDIT_RECALC;
1262 static void toggle_key_select(PEData *data, int point_index, int key_index)
1264 PTCacheEdit *edit = data->edit;
1265 PTCacheEditPoint *point = edit->points + point_index;
1266 PTCacheEditKey *key = point->keys + key_index;
1268 key->flag ^= PEK_SELECT;
1269 point->flag |= PEP_EDIT_RECALC;
1272 /************************ de select all operator ************************/
1274 static int select_all_exec(bContext *C, wmOperator *op)
1276 Scene *scene= CTX_data_scene(C);
1277 Object *ob= CTX_data_active_object(C);
1278 PTCacheEdit *edit= PE_get_current(scene, ob);
1280 int action = RNA_enum_get(op->ptr, "action");
1282 if (action == SEL_TOGGLE) {
1283 action = SEL_SELECT;
1284 LOOP_VISIBLE_POINTS {
1285 LOOP_SELECTED_KEYS {
1286 action = SEL_DESELECT;
1290 if (action == SEL_DESELECT)
1295 LOOP_VISIBLE_POINTS {
1299 if ((key->flag & PEK_SELECT) == 0) {
1300 key->flag |= PEK_SELECT;
1301 point->flag |= PEP_EDIT_RECALC;
1305 if (key->flag & PEK_SELECT) {
1306 key->flag &= ~PEK_SELECT;
1307 point->flag |= PEP_EDIT_RECALC;
1311 if ((key->flag & PEK_SELECT) == 0) {
1312 key->flag |= PEK_SELECT;
1313 point->flag |= PEP_EDIT_RECALC;
1315 key->flag &= ~PEK_SELECT;
1316 point->flag |= PEP_EDIT_RECALC;
1323 PE_update_selection(scene, ob, 1);
1324 WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_SELECTED, ob);
1326 return OPERATOR_FINISHED;
1329 void PARTICLE_OT_select_all(wmOperatorType *ot)
1332 ot->name= "Selection of all particles";
1333 ot->idname= "PARTICLE_OT_select_all";
1336 ot->exec= select_all_exec;
1340 ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
1342 WM_operator_properties_select_all(ot);
1345 /************************ pick select operator ************************/
1347 int PE_mouse_particles(bContext *C, short *mval, int extend)
1350 Scene *scene= CTX_data_scene(C);
1351 Object *ob= CTX_data_active_object(C);
1352 PTCacheEdit *edit= PE_get_current(scene, ob);
1355 if(!PE_start_edit(edit))
1356 return OPERATOR_CANCELLED;
1359 LOOP_VISIBLE_POINTS {
1360 LOOP_SELECTED_KEYS {
1361 key->flag &= ~PEK_SELECT;
1362 point->flag |= PEP_EDIT_RECALC;
1367 PE_set_view3d_data(C, &data);
1371 for_mouse_hit_keys(&data, toggle_key_select, 1); /* nearest only */
1373 PE_update_selection(scene, ob, 1);
1374 WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_SELECTED, data.ob);
1376 return OPERATOR_FINISHED;
1379 /************************ select first operator ************************/
1381 static void select_root(PEData *data, int point_index)
1383 if (data->edit->points[point_index].flag & PEP_HIDE)
1386 data->edit->points[point_index].keys->flag |= PEK_SELECT;
1387 data->edit->points[point_index].flag |= PEP_EDIT_RECALC; /* redraw selection only */
1390 static int select_roots_exec(bContext *C, wmOperator *op)
1394 PE_set_data(C, &data);
1395 foreach_point(&data, select_root);
1397 PE_update_selection(data.scene, data.ob, 1);
1398 WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_SELECTED, data.ob);
1400 return OPERATOR_FINISHED;
1403 void PARTICLE_OT_select_roots(wmOperatorType *ot)
1406 ot->name= "Select Roots";
1407 ot->idname= "PARTICLE_OT_select_roots";
1410 ot->exec= select_roots_exec;
1414 ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
1417 /************************ select last operator ************************/
1419 static void select_tip(PEData *data, int point_index)
1421 PTCacheEditPoint *point = data->edit->points + point_index;
1423 if (point->flag & PEP_HIDE)
1426 point->keys[point->totkey - 1].flag |= PEK_SELECT;
1427 point->flag |= PEP_EDIT_RECALC; /* redraw selection only */
1430 static int select_tips_exec(bContext *C, wmOperator *op)
1434 PE_set_data(C, &data);
1435 foreach_point(&data, select_tip);
1437 PE_update_selection(data.scene, data.ob, 1);
1438 WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_SELECTED, data.ob);
1440 return OPERATOR_FINISHED;
1443 void PARTICLE_OT_select_tips(wmOperatorType *ot)
1446 ot->name= "Select Tips";
1447 ot->idname= "PARTICLE_OT_select_tips";
1450 ot->exec= select_tips_exec;
1454 ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
1457 /************************ select linked operator ************************/
1459 static int select_linked_exec(bContext *C, wmOperator *op)
1465 RNA_int_get_array(op->ptr, "location", location);
1466 mval[0]= location[0];
1467 mval[1]= location[1];
1469 view3d_operator_needs_opengl(C);
1471 PE_set_view3d_data(C, &data);
1474 data.select= !RNA_boolean_get(op->ptr, "deselect");
1476 for_mouse_hit_keys(&data, select_keys, 1); /* nearest only */
1477 PE_update_selection(data.scene, data.ob, 1);
1478 WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_SELECTED, data.ob);
1480 return OPERATOR_FINISHED;
1483 static int select_linked_invoke(bContext *C, wmOperator *op, wmEvent *event)
1485 ARegion *ar= CTX_wm_region(C);
1488 location[0]= event->x - ar->winrct.xmin;
1489 location[1]= event->y - ar->winrct.ymin;
1490 RNA_int_set_array(op->ptr, "location", location);
1492 return select_linked_exec(C, op);
1495 void PARTICLE_OT_select_linked(wmOperatorType *ot)
1498 ot->name= "Select Linked";
1499 ot->idname= "PARTICLE_OT_select_linked";
1502 ot->exec= select_linked_exec;
1503 ot->invoke= select_linked_invoke;
1504 ot->poll= PE_poll_view3d;
1507 ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
1510 RNA_def_boolean(ot->srna, "deselect", 0, "Deselect", "Deselect linked keys rather than selecting them.");
1511 RNA_def_int_vector(ot->srna, "location", 2, NULL, 0, INT_MAX, "Location", "", 0, 16384);
1514 /************************ border select operator ************************/
1516 int PE_border_select(bContext *C, rcti *rect, int select, int extend)
1518 Scene *scene= CTX_data_scene(C);
1519 Object *ob= CTX_data_active_object(C);
1520 PTCacheEdit *edit= PE_get_current(scene, ob);
1523 if(!PE_start_edit(edit))
1524 return OPERATOR_CANCELLED;
1526 if (extend == 0 && select) {
1529 LOOP_VISIBLE_POINTS {
1530 LOOP_SELECTED_KEYS {
1531 key->flag &= ~PEK_SELECT;
1532 point->flag |= PEP_EDIT_RECALC;
1537 PE_set_view3d_data(C, &data);
1539 data.select= select;
1541 for_mouse_hit_keys(&data, select_key, 0);
1543 PE_update_selection(scene, ob, 1);
1544 WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_SELECTED, ob);
1546 return OPERATOR_FINISHED;
1549 /************************ circle select operator ************************/
1551 int PE_circle_select(bContext *C, int selecting, short *mval, float rad)
1553 Scene *scene= CTX_data_scene(C);
1554 Object *ob= CTX_data_active_object(C);
1555 PTCacheEdit *edit= PE_get_current(scene, ob);
1558 if(!PE_start_edit(edit))
1559 return OPERATOR_FINISHED;
1561 PE_set_view3d_data(C, &data);
1564 data.select= selecting;
1566 for_mouse_hit_keys(&data, select_key, 0);
1568 PE_update_selection(scene, ob, 1);
1569 WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_SELECTED, ob);
1571 return OPERATOR_FINISHED;
1574 /************************ lasso select operator ************************/
1576 int PE_lasso_select(bContext *C, short mcords[][2], short moves, short select)
1578 Scene *scene= CTX_data_scene(C);
1579 Object *ob= CTX_data_active_object(C);
1580 ARegion *ar= CTX_wm_region(C);
1581 ParticleEditSettings *pset= PE_settings(scene);
1582 PTCacheEdit *edit = PE_get_current(scene, ob);
1583 ParticleSystem *psys = edit->psys;
1584 ParticleSystemModifierData *psmd = psys_get_modifier(ob, psys);
1586 float co[3], mat[4][4];
1589 if(!PE_start_edit(edit))
1590 return OPERATOR_CANCELLED;
1594 LOOP_VISIBLE_POINTS {
1595 if(edit->psys && !(psys->flag & PSYS_GLOBAL_HAIR))
1596 psys_mat_hair_to_global(ob, psmd->dm, psys->part->from, psys->particles + p, mat);
1598 if(pset->selectmode==SCE_SELECT_POINT) {
1600 VECCOPY(co, key->co);
1602 project_short(ar, co, vertco);
1603 if((vertco[0] != IS_CLIPPED) && lasso_inside(mcords,moves,vertco[0],vertco[1])) {
1604 if(select && !(key->flag & PEK_SELECT)) {
1605 key->flag |= PEK_SELECT;
1606 point->flag |= PEP_EDIT_RECALC;
1608 else if(key->flag & PEK_SELECT) {
1609 key->flag &= ~PEK_SELECT;
1610 point->flag |= PEP_EDIT_RECALC;
1615 else if(pset->selectmode==SCE_SELECT_END) {
1616 key= point->keys + point->totkey - 1;
1618 VECCOPY(co, key->co);
1620 project_short(ar, co,vertco);
1621 if((vertco[0] != IS_CLIPPED) && lasso_inside(mcords,moves,vertco[0],vertco[1])) {
1622 if(select && !(key->flag & PEK_SELECT)) {
1623 key->flag |= PEK_SELECT;
1624 point->flag |= PEP_EDIT_RECALC;
1626 else if(key->flag & PEK_SELECT) {
1627 key->flag &= ~PEK_SELECT;
1628 point->flag |= PEP_EDIT_RECALC;
1634 PE_update_selection(scene, ob, 1);
1635 WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_SELECTED, ob);
1637 return OPERATOR_FINISHED;
1640 /*************************** hide operator **************************/
1642 static int hide_exec(bContext *C, wmOperator *op)
1644 Object *ob= CTX_data_active_object(C);
1645 Scene *scene= CTX_data_scene(C);
1646 PTCacheEdit *edit= PE_get_current(scene, ob);
1649 if(RNA_enum_get(op->ptr, "unselected")) {
1650 LOOP_UNSELECTED_POINTS {
1651 point->flag |= PEP_HIDE;
1652 point->flag |= PEP_EDIT_RECALC;
1655 key->flag &= ~PEK_SELECT;
1659 LOOP_SELECTED_POINTS {
1660 point->flag |= PEP_HIDE;
1661 point->flag |= PEP_EDIT_RECALC;
1664 key->flag &= ~PEK_SELECT;
1668 PE_update_selection(scene, ob, 1);
1669 WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_SELECTED, ob);
1671 return OPERATOR_FINISHED;
1674 void PARTICLE_OT_hide(wmOperatorType *ot)
1677 ot->name= "Hide Selected";
1678 ot->idname= "PARTICLE_OT_hide";
1681 ot->exec= hide_exec;
1685 ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
1688 RNA_def_boolean(ot->srna, "unselected", 0, "Unselected", "Hide unselected rather than selected.");
1691 /*************************** reveal operator **************************/
1693 static int reveal_exec(bContext *C, wmOperator *op)
1695 Object *ob= CTX_data_active_object(C);
1696 Scene *scene= CTX_data_scene(C);
1697 PTCacheEdit *edit= PE_get_current(scene, ob);
1701 if(point->flag & PEP_HIDE) {
1702 point->flag &= ~PEP_HIDE;
1703 point->flag |= PEP_EDIT_RECALC;
1706 key->flag |= PEK_SELECT;
1710 PE_update_selection(scene, ob, 1);
1711 WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_SELECTED, ob);
1713 return OPERATOR_FINISHED;
1716 void PARTICLE_OT_reveal(wmOperatorType *ot)
1720 ot->idname= "PARTICLE_OT_reveal";
1723 ot->exec= reveal_exec;
1727 ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
1730 /************************ select less operator ************************/
1732 static void select_less_keys(PEData *data, int point_index)
1734 PTCacheEdit *edit= data->edit;
1735 PTCacheEditPoint *point = edit->points + point_index;
1738 LOOP_SELECTED_KEYS {
1740 if(((key+1)->flag&PEK_SELECT)==0)
1741 key->flag |= PEK_TAG;
1743 else if(k==point->totkey-1) {
1744 if(((key-1)->flag&PEK_SELECT)==0)
1745 key->flag |= PEK_TAG;
1748 if((((key-1)->flag & (key+1)->flag) & PEK_SELECT)==0)
1749 key->flag |= PEK_TAG;
1754 if(key->flag&PEK_TAG) {
1755 key->flag &= ~(PEK_TAG|PEK_SELECT);
1756 point->flag |= PEP_EDIT_RECALC; /* redraw selection only */
1761 static int select_less_exec(bContext *C, wmOperator *op)
1765 PE_set_data(C, &data);
1766 foreach_point(&data, select_less_keys);
1768 PE_update_selection(data.scene, data.ob, 1);
1769 WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_SELECTED, data.ob);
1771 return OPERATOR_FINISHED;
1774 void PARTICLE_OT_select_less(wmOperatorType *ot)
1777 ot->name= "Select Less";
1778 ot->idname= "PARTICLE_OT_select_less";
1781 ot->exec= select_less_exec;
1785 ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
1788 /************************ select more operator ************************/
1790 static void select_more_keys(PEData *data, int point_index)
1792 PTCacheEdit *edit= data->edit;
1793 PTCacheEditPoint *point = edit->points + point_index;
1797 if(key->flag & PEK_SELECT) continue;
1800 if((key+1)->flag&PEK_SELECT)
1801 key->flag |= PEK_TAG;
1803 else if(k==point->totkey-1) {
1804 if((key-1)->flag&PEK_SELECT)
1805 key->flag |= PEK_TAG;
1808 if(((key-1)->flag | (key+1)->flag) & PEK_SELECT)
1809 key->flag |= PEK_TAG;
1814 if(key->flag&PEK_TAG) {
1815 key->flag &= ~PEK_TAG;
1816 key->flag |= PEK_SELECT;
1817 point->flag |= PEP_EDIT_RECALC; /* redraw selection only */
1822 static int select_more_exec(bContext *C, wmOperator *op)
1826 PE_set_data(C, &data);
1827 foreach_point(&data, select_more_keys);
1829 PE_update_selection(data.scene, data.ob, 1);
1830 WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_SELECTED, data.ob);
1832 return OPERATOR_FINISHED;
1835 void PARTICLE_OT_select_more(wmOperatorType *ot)
1838 ot->name= "Select More";
1839 ot->idname= "PARTICLE_OT_select_more";
1842 ot->exec= select_more_exec;
1846 ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
1849 static int select_inverse_exec(bContext *C, wmOperator *op)
1855 PE_set_data(C, &data);
1857 edit= PE_get_current(data.scene, data.ob);
1859 LOOP_VISIBLE_POINTS {
1861 key->flag ^= PEK_SELECT;
1862 point->flag |= PEP_EDIT_RECALC; /* redraw selection only */
1866 PE_update_selection(data.scene, data.ob, 1);
1867 WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_SELECTED, data.ob);
1869 return OPERATOR_FINISHED;
1872 void PARTICLE_OT_select_inverse(wmOperatorType *ot)
1875 ot->name= "Select Inverse";
1876 ot->idname= "PARTICLE_OT_select_inverse";
1879 ot->exec= select_inverse_exec;
1883 ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
1886 /************************ rekey operator ************************/
1888 static void rekey_particle(PEData *data, int pa_index)
1890 PTCacheEdit *edit= data->edit;
1891 ParticleSystem *psys= edit->psys;
1892 ParticleSimulationData sim = {data->scene, data->ob, edit->psys, NULL};
1893 ParticleData *pa= psys->particles + pa_index;
1894 PTCacheEditPoint *point = edit->points + pa_index;
1896 HairKey *key, *new_keys, *okey;
1897 PTCacheEditKey *ekey;
1898 float dval, sta, end;
1901 pa->flag |= PARS_REKEY;
1903 key= new_keys= MEM_callocN(data->totrekey * sizeof(HairKey),"Hair re-key keys");
1906 /* root and tip stay the same */
1907 VECCOPY(key->co, okey->co);
1908 VECCOPY((key + data->totrekey - 1)->co, (okey + pa->totkey - 1)->co);
1910 sta= key->time= okey->time;
1911 end= (key + data->totrekey - 1)->time= (okey + pa->totkey - 1)->time;
1912 dval= (end - sta) / (float)(data->totrekey - 1);
1914 /* interpolate new keys from old ones */
1915 for(k=1,key++; k<data->totrekey-1; k++,key++) {
1916 state.time= (float)k / (float)(data->totrekey-1);
1917 psys_get_particle_on_path(&sim, pa_index, &state, 0);
1918 VECCOPY(key->co, state.co);
1919 key->time= sta + k * dval;
1924 MEM_freeN(pa->hair);
1927 point->totkey=pa->totkey=data->totrekey;
1931 MEM_freeN(point->keys);
1932 ekey= point->keys= MEM_callocN(pa->totkey * sizeof(PTCacheEditKey),"Hair re-key edit keys");
1934 for(k=0, key=pa->hair; k<pa->totkey; k++, key++, ekey++) {
1936 ekey->time= &key->time;
1937 if(!(psys->flag & PSYS_GLOBAL_HAIR))
1938 ekey->flag |= PEK_USE_WCO;
1941 pa->flag &= ~PARS_REKEY;
1942 point->flag |= PEP_EDIT_RECALC;
1945 static int rekey_exec(bContext *C, wmOperator *op)
1949 PE_set_data(C, &data);
1951 data.dval= 1.0f / (float)(data.totrekey-1);
1952 data.totrekey= RNA_int_get(op->ptr, "keys");
1954 foreach_selected_point(&data, rekey_particle);
1956 recalc_lengths(data.edit);
1957 PE_update_object(data.scene, data.ob, 1);
1958 WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_EDITED, data.ob);
1960 return OPERATOR_FINISHED;
1963 void PARTICLE_OT_rekey(wmOperatorType *ot)
1967 ot->idname= "PARTICLE_OT_rekey";
1970 ot->exec= rekey_exec;
1971 ot->invoke= WM_operator_props_popup;
1975 ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
1978 RNA_def_int(ot->srna, "keys", 2, 2, INT_MAX, "Number of Keys", "", 2, 100);
1981 static void rekey_particle_to_time(Scene *scene, Object *ob, int pa_index, float path_time)
1983 PTCacheEdit *edit= PE_get_current(scene, ob);
1984 ParticleSystem *psys;
1985 ParticleSimulationData sim = {scene, ob, edit ? edit->psys : NULL, NULL};
1988 HairKey *new_keys, *key;
1989 PTCacheEditKey *ekey;
1992 if(!edit || !edit->psys) return;
1996 pa= psys->particles + pa_index;
1998 pa->flag |= PARS_REKEY;
2000 key= new_keys= MEM_dupallocN(pa->hair);
2002 /* interpolate new keys from old ones (roots stay the same) */
2003 for(k=1, key++; k < pa->totkey; k++, key++) {
2004 state.time= path_time * (float)k / (float)(pa->totkey-1);
2005 psys_get_particle_on_path(&sim, pa_index, &state, 0);
2006 VECCOPY(key->co, state.co);
2009 /* replace hair keys */
2011 MEM_freeN(pa->hair);
2014 /* update edit pointers */
2015 for(k=0, key=pa->hair, ekey=edit->points[pa_index].keys; k<pa->totkey; k++, key++, ekey++) {
2017 ekey->time= &key->time;
2020 pa->flag &= ~PARS_REKEY;
2023 /************************* utilities **************************/
2025 static int remove_tagged_particles(Scene *scene, Object *ob, ParticleSystem *psys, int mirror)
2027 PTCacheEdit *edit = psys->edit;
2028 ParticleData *pa, *npa=0, *new_pars=0;
2030 PTCacheEditPoint *npoint=0, *new_points=0;
2031 ParticleSystemModifierData *psmd;
2032 int i, totpart, new_totpart= psys->totpart, removed= 0;
2036 psmd= psys_get_modifier(ob, psys);
2037 totpart= psys->totpart;
2039 LOOP_TAGGED_POINTS {
2040 PE_mirror_particle(ob, psmd->dm, psys, psys->particles + p, NULL);
2044 LOOP_TAGGED_POINTS {
2049 if(new_totpart != psys->totpart) {
2051 npa= new_pars= MEM_callocN(new_totpart * sizeof(ParticleData), "ParticleData array");
2052 npoint= new_points= MEM_callocN(new_totpart * sizeof(PTCacheEditPoint), "PTCacheEditKey array");
2055 pa= psys->particles;
2056 point= edit->points;
2057 for(i=0; i<psys->totpart; i++, pa++, point++) {
2058 if(point->flag & PEP_TAG) {
2060 MEM_freeN(point->keys);
2062 MEM_freeN(pa->hair);
2065 memcpy(npa, pa, sizeof(ParticleData));
2066 memcpy(npoint, point, sizeof(PTCacheEditPoint));
2072 if(psys->particles) MEM_freeN(psys->particles);
2073 psys->particles= new_pars;
2075 if(edit->points) MEM_freeN(edit->points);
2076 edit->points= new_points;
2078 if(edit->mirror_cache) {
2079 MEM_freeN(edit->mirror_cache);
2080 edit->mirror_cache= NULL;
2084 MEM_freeN(psys->child);
2089 edit->totpoint= psys->totpart= new_totpart;
2095 static void remove_tagged_keys(Scene *scene, Object *ob, ParticleSystem *psys)
2097 PTCacheEdit *edit= psys->edit;
2099 HairKey *hkey, *nhkey, *new_hkeys=0;
2101 PTCacheEditKey *nkey, *new_keys;
2102 ParticleSystemModifierData *psmd;
2105 if(pe_x_mirror(ob)) {
2106 /* mirror key tags */
2107 psmd= psys_get_modifier(ob, psys);
2111 PE_mirror_particle(ob, psmd->dm, psys, psys->particles + p, NULL);
2118 new_totkey= point->totkey;
2122 /* we can't have elements with less than two keys*/
2124 point->flag |= PEP_TAG;
2126 remove_tagged_particles(scene, ob, psys, pe_x_mirror(ob));
2129 pa = psys->particles + p;
2130 new_totkey= pa->totkey;
2136 if(new_totkey != pa->totkey) {
2137 nhkey= new_hkeys= MEM_callocN(new_totkey*sizeof(HairKey), "HairKeys");
2138 nkey= new_keys= MEM_callocN(new_totkey*sizeof(PTCacheEditKey), "particle edit keys");
2142 while(key->flag & PEK_TAG && hkey < pa->hair + pa->totkey) {
2147 if(hkey < pa->hair + pa->totkey) {
2148 VECCOPY(nhkey->co, hkey->co);
2149 nhkey->editflag = hkey->editflag;
2150 nhkey->time= hkey->time;
2151 nhkey->weight= hkey->weight;
2153 nkey->co= nhkey->co;
2154 nkey->time= &nhkey->time;
2155 /* these can be copied from old edit keys */
2156 nkey->flag = key->flag;
2157 nkey->ftime = key->ftime;
2158 nkey->length = key->length;
2159 VECCOPY(nkey->world_co, key->world_co);
2167 MEM_freeN(pa->hair);
2170 MEM_freeN(point->keys);
2172 pa->hair= new_hkeys;
2173 point->keys= new_keys;
2175 point->totkey= pa->totkey= new_totkey;
2177 /* flag for recalculating length */
2178 point->flag |= PEP_EDIT_RECALC;
2183 /************************ subdivide opertor *********************/
2185 /* works like normal edit mode subdivide, inserts keys between neighbouring selected keys */
2186 static void subdivide_particle(PEData *data, int pa_index)
2188 PTCacheEdit *edit= data->edit;
2189 ParticleSystem *psys= edit->psys;
2190 ParticleSimulationData sim = {data->scene, data->ob, edit->psys, NULL};
2191 ParticleData *pa= psys->particles + pa_index;
2192 PTCacheEditPoint *point = edit->points + pa_index;
2194 HairKey *key, *nkey, *new_keys;
2195 PTCacheEditKey *ekey, *nekey, *new_ekeys;
2201 for(k=0, ekey=point->keys; k<pa->totkey-1; k++,ekey++) {
2202 if(ekey->flag&PEK_SELECT && (ekey+1)->flag&PEK_SELECT)
2206 if(totnewkey==0) return;
2208 pa->flag |= PARS_REKEY;
2210 nkey= new_keys= MEM_callocN((pa->totkey+totnewkey)*(sizeof(HairKey)),"Hair subdivide keys");
2211 nekey= new_ekeys= MEM_callocN((pa->totkey+totnewkey)*(sizeof(PTCacheEditKey)),"Hair subdivide edit keys");
2214 endtime= key[pa->totkey-1].time;
2216 for(k=0, ekey=point->keys; k<pa->totkey-1; k++, key++, ekey++) {
2218 memcpy(nkey,key,sizeof(HairKey));
2219 memcpy(nekey,ekey,sizeof(PTCacheEditKey));
2221 nekey->co= nkey->co;
2222 nekey->time= &nkey->time;
2227 if(ekey->flag & PEK_SELECT && (ekey+1)->flag & PEK_SELECT) {
2228 nkey->time= (key->time + (key+1)->time)*0.5f;
2229 state.time= (endtime != 0.0f)? nkey->time/endtime: 0.0f;
2230 psys_get_particle_on_path(&sim, pa_index, &state, 0);
2231 VECCOPY(nkey->co, state.co);
2233 nekey->co= nkey->co;
2234 nekey->time= &nkey->time;
2235 nekey->flag |= PEK_SELECT;
2236 if(!(psys->flag & PSYS_GLOBAL_HAIR))
2237 nekey->flag |= PEK_USE_WCO;
2243 /*tip still not copied*/
2244 memcpy(nkey,key,sizeof(HairKey));
2245 memcpy(nekey,ekey,sizeof(PTCacheEditKey));
2247 nekey->co= nkey->co;
2248 nekey->time= &nkey->time;
2251 MEM_freeN(pa->hair);
2255 MEM_freeN(point->keys);
2256 point->keys= new_ekeys;
2258 point->totkey = pa->totkey = pa->totkey + totnewkey;
2259 point->flag |= PEP_EDIT_RECALC;
2260 pa->flag &= ~PARS_REKEY;
2263 static int subdivide_exec(bContext *C, wmOperator *op)
2267 PE_set_data(C, &data);
2268 foreach_point(&data, subdivide_particle);
2270 recalc_lengths(data.edit);
2271 PE_update_object(data.scene, data.ob, 1);
2272 WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_EDITED, data.ob);
2274 return OPERATOR_FINISHED;
2277 void PARTICLE_OT_subdivide(wmOperatorType *ot)
2280 ot->name= "Subdivide";
2281 ot->idname= "PARTICLE_OT_subdivide";
2284 ot->exec= subdivide_exec;
2288 ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
2291 /************************ remove doubles opertor *********************/
2293 static int remove_doubles_exec(bContext *C, wmOperator *op)
2295 Scene *scene= CTX_data_scene(C);
2296 Object *ob= CTX_data_active_object(C);
2297 PTCacheEdit *edit= PE_get_current(scene, ob);
2298 ParticleSystem *psys = edit->psys;
2299 ParticleSystemModifierData *psmd;
2301 KDTreeNearest nearest[10];
2303 float mat[4][4], co[3], threshold= RNA_float_get(op->ptr, "threshold");
2304 int n, totn, removed, totremoved;
2306 if(psys->flag & PSYS_GLOBAL_HAIR)
2307 return OPERATOR_CANCELLED;
2310 psmd= psys_get_modifier(ob, psys);
2316 tree=BLI_kdtree_new(psys->totpart);
2318 /* insert particles into kd tree */
2319 LOOP_SELECTED_POINTS {
2320 psys_mat_hair_to_object(ob, psmd->dm, psys->part->from, psys->particles+p, mat);
2321 VECCOPY(co, point->keys->co);
2323 BLI_kdtree_insert(tree, p, co, NULL);
2326 BLI_kdtree_balance(tree);
2328 /* tag particles to be removed */
2329 LOOP_SELECTED_POINTS {
2330 psys_mat_hair_to_object(ob, psmd->dm, psys->part->from, psys->particles+p, mat);
2331 VECCOPY(co, point->keys->co);
2334 totn= BLI_kdtree_find_n_nearest(tree,10,co,NULL,nearest);
2336 for(n=0; n<totn; n++) {
2337 /* this needs a custom threshold still */
2338 if(nearest[n].index > p && nearest[n].dist < threshold) {
2339 if(!(point->flag & PEP_TAG)) {
2340 point->flag |= PEP_TAG;
2347 BLI_kdtree_free(tree);
2349 /* remove tagged particles - don't do mirror here! */
2350 remove_tagged_particles(scene, ob, psys, 0);
2351 totremoved += removed;
2355 return OPERATOR_CANCELLED;
2357 BKE_reportf(op->reports, RPT_INFO, "Remove %d double particles.", totremoved);
2359 DAG_id_flush_update(&ob->id, OB_RECALC_DATA);
2360 WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_EDITED, ob);
2362 return OPERATOR_FINISHED;
2365 void PARTICLE_OT_remove_doubles(wmOperatorType *ot)
2368 ot->name= "Remove Doubles";
2369 ot->idname= "PARTICLE_OT_remove_doubles";
2372 ot->exec= remove_doubles_exec;
2376 ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
2379 RNA_def_float(ot->srna, "threshold", 0.0002f, 0.0f, FLT_MAX, "Threshold", "Threshold distance withing which particles are removed", 0.00001f, 0.1f);
2383 static int weight_set_exec(bContext *C, wmOperator *op)
2385 Scene *scene= CTX_data_scene(C);
2386 ParticleEditSettings *pset= PE_settings(scene);
2387 Object *ob= CTX_data_active_object(C);
2388 PTCacheEdit *edit= PE_get_current(scene, ob);
2389 ParticleSystem *psys = edit->psys;
2394 ParticleBrushData *brush= &pset->brush[pset->brushtype];
2395 float factor= RNA_float_get(op->ptr, "factor");
2397 weight= brush->strength;
2400 LOOP_SELECTED_POINTS {
2401 ParticleData *pa= psys->particles + p;
2403 LOOP_SELECTED_KEYS {
2405 hkey->weight= interpf(weight, hkey->weight, factor);
2409 DAG_id_flush_update(&ob->id, OB_RECALC_DATA);
2410 WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_EDITED, ob);
2412 return OPERATOR_FINISHED;
2415 void PARTICLE_OT_weight_set(wmOperatorType *ot)
2418 ot->name= "Weight Set";
2419 ot->idname= "PARTICLE_OT_weight_set";
2422 ot->exec= weight_set_exec;
2426 ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
2428 RNA_def_float(ot->srna, "factor", 1, 0, 1, "Factor", "", 0, 1);
2431 /************************ cursor drawing *******************************/
2433 static void brush_drawcursor(bContext *C, int x, int y, void *customdata)
2435 ParticleEditSettings *pset= PE_settings(CTX_data_scene(C));
2436 ParticleBrushData *brush;
2438 if(pset->brushtype < 0)
2441 brush= &pset->brush[pset->brushtype];
2446 glTranslatef((float)x, (float)y, 0.0f);
2448 glColor4ub(255, 255, 255, 128);
2449 glEnable(GL_LINE_SMOOTH );
2451 glutil_draw_lined_arc(0.0, M_PI*2.0, brush->size, 40);
2452 glDisable(GL_BLEND);
2453 glDisable(GL_LINE_SMOOTH );
2459 static void toggle_particle_cursor(bContext *C, int enable)
2461 ParticleEditSettings *pset= PE_settings(CTX_data_scene(C));
2463 if(pset->paintcursor && !enable) {
2464 WM_paint_cursor_end(CTX_wm_manager(C), pset->paintcursor);
2465 pset->paintcursor = NULL;
2468 pset->paintcursor= WM_paint_cursor_activate(CTX_wm_manager(C), PE_poll_view3d, brush_drawcursor, NULL);
2471 /********************* radial control operator *********************/
2473 static int brush_radial_control_invoke(bContext *C, wmOperator *op, wmEvent *event)
2475 ParticleEditSettings *pset= PE_settings(CTX_data_scene(C));
2476 ParticleBrushData *brush;
2477 int mode = RNA_enum_get(op->ptr, "mode");
2478 float original_value=1.0f;
2480 if(pset->brushtype < 0)
2481 return OPERATOR_CANCELLED;
2483 brush= &pset->brush[pset->brushtype];
2485 toggle_particle_cursor(C, 0);
2487 if(mode == WM_RADIALCONTROL_SIZE)
2488 original_value = brush->size;
2489 else if(mode == WM_RADIALCONTROL_STRENGTH)
2490 original_value = brush->strength;
2492 RNA_float_set(op->ptr, "initial_value", original_value);
2494 return WM_radial_control_invoke(C, op, event);
2497 static int brush_radial_control_modal(bContext *C, wmOperator *op, wmEvent *event)
2499 int ret = WM_radial_control_modal(C, op, event);
2501 if(ret != OPERATOR_RUNNING_MODAL)
2502 toggle_particle_cursor(C, 1);
2507 static int brush_radial_control_exec(bContext *C, wmOperator *op)
2509 ParticleEditSettings *pset= PE_settings(CTX_data_scene(C));
2510 ParticleBrushData *brush;
2511 int mode = RNA_enum_get(op->ptr, "mode");
2512 float new_value = RNA_float_get(op->ptr, "new_value");
2514 if(pset->brushtype < 0)
2515 return OPERATOR_CANCELLED;
2517 brush= &pset->brush[pset->brushtype];
2519 if(mode == WM_RADIALCONTROL_SIZE)
2520 brush->size= new_value;
2521 else if(mode == WM_RADIALCONTROL_STRENGTH)
2522 brush->strength= new_value;
2524 WM_event_add_notifier(C, NC_WINDOW, NULL);
2526 return OPERATOR_FINISHED;
2529 void PARTICLE_OT_brush_radial_control(wmOperatorType *ot)
2531 WM_OT_radial_control_partial(ot);
2533 ot->name= "Brush Radial Control";
2534 ot->idname= "PARTICLE_OT_brush_radial_control";
2536 ot->invoke= brush_radial_control_invoke;
2537 ot->modal= brush_radial_control_modal;
2538 ot->exec= brush_radial_control_exec;
2542 ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO|OPTYPE_BLOCKING;
2545 /*************************** delete operator **************************/
2547 enum { DEL_PARTICLE, DEL_KEY };
2549 static EnumPropertyItem delete_type_items[]= {
2550 {DEL_PARTICLE, "PARTICLE", 0, "Particle", ""},
2551 {DEL_KEY, "KEY", 0, "Key", ""},
2552 {0, NULL, 0, NULL, NULL}};
2554 static void set_delete_particle(PEData *data, int pa_index)
2556 PTCacheEdit *edit= data->edit;
2558 edit->points[pa_index].flag |= PEP_TAG;
2561 static void set_delete_particle_key(PEData *data, int pa_index, int key_index)
2563 PTCacheEdit *edit= data->edit;
2565 edit->points[pa_index].keys[key_index].flag |= PEK_TAG;
2568 static int delete_exec(bContext *C, wmOperator *op)
2571 int type= RNA_enum_get(op->ptr, "type");
2573 PE_set_data(C, &data);
2575 if(type == DEL_KEY) {
2576 foreach_selected_key(&data, set_delete_particle_key);
2577 remove_tagged_keys(data.scene, data.ob, data.edit->psys);
2578 recalc_lengths(data.edit);
2580 else if(type == DEL_PARTICLE) {
2581 foreach_selected_point(&data, set_delete_particle);
2582 remove_tagged_particles(data.scene, data.ob, data.edit->psys, pe_x_mirror(data.ob));
2583 recalc_lengths(data.edit);
2586 DAG_id_flush_update(&data.ob->id, OB_RECALC_DATA);
2587 WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_EDITED, data.ob);
2589 return OPERATOR_FINISHED;
2592 void PARTICLE_OT_delete(wmOperatorType *ot)
2596 ot->idname= "PARTICLE_OT_delete";
2599 ot->exec= delete_exec;
2600 ot->invoke= WM_menu_invoke;
2601 ot->poll= PE_hair_poll;
2604 ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
2607 ot->prop= RNA_def_enum(ot->srna, "type", delete_type_items, DEL_PARTICLE, "Type", "Delete a full particle or only keys.");
2610 /*************************** mirror operator **************************/
2612 static void PE_mirror_x(Scene *scene, Object *ob, int tagged)
2614 Mesh *me= (Mesh*)(ob->data);
2615 ParticleSystemModifierData *psmd;
2616 PTCacheEdit *edit= PE_get_current(scene, ob);
2617 ParticleSystem *psys = edit->psys;
2618 ParticleData *pa, *newpa, *new_pars;
2619 PTCacheEditPoint *newpoint, *new_points;
2623 int rotation, totpart, newtotpart;
2625 if(psys->flag & PSYS_GLOBAL_HAIR)
2628 psmd= psys_get_modifier(ob, psys);
2632 mirrorfaces= mesh_get_x_mirror_faces(ob, NULL);
2634 if(!edit->mirror_cache)
2635 PE_update_mirror_cache(ob, psys);
2637 totpart= psys->totpart;
2638 newtotpart= psys->totpart;
2639 LOOP_VISIBLE_POINTS {
2640 pa = psys->particles + p;
2642 if(point_is_selected(point)) {
2643 if(edit->mirror_cache[p] != -1) {
2644 /* already has a mirror, don't need to duplicate */
2645 PE_mirror_particle(ob, psmd->dm, psys, pa, NULL);
2649 point->flag |= PEP_TAG;
2653 if((point->flag & PEP_TAG) && mirrorfaces[pa->num*2] != -1)
2657 if(newtotpart != psys->totpart) {
2658 /* allocate new arrays and copy existing */
2659 new_pars= MEM_callocN(newtotpart*sizeof(ParticleData), "ParticleData new");
2660 new_points= MEM_callocN(newtotpart*sizeof(PTCacheEditPoint), "PTCacheEditPoint new");
2662 if(psys->particles) {
2663 memcpy(new_pars, psys->particles, totpart*sizeof(ParticleData));
2664 MEM_freeN(psys->particles);
2666 psys->particles= new_pars;
2669 memcpy(new_points, edit->points, totpart*sizeof(PTCacheEditPoint));
2670 MEM_freeN(edit->points);
2672 edit->points= new_points;
2674 if(edit->mirror_cache) {
2675 MEM_freeN(edit->mirror_cache);
2676 edit->mirror_cache= NULL;
2679 edit->totpoint= psys->totpart= newtotpart;
2681 /* create new elements */
2682 newpa= psys->particles + totpart;
2683 newpoint= edit->points + totpart;
2685 for(p=0, point=edit->points; p<totpart; p++, point++) {
2686 pa = psys->particles + p;
2688 if(point->flag & PEP_HIDE)
2690 if(!(point->flag & PEP_TAG) || mirrorfaces[pa->num*2] == -1)
2696 if(pa->hair) newpa->hair= MEM_dupallocN(pa->hair);
2697 if(point->keys) newpoint->keys= MEM_dupallocN(point->keys);
2699 /* rotate weights according to vertex index rotation */
2700 rotation= mirrorfaces[pa->num*2+1];
2701 newpa->fuv[0]= pa->fuv[2];
2702 newpa->fuv[1]= pa->fuv[1];
2703 newpa->fuv[2]= pa->fuv[0];
2704 newpa->fuv[3]= pa->fuv[3];
2705 while(rotation-- > 0)
2706 if(me->mface[pa->num].v4)
2707 SHIFT4(float, newpa->fuv[0], newpa->fuv[1], newpa->fuv[2], newpa->fuv[3])
2709 SHIFT3(float, newpa->fuv[0], newpa->fuv[1], newpa->fuv[2])
2711 /* assign face inddex */
2712 newpa->num= mirrorfaces[pa->num*2];
2713 newpa->num_dmcache= psys_particle_dm_face_lookup(ob,psmd->dm,newpa->num,newpa->fuv, NULL);
2715 /* update edit key pointers */
2716 key= newpoint->keys;
2717 for(k=0, hkey=newpa->hair; k<newpa->totkey; k++, hkey++, key++) {
2719 key->time= &hkey->time;
2722 /* map key positions as mirror over x axis */
2723 PE_mirror_particle(ob, psmd->dm, psys, pa, newpa);
2731 point->flag &= ~PEP_TAG;
2734 MEM_freeN(mirrorfaces);
2737 static int mirror_exec(bContext *C, wmOperator *op)
2739 Scene *scene= CTX_data_scene(C);
2740 Object *ob= CTX_data_active_object(C);
2741 PTCacheEdit *edit= PE_get_current(scene, ob);
2743 PE_mirror_x(scene, ob, 0);
2745 update_world_cos(ob, edit);
2746 WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_EDITED, ob);
2747 DAG_id_flush_update(&ob->id, OB_RECALC_DATA);
2749 return OPERATOR_FINISHED;
2752 void PARTICLE_OT_mirror(wmOperatorType *ot)
2756 ot->idname= "PARTICLE_OT_mirror";
2759 ot->exec= mirror_exec;
2763 ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
2766 /************************* brush edit callbacks ********************/
2768 static void brush_comb(PEData *data, float mat[][4], float imat[][4], int point_index, int key_index, PTCacheEditKey *key)
2770 ParticleEditSettings *pset= PE_settings(data->scene);
2773 if(pset->flag & PE_LOCK_FIRST && key_index == 0) return;
2775 fac= (float)pow((double)(1.0f - data->dist / data->rad), (double)data->combfac);
2777 VECCOPY(cvec,data->dvec);
2778 mul_mat3_m4_v3(imat,cvec);
2779 mul_v3_fl(cvec, fac);
2780 VECADD(key->co, key->co, cvec);
2782 (data->edit->points + point_index)->flag |= PEP_EDIT_RECALC;
2785 static void brush_cut(PEData *data, int pa_index)
2787 PTCacheEdit *edit = data->edit;
2788 ARegion *ar= data->vc.ar;
2789 Object *ob= data->ob;
2790 ParticleEditSettings *pset= PE_settings(data->scene);
2791 ParticleCacheKey *key= edit->pathcache[pa_index];
2792 float rad2, cut_time= 1.0;
2793 float x0, x1, v0, v1, o0, o1, xo0, xo1, d, dv;
2794 int k, cut, keys= (int)pow(2.0, (double)pset->draw_step);
2797 /* blunt scissors */
2798 if(BLI_frand() > data->cutfac) return;
2800 /* don't cut hidden */
2801 if(edit->points[pa_index].flag & PEP_HIDE)
2804 rad2= data->rad * data->rad;
2808 project_short_noclip(ar, key->co, vertco);
2809 x0= (float)vertco[0];
2810 x1= (float)vertco[1];
2812 o0= (float)data->mval[0];
2813 o1= (float)data->mval[1];
2818 /* check if root is inside circle */
2819 if(xo0*xo0 + xo1*xo1 < rad2 && key_test_depth(data, key->co)) {
2824 /* calculate path time closest to root that was inside the circle */
2825 for(k=1, key++; k<=keys; k++, key++) {
2826 project_short_noclip(ar, key->co, vertco);
2828 if(key_test_depth(data, key->co) == 0) {
2829 x0= (float)vertco[0];
2830 x1= (float)vertco[1];
2837 v0= (float)vertco[0] - x0;
2838 v1= (float)vertco[1] - x1;
2842 d= (v0*xo1 - v1*xo0);
2849 cut_time= -(v0*xo0 + v1*xo1 + d);
2851 if(cut_time > 0.0f) {
2854 if(cut_time < 1.0f) {
2855 cut_time += (float)(k-1);
2856 cut_time /= (float)keys;
2863 x0= (float)vertco[0];
2864 x1= (float)vertco[1];
2872 if(cut_time < 0.0f) {
2873 edit->points[pa_index].flag |= PEP_TAG;
2876 rekey_particle_to_time(data->scene, ob, pa_index, cut_time);
2877 edit->points[pa_index].flag |= PEP_EDIT_RECALC;
2882 static void brush_length(PEData *data, int point_index)
2884 PTCacheEdit *edit= data->edit;
2885 PTCacheEditPoint *point = edit->points + point_index;
2887 float dvec[3],pvec[3] = {0.0f, 0.0f, 0.0f};
2891 VECCOPY(pvec,key->co);
2894 VECSUB(dvec,key->co,pvec);
2895 VECCOPY(pvec,key->co);
2896 mul_v3_fl(dvec,data->growfac);
2897 VECADD(key->co,(key-1)->co,dvec);
2901 point->flag |= PEP_EDIT_RECALC;
2904 static void brush_puff(PEData *data, int point_index)
2906 PTCacheEdit *edit = data->edit;
2907 ParticleSystem *psys = edit->psys;
2908 PTCacheEditPoint *point = edit->points + point_index;
2910 float mat[4][4], imat[4][4];
2912 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;
2913 int puff_volume = 0;
2917 ParticleEditSettings *pset= PE_settings(data->scene);
2918 ParticleBrushData *brush= &pset->brush[pset->brushtype];
2919 puff_volume = brush->flag & PE_BRUSH_DATA_PUFF_VOLUME;
2922 if(psys && !(psys->flag & PSYS_GLOBAL_HAIR)) {
2923 psys_mat_hair_to_global(data->ob, data->dm, psys->part->from, psys->particles + point_index, mat);
2924 invert_m4_m4(imat,mat);
2933 /* find root coordinate and normal on emitter */
2934 VECCOPY(co, key->co);
2936 mul_v3_m4v3(kco, data->ob->imat, co); /* use 'kco' as the object space version of worldspace 'co', ob->imat is set before calling */
2938 point_index= BLI_kdtree_find_nearest(edit->emitter_field, kco, NULL, NULL);
2939 if(point_index == -1) return;
2941 VECCOPY(rootco, co);
2942 copy_v3_v3(nor, &edit->emitter_cosnos[point_index*6+3]);
2943 mul_mat3_m4_v3(data->ob->obmat, nor); /* normal into worldspace */
2948 fac= (float)pow((double)(1.0f - data->dist / data->rad), (double)data->pufffac);
2954 /* compute position as if hair was standing up straight.
2956 VECCOPY(lastco, co);
2957 VECCOPY(co, key->co);
2959 length += len_v3v3(lastco, co);
2960 if((data->select==0 || (key->flag & PEK_SELECT)) && !(key->flag & PEK_HIDE)) {
2961 VECADDFAC(kco, rootco, nor, length);
2963 /* blend between the current and straight position */
2964 VECSUB(dco, kco, co);
2965 VECADDFAC(co, co, dco, fac);
2967 /* re-use dco to compare before and after translation and add to the offset */
2968 VECCOPY(dco, key->co);
2970 mul_v3_m4v3(key->co, imat, co);
2973 /* accumulate the total distance moved to apply to unselected
2974 * keys that come after */
2975 ofs[0] += key->co[0] - dco[0];
2976 ofs[1] += key->co[1] - dco[1];
2977 ofs[2] += key->co[2] - dco[2];
2985 /* this is simple but looks bad, adds annoying kinks */
2986 add_v3_v3(key->co, ofs);
2988 /* translate (not rotate) the rest of the hair if its not selected */
2989 if(ofs[0] || ofs[1] || ofs[2]) {
2990 #if 0 /* kindof works but looks worse then whats below */
2992 /* Move the unselected point on a vector based on the
2993 * hair direction and the offset */
2995 VECSUB(dco, lastco, co);
2996 mul_mat3_m4_v3(imat, dco); /* into particle space */
2998 /* move the point allong a vector perpendicular to the
2999 * hairs direction, reduces odd kinks, */
3000 cross_v3_v3v3(c1, ofs, dco);
3001 cross_v3_v3v3(c2, c1, dco);
3003 mul_v3_fl(c2, len_v3(ofs));
3004 add_v3_v3(key->co, c2);
3006 /* Move the unselected point on a vector based on the
3007 * the normal of the closest geometry */
3008 float oco[3], onor[3];
3009 VECCOPY(oco, key->co);
3010 mul_m4_v3(mat, oco);
3011 mul_v3_m4v3(kco, data->ob->imat, oco); /* use 'kco' as the object space version of worldspace 'co', ob->imat is set before calling */
3013 point_index= BLI_kdtree_find_nearest(edit->emitter_field, kco, NULL, NULL);
3014 if(point_index != -1) {
3015 copy_v3_v3(onor, &edit->emitter_cosnos[point_index*6+3]);
3016 mul_mat3_m4_v3(data->ob->obmat, onor); /* normal into worldspace */
3017 mul_mat3_m4_v3(imat, onor); /* worldspace into particle space */
3021 mul_v3_fl(onor, len_v3(ofs));
3022 add_v3_v3(key->co, onor);
3033 point->flag |= PEP_EDIT_RECALC;
3037 static void brush_weight(PEData *data, float mat[][4], float imat[][4], int point_index, int key_index, PTCacheEditKey *key)
3039 /* roots have full weight allways */
3041 PTCacheEdit *edit = data->edit;
3042 ParticleSystem *psys = edit->psys;
3044 ParticleData *pa= psys->particles + point_index;
3045 pa->hair[key_index].weight = data->weightfac;
3047 (data->edit->points + point_index)->flag |= PEP_EDIT_RECALC;
3051 static void brush_smooth_get(PEData *data, float mat[][4], float imat[][4], int point_index, int key_index, PTCacheEditKey *key)
3056 sub_v3_v3v3(dvec,key->co,(key-1)->co);
3057 mul_mat3_m4_v3(mat,dvec);
3058 VECADD(data->vec,data->vec,dvec);
3063 static void brush_smooth_do(PEData *data, float mat[][4], float imat[][4], int point_index, int key_index, PTCacheEditKey *key)
3065 float vec[3], dvec[3];
3068 VECCOPY(vec,data->vec);
3069 mul_mat3_m4_v3(imat,vec);
3071 sub_v3_v3v3(dvec,key->co,(key-1)->co);
3073 VECSUB(dvec,vec,dvec);
3074 mul_v3_fl(dvec,data->smoothfac);
3076 VECADD(key->co,key->co,dvec);
3079 (data->edit->points + point_index)->flag |= PEP_EDIT_RECALC;
3082 static int brush_add(PEData *data, short number)
3084 Scene *scene= data->scene;
3085 Object *ob= data->ob;
3086 PTCacheEdit *edit = data->edit;
3087 ParticleSystem *psys= edit->psys;
3088 ParticleData *add_pars= MEM_callocN(number*sizeof(ParticleData),"ParticleData add");
3089 ParticleSystemModifierData *psmd= psys_get_modifier(ob,psys);
3090 ParticleSimulationData sim = {scene, ob, psys, psmd};
3091 ParticleEditSettings *pset= PE_settings(scene);
3092 int i, k, n= 0, totpart= psys->totpart;
3094 short dmx= 0, dmy= 0;
3095 float co1[3], co2[3], min_d, imat[4][4];
3096 float framestep, timestep= psys_get_timestep(&sim);
3097 short size= pset->brush[PE_BRUSH_ADD].size;
3098 short size2= size*size;
3100 invert_m4_m4(imat,ob->obmat);
3102 if(psys->flag & PSYS_GLOBAL_HAIR)
3105 BLI_srandom(psys->seed+data->mval[0]+data->mval[1]);
3107 /* painting onto the deformed mesh, could be an option? */
3108 if(psmd->dm->deformedOnly)
3111 dm= mesh_get_derived_deform(scene, ob, CD_MASK_BAREMESH);
3113 for(i=0; i<number; i++) {
3116 while(dmx*dmx+dmy*dmy>size2) {
3117 dmx=(short)((2.0f*BLI_frand()-1.0f)*size);
3118 dmy=(short)((2.0f*BLI_frand()-1.0f)*size);
3122 mco[0]= data->mval[0] + dmx;
3123 mco[1]= data->mval[1] + dmy;
3124 viewline(data->vc.ar, data->vc.v3d, mco, co1, co2);
3126 mul_m4_v3(imat,co1);
3127 mul_m4_v3(imat,co2);
3130 /* warning, returns the derived mesh face */
3131 if(psys_intersect_dm(scene, ob,dm,0,co1,co2,&min_d,&add_pars[n].num,add_pars[n].fuv,0,0,0,0)) {
3132 add_pars[n].num_dmcache= psys_particle_dm_face_lookup(ob,psmd->dm,add_pars[n].num,add_pars[n].fuv,NULL);
3137 int newtotpart=totpart+n;
3138 float hairmat[4][4], cur_co[3];
3140 ParticleData *pa, *new_pars= MEM_callocN(newtotpart*sizeof(ParticleData),"ParticleData new");
3141 PTCacheEditPoint *point, *new_points= MEM_callocN(newtotpart*sizeof(PTCacheEditPoint),"PTCacheEditPoint array new");
3142 PTCacheEditKey *key;
3145 /* save existing elements */
3146 memcpy(new_pars, psys->particles, totpart * sizeof(ParticleData));
3147 memcpy(new_points, edit->points, totpart * sizeof(PTCacheEditPoint));
3149 /* change old arrays to new ones */
3150 if(psys->particles) MEM_freeN(psys->particles);
3151 psys->particles= new_pars;
3153 if(edit->points) MEM_freeN(edit->points);
3154 edit->points= new_points;
3156 if(edit->mirror_cache) {
3157 MEM_freeN(edit->mirror_cache);
3158 edit->mirror_cache= NULL;
3161 /* create tree for interpolation */
3162 if(pset->flag & PE_INTERPOLATE_ADDED && psys->totpart) {
3163 tree=BLI_kdtree_new(psys->totpart);
3165 for(i=0, pa=psys->particles; i<totpart; i++, pa++) {
3166 psys_particle_on_dm(psmd->dm,psys->part->from,pa->num,pa->num_dmcache,pa->fuv,pa->foffset,cur_co,0,0,0,0,0);
3167 BLI_kdtree_insert(tree, i, cur_co, NULL);
3170 BLI_kdtree_balance(tree);
3173 edit->totpoint= psys->totpart= newtotpart;