2 * ***** BEGIN GPL LICENSE BLOCK *****
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software Foundation,
16 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 * The Original Code is Copyright (C) 2007 by Janne Karhu.
19 * All rights reserved.
21 * The Original Code is: all of this file.
23 * Contributor(s): none yet.
25 * ***** END GPL LICENSE BLOCK *****
28 /** \file blender/editors/physics/particle_edit.c
38 #include "MEM_guardedalloc.h"
40 #include "DNA_scene_types.h"
41 #include "DNA_mesh_types.h"
42 #include "DNA_meshdata_types.h"
43 #include "DNA_view3d_types.h"
44 #include "DNA_screen_types.h"
45 #include "DNA_space_types.h"
48 #include "BLI_lasso.h"
49 #include "BLI_listbase.h"
50 #include "BLI_string.h"
51 #include "BLI_kdtree.h"
53 #include "BLI_utildefines.h"
55 #include "BKE_context.h"
56 #include "BKE_depsgraph.h"
57 #include "BKE_DerivedMesh.h"
58 #include "BKE_global.h"
59 #include "BKE_object.h"
61 #include "BKE_modifier.h"
62 #include "BKE_particle.h"
63 #include "BKE_report.h"
64 #include "BKE_bvhutils.h"
65 #include "BKE_pointcache.h"
68 #include "BIF_glutil.h"
70 #include "ED_object.h"
71 #include "ED_physics.h"
73 #include "ED_particle.h"
74 #include "ED_view3d.h"
76 #include "UI_resources.h"
81 #include "RNA_access.h"
82 #include "RNA_define.h"
84 #include "physics_intern.h"
86 void PE_create_particle_edit(Scene *scene, Object *ob, PointCache *cache, ParticleSystem *psys);
87 void PTCacheUndo_clear(PTCacheEdit *edit);
88 void recalc_lengths(PTCacheEdit *edit);
89 void recalc_emitter_field(Object *ob, ParticleSystem *psys);
90 void update_world_cos(Object *ob, PTCacheEdit *edit);
92 #define KEY_K PTCacheEditKey *key; int k
93 #define POINT_P PTCacheEditPoint *point; int p
94 #define LOOP_POINTS for (p=0, point=edit->points; p<edit->totpoint; p++, point++)
95 #define LOOP_VISIBLE_POINTS for (p=0, point=edit->points; p<edit->totpoint; p++, point++) if (!(point->flag & PEP_HIDE))
96 #define LOOP_SELECTED_POINTS for (p=0, point=edit->points; p<edit->totpoint; p++, point++) if (point_is_selected(point))
97 #define LOOP_UNSELECTED_POINTS for (p=0, point=edit->points; p<edit->totpoint; p++, point++) if (!point_is_selected(point))
98 #define LOOP_EDITED_POINTS for (p=0, point=edit->points; p<edit->totpoint; p++, point++) if (point->flag & PEP_EDIT_RECALC)
99 #define LOOP_TAGGED_POINTS for (p=0, point=edit->points; p<edit->totpoint; p++, point++) if (point->flag & PEP_TAG)
100 #define LOOP_KEYS for (k=0, key=point->keys; k<point->totkey; k++, key++)
101 #define LOOP_VISIBLE_KEYS for (k=0, key=point->keys; k<point->totkey; k++, key++) if (!(key->flag & PEK_HIDE))
102 #define LOOP_SELECTED_KEYS for (k=0, key=point->keys; k<point->totkey; k++, key++) if ((key->flag & PEK_SELECT) && !(key->flag & PEK_HIDE))
103 #define LOOP_TAGGED_KEYS for (k=0, key=point->keys; k<point->totkey; k++, key++) if (key->flag & PEK_TAG)
105 #define KEY_WCO ((key->flag & PEK_USE_WCO) ? key->world_co : key->co)
107 /**************************** utilities *******************************/
109 int PE_poll(bContext *C)
111 Scene *scene= CTX_data_scene(C);
112 Object *ob= CTX_data_active_object(C);
114 if (!scene || !ob || !(ob->mode & OB_MODE_PARTICLE_EDIT))
117 return (PE_get_current(scene, ob) != NULL);
120 int PE_hair_poll(bContext *C)
122 Scene *scene= CTX_data_scene(C);
123 Object *ob= CTX_data_active_object(C);
126 if (!scene || !ob || !(ob->mode & OB_MODE_PARTICLE_EDIT))
129 edit= PE_get_current(scene, ob);
131 return (edit && edit->psys);
134 int PE_poll_view3d(bContext *C)
136 ScrArea *sa = CTX_wm_area(C);
137 ARegion *ar = CTX_wm_region(C);
139 return (PE_poll(C) &&
140 (sa && sa->spacetype == SPACE_VIEW3D) &&
141 (ar && ar->regiontype == RGN_TYPE_WINDOW));
144 void PE_free_ptcache_edit(PTCacheEdit *edit)
150 PTCacheUndo_clear(edit);
155 MEM_freeN(point->keys);
158 MEM_freeN(edit->points);
161 if (edit->mirror_cache)
162 MEM_freeN(edit->mirror_cache);
164 if (edit->emitter_cosnos) {
165 MEM_freeN(edit->emitter_cosnos);
166 edit->emitter_cosnos= 0;
169 if (edit->emitter_field) {
170 BLI_kdtree_free(edit->emitter_field);
171 edit->emitter_field= 0;
174 psys_free_path_cache(edit->psys, edit);
179 /************************************************/
180 /* Edit Mode Helpers */
181 /************************************************/
183 int PE_start_edit(PTCacheEdit *edit)
188 edit->psys->flag |= PSYS_EDITED;
195 ParticleEditSettings *PE_settings(Scene *scene)
197 return scene->toolsettings ? &scene->toolsettings->particle : NULL;
200 static float pe_brush_size_get(const Scene *UNUSED(scene), ParticleBrushData *brush)
202 // here we can enable unified brush size, needs more work...
203 // UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings;
204 // float size = (ups->flag & UNIFIED_PAINT_SIZE) ? ups->size : brush->size;
206 return brush->size * U.pixelsize;
210 /* always gets at least the first particlesystem even if PSYS_CURRENT flag is not set
212 * note: this function runs on poll, therefor it can runs many times a second
214 static PTCacheEdit *pe_get_current(Scene *scene, Object *ob, int create)
216 ParticleEditSettings *pset= PE_settings(scene);
217 PTCacheEdit *edit = NULL;
221 if (pset==NULL || ob==NULL)
227 BKE_ptcache_ids_from_object(&pidlist, ob, NULL, 0);
229 /* in the case of only one editable thing, set pset->edittype accordingly */
230 if (BLI_listbase_is_single(&pidlist)) {
233 case PTCACHE_TYPE_PARTICLES:
234 pset->edittype = PE_TYPE_PARTICLES;
236 case PTCACHE_TYPE_SOFTBODY:
237 pset->edittype = PE_TYPE_SOFTBODY;
239 case PTCACHE_TYPE_CLOTH:
240 pset->edittype = PE_TYPE_CLOTH;
245 for (pid=pidlist.first; pid; pid=pid->next) {
246 if (pset->edittype == PE_TYPE_PARTICLES && pid->type == PTCACHE_TYPE_PARTICLES) {
247 ParticleSystem *psys = pid->calldata;
249 if (psys->flag & PSYS_CURRENT) {
250 if (psys->part && psys->part->type == PART_HAIR) {
251 if (psys->flag & PSYS_HAIR_DYNAMICS && psys->pointcache->flag & PTCACHE_BAKED) {
252 if (create && !psys->pointcache->edit)
253 PE_create_particle_edit(scene, ob, pid->cache, NULL);
254 edit = pid->cache->edit;
257 if (create && !psys->edit && psys->flag & PSYS_HAIR_DONE)
258 PE_create_particle_edit(scene, ob, NULL, psys);
263 if (create && pid->cache->flag & PTCACHE_BAKED && !pid->cache->edit)
264 PE_create_particle_edit(scene, ob, pid->cache, psys);
265 edit = pid->cache->edit;
271 else if (pset->edittype == PE_TYPE_SOFTBODY && pid->type == PTCACHE_TYPE_SOFTBODY) {
272 if (create && pid->cache->flag & PTCACHE_BAKED && !pid->cache->edit) {
273 pset->flag |= PE_FADE_TIME;
274 // NICE TO HAVE but doesn't work: pset->brushtype = PE_BRUSH_COMB;
275 PE_create_particle_edit(scene, ob, pid->cache, NULL);
277 edit = pid->cache->edit;
280 else if (pset->edittype == PE_TYPE_CLOTH && pid->type == PTCACHE_TYPE_CLOTH) {
281 if (create && pid->cache->flag & PTCACHE_BAKED && !pid->cache->edit) {
282 pset->flag |= PE_FADE_TIME;
283 // NICE TO HAVE but doesn't work: pset->brushtype = PE_BRUSH_COMB;
284 PE_create_particle_edit(scene, ob, pid->cache, NULL);
286 edit = pid->cache->edit;
294 BLI_freelistN(&pidlist);
299 PTCacheEdit *PE_get_current(Scene *scene, Object *ob)
301 return pe_get_current(scene, ob, 0);
304 PTCacheEdit *PE_create_current(Scene *scene, Object *ob)
306 return pe_get_current(scene, ob, 1);
309 void PE_current_changed(Scene *scene, Object *ob)
311 if (ob->mode == OB_MODE_PARTICLE_EDIT)
312 PE_create_current(scene, ob);
315 void PE_hide_keys_time(Scene *scene, PTCacheEdit *edit, float cfra)
317 ParticleEditSettings *pset=PE_settings(scene);
321 if (pset->flag & PE_FADE_TIME && pset->selectmode==SCE_SELECT_POINT) {
324 if (fabsf(cfra - *key->time) < pset->fade_frames)
325 key->flag &= ~PEK_HIDE;
327 key->flag |= PEK_HIDE;
328 //key->flag &= ~PEK_SELECT;
336 key->flag &= ~PEK_HIDE;
342 static int pe_x_mirror(Object *ob)
344 if (ob->type == OB_MESH)
345 return (((Mesh *)ob->data)->editflag & ME_EDIT_MIRROR_X);
350 /****************** common struct passed to callbacks ******************/
352 typedef struct PEData {
360 BVHTreeFromMesh shape_bvh;
383 int select_toggle_action;
386 static void PE_set_data(bContext *C, PEData *data)
388 memset(data, 0, sizeof(*data));
390 data->scene= CTX_data_scene(C);
391 data->ob= CTX_data_active_object(C);
392 data->edit= PE_get_current(data->scene, data->ob);
395 static void PE_set_view3d_data(bContext *C, PEData *data)
397 PE_set_data(C, data);
399 view3d_set_viewcontext(C, &data->vc);
400 /* note, the object argument means the modelview matrix does not account for the objects matrix, use viewmat rather than (obmat * viewmat) */
401 view3d_get_transformation(data->vc.ar, data->vc.rv3d, NULL, &data->mats);
403 if (V3D_IS_ZBUF(data->vc.v3d)) {
404 if (data->vc.v3d->flag & V3D_INVALID_BACKBUF) {
405 /* needed or else the draw matrix can be incorrect */
406 view3d_operator_needs_opengl(C);
408 ED_view3d_backbuf_validate(&data->vc);
409 /* we may need to force an update here by setting the rv3d as dirty
410 * for now it seems ok, but take care!:
411 * rv3d->depths->dirty = 1; */
412 ED_view3d_depth_update(data->vc.ar);
417 static bool PE_create_shape_tree(PEData *data, Object *shapeob)
419 DerivedMesh *dm = shapeob->derivedFinal;
421 memset(&data->shape_bvh, 0, sizeof(data->shape_bvh));
427 DM_ensure_looptri(dm);
428 return (bvhtree_from_mesh_looptri(&data->shape_bvh, dm, 0.0f, 4, 8) != NULL);
431 static void PE_free_shape_tree(PEData *data)
433 free_bvhtree_from_mesh(&data->shape_bvh);
436 /*************************** selection utilities *******************************/
438 static bool key_test_depth(PEData *data, const float co[3], const int screen_co[2])
440 View3D *v3d= data->vc.v3d;
441 ViewDepths *vd = data->vc.rv3d->depths;
446 if (!V3D_IS_ZBUF(v3d))
449 /* used to calculate here but all callers have the screen_co already, so pass as arg */
451 if (ED_view3d_project_int_global(data->vc.ar, co, screen_co,
452 V3D_PROJ_TEST_CLIP_BB | V3D_PROJ_TEST_CLIP_WIN | V3D_PROJ_TEST_CLIP_NEAR) != V3D_PROJ_RET_OK)
458 gluProject(co[0], co[1], co[2], data->mats.modelview, data->mats.projection,
459 (GLint *)data->mats.viewport, &ux, &uy, &uz);
461 /* check if screen_co is within bounds because brush_cut uses out of screen coords */
462 if (screen_co[0] >= 0 && screen_co[0] < vd->w && screen_co[1] >= 0 && screen_co[1] < vd->h) {
463 BLI_assert(vd && vd->depths);
464 /* we know its not clipped */
465 depth = vd->depths[screen_co[1] * vd->w + screen_co[0]];
470 if ((float)uz - 0.00001f > depth)
476 static bool key_inside_circle(PEData *data, float rad, const float co[3], float *distance)
481 /* TODO, should this check V3D_PROJ_TEST_CLIP_BB too? */
482 if (ED_view3d_project_int_global(data->vc.ar, co, screen_co, V3D_PROJ_TEST_CLIP_WIN) != V3D_PROJ_RET_OK) {
486 dx= data->mval[0] - screen_co[0];
487 dy= data->mval[1] - screen_co[1];
488 dist = sqrtf(dx * dx + dy * dy);
493 if (key_test_depth(data, co, screen_co)) {
503 static bool key_inside_rect(PEData *data, const float co[3])
507 if (ED_view3d_project_int_global(data->vc.ar, co, screen_co, V3D_PROJ_TEST_CLIP_WIN) != V3D_PROJ_RET_OK) {
511 if (screen_co[0] > data->rect->xmin && screen_co[0] < data->rect->xmax &&
512 screen_co[1] > data->rect->ymin && screen_co[1] < data->rect->ymax)
514 return key_test_depth(data, co, screen_co);
520 static bool key_inside_test(PEData *data, const float co[3])
523 return key_inside_circle(data, data->rad, co, NULL);
525 return key_inside_rect(data, co);
528 static bool point_is_selected(PTCacheEditPoint *point)
532 if (point->flag & PEP_HIDE)
542 /*************************** iterators *******************************/
544 typedef void (*ForPointFunc)(PEData *data, int point_index);
545 typedef void (*ForKeyFunc)(PEData *data, int point_index, int key_index);
546 typedef void (*ForKeyMatFunc)(PEData *data, float mat[4][4], float imat[4][4], int point_index, int key_index, PTCacheEditKey *key);
548 static void for_mouse_hit_keys(PEData *data, ForKeyFunc func, int nearest)
550 ParticleEditSettings *pset= PE_settings(data->scene);
551 PTCacheEdit *edit= data->edit;
553 int nearest_point, nearest_key;
554 float dist= data->rad;
556 /* in path select mode we have no keys */
557 if (pset->selectmode==SCE_SELECT_PATH)
563 LOOP_VISIBLE_POINTS {
564 if (pset->selectmode == SCE_SELECT_END) {
566 /* only do end keys */
567 key= point->keys + point->totkey-1;
570 if (key_inside_circle(data, dist, KEY_WCO, &dist)) {
572 nearest_key= point->totkey-1;
575 else if (key_inside_test(data, KEY_WCO))
576 func(data, p, point->totkey-1);
583 if (key_inside_circle(data, dist, KEY_WCO, &dist)) {
588 else if (key_inside_test(data, KEY_WCO))
594 /* do nearest only */
595 if (nearest && nearest_point > -1)
596 func(data, nearest_point, nearest_key);
599 static void foreach_mouse_hit_point(PEData *data, ForPointFunc func, int selected)
601 ParticleEditSettings *pset= PE_settings(data->scene);
602 PTCacheEdit *edit= data->edit;
605 /* all is selected in path mode */
606 if (pset->selectmode==SCE_SELECT_PATH)
609 LOOP_VISIBLE_POINTS {
610 if (pset->selectmode==SCE_SELECT_END) {
612 /* only do end keys */
613 key= point->keys + point->totkey - 1;
615 if (selected==0 || key->flag & PEK_SELECT)
616 if (key_inside_circle(data, data->rad, KEY_WCO, &data->dist))
623 if (selected==0 || key->flag & PEK_SELECT) {
624 if (key_inside_circle(data, data->rad, KEY_WCO, &data->dist)) {
634 static void foreach_mouse_hit_key(PEData *data, ForKeyMatFunc func, int selected)
636 PTCacheEdit *edit = data->edit;
637 ParticleSystem *psys = edit->psys;
638 ParticleSystemModifierData *psmd = NULL;
639 ParticleEditSettings *pset= PE_settings(data->scene);
641 float mat[4][4], imat[4][4];
647 psmd= psys_get_modifier(data->ob, edit->psys);
649 /* all is selected in path mode */
650 if (pset->selectmode==SCE_SELECT_PATH)
653 LOOP_VISIBLE_POINTS {
654 if (pset->selectmode==SCE_SELECT_END) {
656 /* only do end keys */
657 key= point->keys + point->totkey-1;
659 if (selected==0 || key->flag & PEK_SELECT) {
660 if (key_inside_circle(data, data->rad, KEY_WCO, &data->dist)) {
661 if (edit->psys && !(edit->psys->flag & PSYS_GLOBAL_HAIR)) {
662 psys_mat_hair_to_global(data->ob, psmd->dm_final, psys->part->from, psys->particles + p, mat);
663 invert_m4_m4(imat, mat);
666 func(data, mat, imat, p, point->totkey-1, key);
674 if (selected==0 || key->flag & PEK_SELECT) {
675 if (key_inside_circle(data, data->rad, KEY_WCO, &data->dist)) {
676 if (edit->psys && !(edit->psys->flag & PSYS_GLOBAL_HAIR)) {
677 psys_mat_hair_to_global(data->ob, psmd->dm_final, psys->part->from, psys->particles + p, mat);
678 invert_m4_m4(imat, mat);
681 func(data, mat, imat, p, k, key);
689 static void foreach_selected_point(PEData *data, ForPointFunc func)
691 PTCacheEdit *edit = data->edit;
694 LOOP_SELECTED_POINTS {
699 static void foreach_selected_key(PEData *data, ForKeyFunc func)
701 PTCacheEdit *edit = data->edit;
704 LOOP_VISIBLE_POINTS {
711 static void foreach_point(PEData *data, ForPointFunc func)
713 PTCacheEdit *edit = data->edit;
721 static int count_selected_keys(Scene *scene, PTCacheEdit *edit)
723 ParticleEditSettings *pset= PE_settings(scene);
727 LOOP_VISIBLE_POINTS {
728 if (pset->selectmode==SCE_SELECT_POINT) {
733 else if (pset->selectmode==SCE_SELECT_END) {
735 key = point->keys + point->totkey - 1;
736 if (key->flag & PEK_SELECT)
745 /************************************************/
746 /* Particle Edit Mirroring */
747 /************************************************/
749 static void PE_update_mirror_cache(Object *ob, ParticleSystem *psys)
752 ParticleSystemModifierData *psmd;
754 KDTreeNearest nearest;
757 float mat[4][4], co[3];
761 psmd= psys_get_modifier(ob, psys);
762 totpart= psys->totpart;
767 tree= BLI_kdtree_new(totpart);
769 /* insert particles into kd tree */
772 psys_mat_hair_to_orco(ob, psmd->dm_final, psys->part->from, pa, mat);
773 copy_v3_v3(co, key->co);
775 BLI_kdtree_insert(tree, p, co);
778 BLI_kdtree_balance(tree);
780 /* lookup particles and set in mirror cache */
781 if (!edit->mirror_cache)
782 edit->mirror_cache= MEM_callocN(sizeof(int)*totpart, "PE mirror cache");
786 psys_mat_hair_to_orco(ob, psmd->dm_final, psys->part->from, pa, mat);
787 copy_v3_v3(co, key->co);
791 index= BLI_kdtree_find_nearest(tree, co, &nearest);
793 /* this needs a custom threshold still, duplicated for editmode mirror */
794 if (index != -1 && index != p && (nearest.dist <= 0.0002f))
795 edit->mirror_cache[p] = index;
797 edit->mirror_cache[p] = -1;
800 /* make sure mirrors are in two directions */
802 if (edit->mirror_cache[p]) {
803 index= edit->mirror_cache[p];
804 if (edit->mirror_cache[index] != p)
805 edit->mirror_cache[p] = -1;
809 BLI_kdtree_free(tree);
812 static void PE_mirror_particle(Object *ob, DerivedMesh *dm, ParticleSystem *psys, ParticleData *pa, ParticleData *mpa)
814 HairKey *hkey, *mhkey;
815 PTCacheEditPoint *point, *mpoint;
816 PTCacheEditKey *key, *mkey;
818 float mat[4][4], mmat[4][4], immat[4][4];
822 i= pa - psys->particles;
824 /* find mirrored particle if needed */
826 if (!edit->mirror_cache)
827 PE_update_mirror_cache(ob, psys);
829 if (!edit->mirror_cache)
830 return; /* something went wrong! */
832 mi= edit->mirror_cache[i];
835 mpa= psys->particles + mi;
838 mi= mpa - psys->particles;
840 point = edit->points + i;
841 mpoint = edit->points + mi;
843 /* make sure they have the same amount of keys */
844 if (pa->totkey != mpa->totkey) {
845 if (mpa->hair) MEM_freeN(mpa->hair);
846 if (mpoint->keys) MEM_freeN(mpoint->keys);
848 mpa->hair= MEM_dupallocN(pa->hair);
849 mpa->totkey= pa->totkey;
850 mpoint->keys= MEM_dupallocN(point->keys);
851 mpoint->totkey= point->totkey;
855 for (k=0; k<mpa->totkey; k++, mkey++, mhkey++) {
857 mkey->time= &mhkey->time;
858 mkey->flag &= ~PEK_SELECT;
862 /* mirror positions and tags */
863 psys_mat_hair_to_orco(ob, dm, psys->part->from, pa, mat);
864 psys_mat_hair_to_orco(ob, dm, psys->part->from, mpa, mmat);
865 invert_m4_m4(immat, mmat);
871 for (k=0; k<pa->totkey; k++, hkey++, mhkey++, key++, mkey++) {
872 copy_v3_v3(mhkey->co, hkey->co);
873 mul_m4_v3(mat, mhkey->co);
874 mhkey->co[0] = -mhkey->co[0];
875 mul_m4_v3(immat, mhkey->co);
877 if (key->flag & PEK_TAG)
878 mkey->flag |= PEK_TAG;
880 mkey->length = key->length;
883 if (point->flag & PEP_TAG)
884 mpoint->flag |= PEP_TAG;
885 if (point->flag & PEP_EDIT_RECALC)
886 mpoint->flag |= PEP_EDIT_RECALC;
889 static void PE_apply_mirror(Object *ob, ParticleSystem *psys)
892 ParticleSystemModifierData *psmd;
899 psmd= psys_get_modifier(ob, psys);
904 if (!edit->mirror_cache)
905 PE_update_mirror_cache(ob, psys);
907 if (!edit->mirror_cache)
908 return; /* something went wrong */
910 /* we delay settings the PARS_EDIT_RECALC for mirrored particles
911 * to avoid doing mirror twice */
913 if (point->flag & PEP_EDIT_RECALC) {
914 PE_mirror_particle(ob, psmd->dm_final, psys, psys->particles + p, NULL);
916 if (edit->mirror_cache[p] != -1)
917 edit->points[edit->mirror_cache[p]].flag &= ~PEP_EDIT_RECALC;
922 if (point->flag & PEP_EDIT_RECALC)
923 if (edit->mirror_cache[p] != -1)
924 edit->points[edit->mirror_cache[p]].flag |= PEP_EDIT_RECALC;
928 /************************************************/
929 /* Edit Calculation */
930 /************************************************/
931 /* tries to stop edited particles from going through the emitter's surface */
932 static void pe_deflect_emitter(Scene *scene, Object *ob, PTCacheEdit *edit)
934 ParticleEditSettings *pset= PE_settings(scene);
935 ParticleSystem *psys;
936 ParticleSystemModifierData *psmd;
939 float *vec, *nor, dvec[3], dot, dist_1st=0.0f;
940 float hairimat[4][4], hairmat[4][4];
941 const float dist = ED_view3d_select_dist_px() * 0.01f;
943 if (edit==NULL || edit->psys==NULL || (pset->flag & PE_DEFLECT_EMITTER)==0 || (edit->psys->flag & PSYS_GLOBAL_HAIR))
947 psmd = psys_get_modifier(ob, psys);
953 psys_mat_hair_to_object(ob, psmd->dm_final, psys->part->from, psys->particles + p, hairmat);
956 mul_m4_v3(hairmat, key->co);
961 dist_1st = len_v3v3((key+1)->co, key->co);
962 dist_1st *= dist * pset->emitterdist;
965 index= BLI_kdtree_find_nearest(edit->emitter_field, key->co, NULL);
967 vec=edit->emitter_cosnos +index*6;
970 sub_v3_v3v3(dvec, key->co, vec);
972 dot=dot_v3v3(dvec, nor);
973 copy_v3_v3(dvec, nor);
978 mul_v3_fl(dvec, dist_1st-dot);
979 add_v3_v3(key->co, dvec);
984 mul_v3_fl(dvec, dist_1st-dot);
985 add_v3_v3(key->co, dvec);
992 invert_m4_m4(hairimat, hairmat);
995 mul_m4_v3(hairimat, key->co);
999 /* force set distances between neighboring keys */
1000 static void PE_apply_lengths(Scene *scene, PTCacheEdit *edit)
1003 ParticleEditSettings *pset=PE_settings(scene);
1007 if (edit==0 || (pset->flag & PE_KEEP_LENGTHS)==0)
1010 if (edit->psys && edit->psys->flag & PSYS_GLOBAL_HAIR)
1013 LOOP_EDITED_POINTS {
1016 sub_v3_v3v3(dv1, key->co, (key - 1)->co);
1018 mul_v3_fl(dv1, (key - 1)->length);
1019 add_v3_v3v3(key->co, (key - 1)->co, dv1);
1024 /* try to find a nice solution to keep distances between neighboring keys */
1025 static void pe_iterate_lengths(Scene *scene, PTCacheEdit *edit)
1027 ParticleEditSettings *pset=PE_settings(scene);
1029 PTCacheEditKey *key;
1032 float dv0[3] = {0.0f, 0.0f, 0.0f};
1033 float dv1[3] = {0.0f, 0.0f, 0.0f};
1034 float dv2[3] = {0.0f, 0.0f, 0.0f};
1036 if (edit==0 || (pset->flag & PE_KEEP_LENGTHS)==0)
1039 if (edit->psys && edit->psys->flag & PSYS_GLOBAL_HAIR)
1042 LOOP_EDITED_POINTS {
1043 for (j=1; j<point->totkey; j++) {
1044 float mul= 1.0f / (float)point->totkey;
1046 if (pset->flag & PE_LOCK_FIRST) {
1047 key= point->keys + 1;
1049 dv1[0] = dv1[1] = dv1[2] = 0.0;
1054 dv0[0] = dv0[1] = dv0[2] = 0.0;
1057 for (; k<point->totkey; k++, key++) {
1059 sub_v3_v3v3(dv0, (key - 1)->co, key->co);
1060 tlen= normalize_v3(dv0);
1061 mul_v3_fl(dv0, (mul * (tlen - (key - 1)->length)));
1064 if (k < point->totkey - 1) {
1065 sub_v3_v3v3(dv2, (key + 1)->co, key->co);
1066 tlen= normalize_v3(dv2);
1067 mul_v3_fl(dv2, mul * (tlen - key->length));
1071 add_v3_v3((key-1)->co, dv1);
1074 add_v3_v3v3(dv1, dv0, dv2);
1079 /* set current distances to be kept between neighbouting keys */
1080 void recalc_lengths(PTCacheEdit *edit)
1087 LOOP_EDITED_POINTS {
1089 for (k=0; k<point->totkey-1; k++, key++) {
1090 key->length= len_v3v3(key->co, (key + 1)->co);
1095 /* calculate a tree for finding nearest emitter's vertice */
1096 void recalc_emitter_field(Object *ob, ParticleSystem *psys)
1098 DerivedMesh *dm=psys_get_modifier(ob, psys)->dm_final;
1099 PTCacheEdit *edit= psys->edit;
1101 int i, totface /*, totvert*/;
1106 if (edit->emitter_cosnos)
1107 MEM_freeN(edit->emitter_cosnos);
1109 BLI_kdtree_free(edit->emitter_field);
1111 totface=dm->getNumTessFaces(dm);
1112 /*totvert=dm->getNumVerts(dm);*/ /*UNSUED*/
1114 edit->emitter_cosnos=MEM_callocN(totface*6*sizeof(float), "emitter cosnos");
1116 edit->emitter_field= BLI_kdtree_new(totface);
1118 vec=edit->emitter_cosnos;
1121 for (i=0; i<totface; i++, vec+=6, nor+=6) {
1122 MFace *mface=dm->getTessFaceData(dm, i, CD_MFACE);
1125 mvert=dm->getVertData(dm, mface->v1, CD_MVERT);
1126 copy_v3_v3(vec, mvert->co);
1127 VECCOPY(nor, mvert->no);
1129 mvert=dm->getVertData(dm, mface->v2, CD_MVERT);
1130 add_v3_v3v3(vec, vec, mvert->co);
1131 VECADD(nor, nor, mvert->no);
1133 mvert=dm->getVertData(dm, mface->v3, CD_MVERT);
1134 add_v3_v3v3(vec, vec, mvert->co);
1135 VECADD(nor, nor, mvert->no);
1138 mvert=dm->getVertData(dm, mface->v4, CD_MVERT);
1139 add_v3_v3v3(vec, vec, mvert->co);
1140 VECADD(nor, nor, mvert->no);
1142 mul_v3_fl(vec, 0.25);
1145 mul_v3_fl(vec, 1.0f / 3.0f);
1149 BLI_kdtree_insert(edit->emitter_field, i, vec);
1152 BLI_kdtree_balance(edit->emitter_field);
1155 static void PE_update_selection(Scene *scene, Object *ob, int useflag)
1157 PTCacheEdit *edit= PE_get_current(scene, ob);
1161 /* flag all particles to be updated if not using flag */
1164 point->flag |= PEP_EDIT_RECALC;
1166 /* flush edit key flag to hair key flag to preserve selection
1168 if (edit->psys) LOOP_POINTS {
1169 hkey = edit->psys->particles[p].hair;
1171 hkey->editflag= key->flag;
1176 psys_cache_edit_paths(scene, ob, edit, CFRA, G.is_rendering);
1179 /* disable update flag */
1181 point->flag &= ~PEP_EDIT_RECALC;
1184 void update_world_cos(Object *ob, PTCacheEdit *edit)
1186 ParticleSystem *psys = edit->psys;
1187 ParticleSystemModifierData *psmd= psys_get_modifier(ob, psys);
1189 float hairmat[4][4];
1191 if (psys==0 || psys->edit==0 || psmd->dm_final==NULL)
1195 if (!(psys->flag & PSYS_GLOBAL_HAIR))
1196 psys_mat_hair_to_global(ob, psmd->dm_final, psys->part->from, psys->particles+p, hairmat);
1199 copy_v3_v3(key->world_co, key->co);
1200 if (!(psys->flag & PSYS_GLOBAL_HAIR))
1201 mul_m4_v3(hairmat, key->world_co);
1205 static void update_velocities(PTCacheEdit *edit)
1207 /*TODO: get frs_sec properly */
1208 float vec1[3], vec2[3], frs_sec, dfra;
1211 /* hair doesn't use velocities */
1212 if (edit->psys || !edit->points || !edit->points->keys->vel)
1215 frs_sec = edit->pid.flag & PTCACHE_VEL_PER_SEC ? 25.0f : 1.0f;
1217 LOOP_EDITED_POINTS {
1220 dfra = *(key+1)->time - *key->time;
1225 sub_v3_v3v3(key->vel, (key+1)->co, key->co);
1227 if (point->totkey>2) {
1228 sub_v3_v3v3(vec1, (key+1)->co, (key+2)->co);
1229 project_v3_v3v3(vec2, vec1, key->vel);
1230 sub_v3_v3v3(vec2, vec1, vec2);
1231 madd_v3_v3fl(key->vel, vec2, 0.5f);
1234 else if (k==point->totkey-1) {
1235 dfra = *key->time - *(key-1)->time;
1240 sub_v3_v3v3(key->vel, key->co, (key-1)->co);
1242 if (point->totkey>2) {
1243 sub_v3_v3v3(vec1, (key-2)->co, (key-1)->co);
1244 project_v3_v3v3(vec2, vec1, key->vel);
1245 sub_v3_v3v3(vec2, vec1, vec2);
1246 madd_v3_v3fl(key->vel, vec2, 0.5f);
1250 dfra = *(key+1)->time - *(key-1)->time;
1255 sub_v3_v3v3(key->vel, (key+1)->co, (key-1)->co);
1257 mul_v3_fl(key->vel, frs_sec/dfra);
1262 void PE_update_object(Scene *scene, Object *ob, int useflag)
1264 /* use this to do partial particle updates, not usable when adding or
1265 * removing, then a full redo is necessary and calling this may crash */
1266 ParticleEditSettings *pset= PE_settings(scene);
1267 PTCacheEdit *edit = PE_get_current(scene, ob);
1273 /* flag all particles to be updated if not using flag */
1276 point->flag |= PEP_EDIT_RECALC;
1279 /* do post process on particle edit keys */
1280 pe_iterate_lengths(scene, edit);
1281 pe_deflect_emitter(scene, ob, edit);
1282 PE_apply_lengths(scene, edit);
1283 if (pe_x_mirror(ob))
1284 PE_apply_mirror(ob, edit->psys);
1286 update_world_cos(ob, edit);
1287 if (pset->flag & PE_AUTO_VELOCITY)
1288 update_velocities(edit);
1289 PE_hide_keys_time(scene, edit, CFRA);
1291 /* regenerate path caches */
1292 psys_cache_edit_paths(scene, ob, edit, CFRA, G.is_rendering);
1294 /* disable update flag */
1296 point->flag &= ~PEP_EDIT_RECALC;
1300 edit->psys->flag &= ~PSYS_HAIR_UPDATED;
1303 /************************************************/
1304 /* Edit Selections */
1305 /************************************************/
1307 /*-----selection callbacks-----*/
1309 static void select_key(PEData *data, int point_index, int key_index)
1311 PTCacheEdit *edit = data->edit;
1312 PTCacheEditPoint *point = edit->points + point_index;
1313 PTCacheEditKey *key = point->keys + key_index;
1316 key->flag |= PEK_SELECT;
1318 key->flag &= ~PEK_SELECT;
1320 point->flag |= PEP_EDIT_RECALC;
1323 static void select_keys(PEData *data, int point_index, int UNUSED(key_index))
1325 PTCacheEdit *edit = data->edit;
1326 PTCacheEditPoint *point = edit->points + point_index;
1331 key->flag |= PEK_SELECT;
1333 key->flag &= ~PEK_SELECT;
1336 point->flag |= PEP_EDIT_RECALC;
1339 static void extend_key_select(PEData *data, int point_index, int key_index)
1341 PTCacheEdit *edit = data->edit;
1342 PTCacheEditPoint *point = edit->points + point_index;
1343 PTCacheEditKey *key = point->keys + key_index;
1345 key->flag |= PEK_SELECT;
1346 point->flag |= PEP_EDIT_RECALC;
1349 static void deselect_key_select(PEData *data, int point_index, int key_index)
1351 PTCacheEdit *edit = data->edit;
1352 PTCacheEditPoint *point = edit->points + point_index;
1353 PTCacheEditKey *key = point->keys + key_index;
1355 key->flag &= ~PEK_SELECT;
1356 point->flag |= PEP_EDIT_RECALC;
1359 static void toggle_key_select(PEData *data, int point_index, int key_index)
1361 PTCacheEdit *edit = data->edit;
1362 PTCacheEditPoint *point = edit->points + point_index;
1363 PTCacheEditKey *key = point->keys + key_index;
1365 key->flag ^= PEK_SELECT;
1366 point->flag |= PEP_EDIT_RECALC;
1369 /************************ de select all operator ************************/
1371 static void select_action_apply(PTCacheEditPoint *point, PTCacheEditKey *key, int action)
1375 if ((key->flag & PEK_SELECT) == 0) {
1376 key->flag |= PEK_SELECT;
1377 point->flag |= PEP_EDIT_RECALC;
1381 if (key->flag & PEK_SELECT) {
1382 key->flag &= ~PEK_SELECT;
1383 point->flag |= PEP_EDIT_RECALC;
1387 if ((key->flag & PEK_SELECT) == 0) {
1388 key->flag |= PEK_SELECT;
1389 point->flag |= PEP_EDIT_RECALC;
1392 key->flag &= ~PEK_SELECT;
1393 point->flag |= PEP_EDIT_RECALC;
1399 static int pe_select_all_exec(bContext *C, wmOperator *op)
1401 Scene *scene= CTX_data_scene(C);
1402 Object *ob= CTX_data_active_object(C);
1403 PTCacheEdit *edit= PE_get_current(scene, ob);
1405 int action = RNA_enum_get(op->ptr, "action");
1407 if (action == SEL_TOGGLE) {
1408 action = SEL_SELECT;
1409 LOOP_VISIBLE_POINTS {
1410 LOOP_SELECTED_KEYS {
1411 action = SEL_DESELECT;
1415 if (action == SEL_DESELECT)
1420 LOOP_VISIBLE_POINTS {
1422 select_action_apply(point, key, action);
1426 PE_update_selection(scene, ob, 1);
1427 WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_SELECTED, ob);
1429 return OPERATOR_FINISHED;
1432 void PARTICLE_OT_select_all(wmOperatorType *ot)
1435 ot->name = "(De)select All";
1436 ot->idname = "PARTICLE_OT_select_all";
1437 ot->description = "(De)select all particles' keys";
1440 ot->exec = pe_select_all_exec;
1444 ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
1446 WM_operator_properties_select_all(ot);
1449 /************************ pick select operator ************************/
1451 int PE_mouse_particles(bContext *C, const int mval[2], bool extend, bool deselect, bool toggle)
1454 Scene *scene= CTX_data_scene(C);
1455 Object *ob= CTX_data_active_object(C);
1456 PTCacheEdit *edit= PE_get_current(scene, ob);
1459 if (!PE_start_edit(edit))
1460 return OPERATOR_CANCELLED;
1462 if (!extend && !deselect && !toggle) {
1463 LOOP_VISIBLE_POINTS {
1464 LOOP_SELECTED_KEYS {
1465 key->flag &= ~PEK_SELECT;
1466 point->flag |= PEP_EDIT_RECALC;
1471 PE_set_view3d_data(C, &data);
1473 data.rad = ED_view3d_select_dist_px();
1475 /* 1 = nearest only */
1477 for_mouse_hit_keys(&data, extend_key_select, 1);
1479 for_mouse_hit_keys(&data, deselect_key_select, 1);
1481 for_mouse_hit_keys(&data, toggle_key_select, 1);
1483 PE_update_selection(scene, ob, 1);
1484 WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_SELECTED, data.ob);
1486 return OPERATOR_FINISHED;
1489 /************************ select root operator ************************/
1491 static void select_root(PEData *data, int point_index)
1493 PTCacheEditPoint *point = data->edit->points + point_index;
1494 PTCacheEditKey *key = point->keys;
1496 if (point->flag & PEP_HIDE)
1499 if (data->select_action != SEL_TOGGLE)
1500 select_action_apply(point, key, data->select_action);
1501 else if (key->flag & PEK_SELECT)
1502 data->select_toggle_action = SEL_DESELECT;
1505 static int select_roots_exec(bContext *C, wmOperator *op)
1508 int action = RNA_enum_get(op->ptr, "action");
1510 PE_set_data(C, &data);
1512 if (action == SEL_TOGGLE) {
1513 data.select_action = SEL_TOGGLE;
1514 data.select_toggle_action = SEL_SELECT;
1516 foreach_point(&data, select_root);
1518 action = data.select_toggle_action;
1521 data.select_action = action;
1522 foreach_point(&data, select_root);
1524 PE_update_selection(data.scene, data.ob, 1);
1525 WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_SELECTED, data.ob);
1527 return OPERATOR_FINISHED;
1530 void PARTICLE_OT_select_roots(wmOperatorType *ot)
1533 ot->name = "Select Roots";
1534 ot->idname = "PARTICLE_OT_select_roots";
1535 ot->description = "Select roots of all visible particles";
1538 ot->exec = select_roots_exec;
1542 ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
1545 WM_operator_properties_select_action(ot, SEL_SELECT);
1548 /************************ select tip operator ************************/
1550 static void select_tip(PEData *data, int point_index)
1552 PTCacheEditPoint *point = data->edit->points + point_index;
1553 PTCacheEditKey *key;
1555 if (point->totkey == 0) {
1559 key = &point->keys[point->totkey - 1];
1561 if (point->flag & PEP_HIDE)
1564 if (data->select_action != SEL_TOGGLE)
1565 select_action_apply(point, key, data->select_action);
1566 else if (key->flag & PEK_SELECT)
1567 data->select_toggle_action = SEL_DESELECT;
1570 static int select_tips_exec(bContext *C, wmOperator *op)
1573 int action = RNA_enum_get(op->ptr, "action");
1575 PE_set_data(C, &data);
1577 if (action == SEL_TOGGLE) {
1578 data.select_action = SEL_TOGGLE;
1579 data.select_toggle_action = SEL_SELECT;
1581 foreach_point(&data, select_tip);
1583 action = data.select_toggle_action;
1586 data.select_action = action;
1587 foreach_point(&data, select_tip);
1589 PE_update_selection(data.scene, data.ob, 1);
1590 WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_SELECTED, data.ob);
1592 return OPERATOR_FINISHED;
1595 void PARTICLE_OT_select_tips(wmOperatorType *ot)
1598 ot->name = "Select Tips";
1599 ot->idname = "PARTICLE_OT_select_tips";
1600 ot->description = "Select tips of all visible particles";
1603 ot->exec = select_tips_exec;
1607 ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
1610 WM_operator_properties_select_action(ot, SEL_SELECT);
1613 /*********************** select random operator ************************/
1615 enum { RAN_HAIR, RAN_POINTS };
1617 static EnumPropertyItem select_random_type_items[] = {
1618 {RAN_HAIR, "HAIR", 0, "Hair", ""},
1619 {RAN_POINTS, "POINTS", 0, "Points", ""},
1620 {0, NULL, 0, NULL, NULL}
1623 static int select_random_exec(bContext *C, wmOperator *op)
1630 /* used by LOOP_VISIBLE_POINTS, LOOP_VISIBLE_KEYS and LOOP_KEYS */
1632 PTCacheEditPoint *point;
1633 PTCacheEditKey *key;
1637 const float randfac = RNA_float_get(op->ptr, "percent") / 100.0f;
1638 const int seed = WM_operator_properties_select_random_seed_increment_get(op);
1639 const bool select = (RNA_enum_get(op->ptr, "action") == SEL_SELECT);
1642 type = RNA_enum_get(op->ptr, "type");
1644 PE_set_data(C, &data);
1645 data.select_action = SEL_SELECT;
1646 scene = CTX_data_scene(C);
1647 ob = CTX_data_active_object(C);
1648 edit = PE_get_current(scene, ob);
1650 rng = BLI_rng_new_srandom(seed);
1654 LOOP_VISIBLE_POINTS {
1655 int flag = ((BLI_rng_get_float(rng) < randfac) == select) ? SEL_SELECT : SEL_DESELECT;
1657 select_action_apply (point, key, flag);
1662 LOOP_VISIBLE_POINTS {
1664 int flag = ((BLI_rng_get_float(rng) < randfac) == select) ? SEL_SELECT : SEL_DESELECT;
1665 select_action_apply (point, key, flag);
1673 PE_update_selection(data.scene, data.ob, 1);
1674 WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_SELECTED, data.ob);
1676 return OPERATOR_FINISHED;
1679 void PARTICLE_OT_select_random(wmOperatorType *ot)
1682 ot->name = "Select Random";
1683 ot->idname = "PARTICLE_OT_select_random";
1684 ot->description = "Select a randomly distributed set of hair or points";
1687 ot->exec = select_random_exec;
1691 ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
1694 WM_operator_properties_select_random(ot);
1695 ot->prop = RNA_def_enum (ot->srna, "type", select_random_type_items, RAN_HAIR,
1696 "Type", "Select either hair or points");
1699 /************************ select linked operator ************************/
1701 static int select_linked_exec(bContext *C, wmOperator *op)
1707 RNA_int_get_array(op->ptr, "location", location);
1708 mval[0] = location[0];
1709 mval[1] = location[1];
1711 PE_set_view3d_data(C, &data);
1714 data.select= !RNA_boolean_get(op->ptr, "deselect");
1716 for_mouse_hit_keys(&data, select_keys, 1); /* nearest only */
1717 PE_update_selection(data.scene, data.ob, 1);
1718 WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_SELECTED, data.ob);
1720 return OPERATOR_FINISHED;
1723 static int select_linked_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1725 RNA_int_set_array(op->ptr, "location", event->mval);
1726 return select_linked_exec(C, op);
1729 void PARTICLE_OT_select_linked(wmOperatorType *ot)
1732 ot->name = "Select Linked";
1733 ot->idname = "PARTICLE_OT_select_linked";
1734 ot->description = "Select nearest particle from mouse pointer";
1737 ot->exec = select_linked_exec;
1738 ot->invoke = select_linked_invoke;
1739 ot->poll = PE_poll_view3d;
1742 ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
1745 RNA_def_boolean(ot->srna, "deselect", 0, "Deselect", "Deselect linked keys rather than selecting them");
1746 RNA_def_int_vector(ot->srna, "location", 2, NULL, 0, INT_MAX, "Location", "", 0, 16384);
1749 /************************ border select operator ************************/
1750 void PE_deselect_all_visible(PTCacheEdit *edit)
1754 LOOP_VISIBLE_POINTS {
1755 LOOP_SELECTED_KEYS {
1756 key->flag &= ~PEK_SELECT;
1757 point->flag |= PEP_EDIT_RECALC;
1762 int PE_border_select(bContext *C, rcti *rect, bool select, bool extend)
1764 Scene *scene= CTX_data_scene(C);
1765 Object *ob= CTX_data_active_object(C);
1766 PTCacheEdit *edit= PE_get_current(scene, ob);
1769 if (!PE_start_edit(edit))
1770 return OPERATOR_CANCELLED;
1772 if (extend == 0 && select)
1773 PE_deselect_all_visible(edit);
1775 PE_set_view3d_data(C, &data);
1777 data.select= select;
1779 for_mouse_hit_keys(&data, select_key, 0);
1781 PE_update_selection(scene, ob, 1);
1782 WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_SELECTED, ob);
1784 return OPERATOR_FINISHED;
1787 /************************ circle select operator ************************/
1789 int PE_circle_select(bContext *C, int selecting, const int mval[2], float rad)
1791 Scene *scene= CTX_data_scene(C);
1792 Object *ob= CTX_data_active_object(C);
1793 PTCacheEdit *edit= PE_get_current(scene, ob);
1796 if (!PE_start_edit(edit))
1797 return OPERATOR_FINISHED;
1799 PE_set_view3d_data(C, &data);
1802 data.select= selecting;
1804 for_mouse_hit_keys(&data, select_key, 0);
1806 PE_update_selection(scene, ob, 1);
1807 WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_SELECTED, ob);
1809 return OPERATOR_FINISHED;
1812 /************************ lasso select operator ************************/
1814 int PE_lasso_select(bContext *C, const int mcords[][2], const short moves, bool extend, bool select)
1816 Scene *scene= CTX_data_scene(C);
1817 Object *ob= CTX_data_active_object(C);
1818 ARegion *ar= CTX_wm_region(C);
1819 ParticleEditSettings *pset= PE_settings(scene);
1820 PTCacheEdit *edit = PE_get_current(scene, ob);
1821 ParticleSystem *psys = edit->psys;
1822 ParticleSystemModifierData *psmd = psys_get_modifier(ob, psys);
1824 float co[3], mat[4][4];
1831 if (!PE_start_edit(edit))
1832 return OPERATOR_CANCELLED;
1834 if (extend == 0 && select)
1835 PE_deselect_all_visible(edit);
1837 /* only for depths */
1838 PE_set_view3d_data(C, &data);
1840 LOOP_VISIBLE_POINTS {
1841 if (edit->psys && !(psys->flag & PSYS_GLOBAL_HAIR))
1842 psys_mat_hair_to_global(ob, psmd->dm_final, psys->part->from, psys->particles + p, mat);
1844 if (pset->selectmode==SCE_SELECT_POINT) {
1846 copy_v3_v3(co, key->co);
1848 if ((ED_view3d_project_int_global(ar, co, screen_co, V3D_PROJ_TEST_CLIP_WIN) == V3D_PROJ_RET_OK) &&
1849 BLI_lasso_is_point_inside(mcords, moves, screen_co[0], screen_co[1], IS_CLIPPED) &&
1850 key_test_depth(&data, co, screen_co))
1853 if (!(key->flag & PEK_SELECT)) {
1854 key->flag |= PEK_SELECT;
1855 point->flag |= PEP_EDIT_RECALC;
1859 if (key->flag & PEK_SELECT) {
1860 key->flag &= ~PEK_SELECT;
1861 point->flag |= PEP_EDIT_RECALC;
1867 else if (pset->selectmode==SCE_SELECT_END) {
1868 if (point->totkey) {
1869 key= point->keys + point->totkey - 1;
1871 copy_v3_v3(co, key->co);
1873 if ((ED_view3d_project_int_global(ar, co, screen_co, V3D_PROJ_TEST_CLIP_WIN) == V3D_PROJ_RET_OK) &&
1874 BLI_lasso_is_point_inside(mcords, moves, screen_co[0], screen_co[1], IS_CLIPPED) &&
1875 key_test_depth(&data, co, screen_co))
1878 if (!(key->flag & PEK_SELECT)) {
1879 key->flag |= PEK_SELECT;
1880 point->flag |= PEP_EDIT_RECALC;
1884 if (key->flag & PEK_SELECT) {
1885 key->flag &= ~PEK_SELECT;
1886 point->flag |= PEP_EDIT_RECALC;
1894 PE_update_selection(scene, ob, 1);
1895 WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_SELECTED, ob);
1897 return OPERATOR_FINISHED;
1900 /*************************** hide operator **************************/
1902 static int hide_exec(bContext *C, wmOperator *op)
1904 Object *ob= CTX_data_active_object(C);
1905 Scene *scene= CTX_data_scene(C);
1906 PTCacheEdit *edit= PE_get_current(scene, ob);
1909 if (RNA_enum_get(op->ptr, "unselected")) {
1910 LOOP_UNSELECTED_POINTS {
1911 point->flag |= PEP_HIDE;
1912 point->flag |= PEP_EDIT_RECALC;
1915 key->flag &= ~PEK_SELECT;
1919 LOOP_SELECTED_POINTS {
1920 point->flag |= PEP_HIDE;
1921 point->flag |= PEP_EDIT_RECALC;
1924 key->flag &= ~PEK_SELECT;
1928 PE_update_selection(scene, ob, 1);
1929 WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_SELECTED, ob);
1931 return OPERATOR_FINISHED;
1934 void PARTICLE_OT_hide(wmOperatorType *ot)
1937 ot->name = "Hide Selected";
1938 ot->idname = "PARTICLE_OT_hide";
1939 ot->description = "Hide selected particles";
1942 ot->exec = hide_exec;
1946 ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
1949 RNA_def_boolean(ot->srna, "unselected", 0, "Unselected", "Hide unselected rather than selected");
1952 /*************************** reveal operator **************************/
1954 static int reveal_exec(bContext *C, wmOperator *UNUSED(op))
1956 Object *ob= CTX_data_active_object(C);
1957 Scene *scene= CTX_data_scene(C);
1958 PTCacheEdit *edit= PE_get_current(scene, ob);
1962 if (point->flag & PEP_HIDE) {
1963 point->flag &= ~PEP_HIDE;
1964 point->flag |= PEP_EDIT_RECALC;
1967 key->flag |= PEK_SELECT;
1971 PE_update_selection(scene, ob, 1);
1972 WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_SELECTED, ob);
1974 return OPERATOR_FINISHED;
1977 void PARTICLE_OT_reveal(wmOperatorType *ot)
1980 ot->name = "Reveal";
1981 ot->idname = "PARTICLE_OT_reveal";
1982 ot->description = "Show hidden particles";
1985 ot->exec = reveal_exec;
1989 ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
1992 /************************ select less operator ************************/
1994 static void select_less_keys(PEData *data, int point_index)
1996 PTCacheEdit *edit= data->edit;
1997 PTCacheEditPoint *point = edit->points + point_index;
2000 LOOP_SELECTED_KEYS {
2002 if (((key+1)->flag&PEK_SELECT)==0)
2003 key->flag |= PEK_TAG;
2005 else if (k==point->totkey-1) {
2006 if (((key-1)->flag&PEK_SELECT)==0)
2007 key->flag |= PEK_TAG;
2010 if ((((key-1)->flag & (key+1)->flag) & PEK_SELECT)==0)
2011 key->flag |= PEK_TAG;
2016 if (key->flag&PEK_TAG) {
2017 key->flag &= ~(PEK_TAG|PEK_SELECT);
2018 point->flag |= PEP_EDIT_RECALC; /* redraw selection only */
2023 static int select_less_exec(bContext *C, wmOperator *UNUSED(op))
2027 PE_set_data(C, &data);
2028 foreach_point(&data, select_less_keys);
2030 PE_update_selection(data.scene, data.ob, 1);
2031 WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_SELECTED, data.ob);
2033 return OPERATOR_FINISHED;
2036 void PARTICLE_OT_select_less(wmOperatorType *ot)
2039 ot->name = "Select Less";
2040 ot->idname = "PARTICLE_OT_select_less";
2041 ot->description = "Deselect boundary selected keys of each particle";
2044 ot->exec = select_less_exec;
2048 ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
2051 /************************ select more operator ************************/
2053 static void select_more_keys(PEData *data, int point_index)
2055 PTCacheEdit *edit= data->edit;
2056 PTCacheEditPoint *point = edit->points + point_index;
2060 if (key->flag & PEK_SELECT) continue;
2063 if ((key+1)->flag&PEK_SELECT)
2064 key->flag |= PEK_TAG;
2066 else if (k==point->totkey-1) {
2067 if ((key-1)->flag&PEK_SELECT)
2068 key->flag |= PEK_TAG;
2071 if (((key-1)->flag | (key+1)->flag) & PEK_SELECT)
2072 key->flag |= PEK_TAG;
2077 if (key->flag&PEK_TAG) {
2078 key->flag &= ~PEK_TAG;
2079 key->flag |= PEK_SELECT;
2080 point->flag |= PEP_EDIT_RECALC; /* redraw selection only */
2085 static int select_more_exec(bContext *C, wmOperator *UNUSED(op))
2089 PE_set_data(C, &data);
2090 foreach_point(&data, select_more_keys);
2092 PE_update_selection(data.scene, data.ob, 1);
2093 WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_SELECTED, data.ob);
2095 return OPERATOR_FINISHED;
2098 void PARTICLE_OT_select_more(wmOperatorType *ot)
2101 ot->name = "Select More";
2102 ot->idname = "PARTICLE_OT_select_more";
2103 ot->description = "Select keys linked to boundary selected keys of each particle";
2106 ot->exec = select_more_exec;
2110 ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
2113 /************************ rekey operator ************************/
2115 static void rekey_particle(PEData *data, int pa_index)
2117 PTCacheEdit *edit= data->edit;
2118 ParticleSystem *psys= edit->psys;
2119 ParticleSimulationData sim= {0};
2120 ParticleData *pa= psys->particles + pa_index;
2121 PTCacheEditPoint *point = edit->points + pa_index;
2123 HairKey *key, *new_keys, *okey;
2124 PTCacheEditKey *ekey;
2125 float dval, sta, end;
2128 sim.scene= data->scene;
2130 sim.psys= edit->psys;
2132 pa->flag |= PARS_REKEY;
2134 key= new_keys= MEM_callocN(data->totrekey * sizeof(HairKey), "Hair re-key keys");
2137 /* root and tip stay the same */
2138 copy_v3_v3(key->co, okey->co);
2139 copy_v3_v3((key + data->totrekey - 1)->co, (okey + pa->totkey - 1)->co);
2141 sta= key->time= okey->time;
2142 end= (key + data->totrekey - 1)->time= (okey + pa->totkey - 1)->time;
2143 dval= (end - sta) / (float)(data->totrekey - 1);
2145 /* interpolate new keys from old ones */
2146 for (k=1, key++; k<data->totrekey-1; k++, key++) {
2147 state.time= (float)k / (float)(data->totrekey-1);
2148 psys_get_particle_on_path(&sim, pa_index, &state, 0);
2149 copy_v3_v3(key->co, state.co);
2150 key->time= sta + k * dval;
2155 MEM_freeN(pa->hair);
2158 point->totkey=pa->totkey=data->totrekey;
2162 MEM_freeN(point->keys);
2163 ekey= point->keys= MEM_callocN(pa->totkey * sizeof(PTCacheEditKey), "Hair re-key edit keys");
2165 for (k=0, key=pa->hair; k<pa->totkey; k++, key++, ekey++) {
2167 ekey->time= &key->time;
2168 ekey->flag |= PEK_SELECT;
2169 if (!(psys->flag & PSYS_GLOBAL_HAIR))
2170 ekey->flag |= PEK_USE_WCO;
2173 pa->flag &= ~PARS_REKEY;
2174 point->flag |= PEP_EDIT_RECALC;
2177 static int rekey_exec(bContext *C, wmOperator *op)
2181 PE_set_data(C, &data);
2183 data.dval= 1.0f / (float)(data.totrekey-1);
2184 data.totrekey= RNA_int_get(op->ptr, "keys_number");
2186 foreach_selected_point(&data, rekey_particle);
2188 recalc_lengths(data.edit);
2189 PE_update_object(data.scene, data.ob, 1);
2190 WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_EDITED, data.ob);
2192 return OPERATOR_FINISHED;
2195 void PARTICLE_OT_rekey(wmOperatorType *ot)
2199 ot->idname = "PARTICLE_OT_rekey";
2200 ot->description = "Change the number of keys of selected particles (root and tip keys included)";
2203 ot->exec = rekey_exec;
2204 ot->invoke = WM_operator_props_popup;
2205 ot->poll = PE_hair_poll;
2208 ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
2211 RNA_def_int(ot->srna, "keys_number", 2, 2, INT_MAX, "Number of Keys", "", 2, 100);
2214 static void rekey_particle_to_time(Scene *scene, Object *ob, int pa_index, float path_time)
2216 PTCacheEdit *edit= PE_get_current(scene, ob);
2217 ParticleSystem *psys;
2218 ParticleSimulationData sim= {0};
2221 HairKey *new_keys, *key;
2222 PTCacheEditKey *ekey;
2225 if (!edit || !edit->psys) return;
2233 pa= psys->particles + pa_index;
2235 pa->flag |= PARS_REKEY;
2237 key= new_keys= MEM_dupallocN(pa->hair);
2239 /* interpolate new keys from old ones (roots stay the same) */
2240 for (k=1, key++; k < pa->totkey; k++, key++) {
2241 state.time= path_time * (float)k / (float)(pa->totkey-1);
2242 psys_get_particle_on_path(&sim, pa_index, &state, 0);
2243 copy_v3_v3(key->co, state.co);
2246 /* replace hair keys */
2248 MEM_freeN(pa->hair);
2251 /* update edit pointers */
2252 for (k=0, key=pa->hair, ekey=edit->points[pa_index].keys; k<pa->totkey; k++, key++, ekey++) {
2254 ekey->time= &key->time;
2257 pa->flag &= ~PARS_REKEY;
2260 /************************* utilities **************************/
2262 static int remove_tagged_particles(Object *ob, ParticleSystem *psys, int mirror)
2264 PTCacheEdit *edit = psys->edit;
2265 ParticleData *pa, *npa=0, *new_pars=0;
2267 PTCacheEditPoint *npoint=0, *new_points=0;
2268 ParticleSystemModifierData *psmd;
2269 int i, new_totpart= psys->totpart, removed= 0;
2273 psmd= psys_get_modifier(ob, psys);
2275 LOOP_TAGGED_POINTS {
2276 PE_mirror_particle(ob, psmd->dm_final, psys, psys->particles + p, NULL);
2280 LOOP_TAGGED_POINTS {
2285 if (new_totpart != psys->totpart) {
2287 npa= new_pars= MEM_callocN(new_totpart * sizeof(ParticleData), "ParticleData array");
2288 npoint= new_points= MEM_callocN(new_totpart * sizeof(PTCacheEditPoint), "PTCacheEditKey array");
2290 if (ELEM(NULL, new_pars, new_points)) {
2291 /* allocation error! */
2293 MEM_freeN(new_pars);
2295 MEM_freeN(new_points);
2300 pa= psys->particles;
2301 point= edit->points;
2302 for (i=0; i<psys->totpart; i++, pa++, point++) {
2303 if (point->flag & PEP_TAG) {
2305 MEM_freeN(point->keys);
2307 MEM_freeN(pa->hair);
2310 memcpy(npa, pa, sizeof(ParticleData));
2311 memcpy(npoint, point, sizeof(PTCacheEditPoint));
2317 if (psys->particles) MEM_freeN(psys->particles);
2318 psys->particles= new_pars;
2320 if (edit->points) MEM_freeN(edit->points);
2321 edit->points= new_points;
2323 if (edit->mirror_cache) {
2324 MEM_freeN(edit->mirror_cache);
2325 edit->mirror_cache= NULL;
2329 MEM_freeN(psys->child);
2334 edit->totpoint= psys->totpart= new_totpart;
2340 static void remove_tagged_keys(Object *ob, ParticleSystem *psys)
2342 PTCacheEdit *edit= psys->edit;
2344 HairKey *hkey, *nhkey, *new_hkeys=0;
2346 PTCacheEditKey *nkey, *new_keys;
2347 ParticleSystemModifierData *psmd;
2350 if (pe_x_mirror(ob)) {
2351 /* mirror key tags */
2352 psmd= psys_get_modifier(ob, psys);
2356 PE_mirror_particle(ob, psmd->dm_final, psys, psys->particles + p, NULL);
2363 new_totkey= point->totkey;
2367 /* we can't have elements with less than two keys*/
2369 point->flag |= PEP_TAG;
2371 remove_tagged_particles(ob, psys, pe_x_mirror(ob));
2374 pa = psys->particles + p;
2375 new_totkey= pa->totkey;
2381 if (new_totkey != pa->totkey) {
2382 nhkey= new_hkeys= MEM_callocN(new_totkey*sizeof(HairKey), "HairKeys");
2383 nkey= new_keys= MEM_callocN(new_totkey*sizeof(PTCacheEditKey), "particle edit keys");
2387 while (key->flag & PEK_TAG && hkey < pa->hair + pa->totkey) {
2392 if (hkey < pa->hair + pa->totkey) {
2393 copy_v3_v3(nhkey->co, hkey->co);
2394 nhkey->editflag = hkey->editflag;
2395 nhkey->time= hkey->time;
2396 nhkey->weight= hkey->weight;
2398 nkey->co= nhkey->co;
2399 nkey->time= &nhkey->time;
2400 /* these can be copied from old edit keys */
2401 nkey->flag = key->flag;
2402 nkey->ftime = key->ftime;
2403 nkey->length = key->length;
2404 copy_v3_v3(nkey->world_co, key->world_co);
2412 MEM_freeN(pa->hair);
2415 MEM_freeN(point->keys);
2417 pa->hair= new_hkeys;
2418 point->keys= new_keys;
2420 point->totkey= pa->totkey= new_totkey;
2422 /* flag for recalculating length */
2423 point->flag |= PEP_EDIT_RECALC;
2428 /************************ subdivide opertor *********************/
2430 /* works like normal edit mode subdivide, inserts keys between neighboring selected keys */
2431 static void subdivide_particle(PEData *data, int pa_index)
2433 PTCacheEdit *edit= data->edit;
2434 ParticleSystem *psys= edit->psys;
2435 ParticleSimulationData sim= {0};
2436 ParticleData *pa= psys->particles + pa_index;
2437 PTCacheEditPoint *point = edit->points + pa_index;
2439 HairKey *key, *nkey, *new_keys;
2440 PTCacheEditKey *ekey, *nekey, *new_ekeys;
2446 sim.scene= data->scene;
2448 sim.psys= edit->psys;
2450 for (k=0, ekey=point->keys; k<pa->totkey-1; k++, ekey++) {
2451 if (ekey->flag&PEK_SELECT && (ekey+1)->flag&PEK_SELECT)
2455 if (totnewkey==0) return;
2457 pa->flag |= PARS_REKEY;
2459 nkey= new_keys= MEM_callocN((pa->totkey+totnewkey)*(sizeof(HairKey)), "Hair subdivide keys");
2460 nekey= new_ekeys= MEM_callocN((pa->totkey+totnewkey)*(sizeof(PTCacheEditKey)), "Hair subdivide edit keys");
2463 endtime= key[pa->totkey-1].time;
2465 for (k=0, ekey=point->keys; k<pa->totkey-1; k++, key++, ekey++) {
2467 memcpy(nkey, key, sizeof(HairKey));
2468 memcpy(nekey, ekey, sizeof(PTCacheEditKey));
2470 nekey->co= nkey->co;
2471 nekey->time= &nkey->time;
2476 if (ekey->flag & PEK_SELECT && (ekey+1)->flag & PEK_SELECT) {
2477 nkey->time = (key->time + (key + 1)->time) * 0.5f;
2478 state.time = (endtime != 0.0f) ? nkey->time / endtime: 0.0f;
2479 psys_get_particle_on_path(&sim, pa_index, &state, 0);
2480 copy_v3_v3(nkey->co, state.co);
2482 nekey->co= nkey->co;
2483 nekey->time = &nkey->time;
2484 nekey->flag |= PEK_SELECT;
2485 if (!(psys->flag & PSYS_GLOBAL_HAIR))
2486 nekey->flag |= PEK_USE_WCO;
2492 /*tip still not copied*/
2493 memcpy(nkey, key, sizeof(HairKey));
2494 memcpy(nekey, ekey, sizeof(PTCacheEditKey));
2496 nekey->co= nkey->co;
2497 nekey->time= &nkey->time;
2500 MEM_freeN(pa->hair);
2504 MEM_freeN(point->keys);
2505 point->keys= new_ekeys;
2507 point->totkey = pa->totkey = pa->totkey + totnewkey;
2508 point->flag |= PEP_EDIT_RECALC;
2509 pa->flag &= ~PARS_REKEY;
2512 static int subdivide_exec(bContext *C, wmOperator *UNUSED(op))
2516 PE_set_data(C, &data);
2517 foreach_point(&data, subdivide_particle);
2519 recalc_lengths(data.edit);
2520 PE_update_object(data.scene, data.ob, 1);
2521 WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_EDITED, data.ob);
2523 return OPERATOR_FINISHED;
2526 void PARTICLE_OT_subdivide(wmOperatorType *ot)
2529 ot->name = "Subdivide";
2530 ot->idname = "PARTICLE_OT_subdivide";
2531 ot->description = "Subdivide selected particles segments (adds keys)";
2534 ot->exec = subdivide_exec;
2535 ot->poll = PE_hair_poll;
2538 ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
2541 /************************ remove doubles opertor *********************/
2543 static int remove_doubles_exec(bContext *C, wmOperator *op)
2545 Scene *scene= CTX_data_scene(C);
2546 Object *ob= CTX_data_active_object(C);
2547 PTCacheEdit *edit= PE_get_current(scene, ob);
2548 ParticleSystem *psys = edit->psys;
2549 ParticleSystemModifierData *psmd;
2551 KDTreeNearest nearest[10];
2553 float mat[4][4], co[3], threshold= RNA_float_get(op->ptr, "threshold");
2554 int n, totn, removed, totremoved;
2556 if (psys->flag & PSYS_GLOBAL_HAIR)
2557 return OPERATOR_CANCELLED;
2560 psmd= psys_get_modifier(ob, psys);
2566 tree=BLI_kdtree_new(psys->totpart);
2568 /* insert particles into kd tree */
2569 LOOP_SELECTED_POINTS {
2570 psys_mat_hair_to_object(ob, psmd->dm_final, psys->part->from, psys->particles+p, mat);
2571 copy_v3_v3(co, point->keys->co);
2573 BLI_kdtree_insert(tree, p, co);
2576 BLI_kdtree_balance(tree);
2578 /* tag particles to be removed */
2579 LOOP_SELECTED_POINTS {
2580 psys_mat_hair_to_object(ob, psmd->dm_final, psys->part->from, psys->particles+p, mat);
2581 copy_v3_v3(co, point->keys->co);
2584 totn = BLI_kdtree_find_nearest_n(tree, co, nearest, 10);
2586 for (n=0; n<totn; n++) {
2587 /* this needs a custom threshold still */
2588 if (nearest[n].index > p && nearest[n].dist < threshold) {
2589 if (!(point->flag & PEP_TAG)) {
2590 point->flag |= PEP_TAG;
2597 BLI_kdtree_free(tree);
2599 /* remove tagged particles - don't do mirror here! */
2600 remove_tagged_particles(ob, psys, 0);
2601 totremoved += removed;
2604 if (totremoved == 0)
2605 return OPERATOR_CANCELLED;
2607 BKE_reportf(op->reports, RPT_INFO, "Removed %d double particles", totremoved);
2609 DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
2610 WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_EDITED, ob);
2612 return OPERATOR_FINISHED;
2615 void PARTICLE_OT_remove_doubles(wmOperatorType *ot)
2618 ot->name = "Remove Doubles";
2619 ot->idname = "PARTICLE_OT_remove_doubles";
2620 ot->description = "Remove selected particles close enough of others";
2623 ot->exec = remove_doubles_exec;
2624 ot->poll = PE_hair_poll;
2627 ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
2630 RNA_def_float(ot->srna, "threshold", 0.0002f, 0.0f, FLT_MAX,
2631 "Merge Distance", "Threshold distance withing which particles are removed", 0.00001f, 0.1f);
2635 static int weight_set_exec(bContext *C, wmOperator *op)
2637 Scene *scene= CTX_data_scene(C);
2638 ParticleEditSettings *pset= PE_settings(scene);
2639 Object *ob= CTX_data_active_object(C);
2640 PTCacheEdit *edit= PE_get_current(scene, ob);
2641 ParticleSystem *psys = edit->psys;
2646 ParticleBrushData *brush= &pset->brush[pset->brushtype];
2647 float factor= RNA_float_get(op->ptr, "factor");
2649 weight= brush->strength;
2652 LOOP_SELECTED_POINTS {
2653 ParticleData *pa= psys->particles + p;
2655 LOOP_SELECTED_KEYS {
2657 hkey->weight= interpf(weight, hkey->weight, factor);
2661 DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
2662 WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_EDITED, ob);
2664 return OPERATOR_FINISHED;
2667 void PARTICLE_OT_weight_set(wmOperatorType *ot)
2670 ot->name = "Weight Set";
2671 ot->idname = "PARTICLE_OT_weight_set";
2672 ot->description = "Set the weight of selected keys";
2675 ot->exec = weight_set_exec;
2676 ot->poll = PE_hair_poll;
2679 ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
2681 RNA_def_float(ot->srna, "factor", 1, 0, 1, "Factor",
2682 "Interpolation factor between current brush weight, and keys' weights", 0, 1);
2685 /************************ cursor drawing *******************************/
2687 static void brush_drawcursor(bContext *C, int x, int y, void *UNUSED(customdata))
2689 Scene *scene = CTX_data_scene(C);
2690 ParticleEditSettings *pset= PE_settings(scene);
2691 ParticleBrushData *brush;
2693 if (pset->brushtype < 0)
2696 brush= &pset->brush[pset->brushtype];
2701 glTranslatef((float)x, (float)y, 0.0f);
2703 glColor4ub(255, 255, 255, 128);
2704 glEnable(GL_LINE_SMOOTH);
2706 glutil_draw_lined_arc(0.0, M_PI*2.0, pe_brush_size_get(scene, brush), 40);
2707 glDisable(GL_BLEND);
2708 glDisable(GL_LINE_SMOOTH);
2714 static void toggle_particle_cursor(bContext *C, int enable)
2716 ParticleEditSettings *pset= PE_settings(CTX_data_scene(C));
2718 if (pset->paintcursor && !enable) {
2719 WM_paint_cursor_end(CTX_wm_manager(C), pset->paintcursor);
2720 pset->paintcursor = NULL;
2723 pset->paintcursor= WM_paint_cursor_activate(CTX_wm_manager(C), PE_poll_view3d, brush_drawcursor, NULL);
2726 /*************************** delete operator **************************/
2728 enum { DEL_PARTICLE, DEL_KEY };
2730 static EnumPropertyItem delete_type_items[] = {
2731 {DEL_PARTICLE, "PARTICLE", 0, "Particle", ""},
2732 {DEL_KEY, "KEY", 0, "Key", ""},
2733 {0, NULL, 0, NULL, NULL}};
2735 static void set_delete_particle(PEData *data, int pa_index)
2737 PTCacheEdit *edit= data->edit;
2739 edit->points[pa_index].flag |= PEP_TAG;
2742 static void set_delete_particle_key(PEData *data, int pa_index, int key_index)
2744 PTCacheEdit *edit= data->edit;
2746 edit->points[pa_index].keys[key_index].flag |= PEK_TAG;
2749 static int delete_exec(bContext *C, wmOperator *op)
2752 int type= RNA_enum_get(op->ptr, "type");
2754 PE_set_data(C, &data);
2756 if (type == DEL_KEY) {
2757 foreach_selected_key(&data, set_delete_particle_key);
2758 remove_tagged_keys(data.ob, data.edit->psys);
2759 recalc_lengths(data.edit);
2761 else if (type == DEL_PARTICLE) {
2762 foreach_selected_point(&data, set_delete_particle);
2763 remove_tagged_particles(data.ob, data.edit->psys, pe_x_mirror(data.ob));
2764 recalc_lengths(data.edit);
2767 DAG_id_tag_update(&data.ob->id, OB_RECALC_DATA);
2768 WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_EDITED, data.ob);
2770 return OPERATOR_FINISHED;
2773 void PARTICLE_OT_delete(wmOperatorType *ot)
2776 ot->name = "Delete";
2777 ot->idname = "PARTICLE_OT_delete";
2778 ot->description = "Delete selected particles or keys";
2781 ot->exec = delete_exec;
2782 ot->invoke = WM_menu_invoke;
2783 ot->poll = PE_hair_poll;
2786 ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
2789 ot->prop = RNA_def_enum(ot->srna, "type", delete_type_items, DEL_PARTICLE, "Type", "Delete a full particle or only keys");
2792 /*************************** mirror operator **************************/
2794 static void PE_mirror_x(Scene *scene, Object *ob, int tagged)
2796 Mesh *me= (Mesh *)(ob->data);
2797 ParticleSystemModifierData *psmd;
2798 PTCacheEdit *edit= PE_get_current(scene, ob);
2799 ParticleSystem *psys = edit->psys;
2800 ParticleData *pa, *newpa, *new_pars;
2801 PTCacheEditPoint *newpoint, *new_points;
2804 int *mirrorfaces = NULL;
2805 int rotation, totpart, newtotpart;
2807 if (psys->flag & PSYS_GLOBAL_HAIR)
2810 psmd= psys_get_modifier(ob, psys);
2811 if (!psmd->dm_final)
2814 const bool use_dm_final_indices = (psys->part->use_modifier_stack && !psmd->dm_final->deformedOnly);
2816 /* NOTE: this is not nice to use tessfaces but hard to avoid since pa->num uses tessfaces */
2817 BKE_mesh_tessface_ensure(me);
2819 /* Note: In case psys uses DM tessface indices, we mirror final DM itself, not orig mesh. Avoids an (impossible)
2820 * dm -> orig -> dm tessface indices conversion... */
2821 mirrorfaces = mesh_get_x_mirror_faces(ob, NULL, use_dm_final_indices ? psmd->dm_final : NULL);
2823 if (!edit->mirror_cache)
2824 PE_update_mirror_cache(ob, psys);
2826 totpart= psys->totpart;
2827 newtotpart= psys->totpart;
2828 LOOP_VISIBLE_POINTS {
2829 pa = psys->particles + p;
2832 if (point_is_selected(point)) {
2833 if (edit->mirror_cache[p] != -1) {
2834 /* already has a mirror, don't need to duplicate */
2835 PE_mirror_particle(ob, psmd->dm_final, psys, pa, NULL);
2839 point->flag |= PEP_TAG;
2843 if ((point->flag & PEP_TAG) && mirrorfaces[pa->num*2] != -1)
2847 if (newtotpart != psys->totpart) {
2848 MFace *mtessface = use_dm_final_indices ? psmd->dm_final->getTessFaceArray(psmd->dm_final) : me->mface;
2850 /* allocate new arrays and copy existing */
2851 new_pars= MEM_callocN(newtotpart*sizeof(ParticleData), "ParticleData new");
2852 new_points= MEM_callocN(newtotpart*sizeof(PTCacheEditPoint), "PTCacheEditPoint new");
2854 if (psys->particles) {
2855 memcpy(new_pars, psys->particles, totpart*sizeof(ParticleData));
2856 MEM_freeN(psys->particles);
2858 psys->particles= new_pars;
2861 memcpy(new_points, edit->points, totpart*sizeof(PTCacheEditPoint));
2862 MEM_freeN(edit->points);
2864 edit->points= new_points;
2866 if (edit->mirror_cache) {
2867 MEM_freeN(edit->mirror_cache);
2868 edit->mirror_cache= NULL;
2871 edit->totpoint= psys->totpart= newtotpart;
2873 /* create new elements */
2874 newpa= psys->particles + totpart;
2875 newpoint= edit->points + totpart;
2877 for (p=0, point=edit->points; p<totpart; p++, point++) {
2878 pa = psys->particles + p;
2879 const int pa_num = pa->num;
2881 if (point->flag & PEP_HIDE)
2884 if (!(point->flag & PEP_TAG) || mirrorfaces[pa_num * 2] == -1)
2890 if (pa->hair) newpa->hair= MEM_dupallocN(pa->hair);
2891 if (point->keys) newpoint->keys= MEM_dupallocN(point->keys);
2893 /* rotate weights according to vertex index rotation */
2894 rotation= mirrorfaces[pa_num * 2 + 1];
2895 newpa->fuv[0] = pa->fuv[2];
2896 newpa->fuv[1] = pa->fuv[1];
2897 newpa->fuv[2] = pa->fuv[0];
2898 newpa->fuv[3] = pa->fuv[3];
2899 while (rotation--) {
2900 if (mtessface[pa_num].v4) {
2901 SHIFT4(float, newpa->fuv[0], newpa->fuv[1], newpa->fuv[2], newpa->fuv[3]);
2904 SHIFT3(float, newpa->fuv[0], newpa->fuv[1], newpa->fuv[2]);
2908 /* assign face index */
2909 /* NOTE: mesh_get_x_mirror_faces generates -1 for non-found mirror, same as DMCACHE_NOTFOUND... */
2910 newpa->num = mirrorfaces[pa_num * 2];
2912 if (use_dm_final_indices) {
2913 newpa->num_dmcache = DMCACHE_ISCHILD;
2916 newpa->num_dmcache = psys_particle_dm_face_lookup(
2917 psmd->dm_final, psmd->dm_deformed, newpa->num, newpa->fuv, NULL);
2920 /* update edit key pointers */
2921 key= newpoint->keys;
2922 for (k=0, hkey=newpa->hair; k<newpa->totkey; k++, hkey++, key++) {
2924 key->time= &hkey->time;
2927 /* map key positions as mirror over x axis */
2928 PE_mirror_particle(ob, psmd->dm_final, psys, pa, newpa);
2936 point->flag &= ~PEP_TAG;
2939 MEM_freeN(mirrorfaces);
2942 static int mirror_exec(bContext *C, wmOperator *UNUSED(op))
2944 Scene *scene= CTX_data_scene(C);
2945 Object *ob= CTX_data_active_object(C);
2946 PTCacheEdit *edit= PE_get_current(scene, ob);
2948 PE_mirror_x(scene, ob, 0);
2950 update_world_cos(ob, edit);
2951 WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_EDITED, ob);
2952 DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
2954 return OPERATOR_FINISHED;
2957 void PARTICLE_OT_mirror(wmOperatorType *ot)
2960 ot->name = "Mirror";
2961 ot->idname = "PARTICLE_OT_mirror";
2962 ot->description = "Duplicate and mirror the selected particles along the local X axis";
2965 ot->exec = mirror_exec;
2966 ot->poll = PE_hair_poll;
2969 ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
2972 /************************* brush edit callbacks ********************/
2974 static void brush_comb(PEData *data, float UNUSED(mat[4][4]), float imat[4][4], int point_index, int key_index, PTCacheEditKey *key)
2976 ParticleEditSettings *pset= PE_settings(data->scene);
2979 if (pset->flag & PE_LOCK_FIRST && key_index == 0) return;
2981 fac= (float)pow((double)(1.0f - data->dist / data->rad), (double)data->combfac);
2983 copy_v3_v3(cvec, data->dvec);
2984 mul_mat3_m4_v3(imat, cvec);
2985 mul_v3_fl(cvec, fac);
2986 add_v3_v3(key->co, cvec);
2988 (data->edit->points + point_index)->flag |= PEP_EDIT_RECALC;
2991 static void brush_cut(PEData *data, int pa_index)
2993 PTCacheEdit *edit = data->edit;
2994 ARegion *ar= data->vc.ar;
2995 Object *ob= data->ob;
2996 ParticleEditSettings *pset= PE_settings(data->scene);
2997 ParticleCacheKey *key= edit->pathcache[pa_index];
2998 float rad2, cut_time= 1.0;
2999 float x0, x1, v0, v1, o0, o1, xo0, xo1, d, dv;
3000 int k, cut, keys= (int)pow(2.0, (double)pset->draw_step);
3003 /* blunt scissors */
3004 if (BLI_frand() > data->cutfac) return;
3006 /* don't cut hidden */
3007 if (edit->points[pa_index].flag & PEP_HIDE)
3010 if (ED_view3d_project_int_global(ar, key->co, screen_co, V3D_PROJ_TEST_CLIP_NEAR) != V3D_PROJ_RET_OK)
3013 rad2= data->rad * data->rad;
3017 x0 = (float)screen_co[0];
3018 x1 = (float)screen_co[1];
3020 o0= (float)data->mval[0];
3021 o1= (float)data->mval[1];
3026 /* check if root is inside circle */
3027 if (xo0*xo0 + xo1*xo1 < rad2 && key_test_depth(data, key->co, screen_co)) {
3032 /* calculate path time closest to root that was inside the circle */
3033 for (k=1, key++; k<=keys; k++, key++) {
3035 if ((ED_view3d_project_int_global(ar, key->co, screen_co, V3D_PROJ_TEST_CLIP_NEAR) != V3D_PROJ_RET_OK) ||
3036 key_test_depth(data, key->co, screen_co) == 0)
3038 x0 = (float)screen_co[0];
3039 x1 = (float)screen_co[1];
3046 v0 = (float)screen_co[0] - x0;
3047 v1 = (float)screen_co[1] - x1;
3051 d= (v0*xo1 - v1*xo0);
3058 cut_time= -(v0*xo0 + v1*xo1 + d);
3060 if (cut_time > 0.0f) {
3063 if (cut_time < 1.0f) {
3064 cut_time += (float)(k-1);
3065 cut_time /= (float)keys;
3072 x0 = (float)screen_co[0];
3073 x1 = (float)screen_co[1];
3081 if (cut_time < 0.0f) {
3082 edit->points[pa_index].flag |= PEP_TAG;
3085 rekey_particle_to_time(data->scene, ob, pa_index, cut_time);
3086 edit->points[pa_index].flag |= PEP_EDIT_RECALC;
3091 static void brush_length(PEData *data, int point_index)
3093 PTCacheEdit *edit= data->edit;
3094 PTCacheEditPoint *point = edit->points + point_index;
3096 float dvec[3], pvec[3] = {0.0f, 0.0f, 0.0f};
3100 copy_v3_v3(pvec, key->co);
3103 sub_v3_v3v3(dvec, key->co, pvec);
3104 copy_v3_v3(pvec, key->co);
3105 mul_v3_fl(dvec, data->growfac);
3106 add_v3_v3v3(key->co, (key-1)->co, dvec);
3110 point->flag |= PEP_EDIT_RECALC;
3113 static void brush_puff(PEData *data, int point_index)
3115 PTCacheEdit *edit = data->edit;
3116 ParticleSystem *psys = edit->psys;
3117 PTCacheEditPoint *point = edit->points + point_index;
3119 float mat[4][4], imat[4][4];
3121 float onor_prev[3]; /* previous normal (particle-space) */
3122 float ofs_prev[3]; /* accumulate offset for puff_volume (particle-space) */
3123 float co_root[3], no_root[3]; /* root location and normal (global-space) */
3124 float co_prev[3], co[3]; /* track key coords as we loop (global-space) */
3125 float fac = 0.0f, length_accum = 0.0f;
3126 bool puff_volume = false;
3127 bool changed = false;
3132 ParticleEditSettings *pset= PE_settings(data->scene);
3133 ParticleBrushData *brush= &pset->brush[pset->brushtype];
3134 puff_volume = (brush->flag & PE_BRUSH_DATA_PUFF_VOLUME) != 0;
3137 if (psys && !(psys->flag & PSYS_GLOBAL_HAIR)) {
3138 psys_mat_hair_to_global(data->ob, data->dm, psys->part->from, psys->particles + point_index, mat);
3139 invert_m4_m4(imat, mat);
3150 /* find root coordinate and normal on emitter */
3151 copy_v3_v3(co, key->co);
3153 mul_v3_m4v3(kco, data->ob->imat, co); /* use 'kco' as the object space version of worldspace 'co', ob->imat is set before calling */
3155 point_index= BLI_kdtree_find_nearest(edit->emitter_field, kco, NULL);
3156 if (point_index == -1) return;
3158 copy_v3_v3(co_root, co);
3159 copy_v3_v3(no_root, &edit->emitter_cosnos[point_index * 6 + 3]);
3160 mul_mat3_m4_v3(data->ob->obmat, no_root); /* normal into global-space */
3161 normalize_v3(no_root);
3164 copy_v3_v3(onor_prev, no_root);
3165 mul_mat3_m4_v3(imat, onor_prev); /* global-space into particle space */
3166 normalize_v3(onor_prev);
3169 fac= (float)pow((double)(1.0f - data->dist / data->rad), (double)data->pufffac);
3175 /* compute position as if hair was standing up straight.
3178 copy_v3_v3(co_prev, co);
3179 copy_v3_v3(co, key->co);
3181 length = len_v3v3(co_prev, co);
3182 length_accum += length;
3184 if ((data->select==0 || (key->flag & PEK_SELECT)) && !(key->flag & PEK_HIDE)) {
3185 float dco[3]; /* delta temp var */
3187 madd_v3_v3v3fl(kco, co_root, no_root, length_accum);
3189 /* blend between the current and straight position */
3190 sub_v3_v3v3(dco, kco, co);
3191 madd_v3_v3fl(co, dco, fac);
3192 /* keep the same distance from the root or we get glitches [#35406] */
3193 dist_ensure_v3_v3fl(co, co_root, length_accum);
3195 /* re-use dco to compare before and after translation and add to the offset */
3196 copy_v3_v3(dco, key->co);
3198 mul_v3_m4v3(key->co, imat, co);
3201 /* accumulate the total distance moved to apply to unselected
3202 * keys that come after */
3203 sub_v3_v3v3(ofs_prev, key->co, dco);
3211 /* this is simple but looks bad, adds annoying kinks */
3212 add_v3_v3(key->co, ofs);
3214 /* translate (not rotate) the rest of the hair if its not selected */
3216 #if 0 /* kindof works but looks worse then whats below */
3218 /* Move the unselected point on a vector based on the
3219 * hair direction and the offset */
3221 sub_v3_v3v3(dco, lastco, co);
3222 mul_mat3_m4_v3(imat, dco); /* into particle space */
3224 /* move the point along a vector perpendicular to the
3225 * hairs direction, reduces odd kinks, */
3226 cross_v3_v3v3(c1, ofs, dco);
3227 cross_v3_v3v3(c2, c1, dco);
3229 mul_v3_fl(c2, len_v3(ofs));
3230 add_v3_v3(key->co, c2);