1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2007 by Janne Karhu.
17  * All rights reserved.
18  */
19 
20 /** \file
21  * \ingroup edphys
22  */
23 
24 #include <math.h>
25 #include <stdlib.h>
26 #include <string.h>
27 
28 #include "MEM_guardedalloc.h"
29 
30 #include "DNA_mesh_types.h"
31 #include "DNA_meshdata_types.h"
32 #include "DNA_scene_types.h"
33 #include "DNA_screen_types.h"
34 #include "DNA_space_types.h"
35 #include "DNA_view3d_types.h"
36 
37 #include "BLI_kdtree.h"
38 #include "BLI_lasso_2d.h"
39 #include "BLI_listbase.h"
40 #include "BLI_math.h"
41 #include "BLI_rand.h"
42 #include "BLI_rect.h"
43 #include "BLI_task.h"
44 #include "BLI_utildefines.h"
45 
46 #include "BKE_bvhutils.h"
47 #include "BKE_context.h"
48 #include "BKE_global.h"
49 #include "BKE_main.h"
50 #include "BKE_mesh.h"
51 #include "BKE_mesh_runtime.h"
52 #include "BKE_modifier.h"
53 #include "BKE_object.h"
54 #include "BKE_particle.h"
55 #include "BKE_pointcache.h"
56 #include "BKE_report.h"
57 #include "BKE_scene.h"
58 
59 #include "DEG_depsgraph.h"
60 
61 #include "ED_mesh.h"
62 #include "ED_object.h"
63 #include "ED_particle.h"
64 #include "ED_physics.h"
65 #include "ED_screen.h"
66 #include "ED_select_utils.h"
67 #include "ED_view3d.h"
68 
69 #include "GPU_immediate.h"
70 #include "GPU_immediate_util.h"
71 #include "GPU_state.h"
72 
73 #include "UI_resources.h"
74 
75 #include "WM_api.h"
76 #include "WM_message.h"
77 #include "WM_toolsystem.h"
78 #include "WM_types.h"
79 
80 #include "RNA_access.h"
81 #include "RNA_define.h"
82 
83 #include "DEG_depsgraph_query.h"
84 
85 #include "PIL_time_utildefines.h"
86 
87 #include "physics_intern.h"
88 
89 #include "particle_edit_utildefines.h"
90 
91 /**************************** utilities *******************************/
92 
PE_poll(bContext * C)93 bool PE_poll(bContext *C)
94 {
95   Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
96   Scene *scene = CTX_data_scene(C);
97   Object *ob = CTX_data_active_object(C);
98 
99   if (!scene || !ob || !(ob->mode & OB_MODE_PARTICLE_EDIT)) {
100     return false;
101   }
102 
103   PTCacheEdit *edit = PE_get_current(depsgraph, scene, ob);
104   if (edit == NULL) {
105     return false;
106   }
107   if (edit->psmd_eval == NULL || edit->psmd_eval->mesh_final == NULL) {
108     return false;
109   }
110 
111   return true;
112 }
113 
PE_hair_poll(bContext * C)114 bool PE_hair_poll(bContext *C)
115 {
116   Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
117   Scene *scene = CTX_data_scene(C);
118   Object *ob = CTX_data_active_object(C);
119 
120   if (!scene || !ob || !(ob->mode & OB_MODE_PARTICLE_EDIT)) {
121     return false;
122   }
123 
124   PTCacheEdit *edit = PE_get_current(depsgraph, scene, ob);
125   if (edit == NULL || edit->psys == NULL) {
126     return false;
127   }
128   if (edit->psmd_eval == NULL || edit->psmd_eval->mesh_final == NULL) {
129     return false;
130   }
131 
132   return true;
133 }
134 
PE_poll_view3d(bContext * C)135 bool PE_poll_view3d(bContext *C)
136 {
137   ScrArea *area = CTX_wm_area(C);
138   ARegion *region = CTX_wm_region(C);
139 
140   return (PE_poll(C) && (area && area->spacetype == SPACE_VIEW3D) &&
141           (region && region->regiontype == RGN_TYPE_WINDOW));
142 }
143 
PE_free_ptcache_edit(PTCacheEdit * edit)144 void PE_free_ptcache_edit(PTCacheEdit *edit)
145 {
146   POINT_P;
147 
148   if (edit == 0) {
149     return;
150   }
151 
152   if (edit->points) {
153     LOOP_POINTS {
154       if (point->keys) {
155         MEM_freeN(point->keys);
156       }
157     }
158 
159     MEM_freeN(edit->points);
160   }
161 
162   if (edit->mirror_cache) {
163     MEM_freeN(edit->mirror_cache);
164   }
165 
166   if (edit->emitter_cosnos) {
167     MEM_freeN(edit->emitter_cosnos);
168     edit->emitter_cosnos = 0;
169   }
170 
171   if (edit->emitter_field) {
172     BLI_kdtree_3d_free(edit->emitter_field);
173     edit->emitter_field = 0;
174   }
175 
176   psys_free_path_cache(edit->psys, edit);
177 
178   MEM_freeN(edit);
179 }
180 
181 /************************************************/
182 /*          Edit Mode Helpers                   */
183 /************************************************/
184 
PE_start_edit(PTCacheEdit * edit)185 int PE_start_edit(PTCacheEdit *edit)
186 {
187   if (edit) {
188     edit->edited = 1;
189     if (edit->psys) {
190       edit->psys->flag |= PSYS_EDITED;
191     }
192     return 1;
193   }
194 
195   return 0;
196 }
197 
PE_settings(Scene * scene)198 ParticleEditSettings *PE_settings(Scene *scene)
199 {
200   return scene->toolsettings ? &scene->toolsettings->particle : NULL;
201 }
202 
pe_brush_size_get(const Scene * UNUSED (scene),ParticleBrushData * brush)203 static float pe_brush_size_get(const Scene *UNUSED(scene), ParticleBrushData *brush)
204 {
205 #if 0 /* TODO: Here we can enable unified brush size, needs more work. */
206   UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings;
207   float size = (ups->flag & UNIFIED_PAINT_SIZE) ? ups->size : brush->size;
208 #endif
209 
210   return brush->size;
211 }
212 
PE_get_current_from_psys(ParticleSystem * psys)213 PTCacheEdit *PE_get_current_from_psys(ParticleSystem *psys)
214 {
215   if (psys->part && psys->part->type == PART_HAIR) {
216     if ((psys->flag & PSYS_HAIR_DYNAMICS) != 0 && (psys->pointcache->flag & PTCACHE_BAKED) != 0) {
217       return psys->pointcache->edit;
218     }
219     return psys->edit;
220   }
221   if (psys->pointcache->flag & PTCACHE_BAKED) {
222     return psys->pointcache->edit;
223   }
224   return NULL;
225 }
226 
227 /* NOTE: Similar to creation of edit, but only updates pointers in the
228  * existing struct.
229  */
pe_update_hair_particle_edit_pointers(PTCacheEdit * edit)230 static void pe_update_hair_particle_edit_pointers(PTCacheEdit *edit)
231 {
232   ParticleSystem *psys = edit->psys;
233   ParticleData *pa = psys->particles;
234   for (int p = 0; p < edit->totpoint; p++) {
235     PTCacheEditPoint *point = &edit->points[p];
236     HairKey *hair_key = pa->hair;
237     for (int k = 0; k < point->totkey; k++) {
238       PTCacheEditKey *key = &point->keys[k];
239       key->co = hair_key->co;
240       key->time = &hair_key->time;
241       key->flag = hair_key->editflag;
242       if (!(psys->flag & PSYS_GLOBAL_HAIR)) {
243         key->flag |= PEK_USE_WCO;
244         hair_key->editflag |= PEK_USE_WCO;
245       }
246       hair_key++;
247     }
248     pa++;
249   }
250 }
251 
252 /* always gets at least the first particlesystem even if PSYS_CURRENT flag is not set
253  *
254  * note: this function runs on poll, therefore it can runs many times a second
255  * keep it fast! */
pe_get_current(Depsgraph * depsgraph,Scene * scene,Object * ob,int create)256 static PTCacheEdit *pe_get_current(Depsgraph *depsgraph, Scene *scene, Object *ob, int create)
257 {
258   ParticleEditSettings *pset = PE_settings(scene);
259   PTCacheEdit *edit = NULL;
260   ListBase pidlist;
261   PTCacheID *pid;
262 
263   if (pset == NULL || ob == NULL) {
264     return NULL;
265   }
266 
267   pset->scene = scene;
268   pset->object = ob;
269 
270   BKE_ptcache_ids_from_object(&pidlist, ob, NULL, 0);
271 
272   /* in the case of only one editable thing, set pset->edittype accordingly */
273   if (BLI_listbase_is_single(&pidlist)) {
274     pid = pidlist.first;
275     switch (pid->type) {
276       case PTCACHE_TYPE_PARTICLES:
277         pset->edittype = PE_TYPE_PARTICLES;
278         break;
279       case PTCACHE_TYPE_SOFTBODY:
280         pset->edittype = PE_TYPE_SOFTBODY;
281         break;
282       case PTCACHE_TYPE_CLOTH:
283         pset->edittype = PE_TYPE_CLOTH;
284         break;
285     }
286   }
287 
288   for (pid = pidlist.first; pid; pid = pid->next) {
289     if (pset->edittype == PE_TYPE_PARTICLES && pid->type == PTCACHE_TYPE_PARTICLES) {
290       ParticleSystem *psys = pid->calldata;
291 
292       if (psys->flag & PSYS_CURRENT) {
293         if (psys->part && psys->part->type == PART_HAIR) {
294           if (psys->flag & PSYS_HAIR_DYNAMICS && psys->pointcache->flag & PTCACHE_BAKED) {
295             if (create && !psys->pointcache->edit) {
296               PE_create_particle_edit(depsgraph, scene, ob, pid->cache, NULL);
297             }
298             edit = pid->cache->edit;
299           }
300           else {
301             if (create && !psys->edit) {
302               if (psys->flag & PSYS_HAIR_DONE) {
303                 PE_create_particle_edit(depsgraph, scene, ob, NULL, psys);
304               }
305             }
306             edit = psys->edit;
307           }
308         }
309         else {
310           if (create && pid->cache->flag & PTCACHE_BAKED && !pid->cache->edit) {
311             PE_create_particle_edit(depsgraph, scene, ob, pid->cache, psys);
312           }
313           edit = pid->cache->edit;
314         }
315 
316         break;
317       }
318     }
319     else if (pset->edittype == PE_TYPE_SOFTBODY && pid->type == PTCACHE_TYPE_SOFTBODY) {
320       if (create && pid->cache->flag & PTCACHE_BAKED && !pid->cache->edit) {
321         pset->flag |= PE_FADE_TIME;
322         // NICE TO HAVE but doesn't work: pset->brushtype = PE_BRUSH_COMB;
323         PE_create_particle_edit(depsgraph, scene, ob, pid->cache, NULL);
324       }
325       edit = pid->cache->edit;
326       break;
327     }
328     else if (pset->edittype == PE_TYPE_CLOTH && pid->type == PTCACHE_TYPE_CLOTH) {
329       if (create && pid->cache->flag & PTCACHE_BAKED && !pid->cache->edit) {
330         pset->flag |= PE_FADE_TIME;
331         // NICE TO HAVE but doesn't work: pset->brushtype = PE_BRUSH_COMB;
332         PE_create_particle_edit(depsgraph, scene, ob, pid->cache, NULL);
333       }
334       edit = pid->cache->edit;
335       break;
336     }
337   }
338 
339   /* Don't consider inactive or render dependency graphs, since they might be evaluated for a
340    * different number of children. or have different pointer to evaluated particle system or
341    * modifier which will also cause troubles. */
342   if (edit && DEG_is_active(depsgraph)) {
343     edit->pid = *pid;
344     if (edit->flags & PT_CACHE_EDIT_UPDATE_PARTICLE_FROM_EVAL) {
345       if (edit->psys != NULL && edit->psys_eval != NULL) {
346         psys_copy_particles(edit->psys, edit->psys_eval);
347         pe_update_hair_particle_edit_pointers(edit);
348       }
349       edit->flags &= ~PT_CACHE_EDIT_UPDATE_PARTICLE_FROM_EVAL;
350     }
351   }
352 
353   BLI_freelistN(&pidlist);
354 
355   return edit;
356 }
357 
PE_get_current(Depsgraph * depsgraph,Scene * scene,Object * ob)358 PTCacheEdit *PE_get_current(Depsgraph *depsgraph, Scene *scene, Object *ob)
359 {
360   return pe_get_current(depsgraph, scene, ob, 0);
361 }
362 
PE_create_current(Depsgraph * depsgraph,Scene * scene,Object * ob)363 PTCacheEdit *PE_create_current(Depsgraph *depsgraph, Scene *scene, Object *ob)
364 {
365   return pe_get_current(depsgraph, scene, ob, 1);
366 }
367 
PE_current_changed(Depsgraph * depsgraph,Scene * scene,Object * ob)368 void PE_current_changed(Depsgraph *depsgraph, Scene *scene, Object *ob)
369 {
370   if (ob->mode == OB_MODE_PARTICLE_EDIT) {
371     PE_create_current(depsgraph, scene, ob);
372   }
373 }
374 
PE_hide_keys_time(Scene * scene,PTCacheEdit * edit,float cfra)375 void PE_hide_keys_time(Scene *scene, PTCacheEdit *edit, float cfra)
376 {
377   ParticleEditSettings *pset = PE_settings(scene);
378   POINT_P;
379   KEY_K;
380 
381   if (pset->flag & PE_FADE_TIME && pset->selectmode == SCE_SELECT_POINT) {
382     LOOP_POINTS {
383       LOOP_KEYS {
384         if (fabsf(cfra - *key->time) < pset->fade_frames) {
385           key->flag &= ~PEK_HIDE;
386         }
387         else {
388           key->flag |= PEK_HIDE;
389           // key->flag &= ~PEK_SELECT;
390         }
391       }
392     }
393   }
394   else {
395     LOOP_POINTS {
396       LOOP_KEYS {
397         key->flag &= ~PEK_HIDE;
398       }
399     }
400   }
401 }
402 
pe_x_mirror(Object * ob)403 static int pe_x_mirror(Object *ob)
404 {
405   if (ob->type == OB_MESH) {
406     return (((Mesh *)ob->data)->symmetry & ME_SYMMETRY_X);
407   }
408 
409   return 0;
410 }
411 
412 /****************** common struct passed to callbacks ******************/
413 
414 typedef struct PEData {
415   ViewContext vc;
416 
417   const bContext *context;
418   Main *bmain;
419   Scene *scene;
420   ViewLayer *view_layer;
421   Object *ob;
422   Mesh *mesh;
423   PTCacheEdit *edit;
424   BVHTreeFromMesh shape_bvh;
425   Depsgraph *depsgraph;
426 
427   RNG *rng;
428 
429   const int *mval;
430   const rcti *rect;
431   float rad;
432   float dval;
433   int select;
434   eSelectOp sel_op;
435 
436   float *dvec;
437   float combfac;
438   float pufffac;
439   float cutfac;
440   float smoothfac;
441   float weightfac;
442   float growfac;
443   int totrekey;
444 
445   int invert;
446   int tot;
447   float vec[3];
448 
449   int select_action;
450   int select_toggle_action;
451   bool is_changed;
452 } PEData;
453 
PE_set_data(bContext * C,PEData * data)454 static void PE_set_data(bContext *C, PEData *data)
455 {
456   memset(data, 0, sizeof(*data));
457 
458   data->context = C;
459   data->bmain = CTX_data_main(C);
460   data->scene = CTX_data_scene(C);
461   data->view_layer = CTX_data_view_layer(C);
462   data->ob = CTX_data_active_object(C);
463   data->depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
464   data->edit = PE_get_current(data->depsgraph, data->scene, data->ob);
465 }
466 
PE_set_view3d_data(bContext * C,PEData * data)467 static void PE_set_view3d_data(bContext *C, PEData *data)
468 {
469   PE_set_data(C, data);
470 
471   ED_view3d_viewcontext_init(C, &data->vc, data->depsgraph);
472 
473   if (!XRAY_ENABLED(data->vc.v3d)) {
474     if (data->vc.v3d->flag & V3D_INVALID_BACKBUF) {
475       /* needed or else the draw matrix can be incorrect */
476       view3d_operator_needs_opengl(C);
477 
478       ED_view3d_backbuf_depth_validate(&data->vc);
479       /* we may need to force an update here by setting the rv3d as dirty
480        * for now it seems ok, but take care!:
481        * rv3d->depths->dirty = 1; */
482       ED_view3d_depth_update(data->vc.region);
483     }
484   }
485 }
486 
PE_create_shape_tree(PEData * data,Object * shapeob)487 static bool PE_create_shape_tree(PEData *data, Object *shapeob)
488 {
489   Object *shapeob_eval = DEG_get_evaluated_object(data->depsgraph, shapeob);
490   Mesh *mesh = BKE_object_get_evaluated_mesh(shapeob_eval);
491 
492   memset(&data->shape_bvh, 0, sizeof(data->shape_bvh));
493 
494   if (!mesh) {
495     return false;
496   }
497 
498   return (BKE_bvhtree_from_mesh_get(&data->shape_bvh, mesh, BVHTREE_FROM_LOOPTRI, 4) != NULL);
499 }
500 
PE_free_shape_tree(PEData * data)501 static void PE_free_shape_tree(PEData *data)
502 {
503   free_bvhtree_from_mesh(&data->shape_bvh);
504 }
505 
PE_create_random_generator(PEData * data)506 static void PE_create_random_generator(PEData *data)
507 {
508   uint rng_seed = (uint)(PIL_check_seconds_timer_i() & UINT_MAX);
509   rng_seed ^= POINTER_AS_UINT(data->ob);
510   rng_seed ^= POINTER_AS_UINT(data->edit);
511   data->rng = BLI_rng_new(rng_seed);
512 }
513 
PE_free_random_generator(PEData * data)514 static void PE_free_random_generator(PEData *data)
515 {
516   if (data->rng != NULL) {
517     BLI_rng_free(data->rng);
518     data->rng = NULL;
519   }
520 }
521 
522 /*************************** selection utilities *******************************/
523 
key_test_depth(const PEData * data,const float co[3],const int screen_co[2])524 static bool key_test_depth(const PEData *data, const float co[3], const int screen_co[2])
525 {
526   View3D *v3d = data->vc.v3d;
527   ViewDepths *vd = data->vc.rv3d->depths;
528   float depth;
529 
530   /* nothing to do */
531   if (XRAY_ENABLED(v3d)) {
532     return true;
533   }
534 
535   /* used to calculate here but all callers have  the screen_co already, so pass as arg */
536 #if 0
537   if (ED_view3d_project_int_global(data->vc.region,
538                                    co,
539                                    screen_co,
540                                    V3D_PROJ_TEST_CLIP_BB | V3D_PROJ_TEST_CLIP_WIN |
541                                        V3D_PROJ_TEST_CLIP_NEAR) != V3D_PROJ_RET_OK) {
542     return 0;
543   }
544 #endif
545 
546   /* check if screen_co is within bounds because brush_cut uses out of screen coords */
547   if (screen_co[0] >= 0 && screen_co[0] < vd->w && screen_co[1] >= 0 && screen_co[1] < vd->h) {
548     BLI_assert(vd && vd->depths);
549     /* we know its not clipped */
550     depth = vd->depths[screen_co[1] * vd->w + screen_co[0]];
551   }
552   else {
553     return 0;
554   }
555 
556   float win[3];
557   ED_view3d_project(data->vc.region, co, win);
558 
559   if (win[2] - 0.00001f > depth) {
560     return 0;
561   }
562   return 1;
563 }
564 
key_inside_circle(const PEData * data,float rad,const float co[3],float * distance)565 static bool key_inside_circle(const PEData *data, float rad, const float co[3], float *distance)
566 {
567   float dx, dy, dist;
568   int screen_co[2];
569 
570   /* TODO, should this check V3D_PROJ_TEST_CLIP_BB too? */
571   if (ED_view3d_project_int_global(data->vc.region, co, screen_co, V3D_PROJ_TEST_CLIP_WIN) !=
572       V3D_PROJ_RET_OK) {
573     return 0;
574   }
575 
576   dx = data->mval[0] - screen_co[0];
577   dy = data->mval[1] - screen_co[1];
578   dist = sqrtf(dx * dx + dy * dy);
579 
580   if (dist > rad) {
581     return 0;
582   }
583 
584   if (key_test_depth(data, co, screen_co)) {
585     if (distance) {
586       *distance = dist;
587     }
588 
589     return 1;
590   }
591 
592   return 0;
593 }
594 
key_inside_rect(PEData * data,const float co[3])595 static bool key_inside_rect(PEData *data, const float co[3])
596 {
597   int screen_co[2];
598 
599   if (ED_view3d_project_int_global(data->vc.region, co, screen_co, V3D_PROJ_TEST_CLIP_WIN) !=
600       V3D_PROJ_RET_OK) {
601     return 0;
602   }
603 
604   if (screen_co[0] > data->rect->xmin && screen_co[0] < data->rect->xmax &&
605       screen_co[1] > data->rect->ymin && screen_co[1] < data->rect->ymax) {
606     return key_test_depth(data, co, screen_co);
607   }
608 
609   return 0;
610 }
611 
key_inside_test(PEData * data,const float co[3])612 static bool key_inside_test(PEData *data, const float co[3])
613 {
614   if (data->mval) {
615     return key_inside_circle(data, data->rad, co, NULL);
616   }
617   return key_inside_rect(data, co);
618 }
619 
point_is_selected(PTCacheEditPoint * point)620 static bool point_is_selected(PTCacheEditPoint *point)
621 {
622   KEY_K;
623 
624   if (point->flag & PEP_HIDE) {
625     return 0;
626   }
627 
628   LOOP_SELECTED_KEYS {
629     return 1;
630   }
631 
632   return 0;
633 }
634 
635 /*************************** iterators *******************************/
636 
637 typedef void (*ForPointFunc)(PEData *data, int point_index);
638 typedef void (*ForHitPointFunc)(PEData *data, int point_index, float mouse_distance);
639 
640 typedef void (*ForKeyFunc)(PEData *data, int point_index, int key_index, bool is_inside);
641 
642 typedef void (*ForKeyMatFunc)(PEData *data,
643                               const float mat[4][4],
644                               const float imat[4][4],
645                               int point_index,
646                               int key_index,
647                               PTCacheEditKey *key);
648 typedef void (*ForHitKeyMatFunc)(PEData *data,
649                                  float mat[4][4],
650                                  float imat[4][4],
651                                  int point_index,
652                                  int key_index,
653                                  PTCacheEditKey *key,
654                                  float mouse_distance);
655 
656 enum eParticleSelectFlag {
657   PSEL_NEAREST = (1 << 0),
658   PSEL_ALL_KEYS = (1 << 1),
659 };
660 
for_mouse_hit_keys(PEData * data,ForKeyFunc func,const enum eParticleSelectFlag flag)661 static void for_mouse_hit_keys(PEData *data, ForKeyFunc func, const enum eParticleSelectFlag flag)
662 {
663   ParticleEditSettings *pset = PE_settings(data->scene);
664   PTCacheEdit *edit = data->edit;
665   POINT_P;
666   KEY_K;
667   int nearest_point, nearest_key;
668   float dist = data->rad;
669 
670   /* in path select mode we have no keys */
671   if (pset->selectmode == SCE_SELECT_PATH) {
672     return;
673   }
674 
675   nearest_point = -1;
676   nearest_key = -1;
677 
678   LOOP_VISIBLE_POINTS {
679     if (pset->selectmode == SCE_SELECT_END) {
680       if (point->totkey) {
681         /* only do end keys */
682         key = point->keys + point->totkey - 1;
683 
684         if (flag & PSEL_NEAREST) {
685           if (key_inside_circle(data, dist, KEY_WCO, &dist)) {
686             nearest_point = p;
687             nearest_key = point->totkey - 1;
688           }
689         }
690         else {
691           const bool is_inside = key_inside_test(data, KEY_WCO);
692           if (is_inside || (flag & PSEL_ALL_KEYS)) {
693             func(data, p, point->totkey - 1, is_inside);
694           }
695         }
696       }
697     }
698     else {
699       /* do all keys */
700       LOOP_VISIBLE_KEYS {
701         if (flag & PSEL_NEAREST) {
702           if (key_inside_circle(data, dist, KEY_WCO, &dist)) {
703             nearest_point = p;
704             nearest_key = k;
705           }
706         }
707         else {
708           const bool is_inside = key_inside_test(data, KEY_WCO);
709           if (is_inside || (flag & PSEL_ALL_KEYS)) {
710             func(data, p, k, is_inside);
711           }
712         }
713       }
714     }
715   }
716 
717   /* do nearest only */
718   if (flag & PSEL_NEAREST) {
719     if (nearest_point != -1) {
720       func(data, nearest_point, nearest_key, true);
721     }
722   }
723 }
724 
foreach_mouse_hit_point(PEData * data,ForHitPointFunc func,int selected)725 static void foreach_mouse_hit_point(PEData *data, ForHitPointFunc func, int selected)
726 {
727   ParticleEditSettings *pset = PE_settings(data->scene);
728   PTCacheEdit *edit = data->edit;
729   POINT_P;
730   KEY_K;
731 
732   /* all is selected in path mode */
733   if (pset->selectmode == SCE_SELECT_PATH) {
734     selected = 0;
735   }
736 
737   LOOP_VISIBLE_POINTS {
738     if (pset->selectmode == SCE_SELECT_END) {
739       if (point->totkey) {
740         /* only do end keys */
741         key = point->keys + point->totkey - 1;
742 
743         if (selected == 0 || key->flag & PEK_SELECT) {
744           float mouse_distance;
745           if (key_inside_circle(data, data->rad, KEY_WCO, &mouse_distance)) {
746             func(data, p, mouse_distance);
747           }
748         }
749       }
750     }
751     else {
752       /* do all keys */
753       LOOP_VISIBLE_KEYS {
754         if (selected == 0 || key->flag & PEK_SELECT) {
755           float mouse_distance;
756           if (key_inside_circle(data, data->rad, KEY_WCO, &mouse_distance)) {
757             func(data, p, mouse_distance);
758             break;
759           }
760         }
761       }
762     }
763   }
764 }
765 
766 typedef struct KeyIterData {
767   PEData *data;
768   PTCacheEdit *edit;
769   int selected;
770   ForHitKeyMatFunc func;
771 } KeyIterData;
772 
foreach_mouse_hit_key_iter(void * __restrict iter_data_v,const int iter,const TaskParallelTLS * __restrict UNUSED (tls))773 static void foreach_mouse_hit_key_iter(void *__restrict iter_data_v,
774                                        const int iter,
775                                        const TaskParallelTLS *__restrict UNUSED(tls))
776 {
777   KeyIterData *iter_data = (KeyIterData *)iter_data_v;
778   PEData *data = iter_data->data;
779   PTCacheEdit *edit = data->edit;
780   PTCacheEditPoint *point = &edit->points[iter];
781   if (point->flag & PEP_HIDE) {
782     return;
783   }
784   ParticleSystem *psys = edit->psys;
785   ParticleSystemModifierData *psmd_eval = iter_data->edit->psmd_eval;
786   ParticleEditSettings *pset = PE_settings(data->scene);
787   const int selected = iter_data->selected;
788   float mat[4][4], imat[4][4];
789   unit_m4(mat);
790   unit_m4(imat);
791   if (pset->selectmode == SCE_SELECT_END) {
792     if (point->totkey) {
793       /* only do end keys */
794       PTCacheEditKey *key = point->keys + point->totkey - 1;
795 
796       if (selected == 0 || key->flag & PEK_SELECT) {
797         float mouse_distance;
798         if (key_inside_circle(data, data->rad, KEY_WCO, &mouse_distance)) {
799           if (edit->psys && !(edit->psys->flag & PSYS_GLOBAL_HAIR)) {
800             psys_mat_hair_to_global(
801                 data->ob, psmd_eval->mesh_final, psys->part->from, psys->particles + iter, mat);
802             invert_m4_m4(imat, mat);
803           }
804           iter_data->func(data, mat, imat, iter, point->totkey - 1, key, mouse_distance);
805         }
806       }
807     }
808   }
809   else {
810     /* do all keys */
811     PTCacheEditKey *key;
812     int k;
813     LOOP_VISIBLE_KEYS {
814       if (selected == 0 || key->flag & PEK_SELECT) {
815         float mouse_distance;
816         if (key_inside_circle(data, data->rad, KEY_WCO, &mouse_distance)) {
817           if (edit->psys && !(edit->psys->flag & PSYS_GLOBAL_HAIR)) {
818             psys_mat_hair_to_global(
819                 data->ob, psmd_eval->mesh_final, psys->part->from, psys->particles + iter, mat);
820             invert_m4_m4(imat, mat);
821           }
822           iter_data->func(data, mat, imat, iter, k, key, mouse_distance);
823         }
824       }
825     }
826   }
827 }
828 
foreach_mouse_hit_key(PEData * data,ForHitKeyMatFunc func,int selected)829 static void foreach_mouse_hit_key(PEData *data, ForHitKeyMatFunc func, int selected)
830 {
831   PTCacheEdit *edit = data->edit;
832   ParticleEditSettings *pset = PE_settings(data->scene);
833   /* all is selected in path mode */
834   if (pset->selectmode == SCE_SELECT_PATH) {
835     selected = 0;
836   }
837 
838   KeyIterData iter_data;
839   iter_data.data = data;
840   iter_data.edit = edit;
841   iter_data.selected = selected;
842   iter_data.func = func;
843 
844   TaskParallelSettings settings;
845   BLI_parallel_range_settings_defaults(&settings);
846   BLI_task_parallel_range(0, edit->totpoint, &iter_data, foreach_mouse_hit_key_iter, &settings);
847 }
848 
foreach_selected_point(PEData * data,ForPointFunc func)849 static void foreach_selected_point(PEData *data, ForPointFunc func)
850 {
851   PTCacheEdit *edit = data->edit;
852   POINT_P;
853 
854   LOOP_SELECTED_POINTS {
855     func(data, p);
856   }
857 }
858 
foreach_selected_key(PEData * data,ForKeyFunc func)859 static void foreach_selected_key(PEData *data, ForKeyFunc func)
860 {
861   PTCacheEdit *edit = data->edit;
862   POINT_P;
863   KEY_K;
864 
865   LOOP_VISIBLE_POINTS {
866     LOOP_SELECTED_KEYS {
867       func(data, p, k, true);
868     }
869   }
870 }
871 
foreach_point(PEData * data,ForPointFunc func)872 static void foreach_point(PEData *data, ForPointFunc func)
873 {
874   PTCacheEdit *edit = data->edit;
875   POINT_P;
876 
877   LOOP_POINTS {
878     func(data, p);
879   }
880 }
881 
count_selected_keys(Scene * scene,PTCacheEdit * edit)882 static int count_selected_keys(Scene *scene, PTCacheEdit *edit)
883 {
884   ParticleEditSettings *pset = PE_settings(scene);
885   POINT_P;
886   KEY_K;
887   int sel = 0;
888 
889   LOOP_VISIBLE_POINTS {
890     if (pset->selectmode == SCE_SELECT_POINT) {
891       LOOP_SELECTED_KEYS {
892         sel++;
893       }
894     }
895     else if (pset->selectmode == SCE_SELECT_END) {
896       if (point->totkey) {
897         key = point->keys + point->totkey - 1;
898         if (key->flag & PEK_SELECT) {
899           sel++;
900         }
901       }
902     }
903   }
904 
905   return sel;
906 }
907 
908 /************************************************/
909 /*          Particle Edit Mirroring             */
910 /************************************************/
911 
PE_update_mirror_cache(Object * ob,ParticleSystem * psys)912 static void PE_update_mirror_cache(Object *ob, ParticleSystem *psys)
913 {
914   PTCacheEdit *edit;
915   ParticleSystemModifierData *psmd_eval;
916   KDTree_3d *tree;
917   KDTreeNearest_3d nearest;
918   HairKey *key;
919   PARTICLE_P;
920   float mat[4][4], co[3];
921   int index, totpart;
922 
923   edit = psys->edit;
924   psmd_eval = edit->psmd_eval;
925   totpart = psys->totpart;
926 
927   if (!psmd_eval->mesh_final) {
928     return;
929   }
930 
931   tree = BLI_kdtree_3d_new(totpart);
932 
933   /* insert particles into kd tree */
934   LOOP_PARTICLES
935   {
936     key = pa->hair;
937     psys_mat_hair_to_orco(ob, psmd_eval->mesh_final, psys->part->from, pa, mat);
938     copy_v3_v3(co, key->co);
939     mul_m4_v3(mat, co);
940     BLI_kdtree_3d_insert(tree, p, co);
941   }
942 
943   BLI_kdtree_3d_balance(tree);
944 
945   /* lookup particles and set in mirror cache */
946   if (!edit->mirror_cache) {
947     edit->mirror_cache = MEM_callocN(sizeof(int) * totpart, "PE mirror cache");
948   }
949 
950   LOOP_PARTICLES
951   {
952     key = pa->hair;
953     psys_mat_hair_to_orco(ob, psmd_eval->mesh_final, psys->part->from, pa, mat);
954     copy_v3_v3(co, key->co);
955     mul_m4_v3(mat, co);
956     co[0] = -co[0];
957 
958     index = BLI_kdtree_3d_find_nearest(tree, co, &nearest);
959 
960     /* this needs a custom threshold still, duplicated for editmode mirror */
961     if (index != -1 && index != p && (nearest.dist <= 0.0002f)) {
962       edit->mirror_cache[p] = index;
963     }
964     else {
965       edit->mirror_cache[p] = -1;
966     }
967   }
968 
969   /* make sure mirrors are in two directions */
970   LOOP_PARTICLES
971   {
972     if (edit->mirror_cache[p]) {
973       index = edit->mirror_cache[p];
974       if (edit->mirror_cache[index] != p) {
975         edit->mirror_cache[p] = -1;
976       }
977     }
978   }
979 
980   BLI_kdtree_3d_free(tree);
981 }
982 
PE_mirror_particle(Object * ob,Mesh * mesh,ParticleSystem * psys,ParticleData * pa,ParticleData * mpa)983 static void PE_mirror_particle(
984     Object *ob, Mesh *mesh, ParticleSystem *psys, ParticleData *pa, ParticleData *mpa)
985 {
986   HairKey *hkey, *mhkey;
987   PTCacheEditPoint *point, *mpoint;
988   PTCacheEditKey *key, *mkey;
989   PTCacheEdit *edit;
990   float mat[4][4], mmat[4][4], immat[4][4];
991   int i, mi, k;
992 
993   edit = psys->edit;
994   i = pa - psys->particles;
995 
996   /* find mirrored particle if needed */
997   if (!mpa) {
998     if (!edit->mirror_cache) {
999       PE_update_mirror_cache(ob, psys);
1000     }
1001 
1002     if (!edit->mirror_cache) {
1003       return; /* something went wrong! */
1004     }
1005 
1006     mi = edit->mirror_cache[i];
1007     if (mi == -1) {
1008       return;
1009     }
1010     mpa = psys->particles + mi;
1011   }
1012   else {
1013     mi = mpa - psys->particles;
1014   }
1015 
1016   point = edit->points + i;
1017   mpoint = edit->points + mi;
1018 
1019   /* make sure they have the same amount of keys */
1020   if (pa->totkey != mpa->totkey) {
1021     if (mpa->hair) {
1022       MEM_freeN(mpa->hair);
1023     }
1024     if (mpoint->keys) {
1025       MEM_freeN(mpoint->keys);
1026     }
1027 
1028     mpa->hair = MEM_dupallocN(pa->hair);
1029     mpa->totkey = pa->totkey;
1030     mpoint->keys = MEM_dupallocN(point->keys);
1031     mpoint->totkey = point->totkey;
1032 
1033     mhkey = mpa->hair;
1034     mkey = mpoint->keys;
1035     for (k = 0; k < mpa->totkey; k++, mkey++, mhkey++) {
1036       mkey->co = mhkey->co;
1037       mkey->time = &mhkey->time;
1038       mkey->flag &= ~PEK_SELECT;
1039     }
1040   }
1041 
1042   /* mirror positions and tags */
1043   psys_mat_hair_to_orco(ob, mesh, psys->part->from, pa, mat);
1044   psys_mat_hair_to_orco(ob, mesh, psys->part->from, mpa, mmat);
1045   invert_m4_m4(immat, mmat);
1046 
1047   hkey = pa->hair;
1048   mhkey = mpa->hair;
1049   key = point->keys;
1050   mkey = mpoint->keys;
1051   for (k = 0; k < pa->totkey; k++, hkey++, mhkey++, key++, mkey++) {
1052     copy_v3_v3(mhkey->co, hkey->co);
1053     mul_m4_v3(mat, mhkey->co);
1054     mhkey->co[0] = -mhkey->co[0];
1055     mul_m4_v3(immat, mhkey->co);
1056 
1057     if (key->flag & PEK_TAG) {
1058       mkey->flag |= PEK_TAG;
1059     }
1060 
1061     mkey->length = key->length;
1062   }
1063 
1064   if (point->flag & PEP_TAG) {
1065     mpoint->flag |= PEP_TAG;
1066   }
1067   if (point->flag & PEP_EDIT_RECALC) {
1068     mpoint->flag |= PEP_EDIT_RECALC;
1069   }
1070 }
1071 
PE_apply_mirror(Object * ob,ParticleSystem * psys)1072 static void PE_apply_mirror(Object *ob, ParticleSystem *psys)
1073 {
1074   PTCacheEdit *edit;
1075   ParticleSystemModifierData *psmd_eval;
1076   POINT_P;
1077 
1078   if (!psys) {
1079     return;
1080   }
1081 
1082   edit = psys->edit;
1083   psmd_eval = edit->psmd_eval;
1084 
1085   if (psmd_eval == NULL || psmd_eval->mesh_final == NULL) {
1086     return;
1087   }
1088 
1089   if (!edit->mirror_cache) {
1090     PE_update_mirror_cache(ob, psys);
1091   }
1092 
1093   if (!edit->mirror_cache) {
1094     return; /* something went wrong */
1095   }
1096 
1097   /* we delay settings the PARS_EDIT_RECALC for mirrored particles
1098    * to avoid doing mirror twice */
1099   LOOP_POINTS {
1100     if (point->flag & PEP_EDIT_RECALC) {
1101       PE_mirror_particle(ob, psmd_eval->mesh_final, psys, psys->particles + p, NULL);
1102 
1103       if (edit->mirror_cache[p] != -1) {
1104         edit->points[edit->mirror_cache[p]].flag &= ~PEP_EDIT_RECALC;
1105       }
1106     }
1107   }
1108 
1109   LOOP_POINTS {
1110     if (point->flag & PEP_EDIT_RECALC) {
1111       if (edit->mirror_cache[p] != -1) {
1112         edit->points[edit->mirror_cache[p]].flag |= PEP_EDIT_RECALC;
1113       }
1114     }
1115   }
1116 }
1117 
1118 /************************************************/
1119 /*          Edit Calculation                    */
1120 /************************************************/
1121 
1122 typedef struct DeflectEmitterIter {
1123   Object *object;
1124   ParticleSystem *psys;
1125   PTCacheEdit *edit;
1126   float dist;
1127   float emitterdist;
1128 } DeflectEmitterIter;
1129 
deflect_emitter_iter(void * __restrict iter_data_v,const int iter,const TaskParallelTLS * __restrict UNUSED (tls))1130 static void deflect_emitter_iter(void *__restrict iter_data_v,
1131                                  const int iter,
1132                                  const TaskParallelTLS *__restrict UNUSED(tls))
1133 {
1134   DeflectEmitterIter *iter_data = (DeflectEmitterIter *)iter_data_v;
1135   PTCacheEdit *edit = iter_data->edit;
1136   PTCacheEditPoint *point = &edit->points[iter];
1137   if ((point->flag & PEP_EDIT_RECALC) == 0) {
1138     return;
1139   }
1140   Object *object = iter_data->object;
1141   ParticleSystem *psys = iter_data->psys;
1142   ParticleSystemModifierData *psmd_eval = iter_data->edit->psmd_eval;
1143   PTCacheEditKey *key;
1144   int k;
1145   float hairimat[4][4], hairmat[4][4];
1146   int index;
1147   float *vec, *nor, dvec[3], dot, dist_1st = 0.0f;
1148   const float dist = iter_data->dist;
1149   const float emitterdist = iter_data->emitterdist;
1150   psys_mat_hair_to_object(
1151       object, psmd_eval->mesh_final, psys->part->from, psys->particles + iter, hairmat);
1152 
1153   LOOP_KEYS {
1154     mul_m4_v3(hairmat, key->co);
1155   }
1156 
1157   LOOP_KEYS {
1158     if (k == 0) {
1159       dist_1st = len_v3v3((key + 1)->co, key->co);
1160       dist_1st *= dist * emitterdist;
1161     }
1162     else {
1163       index = BLI_kdtree_3d_find_nearest(edit->emitter_field, key->co, NULL);
1164 
1165       vec = edit->emitter_cosnos + index * 6;
1166       nor = vec + 3;
1167 
1168       sub_v3_v3v3(dvec, key->co, vec);
1169 
1170       dot = dot_v3v3(dvec, nor);
1171       copy_v3_v3(dvec, nor);
1172 
1173       if (dot > 0.0f) {
1174         if (dot < dist_1st) {
1175           normalize_v3(dvec);
1176           mul_v3_fl(dvec, dist_1st - dot);
1177           add_v3_v3(key->co, dvec);
1178         }
1179       }
1180       else {
1181         normalize_v3(dvec);
1182         mul_v3_fl(dvec, dist_1st - dot);
1183         add_v3_v3(key->co, dvec);
1184       }
1185       if (k == 1) {
1186         dist_1st *= 1.3333f;
1187       }
1188     }
1189   }
1190 
1191   invert_m4_m4(hairimat, hairmat);
1192 
1193   LOOP_KEYS {
1194     mul_m4_v3(hairimat, key->co);
1195   }
1196 }
1197 
1198 /* tries to stop edited particles from going through the emitter's surface */
pe_deflect_emitter(Scene * scene,Object * ob,PTCacheEdit * edit)1199 static void pe_deflect_emitter(Scene *scene, Object *ob, PTCacheEdit *edit)
1200 {
1201   ParticleEditSettings *pset = PE_settings(scene);
1202   ParticleSystem *psys;
1203   const float dist = ED_view3d_select_dist_px() * 0.01f;
1204 
1205   if (edit == NULL || edit->psys == NULL || (pset->flag & PE_DEFLECT_EMITTER) == 0 ||
1206       (edit->psys->flag & PSYS_GLOBAL_HAIR)) {
1207     return;
1208   }
1209 
1210   psys = edit->psys;
1211 
1212   if (edit->psmd_eval == NULL || edit->psmd_eval->mesh_final == NULL) {
1213     return;
1214   }
1215 
1216   DeflectEmitterIter iter_data;
1217   iter_data.object = ob;
1218   iter_data.psys = psys;
1219   iter_data.edit = edit;
1220   iter_data.dist = dist;
1221   iter_data.emitterdist = pset->emitterdist;
1222 
1223   TaskParallelSettings settings;
1224   BLI_parallel_range_settings_defaults(&settings);
1225   BLI_task_parallel_range(0, edit->totpoint, &iter_data, deflect_emitter_iter, &settings);
1226 }
1227 
1228 typedef struct ApplyLengthsIterData {
1229   PTCacheEdit *edit;
1230 } ApplyLengthsIterData;
1231 
apply_lengths_iter(void * __restrict iter_data_v,const int iter,const TaskParallelTLS * __restrict UNUSED (tls))1232 static void apply_lengths_iter(void *__restrict iter_data_v,
1233                                const int iter,
1234                                const TaskParallelTLS *__restrict UNUSED(tls))
1235 {
1236   ApplyLengthsIterData *iter_data = (ApplyLengthsIterData *)iter_data_v;
1237   PTCacheEdit *edit = iter_data->edit;
1238   PTCacheEditPoint *point = &edit->points[iter];
1239   if ((point->flag & PEP_EDIT_RECALC) == 0) {
1240     return;
1241   }
1242   PTCacheEditKey *key;
1243   int k;
1244   LOOP_KEYS {
1245     if (k) {
1246       float dv1[3];
1247       sub_v3_v3v3(dv1, key->co, (key - 1)->co);
1248       normalize_v3(dv1);
1249       mul_v3_fl(dv1, (key - 1)->length);
1250       add_v3_v3v3(key->co, (key - 1)->co, dv1);
1251     }
1252   }
1253 }
1254 
1255 /* force set distances between neighboring keys */
PE_apply_lengths(Scene * scene,PTCacheEdit * edit)1256 static void PE_apply_lengths(Scene *scene, PTCacheEdit *edit)
1257 {
1258   ParticleEditSettings *pset = PE_settings(scene);
1259 
1260   if (edit == 0 || (pset->flag & PE_KEEP_LENGTHS) == 0) {
1261     return;
1262   }
1263 
1264   if (edit->psys && edit->psys->flag & PSYS_GLOBAL_HAIR) {
1265     return;
1266   }
1267 
1268   ApplyLengthsIterData iter_data;
1269   iter_data.edit = edit;
1270 
1271   TaskParallelSettings settings;
1272   BLI_parallel_range_settings_defaults(&settings);
1273   BLI_task_parallel_range(0, edit->totpoint, &iter_data, apply_lengths_iter, &settings);
1274 }
1275 
1276 typedef struct IterateLengthsIterData {
1277   PTCacheEdit *edit;
1278   ParticleEditSettings *pset;
1279 } IterateLengthsIterData;
1280 
iterate_lengths_iter(void * __restrict iter_data_v,const int iter,const TaskParallelTLS * __restrict UNUSED (tls))1281 static void iterate_lengths_iter(void *__restrict iter_data_v,
1282                                  const int iter,
1283                                  const TaskParallelTLS *__restrict UNUSED(tls))
1284 {
1285   IterateLengthsIterData *iter_data = (IterateLengthsIterData *)iter_data_v;
1286   PTCacheEdit *edit = iter_data->edit;
1287   PTCacheEditPoint *point = &edit->points[iter];
1288   if ((point->flag & PEP_EDIT_RECALC) == 0) {
1289     return;
1290   }
1291   ParticleEditSettings *pset = iter_data->pset;
1292   float tlen;
1293   float dv0[3] = {0.0f, 0.0f, 0.0f};
1294   float dv1[3] = {0.0f, 0.0f, 0.0f};
1295   float dv2[3] = {0.0f, 0.0f, 0.0f};
1296   for (int j = 1; j < point->totkey; j++) {
1297     PTCacheEditKey *key;
1298     int k;
1299     float mul = 1.0f / (float)point->totkey;
1300     if (pset->flag & PE_LOCK_FIRST) {
1301       key = point->keys + 1;
1302       k = 1;
1303       dv1[0] = dv1[1] = dv1[2] = 0.0;
1304     }
1305     else {
1306       key = point->keys;
1307       k = 0;
1308       dv0[0] = dv0[1] = dv0[2] = 0.0;
1309     }
1310 
1311     for (; k < point->totkey; k++, key++) {
1312       if (k) {
1313         sub_v3_v3v3(dv0, (key - 1)->co, key->co);
1314         tlen = normalize_v3(dv0);
1315         mul_v3_fl(dv0, (mul * (tlen - (key - 1)->length)));
1316       }
1317       if (k < point->totkey - 1) {
1318         sub_v3_v3v3(dv2, (key + 1)->co, key->co);
1319         tlen = normalize_v3(dv2);
1320         mul_v3_fl(dv2, mul * (tlen - key->length));
1321       }
1322       if (k) {
1323         add_v3_v3((key - 1)->co, dv1);
1324       }
1325       add_v3_v3v3(dv1, dv0, dv2);
1326     }
1327   }
1328 }
1329 
1330 /* try to find a nice solution to keep distances between neighboring keys */
pe_iterate_lengths(Scene * scene,PTCacheEdit * edit)1331 static void pe_iterate_lengths(Scene *scene, PTCacheEdit *edit)
1332 {
1333   ParticleEditSettings *pset = PE_settings(scene);
1334   if (edit == 0 || (pset->flag & PE_KEEP_LENGTHS) == 0) {
1335     return;
1336   }
1337   if (edit->psys && edit->psys->flag & PSYS_GLOBAL_HAIR) {
1338     return;
1339   }
1340 
1341   IterateLengthsIterData iter_data;
1342   iter_data.edit = edit;
1343   iter_data.pset = pset;
1344 
1345   TaskParallelSettings settings;
1346   BLI_parallel_range_settings_defaults(&settings);
1347   BLI_task_parallel_range(0, edit->totpoint, &iter_data, iterate_lengths_iter, &settings);
1348 }
1349 
1350 /* set current distances to be kept between neighboring keys */
recalc_lengths(PTCacheEdit * edit)1351 void recalc_lengths(PTCacheEdit *edit)
1352 {
1353   POINT_P;
1354   KEY_K;
1355 
1356   if (edit == 0) {
1357     return;
1358   }
1359 
1360   LOOP_EDITED_POINTS {
1361     key = point->keys;
1362     for (k = 0; k < point->totkey - 1; k++, key++) {
1363       key->length = len_v3v3(key->co, (key + 1)->co);
1364     }
1365   }
1366 }
1367 
1368 /* calculate a tree for finding nearest emitter's vertice */
recalc_emitter_field(Depsgraph * UNUSED (depsgraph),Object * UNUSED (ob),ParticleSystem * psys)1369 void recalc_emitter_field(Depsgraph *UNUSED(depsgraph), Object *UNUSED(ob), ParticleSystem *psys)
1370 {
1371   PTCacheEdit *edit = psys->edit;
1372   Mesh *mesh = edit->psmd_eval->mesh_final;
1373   float *vec, *nor;
1374   int i, totface /*, totvert*/;
1375 
1376   if (!mesh) {
1377     return;
1378   }
1379 
1380   if (edit->emitter_cosnos) {
1381     MEM_freeN(edit->emitter_cosnos);
1382   }
1383 
1384   BLI_kdtree_3d_free(edit->emitter_field);
1385 
1386   totface = mesh->totface;
1387   /*totvert=dm->getNumVerts(dm);*/ /*UNUSED*/
1388 
1389   edit->emitter_cosnos = MEM_callocN(sizeof(float[6]) * totface, "emitter cosnos");
1390 
1391   edit->emitter_field = BLI_kdtree_3d_new(totface);
1392 
1393   vec = edit->emitter_cosnos;
1394   nor = vec + 3;
1395 
1396   for (i = 0; i < totface; i++, vec += 6, nor += 6) {
1397     MFace *mface = &mesh->mface[i];
1398     MVert *mvert;
1399 
1400     mvert = &mesh->mvert[mface->v1];
1401     copy_v3_v3(vec, mvert->co);
1402     copy_v3fl_v3s(nor, mvert->no);
1403 
1404     mvert = &mesh->mvert[mface->v2];
1405     add_v3_v3v3(vec, vec, mvert->co);
1406     add_v3fl_v3fl_v3s(nor, nor, mvert->no);
1407 
1408     mvert = &mesh->mvert[mface->v3];
1409     add_v3_v3v3(vec, vec, mvert->co);
1410     add_v3fl_v3fl_v3s(nor, nor, mvert->no);
1411 
1412     if (mface->v4) {
1413       mvert = &mesh->mvert[mface->v4];
1414       add_v3_v3v3(vec, vec, mvert->co);
1415       add_v3fl_v3fl_v3s(nor, nor, mvert->no);
1416 
1417       mul_v3_fl(vec, 0.25);
1418     }
1419     else {
1420       mul_v3_fl(vec, 1.0f / 3.0f);
1421     }
1422 
1423     normalize_v3(nor);
1424 
1425     BLI_kdtree_3d_insert(edit->emitter_field, i, vec);
1426   }
1427 
1428   BLI_kdtree_3d_balance(edit->emitter_field);
1429 }
1430 
PE_update_selection(Depsgraph * depsgraph,Scene * scene,Object * ob,int useflag)1431 static void PE_update_selection(Depsgraph *depsgraph, Scene *scene, Object *ob, int useflag)
1432 {
1433   PTCacheEdit *edit = PE_get_current(depsgraph, scene, ob);
1434   HairKey *hkey;
1435   POINT_P;
1436   KEY_K;
1437 
1438   /* flag all particles to be updated if not using flag */
1439   if (!useflag) {
1440     LOOP_POINTS {
1441       point->flag |= PEP_EDIT_RECALC;
1442     }
1443   }
1444 
1445   /* flush edit key flag to hair key flag to preserve selection
1446    * on save */
1447   if (edit->psys) {
1448     LOOP_POINTS {
1449       hkey = edit->psys->particles[p].hair;
1450       LOOP_KEYS {
1451         hkey->editflag = key->flag;
1452         hkey++;
1453       }
1454     }
1455   }
1456 
1457   psys_cache_edit_paths(depsgraph, scene, ob, edit, CFRA, G.is_rendering);
1458 
1459   /* disable update flag */
1460   LOOP_POINTS {
1461     point->flag &= ~PEP_EDIT_RECALC;
1462   }
1463 
1464   DEG_id_tag_update(&ob->id, ID_RECALC_SELECT);
1465 }
1466 
update_world_cos(Object * ob,PTCacheEdit * edit)1467 void update_world_cos(Object *ob, PTCacheEdit *edit)
1468 {
1469   ParticleSystem *psys = edit->psys;
1470   ParticleSystemModifierData *psmd_eval = edit->psmd_eval;
1471   POINT_P;
1472   KEY_K;
1473   float hairmat[4][4];
1474 
1475   if (psys == 0 || psys->edit == 0 || psmd_eval == NULL || psmd_eval->mesh_final == NULL) {
1476     return;
1477   }
1478 
1479   LOOP_POINTS {
1480     if (!(psys->flag & PSYS_GLOBAL_HAIR)) {
1481       psys_mat_hair_to_global(
1482           ob, psmd_eval->mesh_final, psys->part->from, psys->particles + p, hairmat);
1483     }
1484 
1485     LOOP_KEYS {
1486       copy_v3_v3(key->world_co, key->co);
1487       if (!(psys->flag & PSYS_GLOBAL_HAIR)) {
1488         mul_m4_v3(hairmat, key->world_co);
1489       }
1490     }
1491   }
1492 }
update_velocities(PTCacheEdit * edit)1493 static void update_velocities(PTCacheEdit *edit)
1494 {
1495   /*TODO: get frs_sec properly */
1496   float vec1[3], vec2[3], frs_sec, dfra;
1497   POINT_P;
1498   KEY_K;
1499 
1500   /* hair doesn't use velocities */
1501   if (edit->psys || !edit->points || !edit->points->keys->vel) {
1502     return;
1503   }
1504 
1505   frs_sec = edit->pid.flag & PTCACHE_VEL_PER_SEC ? 25.0f : 1.0f;
1506 
1507   LOOP_EDITED_POINTS {
1508     LOOP_KEYS {
1509       if (k == 0) {
1510         dfra = *(key + 1)->time - *key->time;
1511 
1512         if (dfra <= 0.0f) {
1513           continue;
1514         }
1515 
1516         sub_v3_v3v3(key->vel, (key + 1)->co, key->co);
1517 
1518         if (point->totkey > 2) {
1519           sub_v3_v3v3(vec1, (key + 1)->co, (key + 2)->co);
1520           project_v3_v3v3(vec2, vec1, key->vel);
1521           sub_v3_v3v3(vec2, vec1, vec2);
1522           madd_v3_v3fl(key->vel, vec2, 0.5f);
1523         }
1524       }
1525       else if (k == point->totkey - 1) {
1526         dfra = *key->time - *(key - 1)->time;
1527 
1528         if (dfra <= 0.0f) {
1529           continue;
1530         }
1531 
1532         sub_v3_v3v3(key->vel, key->co, (key - 1)->co);
1533 
1534         if (point->totkey > 2) {
1535           sub_v3_v3v3(vec1, (key - 2)->co, (key - 1)->co);
1536           project_v3_v3v3(vec2, vec1, key->vel);
1537           sub_v3_v3v3(vec2, vec1, vec2);
1538           madd_v3_v3fl(key->vel, vec2, 0.5f);
1539         }
1540       }
1541       else {
1542         dfra = *(key + 1)->time - *(key - 1)->time;
1543 
1544         if (dfra <= 0.0f) {
1545           continue;
1546         }
1547 
1548         sub_v3_v3v3(key->vel, (key + 1)->co, (key - 1)->co);
1549       }
1550       mul_v3_fl(key->vel, frs_sec / dfra);
1551     }
1552   }
1553 }
1554 
PE_update_object(Depsgraph * depsgraph,Scene * scene,Object * ob,int useflag)1555 void PE_update_object(Depsgraph *depsgraph, Scene *scene, Object *ob, int useflag)
1556 {
1557   /* use this to do partial particle updates, not usable when adding or
1558    * removing, then a full redo is necessary and calling this may crash */
1559   ParticleEditSettings *pset = PE_settings(scene);
1560   PTCacheEdit *edit = PE_get_current(depsgraph, scene, ob);
1561   POINT_P;
1562 
1563   if (!edit) {
1564     return;
1565   }
1566 
1567   /* flag all particles to be updated if not using flag */
1568   if (!useflag) {
1569     LOOP_POINTS {
1570       point->flag |= PEP_EDIT_RECALC;
1571     }
1572   }
1573 
1574   /* do post process on particle edit keys */
1575   pe_iterate_lengths(scene, edit);
1576   pe_deflect_emitter(scene, ob, edit);
1577   PE_apply_lengths(scene, edit);
1578   if (pe_x_mirror(ob)) {
1579     PE_apply_mirror(ob, edit->psys);
1580   }
1581   if (edit->psys) {
1582     update_world_cos(ob, edit);
1583   }
1584   if (pset->flag & PE_AUTO_VELOCITY) {
1585     update_velocities(edit);
1586   }
1587 
1588   /* Only do this for emitter particles because drawing PE_FADE_TIME is not respected in 2.8 yet
1589    * and flagging with PEK_HIDE will prevent selection. This might get restored once this is
1590    * supported in drawing (but doesn't make much sense for hair anyways). */
1591   if (edit->psys && edit->psys->part->type == PART_EMITTER) {
1592     PE_hide_keys_time(scene, edit, CFRA);
1593   }
1594 
1595   /* regenerate path caches */
1596   psys_cache_edit_paths(depsgraph, scene, ob, edit, CFRA, G.is_rendering);
1597 
1598   /* disable update flag */
1599   LOOP_POINTS {
1600     point->flag &= ~PEP_EDIT_RECALC;
1601   }
1602 
1603   if (edit->psys) {
1604     edit->psys->flag &= ~PSYS_HAIR_UPDATED;
1605   }
1606 }
1607 
1608 /************************************************/
1609 /*          Edit Selections                     */
1610 /************************************************/
1611 
1612 /*-----selection callbacks-----*/
1613 
select_key(PEData * data,int point_index,int key_index,bool UNUSED (is_inside))1614 static void select_key(PEData *data, int point_index, int key_index, bool UNUSED(is_inside))
1615 {
1616   PTCacheEdit *edit = data->edit;
1617   PTCacheEditPoint *point = edit->points + point_index;
1618   PTCacheEditKey *key = point->keys + key_index;
1619 
1620   if (data->select) {
1621     key->flag |= PEK_SELECT;
1622   }
1623   else {
1624     key->flag &= ~PEK_SELECT;
1625   }
1626 
1627   point->flag |= PEP_EDIT_RECALC;
1628   data->is_changed = true;
1629 }
1630 
select_key_op(PEData * data,int point_index,int key_index,bool is_inside)1631 static void select_key_op(PEData *data, int point_index, int key_index, bool is_inside)
1632 {
1633   PTCacheEdit *edit = data->edit;
1634   PTCacheEditPoint *point = edit->points + point_index;
1635   PTCacheEditKey *key = point->keys + key_index;
1636   const bool is_select = key->flag & PEK_SELECT;
1637   const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
1638   if (sel_op_result != -1) {
1639     SET_FLAG_FROM_TEST(key->flag, sel_op_result, PEK_SELECT);
1640     point->flag |= PEP_EDIT_RECALC;
1641     data->is_changed = true;
1642   }
1643 }
1644 
select_keys(PEData * data,int point_index,int UNUSED (key_index),bool UNUSED (is_inside))1645 static void select_keys(PEData *data,
1646                         int point_index,
1647                         int UNUSED(key_index),
1648                         bool UNUSED(is_inside))
1649 {
1650   PTCacheEdit *edit = data->edit;
1651   PTCacheEditPoint *point = edit->points + point_index;
1652   KEY_K;
1653 
1654   LOOP_KEYS {
1655     if (data->select) {
1656       key->flag |= PEK_SELECT;
1657     }
1658     else {
1659       key->flag &= ~PEK_SELECT;
1660     }
1661   }
1662 
1663   point->flag |= PEP_EDIT_RECALC;
1664 }
1665 
extend_key_select(PEData * data,int point_index,int key_index,bool UNUSED (is_inside))1666 static void extend_key_select(PEData *data, int point_index, int key_index, bool UNUSED(is_inside))
1667 {
1668   PTCacheEdit *edit = data->edit;
1669   PTCacheEditPoint *point = edit->points + point_index;
1670   PTCacheEditKey *key = point->keys + key_index;
1671 
1672   if ((key->flag & PEK_SELECT) == 0) {
1673     key->flag |= PEK_SELECT;
1674     point->flag |= PEP_EDIT_RECALC;
1675     data->is_changed = true;
1676   }
1677 }
1678 
deselect_key_select(PEData * data,int point_index,int key_index,bool UNUSED (is_inside))1679 static void deselect_key_select(PEData *data,
1680                                 int point_index,
1681                                 int key_index,
1682                                 bool UNUSED(is_inside))
1683 {
1684   PTCacheEdit *edit = data->edit;
1685   PTCacheEditPoint *point = edit->points + point_index;
1686   PTCacheEditKey *key = point->keys + key_index;
1687 
1688   if ((key->flag & PEK_SELECT) != 0) {
1689     key->flag &= ~PEK_SELECT;
1690     point->flag |= PEP_EDIT_RECALC;
1691     data->is_changed = true;
1692   }
1693 }
1694 
toggle_key_select(PEData * data,int point_index,int key_index,bool UNUSED (is_inside))1695 static void toggle_key_select(PEData *data, int point_index, int key_index, bool UNUSED(is_inside))
1696 {
1697   PTCacheEdit *edit = data->edit;
1698   PTCacheEditPoint *point = edit->points + point_index;
1699   PTCacheEditKey *key = point->keys + key_index;
1700 
1701   key->flag ^= PEK_SELECT;
1702   point->flag |= PEP_EDIT_RECALC;
1703   data->is_changed = true;
1704 }
1705 
1706 /************************ de select all operator ************************/
1707 
select_action_apply(PTCacheEditPoint * point,PTCacheEditKey * key,int action)1708 static bool select_action_apply(PTCacheEditPoint *point, PTCacheEditKey *key, int action)
1709 {
1710   bool changed = false;
1711   switch (action) {
1712     case SEL_SELECT:
1713       if ((key->flag & PEK_SELECT) == 0) {
1714         key->flag |= PEK_SELECT;
1715         point->flag |= PEP_EDIT_RECALC;
1716         changed = true;
1717       }
1718       break;
1719     case SEL_DESELECT:
1720       if (key->flag & PEK_SELECT) {
1721         key->flag &= ~PEK_SELECT;
1722         point->flag |= PEP_EDIT_RECALC;
1723         changed = true;
1724       }
1725       break;
1726     case SEL_INVERT:
1727       if ((key->flag & PEK_SELECT) == 0) {
1728         key->flag |= PEK_SELECT;
1729         point->flag |= PEP_EDIT_RECALC;
1730         changed = true;
1731       }
1732       else {
1733         key->flag &= ~PEK_SELECT;
1734         point->flag |= PEP_EDIT_RECALC;
1735         changed = true;
1736       }
1737       break;
1738   }
1739   return changed;
1740 }
1741 
pe_select_all_exec(bContext * C,wmOperator * op)1742 static int pe_select_all_exec(bContext *C, wmOperator *op)
1743 {
1744   Scene *scene = CTX_data_scene(C);
1745   Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
1746   Object *ob = CTX_data_active_object(C);
1747   PTCacheEdit *edit = PE_get_current(depsgraph, scene, ob);
1748   POINT_P;
1749   KEY_K;
1750   int action = RNA_enum_get(op->ptr, "action");
1751 
1752   if (action == SEL_TOGGLE) {
1753     action = SEL_SELECT;
1754     LOOP_VISIBLE_POINTS {
1755       LOOP_SELECTED_KEYS {
1756         action = SEL_DESELECT;
1757         break;
1758       }
1759 
1760       if (action == SEL_DESELECT) {
1761         break;
1762       }
1763     }
1764   }
1765 
1766   bool changed = false;
1767   LOOP_VISIBLE_POINTS {
1768     LOOP_VISIBLE_KEYS {
1769       changed |= select_action_apply(point, key, action);
1770     }
1771   }
1772 
1773   if (changed) {
1774     PE_update_selection(depsgraph, scene, ob, 1);
1775     WM_event_add_notifier(C, NC_OBJECT | ND_PARTICLE | NA_SELECTED, ob);
1776   }
1777   return OPERATOR_FINISHED;
1778 }
1779 
PARTICLE_OT_select_all(wmOperatorType * ot)1780 void PARTICLE_OT_select_all(wmOperatorType *ot)
1781 {
1782   /* identifiers */
1783   ot->name = "(De)select All";
1784   ot->idname = "PARTICLE_OT_select_all";
1785   ot->description = "(De)select all particles' keys";
1786 
1787   /* api callbacks */
1788   ot->exec = pe_select_all_exec;
1789   ot->poll = PE_poll;
1790 
1791   /* flags */
1792   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1793 
1794   WM_operator_properties_select_all(ot);
1795 }
1796 
1797 /************************ pick select operator ************************/
1798 
PE_mouse_particles(bContext * C,const int mval[2],bool extend,bool deselect,bool toggle)1799 bool PE_mouse_particles(bContext *C, const int mval[2], bool extend, bool deselect, bool toggle)
1800 {
1801   PEData data;
1802   Scene *scene = CTX_data_scene(C);
1803   Object *ob = CTX_data_active_object(C);
1804   POINT_P;
1805   KEY_K;
1806 
1807   PE_set_view3d_data(C, &data);
1808 
1809   PTCacheEdit *edit = PE_get_current(data.depsgraph, scene, ob);
1810 
1811   if (!PE_start_edit(edit)) {
1812     return false;
1813   }
1814 
1815   if (!extend && !deselect && !toggle) {
1816     LOOP_VISIBLE_POINTS {
1817       LOOP_SELECTED_KEYS {
1818         key->flag &= ~PEK_SELECT;
1819         point->flag |= PEP_EDIT_RECALC;
1820       }
1821     }
1822   }
1823 
1824   data.mval = mval;
1825   data.rad = ED_view3d_select_dist_px();
1826 
1827   /* 1 = nearest only */
1828   if (extend) {
1829     for_mouse_hit_keys(&data, extend_key_select, PSEL_NEAREST);
1830   }
1831   else if (deselect) {
1832     for_mouse_hit_keys(&data, deselect_key_select, PSEL_NEAREST);
1833   }
1834   else {
1835     for_mouse_hit_keys(&data, toggle_key_select, PSEL_NEAREST);
1836   }
1837 
1838   if (data.is_changed) {
1839     PE_update_selection(data.depsgraph, scene, ob, 1);
1840     WM_event_add_notifier(C, NC_OBJECT | ND_PARTICLE | NA_SELECTED, data.ob);
1841   }
1842 
1843   return true;
1844 }
1845 
1846 /************************ select root operator ************************/
1847 
select_root(PEData * data,int point_index)1848 static void select_root(PEData *data, int point_index)
1849 {
1850   PTCacheEditPoint *point = data->edit->points + point_index;
1851   PTCacheEditKey *key = point->keys;
1852 
1853   if (point->flag & PEP_HIDE) {
1854     return;
1855   }
1856 
1857   if (data->select_action != SEL_TOGGLE) {
1858     data->is_changed = select_action_apply(point, key, data->select_action);
1859   }
1860   else if (key->flag & PEK_SELECT) {
1861     data->select_toggle_action = SEL_DESELECT;
1862   }
1863 }
1864 
select_roots_exec(bContext * C,wmOperator * op)1865 static int select_roots_exec(bContext *C, wmOperator *op)
1866 {
1867   PEData data;
1868   int action = RNA_enum_get(op->ptr, "action");
1869 
1870   PE_set_data(C, &data);
1871 
1872   if (action == SEL_TOGGLE) {
1873     data.select_action = SEL_TOGGLE;
1874     data.select_toggle_action = SEL_SELECT;
1875 
1876     foreach_point(&data, select_root);
1877 
1878     action = data.select_toggle_action;
1879   }
1880 
1881   data.select_action = action;
1882   foreach_point(&data, select_root);
1883 
1884   if (data.is_changed) {
1885     PE_update_selection(data.depsgraph, data.scene, data.ob, 1);
1886     WM_event_add_notifier(C, NC_OBJECT | ND_PARTICLE | NA_SELECTED, data.ob);
1887   }
1888   return OPERATOR_FINISHED;
1889 }
1890 
PARTICLE_OT_select_roots(wmOperatorType * ot)1891 void PARTICLE_OT_select_roots(wmOperatorType *ot)
1892 {
1893   /* identifiers */
1894   ot->name = "Select Roots";
1895   ot->idname = "PARTICLE_OT_select_roots";
1896   ot->description = "Select roots of all visible particles";
1897 
1898   /* api callbacks */
1899   ot->exec = select_roots_exec;
1900   ot->poll = PE_poll;
1901 
1902   /* flags */
1903   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1904 
1905   /* properties */
1906   WM_operator_properties_select_action(ot, SEL_SELECT, false);
1907 }
1908 
1909 /************************ select tip operator ************************/
1910 
select_tip(PEData * data,int point_index)1911 static void select_tip(PEData *data, int point_index)
1912 {
1913   PTCacheEditPoint *point = data->edit->points + point_index;
1914   PTCacheEditKey *key;
1915 
1916   if (point->totkey == 0) {
1917     return;
1918   }
1919 
1920   key = &point->keys[point->totkey - 1];
1921 
1922   if (point->flag & PEP_HIDE) {
1923     return;
1924   }
1925 
1926   if (data->select_action != SEL_TOGGLE) {
1927     data->is_changed = select_action_apply(point, key, data->select_action);
1928   }
1929   else if (key->flag & PEK_SELECT) {
1930     data->select_toggle_action = SEL_DESELECT;
1931   }
1932 }
1933 
select_tips_exec(bContext * C,wmOperator * op)1934 static int select_tips_exec(bContext *C, wmOperator *op)
1935 {
1936   PEData data;
1937   int action = RNA_enum_get(op->ptr, "action");
1938 
1939   PE_set_data(C, &data);
1940 
1941   if (action == SEL_TOGGLE) {
1942     data.select_action = SEL_TOGGLE;
1943     data.select_toggle_action = SEL_SELECT;
1944 
1945     foreach_point(&data, select_tip);
1946 
1947     action = data.select_toggle_action;
1948   }
1949 
1950   data.select_action = action;
1951   foreach_point(&data, select_tip);
1952 
1953   if (data.is_changed) {
1954     PE_update_selection(data.depsgraph, data.scene, data.ob, 1);
1955     WM_event_add_notifier(C, NC_OBJECT | ND_PARTICLE | NA_SELECTED, data.ob);
1956 
1957     return OPERATOR_FINISHED;
1958   }
1959   return OPERATOR_CANCELLED;
1960 }
1961 
PARTICLE_OT_select_tips(wmOperatorType * ot)1962 void PARTICLE_OT_select_tips(wmOperatorType *ot)
1963 {
1964   /* identifiers */
1965   ot->name = "Select Tips";
1966   ot->idname = "PARTICLE_OT_select_tips";
1967   ot->description = "Select tips of all visible particles";
1968 
1969   /* api callbacks */
1970   ot->exec = select_tips_exec;
1971   ot->poll = PE_poll;
1972 
1973   /* flags */
1974   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1975 
1976   /* properties */
1977   WM_operator_properties_select_action(ot, SEL_SELECT, false);
1978 }
1979 
1980 /*********************** select random operator ************************/
1981 
1982 enum { RAN_HAIR, RAN_POINTS };
1983 
1984 static const EnumPropertyItem select_random_type_items[] = {
1985     {RAN_HAIR, "HAIR", 0, "Hair", ""},
1986     {RAN_POINTS, "POINTS", 0, "Points", ""},
1987     {0, NULL, 0, NULL, NULL},
1988 };
1989 
select_random_exec(bContext * C,wmOperator * op)1990 static int select_random_exec(bContext *C, wmOperator *op)
1991 {
1992   PEData data;
1993   int type;
1994 
1995   /* used by LOOP_VISIBLE_POINTS, LOOP_VISIBLE_KEYS and LOOP_KEYS */
1996   PTCacheEdit *edit;
1997   PTCacheEditPoint *point;
1998   PTCacheEditKey *key;
1999   int p;
2000   int k;
2001 
2002   const float randfac = RNA_float_get(op->ptr, "percent") / 100.0f;
2003   const int seed = WM_operator_properties_select_random_seed_increment_get(op);
2004   const bool select = (RNA_enum_get(op->ptr, "action") == SEL_SELECT);
2005   RNG *rng;
2006 
2007   type = RNA_enum_get(op->ptr, "type");
2008 
2009   PE_set_data(C, &data);
2010   data.select_action = SEL_SELECT;
2011   edit = PE_get_current(data.depsgraph, data.scene, data.ob);
2012 
2013   rng = BLI_rng_new_srandom(seed);
2014 
2015   switch (type) {
2016     case RAN_HAIR:
2017       LOOP_VISIBLE_POINTS {
2018         int flag = ((BLI_rng_get_float(rng) < randfac) == select) ? SEL_SELECT : SEL_DESELECT;
2019         LOOP_KEYS {
2020           data.is_changed |= select_action_apply(point, key, flag);
2021         }
2022       }
2023       break;
2024     case RAN_POINTS:
2025       LOOP_VISIBLE_POINTS {
2026         LOOP_VISIBLE_KEYS {
2027           int flag = ((BLI_rng_get_float(rng) < randfac) == select) ? SEL_SELECT : SEL_DESELECT;
2028           data.is_changed |= select_action_apply(point, key, flag);
2029         }
2030       }
2031       break;
2032   }
2033 
2034   BLI_rng_free(rng);
2035 
2036   if (data.is_changed) {
2037     PE_update_selection(data.depsgraph, data.scene, data.ob, 1);
2038     WM_event_add_notifier(C, NC_OBJECT | ND_PARTICLE | NA_SELECTED, data.ob);
2039   }
2040   return OPERATOR_FINISHED;
2041 }
2042 
PARTICLE_OT_select_random(wmOperatorType * ot)2043 void PARTICLE_OT_select_random(wmOperatorType *ot)
2044 {
2045   /* identifiers */
2046   ot->name = "Select Random";
2047   ot->idname = "PARTICLE_OT_select_random";
2048   ot->description = "Select a randomly distributed set of hair or points";
2049 
2050   /* api callbacks */
2051   ot->exec = select_random_exec;
2052   ot->poll = PE_poll;
2053 
2054   /* flags */
2055   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2056 
2057   /* properties */
2058   WM_operator_properties_select_random(ot);
2059   ot->prop = RNA_def_enum(ot->srna,
2060                           "type",
2061                           select_random_type_items,
2062                           RAN_HAIR,
2063                           "Type",
2064                           "Select either hair or points");
2065 }
2066 
2067 /************************ select linked operator ************************/
2068 
select_linked_exec(bContext * C,wmOperator * UNUSED (op))2069 static int select_linked_exec(bContext *C, wmOperator *UNUSED(op))
2070 {
2071   PEData data;
2072   PE_set_data(C, &data);
2073   data.select = true;
2074 
2075   foreach_selected_key(&data, select_keys);
2076 
2077   PE_update_selection(data.depsgraph, data.scene, data.ob, 1);
2078   WM_event_add_notifier(C, NC_OBJECT | ND_PARTICLE | NA_SELECTED, data.ob);
2079 
2080   return OPERATOR_FINISHED;
2081 }
2082 
PARTICLE_OT_select_linked(wmOperatorType * ot)2083 void PARTICLE_OT_select_linked(wmOperatorType *ot)
2084 {
2085   /* identifiers */
2086   ot->name = "Select Linked All";
2087   ot->idname = "PARTICLE_OT_select_linked";
2088   ot->description = "Select all keys linked to already selected ones";
2089 
2090   /* api callbacks */
2091   ot->exec = select_linked_exec;
2092   ot->poll = PE_poll;
2093 
2094   /* flags */
2095   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2096 
2097   /* properties */
2098 }
2099 
select_linked_pick_exec(bContext * C,wmOperator * op)2100 static int select_linked_pick_exec(bContext *C, wmOperator *op)
2101 {
2102   PEData data;
2103   int mval[2];
2104   int location[2];
2105 
2106   RNA_int_get_array(op->ptr, "location", location);
2107   mval[0] = location[0];
2108   mval[1] = location[1];
2109 
2110   PE_set_view3d_data(C, &data);
2111   data.mval = mval;
2112   data.rad = 75.0f;
2113   data.select = !RNA_boolean_get(op->ptr, "deselect");
2114 
2115   for_mouse_hit_keys(&data, select_keys, PSEL_NEAREST);
2116   PE_update_selection(data.depsgraph, data.scene, data.ob, 1);
2117   WM_event_add_notifier(C, NC_OBJECT | ND_PARTICLE | NA_SELECTED, data.ob);
2118 
2119   return OPERATOR_FINISHED;
2120 }
2121 
select_linked_pick_invoke(bContext * C,wmOperator * op,const wmEvent * event)2122 static int select_linked_pick_invoke(bContext *C, wmOperator *op, const wmEvent *event)
2123 {
2124   RNA_int_set_array(op->ptr, "location", event->mval);
2125   return select_linked_pick_exec(C, op);
2126 }
2127 
PARTICLE_OT_select_linked_pick(wmOperatorType * ot)2128 void PARTICLE_OT_select_linked_pick(wmOperatorType *ot)
2129 {
2130   /* identifiers */
2131   ot->name = "Select Linked";
2132   ot->idname = "PARTICLE_OT_select_linked_pick";
2133   ot->description = "Select nearest particle from mouse pointer";
2134 
2135   /* api callbacks */
2136   ot->exec = select_linked_pick_exec;
2137   ot->invoke = select_linked_pick_invoke;
2138   ot->poll = PE_poll_view3d;
2139 
2140   /* flags */
2141   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2142 
2143   /* properties */
2144   RNA_def_boolean(
2145       ot->srna, "deselect", 0, "Deselect", "Deselect linked keys rather than selecting them");
2146   RNA_def_int_vector(ot->srna, "location", 2, NULL, 0, INT_MAX, "Location", "", 0, 16384);
2147 }
2148 
2149 /************************ box select operator ************************/
PE_deselect_all_visible_ex(PTCacheEdit * edit)2150 bool PE_deselect_all_visible_ex(PTCacheEdit *edit)
2151 {
2152   bool changed = false;
2153   POINT_P;
2154   KEY_K;
2155 
2156   LOOP_VISIBLE_POINTS {
2157     LOOP_SELECTED_KEYS {
2158       if ((key->flag & PEK_SELECT) != 0) {
2159         key->flag &= ~PEK_SELECT;
2160         point->flag |= PEP_EDIT_RECALC;
2161         changed = true;
2162       }
2163     }
2164   }
2165   return changed;
2166 }
2167 
PE_deselect_all_visible(bContext * C)2168 bool PE_deselect_all_visible(bContext *C)
2169 {
2170   Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
2171   Scene *scene = CTX_data_scene(C);
2172   Object *ob = CTX_data_active_object(C);
2173   PTCacheEdit *edit = PE_get_current(depsgraph, scene, ob);
2174   if (!PE_start_edit(edit)) {
2175     return false;
2176   }
2177   return PE_deselect_all_visible_ex(edit);
2178 }
2179 
PE_box_select(bContext * C,const rcti * rect,const int sel_op)2180 bool PE_box_select(bContext *C, const rcti *rect, const int sel_op)
2181 {
2182   Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
2183   Scene *scene = CTX_data_scene(C);
2184   Object *ob = CTX_data_active_object(C);
2185   PTCacheEdit *edit = PE_get_current(depsgraph, scene, ob);
2186   PEData data;
2187 
2188   if (!PE_start_edit(edit)) {
2189     return false;
2190   }
2191 
2192   PE_set_view3d_data(C, &data);
2193   data.rect = rect;
2194   data.sel_op = sel_op;
2195 
2196   if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
2197     data.is_changed = PE_deselect_all_visible_ex(edit);
2198   }
2199 
2200   if (BLI_rcti_is_empty(rect)) {
2201     /* pass */
2202   }
2203   else {
2204     for_mouse_hit_keys(&data, select_key_op, PSEL_ALL_KEYS);
2205   }
2206 
2207   if (data.is_changed) {
2208     PE_update_selection(data.depsgraph, scene, ob, 1);
2209     WM_event_add_notifier(C, NC_OBJECT | ND_PARTICLE | NA_SELECTED, ob);
2210   }
2211   return data.is_changed;
2212 }
2213 
2214 /************************ circle select operator ************************/
2215 
PE_circle_select(bContext * C,const int sel_op,const int mval[2],float rad)2216 bool PE_circle_select(bContext *C, const int sel_op, const int mval[2], float rad)
2217 {
2218   BLI_assert(ELEM(sel_op, SEL_OP_SET, SEL_OP_ADD, SEL_OP_SUB));
2219   Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
2220   Scene *scene = CTX_data_scene(C);
2221   Object *ob = CTX_data_active_object(C);
2222   PTCacheEdit *edit = PE_get_current(depsgraph, scene, ob);
2223   PEData data;
2224 
2225   if (!PE_start_edit(edit)) {
2226     return false;
2227   }
2228 
2229   const bool select = (sel_op != SEL_OP_SUB);
2230 
2231   PE_set_view3d_data(C, &data);
2232   data.mval = mval;
2233   data.rad = rad;
2234   data.select = select;
2235 
2236   if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
2237     data.is_changed = PE_deselect_all_visible_ex(edit);
2238   }
2239   for_mouse_hit_keys(&data, select_key, 0);
2240   if (data.is_changed) {
2241     PE_update_selection(data.depsgraph, scene, ob, 1);
2242     WM_event_add_notifier(C, NC_OBJECT | ND_PARTICLE | NA_SELECTED, ob);
2243   }
2244   return data.is_changed;
2245 }
2246 
2247 /************************ lasso select operator ************************/
2248 
PE_lasso_select(bContext * C,const int mcoords[][2],const int mcoords_len,const int sel_op)2249 int PE_lasso_select(bContext *C, const int mcoords[][2], const int mcoords_len, const int sel_op)
2250 {
2251   Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
2252   Scene *scene = CTX_data_scene(C);
2253   Object *ob = CTX_data_active_object(C);
2254   ARegion *region = CTX_wm_region(C);
2255   ParticleEditSettings *pset = PE_settings(scene);
2256   PTCacheEdit *edit = PE_get_current(depsgraph, scene, ob);
2257   POINT_P;
2258   KEY_K;
2259   float co[3], mat[4][4];
2260   int screen_co[2];
2261 
2262   PEData data;
2263 
2264   unit_m4(mat);
2265 
2266   if (!PE_start_edit(edit)) {
2267     return OPERATOR_CANCELLED;
2268   }
2269 
2270   /* only for depths */
2271   PE_set_view3d_data(C, &data);
2272 
2273   if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
2274     data.is_changed |= PE_deselect_all_visible_ex(edit);
2275   }
2276 
2277   ParticleSystem *psys = edit->psys;
2278   ParticleSystemModifierData *psmd_eval = edit->psmd_eval;
2279   LOOP_VISIBLE_POINTS {
2280     if (edit->psys && !(psys->flag & PSYS_GLOBAL_HAIR)) {
2281       psys_mat_hair_to_global(
2282           ob, psmd_eval->mesh_final, psys->part->from, psys->particles + p, mat);
2283     }
2284 
2285     if (pset->selectmode == SCE_SELECT_POINT) {
2286       LOOP_VISIBLE_KEYS {
2287         copy_v3_v3(co, key->co);
2288         mul_m4_v3(mat, co);
2289         const bool is_select = key->flag & PEK_SELECT;
2290         const bool is_inside =
2291             ((ED_view3d_project_int_global(region, co, screen_co, V3D_PROJ_TEST_CLIP_WIN) ==
2292               V3D_PROJ_RET_OK) &&
2293              BLI_lasso_is_point_inside(
2294                  mcoords, mcoords_len, screen_co[0], screen_co[1], IS_CLIPPED) &&
2295              key_test_depth(&data, co, screen_co));
2296         const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside);
2297         if (sel_op_result != -1) {
2298           SET_FLAG_FROM_TEST(key->flag, sel_op_result, PEK_SELECT);
2299           point->flag |= PEP_EDIT_RECALC;
2300           data.is_changed = true;
2301         }
2302       }
2303     }
2304     else if (pset->selectmode == SCE_SELECT_END) {
2305       if (point->totkey) {
2306         key = point->keys + point->totkey - 1;
2307         copy_v3_v3(co, key->co);
2308         mul_m4_v3(mat, co);
2309         const bool is_select = key->flag & PEK_SELECT;
2310         const bool is_inside =
2311             ((ED_view3d_project_int_global(region, co, screen_co, V3D_PROJ_TEST_CLIP_WIN) ==
2312               V3D_PROJ_RET_OK) &&
2313              BLI_lasso_is_point_inside(
2314                  mcoords, mcoords_len, screen_co[0], screen_co[1], IS_CLIPPED) &&
2315              key_test_depth(&data, co, screen_co));
2316         const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside);
2317         if (sel_op_result != -1) {
2318           SET_FLAG_FROM_TEST(key->flag, sel_op_result, PEK_SELECT);
2319           point->flag |= PEP_EDIT_RECALC;
2320           data.is_changed = true;
2321         }
2322       }
2323     }
2324   }
2325 
2326   if (data.is_changed) {
2327     PE_update_selection(data.depsgraph, scene, ob, 1);
2328     WM_event_add_notifier(C, NC_OBJECT | ND_PARTICLE | NA_SELECTED, ob);
2329     return OPERATOR_FINISHED;
2330   }
2331   return OPERATOR_CANCELLED;
2332 }
2333 
2334 /*************************** hide operator **************************/
2335 
hide_exec(bContext * C,wmOperator * op)2336 static int hide_exec(bContext *C, wmOperator *op)
2337 {
2338   Object *ob = CTX_data_active_object(C);
2339   Scene *scene = CTX_data_scene(C);
2340   Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
2341 
2342   PTCacheEdit *edit = PE_get_current(depsgraph, scene, ob);
2343   POINT_P;
2344   KEY_K;
2345 
2346   if (RNA_boolean_get(op->ptr, "unselected")) {
2347     LOOP_UNSELECTED_POINTS {
2348       point->flag |= PEP_HIDE;
2349       point->flag |= PEP_EDIT_RECALC;
2350 
2351       LOOP_KEYS {
2352         key->flag &= ~PEK_SELECT;
2353       }
2354     }
2355   }
2356   else {
2357     LOOP_SELECTED_POINTS {
2358       point->flag |= PEP_HIDE;
2359       point->flag |= PEP_EDIT_RECALC;
2360 
2361       LOOP_KEYS {
2362         key->flag &= ~PEK_SELECT;
2363       }
2364     }
2365   }
2366 
2367   PE_update_selection(depsgraph, scene, ob, 1);
2368   WM_event_add_notifier(C, NC_OBJECT | ND_PARTICLE | NA_SELECTED, ob);
2369 
2370   return OPERATOR_FINISHED;
2371 }
2372 
PARTICLE_OT_hide(wmOperatorType * ot)2373 void PARTICLE_OT_hide(wmOperatorType *ot)
2374 {
2375   /* identifiers */
2376   ot->name = "Hide Selected";
2377   ot->idname = "PARTICLE_OT_hide";
2378   ot->description = "Hide selected particles";
2379 
2380   /* api callbacks */
2381   ot->exec = hide_exec;
2382   ot->poll = PE_poll;
2383 
2384   /* flags */
2385   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2386 
2387   /* props */
2388   RNA_def_boolean(ot->srna, "unselected", 0, "Unselected", "Hide unselected rather than selected");
2389 }
2390 
2391 /*************************** reveal operator **************************/
2392 
reveal_exec(bContext * C,wmOperator * op)2393 static int reveal_exec(bContext *C, wmOperator *op)
2394 {
2395   Object *ob = CTX_data_active_object(C);
2396   Scene *scene = CTX_data_scene(C);
2397   Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
2398   PTCacheEdit *edit = PE_get_current(depsgraph, scene, ob);
2399   const bool select = RNA_boolean_get(op->ptr, "select");
2400   POINT_P;
2401   KEY_K;
2402 
2403   LOOP_POINTS {
2404     if (point->flag & PEP_HIDE) {
2405       point->flag &= ~PEP_HIDE;
2406       point->flag |= PEP_EDIT_RECALC;
2407 
2408       LOOP_KEYS {
2409         SET_FLAG_FROM_TEST(key->flag, select, PEK_SELECT);
2410       }
2411     }
2412   }
2413 
2414   PE_update_selection(depsgraph, scene, ob, 1);
2415   WM_event_add_notifier(C, NC_OBJECT | ND_PARTICLE | NA_SELECTED, ob);
2416 
2417   return OPERATOR_FINISHED;
2418 }
2419 
PARTICLE_OT_reveal(wmOperatorType * ot)2420 void PARTICLE_OT_reveal(wmOperatorType *ot)
2421 {
2422   /* identifiers */
2423   ot->name = "Reveal";
2424   ot->idname = "PARTICLE_OT_reveal";
2425   ot->description = "Show hidden particles";
2426 
2427   /* api callbacks */
2428   ot->exec = reveal_exec;
2429   ot->poll = PE_poll;
2430 
2431   /* flags */
2432   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2433 
2434   /* props */
2435   RNA_def_boolean(ot->srna, "select", true, "Select", "");
2436 }
2437 
2438 /************************ select less operator ************************/
2439 
select_less_keys(PEData * data,int point_index)2440 static void select_less_keys(PEData *data, int point_index)
2441 {
2442   PTCacheEdit *edit = data->edit;
2443   PTCacheEditPoint *point = edit->points + point_index;
2444   KEY_K;
2445 
2446   LOOP_SELECTED_KEYS {
2447     if (k == 0) {
2448       if (((key + 1)->flag & PEK_SELECT) == 0) {
2449         key->flag |= PEK_TAG;
2450       }
2451     }
2452     else if (k == point->totkey - 1) {
2453       if (((key - 1)->flag & PEK_SELECT) == 0) {
2454         key->flag |= PEK_TAG;
2455       }
2456     }
2457     else {
2458       if ((((key - 1)->flag & (key + 1)->flag) & PEK_SELECT) == 0) {
2459         key->flag |= PEK_TAG;
2460       }
2461     }
2462   }
2463 
2464   LOOP_KEYS {
2465     if ((key->flag & PEK_TAG) && (key->flag & PEK_SELECT)) {
2466       key->flag &= ~(PEK_TAG | PEK_SELECT);
2467       point->flag |= PEP_EDIT_RECALC; /* redraw selection only */
2468       data->is_changed = true;
2469     }
2470   }
2471 }
2472 
select_less_exec(bContext * C,wmOperator * UNUSED (op))2473 static int select_less_exec(bContext *C, wmOperator *UNUSED(op))
2474 {
2475   PEData data;
2476 
2477   PE_set_data(C, &data);
2478   foreach_point(&data, select_less_keys);
2479 
2480   PE_update_selection(data.depsgraph, data.scene, data.ob, 1);
2481   WM_event_add_notifier(C, NC_OBJECT | ND_PARTICLE | NA_SELECTED, data.ob);
2482 
2483   return OPERATOR_FINISHED;
2484 }
2485 
PARTICLE_OT_select_less(wmOperatorType * ot)2486 void PARTICLE_OT_select_less(wmOperatorType *ot)
2487 {
2488   /* identifiers */
2489   ot->name = "Select Less";
2490   ot->idname = "PARTICLE_OT_select_less";
2491   ot->description = "Deselect boundary selected keys of each particle";
2492 
2493   /* api callbacks */
2494   ot->exec = select_less_exec;
2495   ot->poll = PE_poll;
2496 
2497   /* flags */
2498   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2499 }
2500 
2501 /************************ select more operator ************************/
2502 
select_more_keys(PEData * data,int point_index)2503 static void select_more_keys(PEData *data, int point_index)
2504 {
2505   PTCacheEdit *edit = data->edit;
2506   PTCacheEditPoint *point = edit->points + point_index;
2507   KEY_K;
2508 
2509   LOOP_KEYS {
2510     if (key->flag & PEK_SELECT) {
2511       continue;
2512     }
2513 
2514     if (k == 0) {
2515       if ((key + 1)->flag & PEK_SELECT) {
2516         key->flag |= PEK_TAG;
2517       }
2518     }
2519     else if (k == point->totkey - 1) {
2520       if ((key - 1)->flag & PEK_SELECT) {
2521         key->flag |= PEK_TAG;
2522       }
2523     }
2524     else {
2525       if (((key - 1)->flag | (key + 1)->flag) & PEK_SELECT) {
2526         key->flag |= PEK_TAG;
2527       }
2528     }
2529   }
2530 
2531   LOOP_KEYS {
2532     if ((key->flag & PEK_TAG) && (key->flag & PEK_SELECT) == 0) {
2533       key->flag &= ~PEK_TAG;
2534       key->flag |= PEK_SELECT;
2535       point->flag |= PEP_EDIT_RECALC; /* redraw selection only */
2536       data->is_changed = true;
2537     }
2538   }
2539 }
2540 
select_more_exec(bContext * C,wmOperator * UNUSED (op))2541 static int select_more_exec(bContext *C, wmOperator *UNUSED(op))
2542 {
2543   PEData data;
2544 
2545   PE_set_data(C, &data);
2546   foreach_point(&data, select_more_keys);
2547 
2548   PE_update_selection(data.depsgraph, data.scene, data.ob, 1);
2549   WM_event_add_notifier(C, NC_OBJECT | ND_PARTICLE | NA_SELECTED, data.ob);
2550 
2551   return OPERATOR_FINISHED;
2552 }
2553 
PARTICLE_OT_select_more(wmOperatorType * ot)2554 void PARTICLE_OT_select_more(wmOperatorType *ot)
2555 {
2556   /* identifiers */
2557   ot->name = "Select More";
2558   ot->idname = "PARTICLE_OT_select_more";
2559   ot->description = "Select keys linked to boundary selected keys of each particle";
2560 
2561   /* api callbacks */
2562   ot->exec = select_more_exec;
2563   ot->poll = PE_poll;
2564 
2565   /* flags */
2566   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2567 }
2568 
2569 /************************ rekey operator ************************/
2570 
rekey_particle(PEData * data,int pa_index)2571 static void rekey_particle(PEData *data, int pa_index)
2572 {
2573   PTCacheEdit *edit = data->edit;
2574   ParticleSystem *psys = edit->psys;
2575   ParticleSimulationData sim = {0};
2576   ParticleData *pa = psys->particles + pa_index;
2577   PTCacheEditPoint *point = edit->points + pa_index;
2578   ParticleKey state;
2579   HairKey *key, *new_keys, *okey;
2580   PTCacheEditKey *ekey;
2581   float dval, sta, end;
2582   int k;
2583 
2584   sim.depsgraph = data->depsgraph;
2585   sim.scene = data->scene;
2586   sim.ob = data->ob;
2587   sim.psys = edit->psys;
2588 
2589   pa->flag |= PARS_REKEY;
2590 
2591   key = new_keys = MEM_callocN(data->totrekey * sizeof(HairKey), "Hair re-key keys");
2592 
2593   okey = pa->hair;
2594   /* root and tip stay the same */
2595   copy_v3_v3(key->co, okey->co);
2596   copy_v3_v3((key + data->totrekey - 1)->co, (okey + pa->totkey - 1)->co);
2597 
2598   sta = key->time = okey->time;
2599   end = (key + data->totrekey - 1)->time = (okey + pa->totkey - 1)->time;
2600   dval = (end - sta) / (float)(data->totrekey - 1);
2601 
2602   /* interpolate new keys from old ones */
2603   for (k = 1, key++; k < data->totrekey - 1; k++, key++) {
2604     state.time = (float)k / (float)(data->totrekey - 1);
2605     psys_get_particle_on_path(&sim, pa_index, &state, 0);
2606     copy_v3_v3(key->co, state.co);
2607     key->time = sta + k * dval;
2608   }
2609 
2610   /* replace keys */
2611   if (pa->hair) {
2612     MEM_freeN(pa->hair);
2613   }
2614   pa->hair = new_keys;
2615 
2616   point->totkey = pa->totkey = data->totrekey;
2617 
2618   if (point->keys) {
2619     MEM_freeN(point->keys);
2620   }
2621   ekey = point->keys = MEM_callocN(pa->totkey * sizeof(PTCacheEditKey), "Hair re-key edit keys");
2622 
2623   for (k = 0, key = pa->hair; k < pa->totkey; k++, key++, ekey++) {
2624     ekey->co = key->co;
2625     ekey->time = &key->time;
2626     ekey->flag |= PEK_SELECT;
2627     if (!(psys->flag & PSYS_GLOBAL_HAIR)) {
2628       ekey->flag |= PEK_USE_WCO;
2629     }
2630   }
2631 
2632   pa->flag &= ~PARS_REKEY;
2633   point->flag |= PEP_EDIT_RECALC;
2634 }
2635 
rekey_exec(bContext * C,wmOperator * op)2636 static int rekey_exec(bContext *C, wmOperator *op)
2637 {
2638   PEData data;
2639 
2640   PE_set_data(C, &data);
2641 
2642   data.dval = 1.0f / (float)(data.totrekey - 1);
2643   data.totrekey = RNA_int_get(op->ptr, "keys_number");
2644 
2645   foreach_selected_point(&data, rekey_particle);
2646 
2647   recalc_lengths(data.edit);
2648   PE_update_object(data.depsgraph, data.scene, data.ob, 1);
2649   WM_event_add_notifier(C, NC_OBJECT | ND_PARTICLE | NA_EDITED, data.ob);
2650 
2651   return OPERATOR_FINISHED;
2652 }
2653 
PARTICLE_OT_rekey(wmOperatorType * ot)2654 void PARTICLE_OT_rekey(wmOperatorType *ot)
2655 {
2656   /* identifiers */
2657   ot->name = "Rekey";
2658   ot->idname = "PARTICLE_OT_rekey";
2659   ot->description = "Change the number of keys of selected particles (root and tip keys included)";
2660 
2661   /* api callbacks */
2662   ot->exec = rekey_exec;
2663   ot->invoke = WM_operator_props_popup;
2664   ot->poll = PE_hair_poll;
2665 
2666   /* flags */
2667   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2668 
2669   /* properties */
2670   RNA_def_int(ot->srna, "keys_number", 2, 2, INT_MAX, "Number of Keys", "", 2, 100);
2671 }
2672 
rekey_particle_to_time(const bContext * C,Scene * scene,Object * ob,int pa_index,float path_time)2673 static void rekey_particle_to_time(
2674     const bContext *C, Scene *scene, Object *ob, int pa_index, float path_time)
2675 {
2676   Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
2677   PTCacheEdit *edit = PE_get_current(depsgraph, scene, ob);
2678   ParticleSystem *psys;
2679   ParticleSimulationData sim = {0};
2680   ParticleData *pa;
2681   ParticleKey state;
2682   HairKey *new_keys, *key;
2683   PTCacheEditKey *ekey;
2684   int k;
2685 
2686   if (!edit || !edit->psys) {
2687     return;
2688   }
2689 
2690   psys = edit->psys;
2691 
2692   sim.depsgraph = depsgraph;
2693   sim.scene = scene;
2694   sim.ob = ob;
2695   sim.psys = psys;
2696 
2697   pa = psys->particles + pa_index;
2698 
2699   pa->flag |= PARS_REKEY;
2700 
2701   key = new_keys = MEM_dupallocN(pa->hair);
2702 
2703   /* interpolate new keys from old ones (roots stay the same) */
2704   for (k = 1, key++; k < pa->totkey; k++, key++) {
2705     state.time = path_time * (float)k / (float)(pa->totkey - 1);
2706     psys_get_particle_on_path(&sim, pa_index, &state, 0);
2707     copy_v3_v3(key->co, state.co);
2708   }
2709 
2710   /* replace hair keys */
2711   if (pa->hair) {
2712     MEM_freeN(pa->hair);
2713   }
2714   pa->hair = new_keys;
2715 
2716   /* update edit pointers */
2717   for (k = 0, key = pa->hair, ekey = edit->points[pa_index].keys; k < pa->totkey;
2718        k++, key++, ekey++) {
2719     ekey->co = key->co;
2720     ekey->time = &key->time;
2721   }
2722 
2723   pa->flag &= ~PARS_REKEY;
2724 }
2725 
2726 /************************* utilities **************************/
2727 
remove_tagged_particles(Object * ob,ParticleSystem * psys,int mirror)2728 static int remove_tagged_particles(Object *ob, ParticleSystem *psys, int mirror)
2729 {
2730   PTCacheEdit *edit = psys->edit;
2731   ParticleData *pa, *npa = 0, *new_pars = 0;
2732   POINT_P;
2733   PTCacheEditPoint *npoint = 0, *new_points = 0;
2734   ParticleSystemModifierData *psmd_eval;
2735   int i, new_totpart = psys->totpart, removed = 0;
2736 
2737   if (mirror) {
2738     /* mirror tags */
2739     psmd_eval = edit->psmd_eval;
2740 
2741     LOOP_TAGGED_POINTS {
2742       PE_mirror_particle(ob, psmd_eval->mesh_final, psys, psys->particles + p, NULL);
2743     }
2744   }
2745 
2746   LOOP_TAGGED_POINTS {
2747     new_totpart--;
2748     removed++;
2749   }
2750 
2751   if (new_totpart != psys->totpart) {
2752     if (new_totpart) {
2753       npa = new_pars = MEM_callocN(new_totpart * sizeof(ParticleData), "ParticleData array");
2754       npoint = new_points = MEM_callocN(new_totpart * sizeof(PTCacheEditPoint),
2755                                         "PTCacheEditKey array");
2756 
2757       if (ELEM(NULL, new_pars, new_points)) {
2758         /* allocation error! */
2759         if (new_pars) {
2760           MEM_freeN(new_pars);
2761         }
2762         if (new_points) {
2763           MEM_freeN(new_points);
2764         }
2765         return 0;
2766       }
2767     }
2768 
2769     pa = psys->particles;
2770     point = edit->points;
2771     for (i = 0; i < psys->totpart; i++, pa++, point++) {
2772       if (point->flag & PEP_TAG) {
2773         if (point->keys) {
2774           MEM_freeN(point->keys);
2775         }
2776         if (pa->hair) {
2777           MEM_freeN(pa->hair);
2778         }
2779       }
2780       else {
2781         memcpy(npa, pa, sizeof(ParticleData));
2782         memcpy(npoint, point, sizeof(PTCacheEditPoint));
2783         npa++;
2784         npoint++;
2785       }
2786     }
2787 
2788     if (psys->particles) {
2789       MEM_freeN(psys->particles);
2790     }
2791     psys->particles = new_pars;
2792 
2793     if (edit->points) {
2794       MEM_freeN(edit->points);
2795     }
2796     edit->points = new_points;
2797 
2798     if (edit->mirror_cache) {
2799       MEM_freeN(edit->mirror_cache);
2800       edit->mirror_cache = NULL;
2801     }
2802 
2803     if (psys->child) {
2804       MEM_freeN(psys->child);
2805       psys->child = NULL;
2806       psys->totchild = 0;
2807     }
2808 
2809     edit->totpoint = psys->totpart = new_totpart;
2810   }
2811 
2812   return removed;
2813 }
2814 
remove_tagged_keys(Depsgraph * depsgraph,Object * ob,ParticleSystem * psys)2815 static void remove_tagged_keys(Depsgraph *depsgraph, Object *ob, ParticleSystem *psys)
2816 {
2817   PTCacheEdit *edit = psys->edit;
2818   ParticleData *pa;
2819   HairKey *hkey, *nhkey, *new_hkeys = 0;
2820   POINT_P;
2821   KEY_K;
2822   PTCacheEditKey *nkey, *new_keys;
2823   short new_totkey;
2824 
2825   if (pe_x_mirror(ob)) {
2826     /* mirror key tags */
2827     ParticleSystemModifierData *psmd = psys_get_modifier(ob, psys);
2828     ParticleSystemModifierData *psmd_eval = (ParticleSystemModifierData *)
2829         BKE_modifier_get_evaluated(depsgraph, ob, &psmd->modifier);
2830 
2831     LOOP_POINTS {
2832       LOOP_TAGGED_KEYS {
2833         PE_mirror_particle(ob, psmd_eval->mesh_final, psys, psys->particles + p, NULL);
2834         break;
2835       }
2836     }
2837   }
2838 
2839   LOOP_POINTS {
2840     new_totkey = point->totkey;
2841     LOOP_TAGGED_KEYS {
2842       new_totkey--;
2843     }
2844     /* we can't have elements with less than two keys*/
2845     if (new_totkey < 2) {
2846       point->flag |= PEP_TAG;
2847     }
2848   }
2849   remove_tagged_particles(ob, psys, pe_x_mirror(ob));
2850 
2851   LOOP_POINTS {
2852     pa = psys->particles + p;
2853     new_totkey = pa->totkey;
2854 
2855     LOOP_TAGGED_KEYS {
2856       new_totkey--;
2857     }
2858 
2859     if (new_totkey != pa->totkey) {
2860       nhkey = new_hkeys = MEM_callocN(new_totkey * sizeof(HairKey), "HairKeys");
2861       nkey = new_keys = MEM_callocN(new_totkey * sizeof(PTCacheEditKey), "particle edit keys");
2862 
2863       hkey = pa->hair;
2864       LOOP_KEYS {
2865         while (key->flag & PEK_TAG && hkey < pa->hair + pa->totkey) {
2866           key++;
2867           hkey++;
2868         }
2869 
2870         if (hkey < pa->hair + pa->totkey) {
2871           copy_v3_v3(nhkey->co, hkey->co);
2872           nhkey->editflag = hkey->editflag;
2873           nhkey->time = hkey->time;
2874           nhkey->weight = hkey->weight;
2875 
2876           nkey->co = nhkey->co;
2877           nkey->time = &nhkey->time;
2878           /* these can be copied from old edit keys */
2879           nkey->flag = key->flag;
2880           nkey->ftime = key->ftime;
2881           nkey->length = key->length;
2882           copy_v3_v3(nkey->world_co, key->world_co);
2883         }
2884         nkey++;
2885         nhkey++;
2886         hkey++;
2887       }
2888 
2889       if (pa->hair) {
2890         MEM_freeN(pa->hair);
2891       }
2892 
2893       if (point->keys) {
2894         MEM_freeN(point->keys);
2895       }
2896 
2897       pa->hair = new_hkeys;
2898       point->keys = new_keys;
2899 
2900       point->totkey = pa->totkey = new_totkey;
2901 
2902       /* flag for recalculating length */
2903       point->flag |= PEP_EDIT_RECALC;
2904     }
2905   }
2906 }
2907 
2908 /************************ subdivide opertor *********************/
2909 
2910 /* works like normal edit mode subdivide, inserts keys between neighboring selected keys */
subdivide_particle(PEData * data,int pa_index)2911 static void subdivide_particle(PEData *data, int pa_index)
2912 {
2913   PTCacheEdit *edit = data->edit;
2914   ParticleSystem *psys = edit->psys;
2915   ParticleSimulationData sim = {0};
2916   ParticleData *pa = psys->particles + pa_index;
2917   PTCacheEditPoint *point = edit->points + pa_index;
2918   ParticleKey state;
2919   HairKey *key, *nkey, *new_keys;
2920   PTCacheEditKey *ekey, *nekey, *new_ekeys;
2921 
2922   int k;
2923   short totnewkey = 0;
2924   float endtime;
2925 
2926   sim.depsgraph = data->depsgraph;
2927   sim.scene = data->scene;
2928   sim.ob = data->ob;
2929   sim.psys = edit->psys;
2930 
2931   for (k = 0, ekey = point->keys; k < pa->totkey - 1; k++, ekey++) {
2932     if (ekey->flag & PEK_SELECT && (ekey + 1)->flag & PEK_SELECT) {
2933       totnewkey++;
2934     }
2935   }
2936 
2937   if (totnewkey == 0) {
2938     return;
2939   }
2940 
2941   pa->flag |= PARS_REKEY;
2942 
2943   nkey = new_keys = MEM_callocN((pa->totkey + totnewkey) * (sizeof(HairKey)),
2944                                 "Hair subdivide keys");
2945   nekey = new_ekeys = MEM_callocN((pa->totkey + totnewkey) * (sizeof(PTCacheEditKey)),
2946                                   "Hair subdivide edit keys");
2947 
2948   key = pa->hair;
2949   endtime = key[pa->totkey - 1].time;
2950 
2951   for (k = 0, ekey = point->keys; k < pa->totkey - 1; k++, key++, ekey++) {
2952 
2953     memcpy(nkey, key, sizeof(HairKey));
2954     memcpy(nekey, ekey, sizeof(PTCacheEditKey));
2955 
2956     nekey->co = nkey->co;
2957     nekey->time = &nkey->time;
2958 
2959     nkey++;
2960     nekey++;
2961 
2962     if (ekey->flag & PEK_SELECT && (ekey + 1)->flag & PEK_SELECT) {
2963       nkey->time = (key->time + (key + 1)->time) * 0.5f;
2964       state.time = (endtime != 0.0f) ? nkey->time / endtime : 0.0f;
2965       psys_get_particle_on_path(&sim, pa_index, &state, 0);
2966       copy_v3_v3(nkey->co, state.co);
2967 
2968       nekey->co = nkey->co;
2969       nekey->time = &nkey->time;
2970       nekey->flag |= PEK_SELECT;
2971       if (!(psys->flag & PSYS_GLOBAL_HAIR)) {
2972         nekey->flag |= PEK_USE_WCO;
2973       }
2974 
2975       nekey++;
2976       nkey++;
2977     }
2978   }
2979   /*tip still not copied*/
2980   memcpy(nkey, key, sizeof(HairKey));
2981   memcpy(nekey, ekey, sizeof(PTCacheEditKey));
2982 
2983   nekey->co = nkey->co;
2984   nekey->time = &nkey->time;
2985 
2986   if (pa->hair) {
2987     MEM_freeN(pa->hair);
2988   }
2989   pa->hair = new_keys;
2990 
2991   if (point->keys) {
2992     MEM_freeN(point->keys);
2993   }
2994   point->keys = new_ekeys;
2995 
2996   point->totkey = pa->totkey = pa->totkey + totnewkey;
2997   point->flag |= PEP_EDIT_RECALC;
2998   pa->flag &= ~PARS_REKEY;
2999 }
3000 
subdivide_exec(bContext * C,wmOperator * UNUSED (op))3001 static int subdivide_exec(bContext *C, wmOperator *UNUSED(op))
3002 {
3003   PEData data;
3004 
3005   PE_set_data(C, &data);
3006   foreach_point(&data, subdivide_particle);
3007 
3008   recalc_lengths(data.edit);
3009   PE_update_selection(data.depsgraph, data.scene, data.ob, 1);
3010   PE_update_object(data.depsgraph, data.scene, data.ob, 1);
3011   DEG_id_tag_update(&data.ob->id, ID_RECALC_SELECT);
3012   WM_event_add_notifier(C, NC_OBJECT | ND_PARTICLE | NA_EDITED, data.ob);
3013 
3014   return OPERATOR_FINISHED;
3015 }
3016 
PARTICLE_OT_subdivide(wmOperatorType * ot)3017 void PARTICLE_OT_subdivide(wmOperatorType *ot)
3018 {
3019   /* identifiers */
3020   ot->name = "Subdivide";
3021   ot->idname = "PARTICLE_OT_subdivide";
3022   ot->description = "Subdivide selected particles segments (adds keys)";
3023 
3024   /* api callbacks */
3025   ot->exec = subdivide_exec;
3026   ot->poll = PE_hair_poll;
3027 
3028   /* flags */
3029   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3030 }
3031 
3032 /************************ remove doubles opertor *********************/
3033 
remove_doubles_exec(bContext * C,wmOperator * op)3034 static int remove_doubles_exec(bContext *C, wmOperator *op)
3035 {
3036   Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
3037   Scene *scene = CTX_data_scene(C);
3038   Object *ob = CTX_data_active_object(C);
3039   PTCacheEdit *edit = PE_get_current(depsgraph, scene, ob);
3040   ParticleSystem *psys = edit->psys;
3041   ParticleSystemModifierData *psmd_eval;
3042   KDTree_3d *tree;
3043   KDTreeNearest_3d nearest[10];
3044   POINT_P;
3045   float mat[4][4], co[3], threshold = RNA_float_get(op->ptr, "threshold");
3046   int n, totn, removed, totremoved;
3047 
3048   if (psys->flag & PSYS_GLOBAL_HAIR) {
3049     return OPERATOR_CANCELLED;
3050   }
3051 
3052   edit = psys->edit;
3053   psmd_eval = edit->psmd_eval;
3054   totremoved = 0;
3055 
3056   do {
3057     removed = 0;
3058 
3059     tree = BLI_kdtree_3d_new(psys->totpart);
3060 
3061     /* insert particles into kd tree */
3062     LOOP_SELECTED_POINTS {
3063       psys_mat_hair_to_object(
3064           ob, psmd_eval->mesh_final, psys->part->from, psys->particles + p, mat);
3065       copy_v3_v3(co, point->keys->co);
3066       mul_m4_v3(mat, co);
3067       BLI_kdtree_3d_insert(tree, p, co);
3068     }
3069 
3070     BLI_kdtree_3d_balance(tree);
3071 
3072     /* tag particles to be removed */
3073     LOOP_SELECTED_POINTS {
3074       psys_mat_hair_to_object(
3075           ob, psmd_eval->mesh_final, psys->part->from, psys->particles + p, mat);
3076       copy_v3_v3(co, point->keys->co);
3077       mul_m4_v3(mat, co);
3078 
3079       totn = BLI_kdtree_3d_find_nearest_n(tree, co, nearest, 10);
3080 
3081       for (n = 0; n < totn; n++) {
3082         /* this needs a custom threshold still */
3083         if (nearest[n].index > p && nearest[n].dist < threshold) {
3084           if (!(point->flag & PEP_TAG)) {
3085             point->flag |= PEP_TAG;
3086             removed++;
3087           }
3088         }
3089       }
3090     }
3091 
3092     BLI_kdtree_3d_free(tree);
3093 
3094     /* remove tagged particles - don't do mirror here! */
3095     remove_tagged_particles(ob, psys, 0);
3096     totremoved += removed;
3097   } while (removed);
3098 
3099   if (totremoved == 0) {
3100     return OPERATOR_CANCELLED;
3101   }
3102 
3103   BKE_reportf(op->reports, RPT_INFO, "Removed %d double particle(s)", totremoved);
3104 
3105   DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
3106   WM_event_add_notifier(C, NC_OBJECT | ND_PARTICLE | NA_EDITED, ob);
3107 
3108   return OPERATOR_FINISHED;
3109 }
3110 
PARTICLE_OT_remove_doubles(wmOperatorType * ot)3111 void PARTICLE_OT_remove_doubles(wmOperatorType *ot)
3112 {
3113   /* identifiers */
3114   ot->name = "Remove Doubles";
3115   ot->idname = "PARTICLE_OT_remove_doubles";
3116   ot->description = "Remove selected particles close enough of others";
3117 
3118   /* api callbacks */
3119   ot->exec = remove_doubles_exec;
3120   ot->poll = PE_hair_poll;
3121 
3122   /* flags */
3123   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3124 
3125   /* properties */
3126   RNA_def_float(ot->srna,
3127                 "threshold",
3128                 0.0002f,
3129                 0.0f,
3130                 FLT_MAX,
3131                 "Merge Distance",
3132                 "Threshold distance within which particles are removed",
3133                 0.00001f,
3134                 0.1f);
3135 }
3136 
weight_set_exec(bContext * C,wmOperator * op)3137 static int weight_set_exec(bContext *C, wmOperator *op)
3138 {
3139   Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
3140   Scene *scene = CTX_data_scene(C);
3141   ParticleEditSettings *pset = PE_settings(scene);
3142   Object *ob = CTX_data_active_object(C);
3143   PTCacheEdit *edit = PE_get_current(depsgraph, scene, ob);
3144   ParticleSystem *psys = edit->psys;
3145   POINT_P;
3146   KEY_K;
3147   HairKey *hkey;
3148   float weight;
3149   ParticleBrushData *brush = &pset->brush[pset->brushtype];
3150   float factor = RNA_float_get(op->ptr, "factor");
3151 
3152   weight = brush->strength;
3153   edit = psys->edit;
3154 
3155   LOOP_SELECTED_POINTS {
3156     ParticleData *pa = psys->particles + p;
3157 
3158     LOOP_SELECTED_KEYS {
3159       hkey = pa->hair + k;
3160       hkey->weight = interpf(weight, hkey->weight, factor);
3161     }
3162   }
3163 
3164   DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
3165   WM_event_add_notifier(C, NC_OBJECT | ND_PARTICLE | NA_EDITED, ob);
3166 
3167   return OPERATOR_FINISHED;
3168 }
3169 
PARTICLE_OT_weight_set(wmOperatorType * ot)3170 void PARTICLE_OT_weight_set(wmOperatorType *ot)
3171 {
3172   /* identifiers */
3173   ot->name = "Weight Set";
3174   ot->idname = "PARTICLE_OT_weight_set";
3175   ot->description = "Set the weight of selected keys";
3176 
3177   /* api callbacks */
3178   ot->exec = weight_set_exec;
3179   ot->poll = PE_hair_poll;
3180 
3181   /* flags */
3182   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3183 
3184   RNA_def_float(ot->srna,
3185                 "factor",
3186                 1,
3187                 0,
3188                 1,
3189                 "Factor",
3190                 "Interpolation factor between current brush weight, and keys' weights",
3191                 0,
3192                 1);
3193 }
3194 
3195 /************************ cursor drawing *******************************/
3196 
brush_drawcursor(bContext * C,int x,int y,void * UNUSED (customdata))3197 static void brush_drawcursor(bContext *C, int x, int y, void *UNUSED(customdata))
3198 {
3199   Scene *scene = CTX_data_scene(C);
3200   ParticleEditSettings *pset = PE_settings(scene);
3201   ParticleBrushData *brush;
3202 
3203   if (!WM_toolsystem_active_tool_is_brush(C)) {
3204     return;
3205   }
3206 
3207   brush = &pset->brush[pset->brushtype];
3208 
3209   if (brush) {
3210     uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
3211     immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
3212 
3213     immUniformColor4ub(255, 255, 255, 128);
3214 
3215     GPU_line_smooth(true);
3216     GPU_blend(GPU_BLEND_ALPHA);
3217 
3218     imm_draw_circle_wire_2d(pos, (float)x, (float)y, pe_brush_size_get(scene, brush), 40);
3219 
3220     GPU_blend(GPU_BLEND_NONE);
3221     GPU_line_smooth(false);
3222 
3223     immUnbindProgram();
3224   }
3225 }
3226 
toggle_particle_cursor(Scene * scene,bool enable)3227 static void toggle_particle_cursor(Scene *scene, bool enable)
3228 {
3229   ParticleEditSettings *pset = PE_settings(scene);
3230 
3231   if (pset->paintcursor && !enable) {
3232     WM_paint_cursor_end(pset->paintcursor);
3233     pset->paintcursor = NULL;
3234   }
3235   else if (enable) {
3236     pset->paintcursor = WM_paint_cursor_activate(
3237         SPACE_VIEW3D, RGN_TYPE_WINDOW, PE_poll_view3d, brush_drawcursor, NULL);
3238   }
3239 }
3240 
3241 /*************************** delete operator **************************/
3242 
3243 enum { DEL_PARTICLE, DEL_KEY };
3244 
3245 static const EnumPropertyItem delete_type_items[] = {
3246     {DEL_PARTICLE, "PARTICLE", 0, "Particle", ""},
3247     {DEL_KEY, "KEY", 0, "Key", ""},
3248     {0, NULL, 0, NULL, NULL},
3249 };
3250 
set_delete_particle(PEData * data,int pa_index)3251 static void set_delete_particle(PEData *data, int pa_index)
3252 {
3253   PTCacheEdit *edit = data->edit;
3254 
3255   edit->points[pa_index].flag |= PEP_TAG;
3256 }
3257 
set_delete_particle_key(PEData * data,int pa_index,int key_index,bool UNUSED (is_inside))3258 static void set_delete_particle_key(PEData *data,
3259                                     int pa_index,
3260                                     int key_index,
3261                                     bool UNUSED(is_inside))
3262 {
3263   PTCacheEdit *edit = data->edit;
3264 
3265   edit->points[pa_index].keys[key_index].flag |= PEK_TAG;
3266 }
3267 
delete_exec(bContext * C,wmOperator * op)3268 static int delete_exec(bContext *C, wmOperator *op)
3269 {
3270   PEData data;
3271   int type = RNA_enum_get(op->ptr, "type");
3272 
3273   PE_set_data(C, &data);
3274 
3275   if (type == DEL_KEY) {
3276     foreach_selected_key(&data, set_delete_particle_key);
3277     remove_tagged_keys(data.depsgraph, data.ob, data.edit->psys);
3278     recalc_lengths(data.edit);
3279   }
3280   else if (type == DEL_PARTICLE) {
3281     foreach_selected_point(&data, set_delete_particle);
3282     remove_tagged_particles(data.ob, data.edit->psys, pe_x_mirror(data.ob));
3283     recalc_lengths(data.edit);
3284   }
3285 
3286   DEG_id_tag_update(&data.ob->id, ID_RECALC_GEOMETRY);
3287   BKE_particle_batch_cache_dirty_tag(data.edit->psys, BKE_PARTICLE_BATCH_DIRTY_ALL);
3288   WM_event_add_notifier(C, NC_OBJECT | ND_PARTICLE | NA_EDITED, data.ob);
3289 
3290   return OPERATOR_FINISHED;
3291 }
3292 
PARTICLE_OT_delete(wmOperatorType * ot)3293 void PARTICLE_OT_delete(wmOperatorType *ot)
3294 {
3295   /* identifiers */
3296   ot->name = "Delete";
3297   ot->idname = "PARTICLE_OT_delete";
3298   ot->description = "Delete selected particles or keys";
3299 
3300   /* api callbacks */
3301   ot->exec = delete_exec;
3302   ot->invoke = WM_menu_invoke;
3303   ot->poll = PE_hair_poll;
3304 
3305   /* flags */
3306   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3307 
3308   /* properties */
3309   ot->prop = RNA_def_enum(ot->srna,
3310                           "type",
3311                           delete_type_items,
3312                           DEL_PARTICLE,
3313                           "Type",
3314                           "Delete a full particle or only keys");
3315 }
3316 
3317 /*************************** mirror operator **************************/
3318 
PE_mirror_x(Depsgraph * depsgraph,Scene * scene,Object * ob,int tagged)3319 static void PE_mirror_x(Depsgraph *depsgraph, Scene *scene, Object *ob, int tagged)
3320 {
3321   Mesh *me = (Mesh *)(ob->data);
3322   ParticleSystemModifierData *psmd_eval;
3323   PTCacheEdit *edit = PE_get_current(depsgraph, scene, ob);
3324   ParticleSystem *psys = edit->psys;
3325   ParticleData *pa, *newpa, *new_pars;
3326   PTCacheEditPoint *newpoint, *new_points;
3327   POINT_P;
3328   KEY_K;
3329   HairKey *hkey;
3330   int *mirrorfaces = NULL;
3331   int rotation, totpart, newtotpart;
3332 
3333   if (psys->flag & PSYS_GLOBAL_HAIR) {
3334     return;
3335   }
3336 
3337   psmd_eval = edit->psmd_eval;
3338   if (!psmd_eval->mesh_final) {
3339     return;
3340   }
3341 
3342   const bool use_dm_final_indices = (psys->part->use_modifier_stack &&
3343                                      !psmd_eval->mesh_final->runtime.deformed_only);
3344 
3345   /* NOTE: this is not nice to use tessfaces but hard to avoid since pa->num uses tessfaces */
3346   BKE_mesh_tessface_ensure(me);
3347 
3348   /* NOTE: In case psys uses Mesh tessface indices, we mirror final Mesh itself, not orig mesh.
3349    * Avoids an (impossible) mesh -> orig -> mesh tessface indices conversion. */
3350   mirrorfaces = mesh_get_x_mirror_faces(
3351       ob, NULL, use_dm_final_indices ? psmd_eval->mesh_final : NULL);
3352 
3353   if (!edit->mirror_cache) {
3354     PE_update_mirror_cache(ob, psys);
3355   }
3356 
3357   totpart = psys->totpart;
3358   newtotpart = psys->totpart;
3359   LOOP_VISIBLE_POINTS {
3360     pa = psys->particles + p;
3361 
3362     if (!tagged) {
3363       if (point_is_selected(point)) {
3364         if (edit->mirror_cache[p] != -1) {
3365           /* already has a mirror, don't need to duplicate */
3366           PE_mirror_particle(ob, psmd_eval->mesh_final, psys, pa, NULL);
3367           continue;
3368         }
3369         point->flag |= PEP_TAG;
3370       }
3371     }
3372 
3373     if ((point->flag & PEP_TAG) && mirrorfaces[pa->num * 2] != -1) {
3374       newtotpart++;
3375     }
3376   }
3377 
3378   if (newtotpart != psys->totpart) {
3379     MFace *mtessface = use_dm_final_indices ? psmd_eval->mesh_final->mface : me->mface;
3380 
3381     /* allocate new arrays and copy existing */
3382     new_pars = MEM_callocN(newtotpart * sizeof(ParticleData), "ParticleData new");
3383     new_points = MEM_callocN(newtotpart * sizeof(PTCacheEditPoint), "PTCacheEditPoint new");
3384 
3385     if (psys->particles) {
3386       memcpy(new_pars, psys->particles, totpart * sizeof(ParticleData));
3387       MEM_freeN(psys->particles);
3388     }
3389     psys->particles = new_pars;
3390 
3391     if (edit->points) {
3392       memcpy(new_points, edit->points, totpart * sizeof(PTCacheEditPoint));
3393       MEM_freeN(edit->points);
3394     }
3395     edit->points = new_points;
3396 
3397     if (edit->mirror_cache) {
3398       MEM_freeN(edit->mirror_cache);
3399       edit->mirror_cache = NULL;
3400     }
3401 
3402     edit->totpoint = psys->totpart = newtotpart;
3403 
3404     /* create new elements */
3405     newpa = psys->particles + totpart;
3406     newpoint = edit->points + totpart;
3407 
3408     for (p = 0, point = edit->points; p < totpart; p++, point++) {
3409       pa = psys->particles + p;
3410       const int pa_num = pa->num;
3411 
3412       if (point->flag & PEP_HIDE) {
3413         continue;
3414       }
3415 
3416       if (!(point->flag & PEP_TAG) || mirrorfaces[pa_num * 2] == -1) {
3417         continue;
3418       }
3419 
3420       /* duplicate */
3421       *newpa = *pa;
3422       *newpoint = *point;
3423       if (pa->hair) {
3424         newpa->hair = MEM_dupallocN(pa->hair);
3425       }
3426       if (point->keys) {
3427         newpoint->keys = MEM_dupallocN(point->keys);
3428       }
3429 
3430       /* rotate weights according to vertex index rotation */
3431       rotation = mirrorfaces[pa_num * 2 + 1];
3432       newpa->fuv[0] = pa->fuv[2];
3433       newpa->fuv[1] = pa->fuv[1];
3434       newpa->fuv[2] = pa->fuv[0];
3435       newpa->fuv[3] = pa->fuv[3];
3436       while (rotation--) {
3437         if (mtessface[pa_num].v4) {
3438           SHIFT4(float, newpa->fuv[0], newpa->fuv[1], newpa->fuv[2], newpa->fuv[3]);
3439         }
3440         else {
3441           SHIFT3(float, newpa->fuv[0], newpa->fuv[1], newpa->fuv[2]);
3442         }
3443       }
3444 
3445       /* assign face index */
3446       /* NOTE: mesh_get_x_mirror_faces generates -1 for non-found mirror,
3447        * same as DMCACHE_NOTFOUND. */
3448       newpa->num = mirrorfaces[pa_num * 2];
3449 
3450       if (use_dm_final_indices) {
3451         newpa->num_dmcache = DMCACHE_ISCHILD;
3452       }
3453       else {
3454         newpa->num_dmcache = psys_particle_dm_face_lookup(
3455             psmd_eval->mesh_final, psmd_eval->mesh_original, newpa->num, newpa->fuv, NULL);
3456       }
3457 
3458       /* update edit key pointers */
3459       key = newpoint->keys;
3460       for (k = 0, hkey = newpa->hair; k < newpa->totkey; k++, hkey++, key++) {
3461         key->co = hkey->co;
3462         key->time = &hkey->time;
3463       }
3464 
3465       /* map key positions as mirror over x axis */
3466       PE_mirror_particle(ob, psmd_eval->mesh_final, psys, pa, newpa);
3467 
3468       newpa++;
3469       newpoint++;
3470     }
3471   }
3472 
3473   LOOP_POINTS {
3474     point->flag &= ~PEP_TAG;
3475   }
3476 
3477   MEM_freeN(mirrorfaces);
3478 }
3479 
mirror_exec(bContext * C,wmOperator * UNUSED (op))3480 static int mirror_exec(bContext *C, wmOperator *UNUSED(op))
3481 {
3482   Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
3483   Scene *scene = CTX_data_scene(C);
3484   Object *ob = CTX_data_active_object(C);
3485   PTCacheEdit *edit = PE_get_current(depsgraph, scene, ob);
3486 
3487   PE_mirror_x(depsgraph, scene, ob, 0);
3488 
3489   update_world_cos(ob, edit);
3490   psys_free_path_cache(NULL, edit);
3491 
3492   WM_event_add_notifier(C, NC_OBJECT | ND_PARTICLE | NA_EDITED, ob);
3493   BKE_particle_batch_cache_dirty_tag(edit->psys, BKE_PARTICLE_BATCH_DIRTY_ALL);
3494   DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
3495 
3496   return OPERATOR_FINISHED;
3497 }
3498 
PARTICLE_OT_mirror(wmOperatorType * ot)3499 void PARTICLE_OT_mirror(wmOperatorType *ot)
3500 {
3501   /* identifiers */
3502   ot->name = "Mirror";
3503   ot->idname = "PARTICLE_OT_mirror";
3504   ot->description = "Duplicate and mirror the selected particles along the local X axis";
3505 
3506   /* api callbacks */
3507   ot->exec = mirror_exec;
3508   ot->poll = PE_hair_poll;
3509 
3510   /* flags */
3511   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3512 }
3513 
3514 /************************* brush edit callbacks ********************/
3515 
brush_comb(PEData * data,float UNUSED (mat[4][4]),float imat[4][4],int point_index,int key_index,PTCacheEditKey * key,float mouse_distance)3516 static void brush_comb(PEData *data,
3517                        float UNUSED(mat[4][4]),
3518                        float imat[4][4],
3519                        int point_index,
3520                        int key_index,
3521                        PTCacheEditKey *key,
3522                        float mouse_distance)
3523 {
3524   ParticleEditSettings *pset = PE_settings(data->scene);
3525   float cvec[3], fac;
3526 
3527   if (pset->flag & PE_LOCK_FIRST && key_index == 0) {
3528     return;
3529   }
3530 
3531   fac = (float)pow((double)(1.0f - mouse_distance / data->rad), (double)data->combfac);
3532 
3533   copy_v3_v3(cvec, data->dvec);
3534   mul_mat3_m4_v3(imat, cvec);
3535   mul_v3_fl(cvec, fac);
3536   add_v3_v3(key->co, cvec);
3537 
3538   (data->edit->points + point_index)->flag |= PEP_EDIT_RECALC;
3539 }
3540 
brush_cut(PEData * data,int pa_index)3541 static void brush_cut(PEData *data, int pa_index)
3542 {
3543   PTCacheEdit *edit = data->edit;
3544   ARegion *region = data->vc.region;
3545   Object *ob = data->ob;
3546   ParticleEditSettings *pset = PE_settings(data->scene);
3547   ParticleCacheKey *key = edit->pathcache[pa_index];
3548   float rad2, cut_time = 1.0;
3549   float x0, x1, v0, v1, o0, o1, xo0, xo1, d, dv;
3550   int k, cut, keys = (int)pow(2.0, (double)pset->draw_step);
3551   int screen_co[2];
3552 
3553   BLI_assert(data->rng != NULL);
3554   /* blunt scissors */
3555   if (BLI_rng_get_float(data->rng) > data->cutfac) {
3556     return;
3557   }
3558 
3559   /* don't cut hidden */
3560   if (edit->points[pa_index].flag & PEP_HIDE) {
3561     return;
3562   }
3563 
3564   if (ED_view3d_project_int_global(region, key->co, screen_co, V3D_PROJ_TEST_CLIP_NEAR) !=
3565       V3D_PROJ_RET_OK) {
3566     return;
3567   }
3568 
3569   rad2 = data->rad * data->rad;
3570 
3571   cut = 0;
3572 
3573   x0 = (float)screen_co[0];
3574   x1 = (float)screen_co[1];
3575 
3576   o0 = (float)data->mval[0];
3577   o1 = (float)data->mval[1];
3578 
3579   xo0 = x0 - o0;
3580   xo1 = x1 - o1;
3581 
3582   /* check if root is inside circle */
3583   if (xo0 * xo0 + xo1 * xo1 < rad2 && key_test_depth(data, key->co, screen_co)) {
3584     cut_time = -1.0f;
3585     cut = 1;
3586   }
3587   else {
3588     /* calculate path time closest to root that was inside the circle */
3589     for (k = 1, key++; k <= keys; k++, key++) {
3590 
3591       if ((ED_view3d_project_int_global(region, key->co, screen_co, V3D_PROJ_TEST_CLIP_NEAR) !=
3592            V3D_PROJ_RET_OK) ||
3593           key_test_depth(data, key->co, screen_co) == 0) {
3594         x0 = (float)screen_co[0];
3595         x1 = (float)screen_co[1];
3596 
3597         xo0 = x0 - o0;
3598         xo1 = x1 - o1;
3599         continue;
3600       }
3601 
3602       v0 = (float)screen_co[0] - x0;
3603       v1 = (float)screen_co[1] - x1;
3604 
3605       dv = v0 * v0 + v1 * v1;
3606 
3607       d = (v0 * xo1 - v1 * xo0);
3608 
3609       d = dv * rad2 - d * d;
3610 
3611       if (d > 0.0f) {
3612         d = sqrtf(d);
3613 
3614         cut_time = -(v0 * xo0 + v1 * xo1 + d);
3615 
3616         if (cut_time > 0.0f) {
3617           cut_time /= dv;
3618 
3619           if (cut_time < 1.0f) {
3620             cut_time += (float)(k - 1);
3621             cut_time /= (float)keys;
3622             cut = 1;
3623             break;
3624           }
3625         }
3626       }
3627 
3628       x0 = (float)screen_co[0];
3629       x1 = (float)screen_co[1];
3630 
3631       xo0 = x0 - o0;
3632       xo1 = x1 - o1;
3633     }
3634   }
3635 
3636   if (cut) {
3637     if (cut_time < 0.0f) {
3638       edit->points[pa_index].flag |= PEP_TAG;
3639     }
3640     else {
3641       rekey_particle_to_time(data->context, data->scene, ob, pa_index, cut_time);
3642       edit->points[pa_index].flag |= PEP_EDIT_RECALC;
3643     }
3644   }
3645 }
3646 
brush_length(PEData * data,int point_index,float UNUSED (mouse_distance))3647 static void brush_length(PEData *data, int point_index, float UNUSED(mouse_distance))
3648 {
3649   PTCacheEdit *edit = data->edit;
3650   PTCacheEditPoint *point = edit->points + point_index;
3651   KEY_K;
3652   float dvec[3], pvec[3] = {0.0f, 0.0f, 0.0f};
3653 
3654   LOOP_KEYS {
3655     if (k == 0) {
3656       copy_v3_v3(pvec, key->co);
3657     }
3658     else {
3659       sub_v3_v3v3(dvec, key->co, pvec);
3660       copy_v3_v3(pvec, key->co);
3661       mul_v3_fl(dvec, data->growfac);
3662       add_v3_v3v3(key->co, (key - 1)->co, dvec);
3663     }
3664   }
3665 
3666   point->flag |= PEP_EDIT_RECALC;
3667 }
3668 
brush_puff(PEData * data,int point_index,float mouse_distance)3669 static void brush_puff(PEData *data, int point_index, float mouse_distance)
3670 {
3671   PTCacheEdit *edit = data->edit;
3672   ParticleSystem *psys = edit->psys;
3673   PTCacheEditPoint *point = edit->points + point_index;
3674   KEY_K;
3675   float mat[4][4], imat[4][4];
3676 
3677   float onor_prev[3];           /* previous normal (particle-space) */
3678   float ofs_prev[3];            /* accumulate offset for puff_volume (particle-space) */
3679   float co_root[3], no_root[3]; /* root location and normal (global-space) */
3680   float co_prev[3], co[3];      /* track key coords as we loop (global-space) */
3681   float fac = 0.0f, length_accum = 0.0f;
3682   bool puff_volume = false;
3683   bool changed = false;
3684 
3685   zero_v3(ofs_prev);
3686 
3687   {
3688     ParticleEditSettings *pset = PE_settings(data->scene);
3689     ParticleBrushData *brush = &pset->brush[pset->brushtype];
3690     puff_volume = (brush->flag & PE_BRUSH_DATA_PUFF_VOLUME) != 0;
3691   }
3692 
3693   if (psys && !(psys->flag & PSYS_GLOBAL_HAIR)) {
3694     psys_mat_hair_to_global(
3695         data->ob, data->mesh, psys->part->from, psys->particles + point_index, mat);
3696     invert_m4_m4(imat, mat);
3697   }
3698   else {
3699     unit_m4(mat);
3700     unit_m4(imat);
3701   }
3702 
3703   LOOP_KEYS {
3704     float kco[3];
3705 
3706     if (k == 0) {
3707       /* find root coordinate and normal on emitter */
3708       copy_v3_v3(co, key->co);
3709       mul_m4_v3(mat, co);
3710 
3711       /* use 'kco' as the object space version of worldspace 'co',
3712        * ob->imat is set before calling */
3713       mul_v3_m4v3(kco, data->ob->imat, co);
3714 
3715       point_index = BLI_kdtree_3d_find_nearest(edit->emitter_field, kco, NULL);
3716       if (point_index == -1) {
3717         return;
3718       }
3719 
3720       copy_v3_v3(co_root, co);
3721       copy_v3_v3(no_root, &edit->emitter_cosnos[point_index * 6 + 3]);
3722       mul_mat3_m4_v3(data->ob->obmat, no_root); /* normal into global-space */
3723       normalize_v3(no_root);
3724 
3725       if (puff_volume) {
3726         copy_v3_v3(onor_prev, no_root);
3727         mul_mat3_m4_v3(imat, onor_prev); /* global-space into particle space */
3728         normalize_v3(onor_prev);
3729       }
3730 
3731       fac = (float)pow((double)(1.0f - mouse_distance / data->rad), (double)data->pufffac);
3732       fac *= 0.025f;
3733       if (data->invert) {
3734         fac = -fac;
3735       }
3736     }
3737     else {
3738       /* compute position as if hair was standing up straight.
3739        * */
3740       float length;
3741       copy_v3_v3(co_prev, co);
3742       copy_v3_v3(co, key->co);
3743       mul_m4_v3(mat, co);
3744       length = len_v3v3(co_prev, co);
3745       length_accum += length;
3746 
3747       if ((data->select == 0 || (key->flag & PEK_SELECT)) && !(key->flag & PEK_HIDE)) {
3748         float dco[3]; /* delta temp var */
3749 
3750         madd_v3_v3v3fl(kco, co_root, no_root, length_accum);
3751 
3752         /* blend between the current and straight position */
3753         sub_v3_v3v3(dco, kco, co);
3754         madd_v3_v3fl(co, dco, fac);
3755         /* keep the same distance from the root or we get glitches T35406. */
3756         dist_ensure_v3_v3fl(co, co_root, length_accum);
3757 
3758         /* re-use dco to compare before and after translation and add to the offset  */
3759         copy_v3_v3(dco, key->co);
3760 
3761         mul_v3_m4v3(key->co, imat, co);
3762 
3763         if (puff_volume) {
3764           /* accumulate the total distance moved to apply to unselected
3765            * keys that come after */
3766           sub_v3_v3v3(ofs_prev, key->co, dco);
3767         }
3768         changed = true;
3769       }
3770       else {
3771 
3772         if (puff_volume) {
3773 #if 0
3774           /* this is simple but looks bad, adds annoying kinks */
3775           add_v3_v3(key->co, ofs);
3776 #else
3777           /* translate (not rotate) the rest of the hair if its not selected  */
3778           {
3779 /* NOLINTNEXTLINE: readability-redundant-preprocessor */
3780 #  if 0 /* kindof works but looks worse than what's below */
3781 
3782             /* Move the unselected point on a vector based on the
3783              * hair direction and the offset */
3784             float c1[3], c2[3];
3785             sub_v3_v3v3(dco, lastco, co);
3786             mul_mat3_m4_v3(imat, dco); /* into particle space */
3787 
3788             /* move the point along a vector perpendicular to the
3789              * hairs direction, reduces odd kinks, */
3790             cross_v3_v3v3(c1, ofs, dco);
3791             cross_v3_v3v3(c2, c1, dco);
3792             normalize_v3(c2);
3793             mul_v3_fl(c2, len_v3(ofs));
3794             add_v3_v3(key->co, c2);
3795 #  else
3796             /* Move the unselected point on a vector based on the
3797              * the normal of the closest geometry */
3798             float oco[3], onor[3];
3799             copy_v3_v3(oco, key->co);
3800             mul_m4_v3(mat, oco);
3801 
3802             /* use 'kco' as the object space version of worldspace 'co',
3803              * ob->imat is set before calling */
3804             mul_v3_m4v3(kco, data->ob->imat, oco);
3805 
3806             point_index = BLI_kdtree_3d_find_nearest(edit->emitter_field, kco, NULL);
3807             if (point_index != -1) {
3808               copy_v3_v3(onor, &edit->emitter_cosnos[point_index * 6 + 3]);
3809               mul_mat3_m4_v3(data->ob->obmat, onor); /* normal into worldspace */
3810               mul_mat3_m4_v3(imat, onor);            /* worldspace into particle space */
3811               normalize_v3(onor);
3812             }
3813             else {
3814               copy_v3_v3(onor, onor_prev);
3815             }
3816 
3817             if (!is_zero_v3(ofs_prev)) {
3818               mul_v3_fl(onor, len_v3(ofs_prev));
3819 
3820               add_v3_v3(key->co, onor);
3821             }
3822 
3823             copy_v3_v3(onor_prev, onor);
3824 #  endif
3825           }
3826 #endif
3827         }
3828       }
3829     }
3830   }
3831 
3832   if (changed) {
3833     point->flag |= PEP_EDIT_RECALC;
3834   }
3835 }
3836 
BKE_brush_weight_get(PEData * data,float UNUSED (mat[4][4]),float UNUSED (imat[4][4]),int point_index,int key_index,PTCacheEditKey * UNUSED (key),float UNUSED (mouse_distance))3837 static void BKE_brush_weight_get(PEData *data,
3838                                  float UNUSED(mat[4][4]),
3839                                  float UNUSED(imat[4][4]),
3840                                  int point_index,
3841                                  int key_index,
3842                                  PTCacheEditKey *UNUSED(key),
3843                                  float UNUSED(mouse_distance))
3844 {
3845   /* roots have full weight always */
3846   if (key_index) {
3847     PTCacheEdit *edit = data->edit;
3848     ParticleSystem *psys = edit->psys;
3849 
3850     ParticleData *pa = psys->particles + point_index;
3851     pa->hair[key_index].weight = data->weightfac;
3852 
3853     (data->edit->points + point_index)->flag |= PEP_EDIT_RECALC;
3854   }
3855 }
3856 
brush_smooth_get(PEData * data,float mat[4][4],float UNUSED (imat[4][4]),int UNUSED (point_index),int key_index,PTCacheEditKey * key,float UNUSED (mouse_distance))3857 static void brush_smooth_get(PEData *data,
3858                              float mat[4][4],
3859                              float UNUSED(imat[4][4]),
3860                              int UNUSED(point_index),
3861                              int key_index,
3862                              PTCacheEditKey *key,
3863                              float UNUSED(mouse_distance))
3864 {
3865   if (key_index) {
3866     float dvec[3];
3867 
3868     sub_v3_v3v3(dvec, key->co, (key - 1)->co);
3869     mul_mat3_m4_v3(mat, dvec);
3870     add_v3_v3(data->vec, dvec);
3871     data->tot++;
3872   }
3873 }
3874 
brush_smooth_do(PEData * data,float UNUSED (mat[4][4]),float imat[4][4],int point_index,int key_index,PTCacheEditKey * key,float UNUSED (mouse_distance))3875 static void brush_smooth_do(PEData *data,
3876                             float UNUSED(mat[4][4]),
3877                             float imat[4][4],
3878                             int point_index,
3879                             int key_index,
3880                             PTCacheEditKey *key,
3881                             float UNUSED(mouse_distance))
3882 {
3883   float vec[3], dvec[3];
3884 
3885   if (key_index) {
3886     copy_v3_v3(vec, data->vec);
3887     mul_mat3_m4_v3(imat, vec);
3888 
3889     sub_v3_v3v3(dvec, key->co, (key - 1)->co);
3890 
3891     sub_v3_v3v3(dvec, vec, dvec);
3892     mul_v3_fl(dvec, data->smoothfac);
3893 
3894     add_v3_v3(key->co, dvec);
3895   }
3896 
3897   (data->edit->points + point_index)->flag |= PEP_EDIT_RECALC;
3898 }
3899 
3900 /* convert from triangle barycentric weights to quad mean value weights */
intersect_dm_quad_weights(const float v1[3],const float v2[3],const float v3[3],const float v4[3],float w[4])3901 static void intersect_dm_quad_weights(
3902     const float v1[3], const float v2[3], const float v3[3], const float v4[3], float w[4])
3903 {
3904   float co[3], vert[4][3];
3905 
3906   copy_v3_v3(vert[0], v1);
3907   copy_v3_v3(vert[1], v2);
3908   copy_v3_v3(vert[2], v3);
3909   copy_v3_v3(vert[3], v4);
3910 
3911   co[0] = v1[0] * w[0] + v2[0] * w[1] + v3[0] * w[2] + v4[0] * w[3];
3912   co[1] = v1[1] * w[0] + v2[1] * w[1] + v3[1] * w[2] + v4[1] * w[3];
3913   co[2] = v1[2] * w[0] + v2[2] * w[1] + v3[2] * w[2] + v4[2] * w[3];
3914 
3915   interp_weights_poly_v3(w, vert, 4, co);
3916 }
3917 
3918 /** Check intersection with an evaluated mesh. */
particle_intersect_mesh(Depsgraph * depsgraph,Scene * UNUSED (scene),Object * ob,Mesh * mesh,float * vert_cos,const float co1[3],const float co2[3],float * min_d,int * min_face,float * min_w,float * face_minmax,float * pa_minmax,float radius,float * ipoint)3919 static int particle_intersect_mesh(Depsgraph *depsgraph,
3920                                    Scene *UNUSED(scene),
3921                                    Object *ob,
3922                                    Mesh *mesh,
3923                                    float *vert_cos,
3924                                    const float co1[3],
3925                                    const float co2[3],
3926                                    float *min_d,
3927                                    int *min_face,
3928                                    float *min_w,
3929                                    float *face_minmax,
3930                                    float *pa_minmax,
3931                                    float radius,
3932                                    float *ipoint)
3933 {
3934   MFace *mface = NULL;
3935   MVert *mvert = NULL;
3936   int i, totface, intersect = 0;
3937   float cur_d, cur_uv[2], v1[3], v2[3], v3[3], v4[3], min[3], max[3], p_min[3], p_max[3];
3938   float cur_ipoint[3];
3939 
3940   if (mesh == NULL) {
3941     psys_disable_all(ob);
3942 
3943     Scene *scene_eval = DEG_get_evaluated_scene(depsgraph);
3944     Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob);
3945 
3946     mesh = mesh_get_eval_final(depsgraph, scene_eval, ob_eval, &CD_MASK_BAREMESH);
3947     if (mesh == NULL) {
3948       mesh = mesh_get_eval_deform(depsgraph, scene_eval, ob_eval, &CD_MASK_BAREMESH);
3949     }
3950 
3951     psys_enable_all(ob);
3952 
3953     if (mesh == NULL) {
3954       return 0;
3955     }
3956   }
3957 
3958   /* BMESH_ONLY, deform dm may not have tessface */
3959   BKE_mesh_tessface_ensure(mesh);
3960 
3961   if (pa_minmax == 0) {
3962     INIT_MINMAX(p_min, p_max);
3963     minmax_v3v3_v3(p_min, p_max, co1);
3964     minmax_v3v3_v3(p_min, p_max, co2);
3965   }
3966   else {
3967     copy_v3_v3(p_min, pa_minmax);
3968     copy_v3_v3(p_max, pa_minmax + 3);
3969   }
3970 
3971   totface = mesh->totface;
3972   mface = mesh->mface;
3973   mvert = mesh->mvert;
3974 
3975   /* lets intersect the faces */
3976   for (i = 0; i < totface; i++, mface++) {
3977     if (vert_cos) {
3978       copy_v3_v3(v1, vert_cos + 3 * mface->v1);
3979       copy_v3_v3(v2, vert_cos + 3 * mface->v2);
3980       copy_v3_v3(v3, vert_cos + 3 * mface->v3);
3981       if (mface->v4) {
3982         copy_v3_v3(v4, vert_cos + 3 * mface->v4);
3983       }
3984     }
3985     else {
3986       copy_v3_v3(v1, mvert[mface->v1].co);
3987       copy_v3_v3(v2, mvert[mface->v2].co);
3988       copy_v3_v3(v3, mvert[mface->v3].co);
3989       if (mface->v4) {
3990         copy_v3_v3(v4, mvert[mface->v4].co);
3991       }
3992     }
3993 
3994     if (face_minmax == 0) {
3995       INIT_MINMAX(min, max);
3996       DO_MINMAX(v1, min, max);
3997       DO_MINMAX(v2, min, max);
3998       DO_MINMAX(v3, min, max);
3999       if (mface->v4) {
4000         DO_MINMAX(v4, min, max);
4001       }
4002       if (isect_aabb_aabb_v3(min, max, p_min, p_max) == 0) {
4003         continue;
4004       }
4005     }
4006     else {
4007       copy_v3_v3(min, face_minmax + 6 * i);
4008       copy_v3_v3(max, face_minmax + 6 * i + 3);
4009       if (isect_aabb_aabb_v3(min, max, p_min, p_max) == 0) {
4010         continue;
4011       }
4012     }
4013 
4014     if (radius > 0.0f) {
4015       if (isect_sweeping_sphere_tri_v3(co1, co2, radius, v2, v3, v1, &cur_d, cur_ipoint)) {
4016         if (cur_d < *min_d) {
4017           *min_d = cur_d;
4018           copy_v3_v3(ipoint, cur_ipoint);
4019           *min_face = i;
4020           intersect = 1;
4021         }
4022       }
4023       if (mface->v4) {
4024         if (isect_sweeping_sphere_tri_v3(co1, co2, radius, v4, v1, v3, &cur_d, cur_ipoint)) {
4025           if (cur_d < *min_d) {
4026             *min_d = cur_d;
4027             copy_v3_v3(ipoint, cur_ipoint);
4028             *min_face = i;
4029             intersect = 1;
4030           }
4031         }
4032       }
4033     }
4034     else {
4035       if (isect_line_segment_tri_v3(co1, co2, v1, v2, v3, &cur_d, cur_uv)) {
4036         if (cur_d < *min_d) {
4037           *min_d = cur_d;
4038           min_w[0] = 1.0f - cur_uv[0] - cur_uv[1];
4039           min_w[1] = cur_uv[0];
4040           min_w[2] = cur_uv[1];
4041           min_w[3] = 0.0f;
4042           if (mface->v4) {
4043             intersect_dm_quad_weights(v1, v2, v3, v4, min_w);
4044           }
4045           *min_face = i;
4046           intersect = 1;
4047         }
4048       }
4049       if (mface->v4) {
4050         if (isect_line_segment_tri_v3(co1, co2, v1, v3, v4, &cur_d, cur_uv)) {
4051           if (cur_d < *min_d) {
4052             *min_d = cur_d;
4053             min_w[0] = 1.0f - cur_uv[0] - cur_uv[1];
4054             min_w[1] = 0.0f;
4055             min_w[2] = cur_uv[0];
4056             min_w[3] = cur_uv[1];
4057             intersect_dm_quad_weights(v1, v2, v3, v4, min_w);
4058             *min_face = i;
4059             intersect = 1;
4060           }
4061         }
4062       }
4063     }
4064   }
4065   return intersect;
4066 }
4067 
4068 typedef struct BrushAddCountIterData {
4069   Depsgraph *depsgraph;
4070   Scene *scene;
4071   Object *object;
4072   Mesh *mesh;
4073   PEData *data;
4074   int number;
4075   short size;
4076   float imat[4][4];
4077   ParticleData *add_pars;
4078 } BrushAddCountIterData;
4079 
4080 typedef struct BrushAddCountIterTLSData {
4081   RNG *rng;
4082   int num_added;
4083 } BrushAddCountIterTLSData;
4084 
brush_add_count_iter(void * __restrict iter_data_v,const int iter,const TaskParallelTLS * __restrict tls_v)4085 static void brush_add_count_iter(void *__restrict iter_data_v,
4086                                  const int iter,
4087                                  const TaskParallelTLS *__restrict tls_v)
4088 {
4089   BrushAddCountIterData *iter_data = (BrushAddCountIterData *)iter_data_v;
4090   Depsgraph *depsgraph = iter_data->depsgraph;
4091   PEData *data = iter_data->data;
4092   PTCacheEdit *edit = data->edit;
4093   ParticleSystem *psys = edit->psys;
4094   ParticleSystemModifierData *psmd_eval = edit->psmd_eval;
4095   ParticleData *add_pars = iter_data->add_pars;
4096   BrushAddCountIterTLSData *tls = tls_v->userdata_chunk;
4097   const int number = iter_data->number;
4098   const short size = iter_data->size;
4099   const int size2 = size * size;
4100   float dmx, dmy;
4101   if (number > 1) {
4102     dmx = size;
4103     dmy = size;
4104     if (tls->rng == NULL) {
4105       tls->rng = BLI_rng_new_srandom(psys->seed + data->mval[0] + data->mval[1] +
4106                                      BLI_task_parallel_thread_id(tls_v));
4107     }
4108     /* rejection sampling to get points in circle */
4109     while (dmx * dmx + dmy * dmy > size2) {
4110       dmx = (2.0f * BLI_rng_get_float(tls->rng) - 1.0f) * size;
4111       dmy = (2.0f * BLI_rng_get_float(tls->rng) - 1.0f) * size;
4112     }
4113   }
4114   else {
4115     dmx = 0.0f;
4116     dmy = 0.0f;
4117   }
4118 
4119   float mco[2];
4120   mco[0] = data->mval[0] + dmx;
4121   mco[1] = data->mval[1] + dmy;
4122 
4123   float co1[3], co2[3];
4124   ED_view3d_win_to_segment_clipped(depsgraph, data->vc.region, data->vc.v3d, mco, co1, co2, true);
4125 
4126   mul_m4_v3(iter_data->imat, co1);
4127   mul_m4_v3(iter_data->imat, co2);
4128   float min_d = 2.0;
4129 
4130   /* warning, returns the derived mesh face */
4131   BLI_assert(iter_data->mesh != NULL);
4132   if (particle_intersect_mesh(depsgraph,
4133                               iter_data->scene,
4134                               iter_data->object,
4135                               iter_data->mesh,
4136                               0,
4137                               co1,
4138                               co2,
4139                               &min_d,
4140                               &add_pars[iter].num_dmcache,
4141                               add_pars[iter].fuv,
4142                               0,
4143                               0,
4144                               0,
4145                               0)) {
4146     if (psys->part->use_modifier_stack && !psmd_eval->mesh_final->runtime.deformed_only) {
4147       add_pars[iter].num = add_pars[iter].num_dmcache;
4148       add_pars[iter].num_dmcache = DMCACHE_ISCHILD;
4149     }
4150     else if (iter_data->mesh == psmd_eval->mesh_original) {
4151       /* Final DM is not same topology as orig mesh,
4152        * we have to map num_dmcache to real final dm. */
4153       add_pars[iter].num = add_pars[iter].num_dmcache;
4154       add_pars[iter].num_dmcache = psys_particle_dm_face_lookup(psmd_eval->mesh_final,
4155                                                                 psmd_eval->mesh_original,
4156                                                                 add_pars[iter].num,
4157                                                                 add_pars[iter].fuv,
4158                                                                 NULL);
4159     }
4160     else {
4161       add_pars[iter].num = add_pars[iter].num_dmcache;
4162     }
4163     if (add_pars[iter].num != DMCACHE_NOTFOUND) {
4164       tls->num_added++;
4165     }
4166   }
4167 }
4168 
brush_add_count_iter_reduce(const void * __restrict UNUSED (userdata),void * __restrict join_v,void * __restrict chunk_v)4169 static void brush_add_count_iter_reduce(const void *__restrict UNUSED(userdata),
4170                                         void *__restrict join_v,
4171                                         void *__restrict chunk_v)
4172 {
4173   BrushAddCountIterTLSData *join = (BrushAddCountIterTLSData *)join_v;
4174   BrushAddCountIterTLSData *tls = (BrushAddCountIterTLSData *)chunk_v;
4175   join->num_added += tls->num_added;
4176 }
4177 
brush_add_count_iter_free(const void * __restrict UNUSED (userdata_v),void * __restrict chunk_v)4178 static void brush_add_count_iter_free(const void *__restrict UNUSED(userdata_v),
4179                                       void *__restrict chunk_v)
4180 {
4181   BrushAddCountIterTLSData *tls = (BrushAddCountIterTLSData *)chunk_v;
4182   if (tls->rng != NULL) {
4183     BLI_rng_free(tls->rng);
4184   }
4185 }
4186 
brush_add(const bContext * C,PEData * data,short number)4187 static int brush_add(const bContext *C, PEData *data, short number)
4188 {
4189   Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
4190   Scene *scene = data->scene;
4191   Object *ob = data->ob;
4192   Mesh *mesh;
4193   PTCacheEdit *edit = data->edit;
4194   ParticleSystem *psys = edit->psys;
4195   ParticleData *add_pars;
4196   ParticleSystemModifierData *psmd_eval = edit->psmd_eval;
4197   ParticleSimulationData sim = {0};
4198   ParticleEditSettings *pset = PE_settings(scene);
4199   int i, k, n = 0, totpart = psys->totpart;
4200   float co1[3], imat[4][4];
4201   float framestep, timestep;
4202   short size = pset->brush[PE_BRUSH_ADD].size;
4203   RNG *rng;
4204 
4205   invert_m4_m4(imat, ob->obmat);
4206 
4207   if (psys->flag & PSYS_GLOBAL_HAIR) {
4208     return 0;
4209   }
4210 
4211   add_pars = MEM_callocN(number * sizeof(ParticleData), "ParticleData add");
4212 
4213   rng = BLI_rng_new_srandom(psys->seed + data->mval[0] + data->mval[1]);
4214 
4215   sim.depsgraph = depsgraph;
4216   sim.scene = scene;
4217   sim.ob = ob;
4218   sim.psys = psys;
4219   sim.psmd = psmd_eval;
4220 
4221   timestep = psys_get_timestep(&sim);
4222 
4223   if (psys->part->use_modifier_stack || psmd_eval->mesh_final->runtime.deformed_only) {
4224     mesh = psmd_eval->mesh_final;
4225   }
4226   else {
4227     mesh = psmd_eval->mesh_original;
4228   }
4229   BLI_assert(mesh);
4230 
4231   /* Calculate positions of new particles to add, based on brush intersection
4232    * with object. New particle data is assigned to a corresponding to check
4233    * index element of add_pars array. This means, that add_pars is a sparse
4234    * array.
4235    */
4236   BrushAddCountIterData iter_data;
4237   iter_data.depsgraph = depsgraph;
4238   iter_data.scene = scene;
4239   iter_data.object = ob;
4240   iter_data.mesh = mesh;
4241   iter_data.data = data;
4242   iter_data.number = number;
4243   iter_data.size = size;
4244   iter_data.add_pars = add_pars;
4245   copy_m4_m4(iter_data.imat, imat);
4246 
4247   BrushAddCountIterTLSData tls = {NULL};
4248 
4249   TaskParallelSettings settings;
4250   BLI_parallel_range_settings_defaults(&settings);
4251   settings.userdata_chunk = &tls;
4252   settings.userdata_chunk_size = sizeof(BrushAddCountIterTLSData);
4253   settings.func_reduce = brush_add_count_iter_reduce;
4254   settings.func_free = brush_add_count_iter_free;
4255   BLI_task_parallel_range(0, number, &iter_data, brush_add_count_iter, &settings);
4256 
4257   /* Convert add_parse to a dense array, where all new particles are in the
4258    * beginning of the array.
4259    */
4260   n = tls.num_added;
4261   for (int current_iter = 0, new_index = 0; current_iter < number; current_iter++) {
4262     if (add_pars[current_iter].num == DMCACHE_NOTFOUND) {
4263       continue;
4264     }
4265     if (new_index != current_iter) {
4266       new_index++;
4267       continue;
4268     }
4269     memcpy(add_pars + new_index, add_pars + current_iter, sizeof(ParticleData));
4270     new_index++;
4271   }
4272 
4273   /* TODO(sergey): Consider multi-threading this part as well. */
4274   if (n) {
4275     int newtotpart = totpart + n;
4276     float hairmat[4][4], cur_co[3];
4277     KDTree_3d *tree = 0;
4278     ParticleData *pa,
4279         *new_pars = MEM_callocN(newtotpart * sizeof(ParticleData), "ParticleData new");
4280     PTCacheEditPoint *point, *new_points = MEM_callocN(newtotpart * sizeof(PTCacheEditPoint),
4281                                                        "PTCacheEditPoint array new");
4282     PTCacheEditKey *key;
4283     HairKey *hkey;
4284 
4285     /* save existing elements */
4286     memcpy(new_pars, psys->particles, totpart * sizeof(ParticleData));
4287     memcpy(new_points, edit->points, totpart * sizeof(PTCacheEditPoint));
4288 
4289     /* change old arrays to new ones */
4290     if (psys->particles) {
4291       MEM_freeN(psys->particles);
4292     }
4293     psys->particles = new_pars;
4294 
4295     if (edit->points) {
4296       MEM_freeN(edit->points);
4297     }
4298     edit->points = new_points;
4299 
4300     if (edit->mirror_cache) {
4301       MEM_freeN(edit->mirror_cache);
4302       edit->mirror_cache = NULL;
4303     }
4304 
4305     /* create tree for interpolation */
4306     if (pset->flag & PE_INTERPOLATE_ADDED && psys->totpart) {
4307       tree = BLI_kdtree_3d_new(psys->totpart);
4308 
4309       for (i = 0, pa = psys->particles; i < totpart; i++, pa++) {
4310         psys_particle_on_dm(psmd_eval->mesh_final,
4311                             psys->part->from,
4312                             pa->num,
4313                             pa->num_dmcache,
4314                             pa->fuv,
4315                             pa->foffset,
4316                             cur_co,
4317                             0,
4318                             0,
4319                             0,
4320                             0);
4321         BLI_kdtree_3d_insert(tree, i, cur_co);
4322       }
4323 
4324       BLI_kdtree_3d_balance(tree);
4325     }
4326 
4327     edit->totpoint = psys->totpart = newtotpart;
4328 
4329     /* create new elements */
4330     pa = psys->particles + totpart;
4331     point = edit->points + totpart;
4332 
4333     for (i = totpart; i < newtotpart; i++, pa++, point++) {
4334       memcpy(pa, add_pars + i - totpart, sizeof(ParticleData));
4335       pa->hair = MEM_callocN(pset->totaddkey * sizeof(HairKey), "BakeKey key add");
4336       key = point->keys = MEM_callocN(pset->totaddkey * sizeof(PTCacheEditKey),
4337                                       "PTCacheEditKey add");
4338       point->totkey = pa->totkey = pset->totaddkey;
4339 
4340       for (k = 0, hkey = pa->hair; k < pa->totkey; k++, hkey++, key++) {
4341         key->co = hkey->co;
4342         key->time = &hkey->time;
4343 
4344         if (!(psys->flag & PSYS_GLOBAL_HAIR)) {
4345           key->flag |= PEK_USE_WCO;
4346         }
4347       }
4348 
4349       pa->size = 1.0f;
4350       init_particle(&sim, pa);
4351       reset_particle(&sim, pa, 0.0, 1.0);
4352       point->flag |= PEP_EDIT_RECALC;
4353       if (pe_x_mirror(ob)) {
4354         point->flag |= PEP_TAG; /* signal for duplicate */
4355       }
4356 
4357       framestep = pa->lifetime / (float)(pset->totaddkey - 1);
4358 
4359       if (tree) {
4360         ParticleData *ppa;
4361         HairKey *thkey;
4362         ParticleKey key3[3];
4363         KDTreeNearest_3d ptn[3];
4364         int w, maxw;
4365         float maxd, totw = 0.0, weight[3];
4366 
4367         psys_particle_on_dm(psmd_eval->mesh_final,
4368                             psys->part->from,
4369                             pa->num,
4370                             pa->num_dmcache,
4371                             pa->fuv,
4372                             pa->foffset,
4373                             co1,
4374                             0,
4375                             0,
4376                             0,
4377                             0);
4378         maxw = BLI_kdtree_3d_find_nearest_n(tree, co1, ptn, 3);
4379 
4380         maxd = ptn[maxw - 1].dist;
4381 
4382         for (w = 0; w < maxw; w++) {
4383           weight[w] = (float)pow(2.0, (double)(-6.0f * ptn[w].dist / maxd));
4384           totw += weight[w];
4385         }
4386         for (; w < 3; w++) {
4387           weight[w] = 0.0f;
4388         }
4389 
4390         if (totw > 0.0f) {
4391           for (w = 0; w < maxw; w++) {
4392             weight[w] /= totw;
4393           }
4394         }
4395         else {
4396           for (w = 0; w < maxw; w++) {
4397             weight[w] = 1.0f / maxw;
4398           }
4399         }
4400 
4401         ppa = psys->particles + ptn[0].index;
4402 
4403         for (k = 0; k < pset->totaddkey; k++) {
4404           thkey = (HairKey *)pa->hair + k;
4405           thkey->time = pa->time + k * framestep;
4406 
4407           key3[0].time = thkey->time / 100.0f;
4408           psys_get_particle_on_path(&sim, ptn[0].index, key3, 0);
4409           mul_v3_fl(key3[0].co, weight[0]);
4410 
4411           /* TODO: interpolating the weight would be nicer */
4412           thkey->weight = (ppa->hair + MIN2(k, ppa->totkey - 1))->weight;
4413 
4414           if (maxw > 1) {
4415             key3[1].time = key3[0].time;
4416             psys_get_particle_on_path(&sim, ptn[1].index, &key3[1], 0);
4417             mul_v3_fl(key3[1].co, weight[1]);
4418             add_v3_v3(key3[0].co, key3[1].co);
4419 
4420             if (maxw > 2) {
4421               key3[2].time = key3[0].time;
4422               psys_get_particle_on_path(&sim, ptn[2].index, &key3[2], 0);
4423               mul_v3_fl(key3[2].co, weight[2]);
4424               add_v3_v3(key3[0].co, key3[2].co);
4425             }
4426           }
4427 
4428           if (k == 0) {
4429             sub_v3_v3v3(co1, pa->state.co, key3[0].co);
4430           }
4431 
4432           add_v3_v3v3(thkey->co, key3[0].co, co1);
4433 
4434           thkey->time = key3[0].time;
4435         }
4436       }
4437       else {
4438         for (k = 0, hkey = pa->hair; k < pset->totaddkey; k++, hkey++) {
4439           madd_v3_v3v3fl(hkey->co, pa->state.co, pa->state.vel, k * framestep * timestep);
4440           hkey->time += k * framestep;
4441           hkey->weight = 1.f - (float)k / (float)(pset->totaddkey - 1);
4442         }
4443       }
4444       for (k = 0, hkey = pa->hair; k < pset->totaddkey; k++, hkey++) {
4445         psys_mat_hair_to_global(ob, psmd_eval->mesh_final, psys->part->from, pa, hairmat);
4446         invert_m4_m4(imat, hairmat);
4447         mul_m4_v3(imat, hkey->co);
4448       }
4449     }
4450 
4451     if (tree) {
4452       BLI_kdtree_3d_free(tree);
4453     }
4454   }
4455 
4456   MEM_freeN(add_pars);
4457 
4458   BLI_rng_free(rng);
4459 
4460   return n;
4461 }
4462 
4463 /************************* brush edit operator ********************/
4464 
4465 typedef struct BrushEdit {
4466   Scene *scene;
4467   ViewLayer *view_layer;
4468   Object *ob;
4469   PTCacheEdit *edit;
4470 
4471   int first;
4472   int lastmouse[2];
4473   float zfac;
4474 
4475   /* optional cached view settings to avoid setting on every mousemove */
4476   PEData data;
4477 } BrushEdit;
4478 
brush_edit_init(bContext * C,wmOperator * op)4479 static int brush_edit_init(bContext *C, wmOperator *op)
4480 {
4481   Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
4482   Scene *scene = CTX_data_scene(C);
4483   ViewLayer *view_layer = CTX_data_view_layer(C);
4484   Object *ob = CTX_data_active_object(C);
4485   PTCacheEdit *edit = PE_get_current(depsgraph, scene, ob);
4486   ARegion *region = CTX_wm_region(C);
4487   BrushEdit *bedit;
4488   float min[3], max[3];
4489 
4490   if (!WM_toolsystem_active_tool_is_brush(C)) {
4491     return 0;
4492   }
4493 
4494   /* set the 'distance factor' for grabbing (used in comb etc) */
4495   INIT_MINMAX(min, max);
4496   PE_minmax(depsgraph, scene, view_layer, min, max);
4497   mid_v3_v3v3(min, min, max);
4498 
4499   bedit = MEM_callocN(sizeof(BrushEdit), "BrushEdit");
4500   bedit->first = 1;
4501   op->customdata = bedit;
4502 
4503   bedit->scene = scene;
4504   bedit->view_layer = view_layer;
4505   bedit->ob = ob;
4506   bedit->edit = edit;
4507 
4508   bedit->zfac = ED_view3d_calc_zfac(region->regiondata, min, NULL);
4509 
4510   /* cache view depths and settings for re-use */
4511   PE_set_view3d_data(C, &bedit->data);
4512   PE_create_random_generator(&bedit->data);
4513 
4514   return 1;
4515 }
4516 
brush_edit_apply(bContext * C,wmOperator * op,PointerRNA * itemptr)4517 static void brush_edit_apply(bContext *C, wmOperator *op, PointerRNA *itemptr)
4518 {
4519   BrushEdit *bedit = op->customdata;
4520   Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
4521   Scene *scene = bedit->scene;
4522   Object *ob = bedit->ob;
4523   PTCacheEdit *edit = bedit->edit;
4524   ParticleEditSettings *pset = PE_settings(scene);
4525   ParticleSystemModifierData *psmd_eval = edit->psmd_eval;
4526   ParticleBrushData *brush = &pset->brush[pset->brushtype];
4527   ARegion *region = CTX_wm_region(C);
4528   float vec[3], mousef[2];
4529   int mval[2];
4530   int flip, mouse[2], removed = 0, added = 0, selected = 0, tot_steps = 1, step = 1;
4531   float dx, dy, dmax;
4532   int lock_root = pset->flag & PE_LOCK_FIRST;
4533 
4534   if (!PE_start_edit(edit)) {
4535     return;
4536   }
4537 
4538   RNA_float_get_array(itemptr, "mouse", mousef);
4539   mouse[0] = mousef[0];
4540   mouse[1] = mousef[1];
4541   flip = RNA_boolean_get(itemptr, "pen_flip");
4542 
4543   if (bedit->first) {
4544     bedit->lastmouse[0] = mouse[0];
4545     bedit->lastmouse[1] = mouse[1];
4546   }
4547 
4548   dx = mouse[0] - bedit->lastmouse[0];
4549   dy = mouse[1] - bedit->lastmouse[1];
4550 
4551   mval[0] = mouse[0];
4552   mval[1] = mouse[1];
4553 
4554   /* disable locking temporatily for disconnected hair */
4555   if (edit->psys && edit->psys->flag & PSYS_GLOBAL_HAIR) {
4556     pset->flag &= ~PE_LOCK_FIRST;
4557   }
4558 
4559   if (((pset->brushtype == PE_BRUSH_ADD) ?
4560            (sqrtf(dx * dx + dy * dy) > pset->brush[PE_BRUSH_ADD].step) :
4561            (dx != 0 || dy != 0)) ||
4562       bedit->first) {
4563     PEData data = bedit->data;
4564     data.context = C; /* TODO(mai): why isnt this set in bedit->data? */
4565 
4566     view3d_operator_needs_opengl(C);
4567     selected = (short)count_selected_keys(scene, edit);
4568 
4569     dmax = max_ff(fabsf(dx), fabsf(dy));
4570     tot_steps = dmax / (0.2f * pe_brush_size_get(scene, brush)) + 1;
4571 
4572     dx /= (float)tot_steps;
4573     dy /= (float)tot_steps;
4574 
4575     for (step = 1; step <= tot_steps; step++) {
4576       mval[0] = bedit->lastmouse[0] + step * dx;
4577       mval[1] = bedit->lastmouse[1] + step * dy;
4578 
4579       switch (pset->brushtype) {
4580         case PE_BRUSH_COMB: {
4581           const float mval_f[2] = {dx, dy};
4582           data.mval = mval;
4583           data.rad = pe_brush_size_get(scene, brush);
4584 
4585           data.combfac = (brush->strength - 0.5f) * 2.0f;
4586           if (data.combfac < 0.0f) {
4587             data.combfac = 1.0f - 9.0f * data.combfac;
4588           }
4589           else {
4590             data.combfac = 1.0f - data.combfac;
4591           }
4592 
4593           invert_m4_m4(ob->imat, ob->obmat);
4594 
4595           ED_view3d_win_to_delta(region, mval_f, vec, bedit->zfac);
4596           data.dvec = vec;
4597 
4598           foreach_mouse_hit_key(&data, brush_comb, selected);
4599           break;
4600         }
4601         case PE_BRUSH_CUT: {
4602           if (edit->psys && edit->pathcache) {
4603             data.mval = mval;
4604             data.rad = pe_brush_size_get(scene, brush);
4605             data.cutfac = brush->strength;
4606 
4607             if (selected) {
4608               foreach_selected_point(&data, brush_cut);
4609             }
4610             else {
4611               foreach_point(&data, brush_cut);
4612             }
4613 
4614             removed = remove_tagged_particles(ob, edit->psys, pe_x_mirror(ob));
4615             if (pset->flag & PE_KEEP_LENGTHS) {
4616               recalc_lengths(edit);
4617             }
4618           }
4619           else {
4620             removed = 0;
4621           }
4622 
4623           break;
4624         }
4625         case PE_BRUSH_LENGTH: {
4626           data.mval = mval;
4627 
4628           data.rad = pe_brush_size_get(scene, brush);
4629           data.growfac = brush->strength / 50.0f;
4630 
4631           if (brush->invert ^ flip) {
4632             data.growfac = 1.0f - data.growfac;
4633           }
4634           else {
4635             data.growfac = 1.0f + data.growfac;
4636           }
4637 
4638           foreach_mouse_hit_point(&data, brush_length, selected);
4639 
4640           if (pset->flag & PE_KEEP_LENGTHS) {
4641             recalc_lengths(edit);
4642           }
4643           break;
4644         }
4645         case PE_BRUSH_PUFF: {
4646           if (edit->psys) {
4647             data.mesh = psmd_eval->mesh_final;
4648             data.mval = mval;
4649             data.rad = pe_brush_size_get(scene, brush);
4650             data.select = selected;
4651 
4652             data.pufffac = (brush->strength - 0.5f) * 2.0f;
4653             if (data.pufffac < 0.0f) {
4654               data.pufffac = 1.0f - 9.0f * data.pufffac;
4655             }
4656             else {
4657               data.pufffac = 1.0f - data.pufffac;
4658             }
4659 
4660             data.invert = (brush->invert ^ flip);
4661             invert_m4_m4(ob->imat, ob->obmat);
4662 
4663             foreach_mouse_hit_point(&data, brush_puff, selected);
4664           }
4665           break;
4666         }
4667         case PE_BRUSH_ADD: {
4668           if (edit->psys && edit->psys->part->from == PART_FROM_FACE) {
4669             data.mval = mval;
4670 
4671             added = brush_add(C, &data, brush->count);
4672 
4673             if (pset->flag & PE_KEEP_LENGTHS) {
4674               recalc_lengths(edit);
4675             }
4676           }
4677           else {
4678             added = 0;
4679           }
4680           break;
4681         }
4682         case PE_BRUSH_SMOOTH: {
4683           data.mval = mval;
4684           data.rad = pe_brush_size_get(scene, brush);
4685 
4686           data.vec[0] = data.vec[1] = data.vec[2] = 0.0f;
4687           data.tot = 0;
4688 
4689           data.smoothfac = brush->strength;
4690 
4691           invert_m4_m4(ob->imat, ob->obmat);
4692 
4693           foreach_mouse_hit_key(&data, brush_smooth_get, selected);
4694 
4695           if (data.tot) {
4696             mul_v3_fl(data.vec, 1.0f / (float)data.tot);
4697             foreach_mouse_hit_key(&data, brush_smooth_do, selected);
4698           }
4699 
4700           break;
4701         }
4702         case PE_BRUSH_WEIGHT: {
4703           if (edit->psys) {
4704             data.mesh = psmd_eval->mesh_final;
4705             data.mval = mval;
4706             data.rad = pe_brush_size_get(scene, brush);
4707 
4708             data.weightfac = brush->strength; /* note that this will never be zero */
4709 
4710             foreach_mouse_hit_key(&data, BKE_brush_weight_get, selected);
4711           }
4712 
4713           break;
4714         }
4715       }
4716       if ((pset->flag & PE_KEEP_LENGTHS) == 0) {
4717         recalc_lengths(edit);
4718       }
4719 
4720       if (ELEM(pset->brushtype, PE_BRUSH_ADD, PE_BRUSH_CUT) && (added || removed)) {
4721         if (pset->brushtype == PE_BRUSH_ADD && pe_x_mirror(ob)) {
4722           PE_mirror_x(depsgraph, scene, ob, 1);
4723         }
4724 
4725         update_world_cos(ob, edit);
4726         psys_free_path_cache(NULL, edit);
4727         DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
4728       }
4729       else {
4730         PE_update_object(depsgraph, scene, ob, 1);
4731       }
4732     }
4733 
4734     if (edit->psys) {
4735       WM_event_add_notifier(C, NC_OBJECT | ND_PARTICLE | NA_EDITED, ob);
4736       BKE_particle_batch_cache_dirty_tag(edit->psys, BKE_PARTICLE_BATCH_DIRTY_ALL);
4737       DEG_id_tag_update(&ob->id, ID_RECALC_PSYS_REDO);
4738     }
4739     else {
4740       DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
4741       WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
4742     }
4743 
4744     bedit->lastmouse[0] = mouse[0];
4745     bedit->lastmouse[1] = mouse[1];
4746     bedit->first = 0;
4747   }
4748 
4749   pset->flag |= lock_root;
4750 }
4751 
brush_edit_exit(wmOperator * op)4752 static void brush_edit_exit(wmOperator *op)
4753 {
4754   BrushEdit *bedit = op->customdata;
4755 
4756   PE_free_random_generator(&bedit->data);
4757   MEM_freeN(bedit);
4758 }
4759 
brush_edit_exec(bContext * C,wmOperator * op)4760 static int brush_edit_exec(bContext *C, wmOperator *op)
4761 {
4762   if (!brush_edit_init(C, op)) {
4763     return OPERATOR_CANCELLED;
4764   }
4765 
4766   RNA_BEGIN (op->ptr, itemptr, "stroke") {
4767     brush_edit_apply(C, op, &itemptr);
4768   }
4769   RNA_END;
4770 
4771   brush_edit_exit(op);
4772 
4773   return OPERATOR_FINISHED;
4774 }
4775 
brush_edit_apply_event(bContext * C,wmOperator * op,const wmEvent * event)4776 static void brush_edit_apply_event(bContext *C, wmOperator *op, const wmEvent *event)
4777 {
4778   PointerRNA itemptr;
4779   float mouse[2];
4780 
4781   copy_v2fl_v2i(mouse, event->mval);
4782 
4783   /* fill in stroke */
4784   RNA_collection_add(op->ptr, "stroke", &itemptr);
4785 
4786   RNA_float_set_array(&itemptr, "mouse", mouse);
4787   RNA_boolean_set(&itemptr, "pen_flip", event->shift != false); /* XXX hardcoded */
4788 
4789   /* apply */
4790   brush_edit_apply(C, op, &itemptr);
4791 }
4792 
brush_edit_invoke(bContext * C,wmOperator * op,const wmEvent * event)4793 static int brush_edit_invoke(bContext *C, wmOperator *op, const wmEvent *event)
4794 {
4795   if (!brush_edit_init(C, op)) {
4796     return OPERATOR_CANCELLED;
4797   }
4798 
4799   brush_edit_apply_event(C, op, event);
4800 
4801   WM_event_add_modal_handler(C, op);
4802 
4803   return OPERATOR_RUNNING_MODAL;
4804 }
4805 
brush_edit_modal(bContext * C,wmOperator * op,const wmEvent * event)4806 static int brush_edit_modal(bContext *C, wmOperator *op, const wmEvent *event)
4807 {
4808   switch (event->type) {
4809     case LEFTMOUSE:
4810     case MIDDLEMOUSE:
4811     case RIGHTMOUSE: /* XXX hardcoded */
4812       if (event->val == KM_RELEASE) {
4813         brush_edit_exit(op);
4814         return OPERATOR_FINISHED;
4815       }
4816       break;
4817     case MOUSEMOVE:
4818       brush_edit_apply_event(C, op, event);
4819       break;
4820   }
4821 
4822   return OPERATOR_RUNNING_MODAL;
4823 }
4824 
brush_edit_cancel(bContext * UNUSED (C),wmOperator * op)4825 static void brush_edit_cancel(bContext *UNUSED(C), wmOperator *op)
4826 {
4827   brush_edit_exit(op);
4828 }
4829 
PARTICLE_OT_brush_edit(wmOperatorType * ot)4830 void PARTICLE_OT_brush_edit(wmOperatorType *ot)
4831 {
4832   /* identifiers */
4833   ot->name = "Brush Edit";
4834   ot->idname = "PARTICLE_OT_brush_edit";
4835   ot->description = "Apply a stroke of brush to the particles";
4836 
4837   /* api callbacks */
4838   ot->exec = brush_edit_exec;
4839   ot->invoke = brush_edit_invoke;
4840   ot->modal = brush_edit_modal;
4841   ot->cancel = brush_edit_cancel;
4842   ot->poll = PE_poll_view3d;
4843 
4844   /* flags */
4845   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING;
4846 
4847   /* properties */
4848   PropertyRNA *prop;
4849   prop = RNA_def_collection_runtime(ot->srna, "stroke", &RNA_OperatorStrokeElement, "Stroke", "");
4850   RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
4851 }
4852 
4853 /*********************** cut shape ***************************/
4854 
shape_cut_poll(bContext * C)4855 static bool shape_cut_poll(bContext *C)
4856 {
4857   if (PE_hair_poll(C)) {
4858     Scene *scene = CTX_data_scene(C);
4859     ParticleEditSettings *pset = PE_settings(scene);
4860 
4861     if (pset->shape_object && (pset->shape_object->type == OB_MESH)) {
4862       return true;
4863     }
4864   }
4865 
4866   return false;
4867 }
4868 
4869 typedef struct PointInsideBVH {
4870   BVHTreeFromMesh bvhdata;
4871   int num_hits;
4872 } PointInsideBVH;
4873 
point_inside_bvh_cb(void * userdata,int index,const BVHTreeRay * ray,BVHTreeRayHit * hit)4874 static void point_inside_bvh_cb(void *userdata,
4875                                 int index,
4876                                 const BVHTreeRay *ray,
4877                                 BVHTreeRayHit *hit)
4878 {
4879   PointInsideBVH *data = userdata;
4880 
4881   data->bvhdata.raycast_callback(&data->bvhdata, index, ray, hit);
4882 
4883   if (hit->index != -1) {
4884     ++data->num_hits;
4885   }
4886 }
4887 
4888 /* true if the point is inside the shape mesh */
shape_cut_test_point(PEData * data,ParticleEditSettings * pset,ParticleCacheKey * key)4889 static bool shape_cut_test_point(PEData *data, ParticleEditSettings *pset, ParticleCacheKey *key)
4890 {
4891   BVHTreeFromMesh *shape_bvh = &data->shape_bvh;
4892   const float dir[3] = {1.0f, 0.0f, 0.0f};
4893   PointInsideBVH userdata;
4894 
4895   userdata.bvhdata = data->shape_bvh;
4896   userdata.num_hits = 0;
4897 
4898   float co_shape[3];
4899   mul_v3_m4v3(co_shape, pset->shape_object->imat, key->co);
4900 
4901   BLI_bvhtree_ray_cast_all(
4902       shape_bvh->tree, co_shape, dir, 0.0f, BVH_RAYCAST_DIST_MAX, point_inside_bvh_cb, &userdata);
4903 
4904   /* for any point inside a watertight mesh the number of hits is uneven */
4905   return (userdata.num_hits % 2) == 1;
4906 }
4907 
shape_cut(PEData * data,int pa_index)4908 static void shape_cut(PEData *data, int pa_index)
4909 {
4910   PTCacheEdit *edit = data->edit;
4911   Object *ob = data->ob;
4912   ParticleEditSettings *pset = PE_settings(data->scene);
4913   ParticleCacheKey *key;
4914 
4915   bool cut;
4916   float cut_time = 1.0;
4917   int k, totkeys = 1 << pset->draw_step;
4918 
4919   /* don't cut hidden */
4920   if (edit->points[pa_index].flag & PEP_HIDE) {
4921     return;
4922   }
4923 
4924   cut = false;
4925 
4926   /* check if root is inside the cut shape */
4927   key = edit->pathcache[pa_index];
4928   if (!shape_cut_test_point(data, pset, key)) {
4929     cut_time = -1.0f;
4930     cut = true;
4931   }
4932   else {
4933     for (k = 0; k < totkeys; k++, key++) {
4934       BVHTreeRayHit hit;
4935 
4936       float co_curr_shape[3], co_next_shape[3];
4937       float dir_shape[3];
4938       float len_shape;
4939 
4940       mul_v3_m4v3(co_curr_shape, pset->shape_object->imat, key->co);
4941       mul_v3_m4v3(co_next_shape, pset->shape_object->imat, (key + 1)->co);
4942 
4943       sub_v3_v3v3(dir_shape, co_next_shape, co_curr_shape);
4944       len_shape = normalize_v3(dir_shape);
4945 
4946       memset(&hit, 0, sizeof(hit));
4947       hit.index = -1;
4948       hit.dist = len_shape;
4949       BLI_bvhtree_ray_cast(data->shape_bvh.tree,
4950                            co_curr_shape,
4951                            dir_shape,
4952                            0.0f,
4953                            &hit,
4954                            data->shape_bvh.raycast_callback,
4955                            &data->shape_bvh);
4956       if (hit.index >= 0) {
4957         if (hit.dist < len_shape) {
4958           cut_time = ((hit.dist / len_shape) + (float)k) / (float)totkeys;
4959           cut = true;
4960           break;
4961         }
4962       }
4963     }
4964   }
4965 
4966   if (cut) {
4967     if (cut_time < 0.0f) {
4968       edit->points[pa_index].flag |= PEP_TAG;
4969     }
4970     else {
4971       rekey_particle_to_time(data->context, data->scene, ob, pa_index, cut_time);
4972       edit->points[pa_index].flag |= PEP_EDIT_RECALC;
4973     }
4974   }
4975 }
4976 
shape_cut_exec(bContext * C,wmOperator * UNUSED (op))4977 static int shape_cut_exec(bContext *C, wmOperator *UNUSED(op))
4978 {
4979   Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
4980   Scene *scene = CTX_data_scene(C);
4981   Object *ob = CTX_data_active_object(C);
4982   ParticleEditSettings *pset = PE_settings(scene);
4983   PTCacheEdit *edit = PE_get_current(depsgraph, scene, ob);
4984   Object *shapeob = pset->shape_object;
4985   int selected = count_selected_keys(scene, edit);
4986   int lock_root = pset->flag & PE_LOCK_FIRST;
4987 
4988   if (!PE_start_edit(edit)) {
4989     return OPERATOR_CANCELLED;
4990   }
4991 
4992   /* disable locking temporatily for disconnected hair */
4993   if (edit->psys && edit->psys->flag & PSYS_GLOBAL_HAIR) {
4994     pset->flag &= ~PE_LOCK_FIRST;
4995   }
4996 
4997   if (edit->psys && edit->pathcache) {
4998     PEData data;
4999     int removed;
5000 
5001     PE_set_data(C, &data);
5002     if (!PE_create_shape_tree(&data, shapeob)) {
5003       /* shapeob may not have faces... */
5004       return OPERATOR_CANCELLED;
5005     }
5006 
5007     if (selected) {
5008       foreach_selected_point(&data, shape_cut);
5009     }
5010     else {
5011       foreach_point(&data, shape_cut);
5012     }
5013 
5014     removed = remove_tagged_particles(ob, edit->psys, pe_x_mirror(ob));
5015     recalc_lengths(edit);
5016 
5017     if (removed) {
5018       update_world_cos(ob, edit);
5019       psys_free_path_cache(NULL, edit);
5020       DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
5021     }
5022     else {
5023       PE_update_object(data.depsgraph, scene, ob, 1);
5024     }
5025 
5026     if (edit->psys) {
5027       WM_event_add_notifier(C, NC_OBJECT | ND_PARTICLE | NA_EDITED, ob);
5028       BKE_particle_batch_cache_dirty_tag(edit->psys, BKE_PARTICLE_BATCH_DIRTY_ALL);
5029       DEG_id_tag_update(&ob->id, ID_RECALC_PSYS_REDO);
5030     }
5031     else {
5032       DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
5033       WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
5034     }
5035 
5036     PE_free_shape_tree(&data);
5037   }
5038 
5039   pset->flag |= lock_root;
5040 
5041   return OPERATOR_FINISHED;
5042 }
5043 
PARTICLE_OT_shape_cut(wmOperatorType * ot)5044 void PARTICLE_OT_shape_cut(wmOperatorType *ot)
5045 {
5046   /* identifiers */
5047   ot->name = "Shape Cut";
5048   ot->idname = "PARTICLE_OT_shape_cut";
5049   ot->description = "Cut hair to conform to the set shape object";
5050 
5051   /* api callbacks */
5052   ot->exec = shape_cut_exec;
5053   ot->poll = shape_cut_poll;
5054 
5055   /* flags */
5056   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
5057 }
5058 
5059 /************************ utilities ******************************/
5060 
PE_minmax(Depsgraph * depsgraph,Scene * scene,ViewLayer * view_layer,float min[3],float max[3])5061 int PE_minmax(
5062     Depsgraph *depsgraph, Scene *scene, ViewLayer *view_layer, float min[3], float max[3])
5063 {
5064   Object *ob = OBACT(view_layer);
5065   PTCacheEdit *edit = PE_get_current(depsgraph, scene, ob);
5066   ParticleSystem *psys;
5067   ParticleSystemModifierData *psmd_eval = NULL;
5068   POINT_P;
5069   KEY_K;
5070   float co[3], mat[4][4];
5071   int ok = 0;
5072 
5073   if (!edit) {
5074     return ok;
5075   }
5076 
5077   if ((psys = edit->psys)) {
5078     psmd_eval = edit->psmd_eval;
5079   }
5080   else {
5081     unit_m4(mat);
5082   }
5083 
5084   LOOP_VISIBLE_POINTS {
5085     if (psys) {
5086       psys_mat_hair_to_global(
5087           ob, psmd_eval->mesh_final, psys->part->from, psys->particles + p, mat);
5088     }
5089 
5090     LOOP_SELECTED_KEYS {
5091       copy_v3_v3(co, key->co);
5092       mul_m4_v3(mat, co);
5093       DO_MINMAX(co, min, max);
5094       ok = 1;
5095     }
5096   }
5097 
5098   if (!ok) {
5099     BKE_object_minmax(ob, min, max, true);
5100     ok = 1;
5101   }
5102 
5103   return ok;
5104 }
5105 
5106 /************************ particle edit toggle operator ************************/
5107 
5108 /* initialize needed data for bake edit */
PE_create_particle_edit(Depsgraph * depsgraph,Scene * scene,Object * ob,PointCache * cache,ParticleSystem * psys)5109 void PE_create_particle_edit(
5110     Depsgraph *depsgraph, Scene *scene, Object *ob, PointCache *cache, ParticleSystem *psys)
5111 {
5112   Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob);
5113   PTCacheEdit *edit;
5114   ParticleSystemModifierData *psmd = (psys) ? psys_get_modifier(ob, psys) : NULL;
5115   ParticleSystemModifierData *psmd_eval = NULL;
5116   POINT_P;
5117   KEY_K;
5118   ParticleData *pa = NULL;
5119   HairKey *hkey;
5120   int totpoint;
5121 
5122   if (psmd != NULL) {
5123     psmd_eval = (ParticleSystemModifierData *)BKE_modifiers_findby_name(ob_eval,
5124                                                                         psmd->modifier.name);
5125   }
5126 
5127   /* no psmd->dm happens in case particle system modifier is not enabled */
5128   if (!(psys && psmd && psmd_eval->mesh_final) && !cache) {
5129     return;
5130   }
5131 
5132   if (cache && cache->flag & PTCACHE_DISK_CACHE) {
5133     return;
5134   }
5135 
5136   if (psys == NULL && (cache && BLI_listbase_is_empty(&cache->mem_cache))) {
5137     return;
5138   }
5139 
5140   edit = (psys) ? psys->edit : cache->edit;
5141 
5142   if (!edit) {
5143     ParticleSystem *psys_eval = NULL;
5144     if (psys) {
5145       psys_eval = psys_eval_get(depsgraph, ob, psys);
5146       psys_copy_particles(psys, psys_eval);
5147     }
5148 
5149     totpoint = psys ? psys->totpart : (int)((PTCacheMem *)cache->mem_cache.first)->totpoint;
5150 
5151     edit = MEM_callocN(sizeof(PTCacheEdit), "PE_create_particle_edit");
5152     edit->points = MEM_callocN(totpoint * sizeof(PTCacheEditPoint), "PTCacheEditPoints");
5153     edit->totpoint = totpoint;
5154 
5155     if (psys && !cache) {
5156       edit->psmd = psmd;
5157       edit->psmd_eval = psmd_eval;
5158       psys->edit = edit;
5159       edit->psys = psys;
5160       edit->psys_eval = psys_eval;
5161 
5162       psys->free_edit = PE_free_ptcache_edit;
5163 
5164       edit->pathcache = NULL;
5165       BLI_listbase_clear(&edit->pathcachebufs);
5166 
5167       pa = psys->particles;
5168       LOOP_POINTS {
5169         point->totkey = pa->totkey;
5170         point->keys = MEM_callocN(point->totkey * sizeof(PTCacheEditKey), "ParticleEditKeys");
5171         point->flag |= PEP_EDIT_RECALC;
5172 
5173         hkey = pa->hair;
5174         LOOP_KEYS {
5175           key->co = hkey->co;
5176           key->time = &hkey->time;
5177           key->flag = hkey->editflag;
5178           if (!(psys->flag & PSYS_GLOBAL_HAIR)) {
5179             key->flag |= PEK_USE_WCO;
5180             hkey->editflag |= PEK_USE_WCO;
5181           }
5182 
5183           hkey++;
5184         }
5185         pa++;
5186       }
5187       update_world_cos(ob, edit);
5188     }
5189     else {
5190       PTCacheMem *pm;
5191       int totframe = 0;
5192 
5193       cache->edit = edit;
5194       cache->free_edit = PE_free_ptcache_edit;
5195       edit->psys = NULL;
5196 
5197       for (pm = cache->mem_cache.first; pm; pm = pm->next) {
5198         totframe++;
5199       }
5200 
5201       for (pm = cache->mem_cache.first; pm; pm = pm->next) {
5202         LOOP_POINTS {
5203           void *cur[BPHYS_TOT_DATA];
5204           if (BKE_ptcache_mem_pointers_seek(p, pm, cur) == 0) {
5205             continue;
5206           }
5207 
5208           if (!point->totkey) {
5209             key = point->keys = MEM_callocN(totframe * sizeof(PTCacheEditKey), "ParticleEditKeys");
5210             point->flag |= PEP_EDIT_RECALC;
5211           }
5212           else {
5213             key = point->keys + point->totkey;
5214           }
5215 
5216           key->co = cur[BPHYS_DATA_LOCATION];
5217           key->vel = cur[BPHYS_DATA_VELOCITY];
5218           key->rot = cur[BPHYS_DATA_ROTATION];
5219           key->ftime = (float)pm->frame;
5220           key->time = &key->ftime;
5221           BKE_ptcache_mem_pointers_incr(cur);
5222 
5223           point->totkey++;
5224         }
5225       }
5226       psys = NULL;
5227     }
5228 
5229     recalc_lengths(edit);
5230     if (psys && !cache) {
5231       recalc_emitter_field(depsgraph, ob, psys);
5232     }
5233 
5234     PE_update_object(depsgraph, scene, ob, 1);
5235   }
5236 }
5237 
particle_edit_toggle_poll(bContext * C)5238 static bool particle_edit_toggle_poll(bContext *C)
5239 {
5240   Object *ob = CTX_data_active_object(C);
5241 
5242   if (ob == NULL || ob->type != OB_MESH) {
5243     return 0;
5244   }
5245   if (!ob->data || ID_IS_LINKED(ob->data)) {
5246     return 0;
5247   }
5248 
5249   return (ob->particlesystem.first || BKE_modifiers_findby_type(ob, eModifierType_Cloth) ||
5250           BKE_modifiers_findby_type(ob, eModifierType_Softbody));
5251 }
5252 
free_all_psys_edit(Object * object)5253 static void free_all_psys_edit(Object *object)
5254 {
5255   for (ParticleSystem *psys = object->particlesystem.first; psys != NULL; psys = psys->next) {
5256     if (psys->edit != NULL) {
5257       BLI_assert(psys->free_edit != NULL);
5258       psys->free_edit(psys->edit);
5259       psys->free_edit = NULL;
5260       psys->edit = NULL;
5261     }
5262   }
5263 }
5264 
ED_object_particle_edit_mode_enter_ex(Depsgraph * depsgraph,Scene * scene,Object * ob)5265 void ED_object_particle_edit_mode_enter_ex(Depsgraph *depsgraph, Scene *scene, Object *ob)
5266 {
5267   PTCacheEdit *edit;
5268 
5269   ob->mode |= OB_MODE_PARTICLE_EDIT;
5270 
5271   edit = PE_create_current(depsgraph, scene, ob);
5272 
5273   /* Mesh may have changed since last entering editmode.
5274    * note, this may have run before if the edit data was just created,
5275    * so could avoid this and speed up a little. */
5276   if (edit && edit->psys) {
5277     /* Make sure pointer to the evaluated modifier data is up to date,
5278      * with possible changes applied when object was outside of the
5279      * edit mode. */
5280     Object *object_eval = DEG_get_evaluated_object(depsgraph, ob);
5281     edit->psmd_eval = (ParticleSystemModifierData *)BKE_modifiers_findby_name(
5282         object_eval, edit->psmd->modifier.name);
5283     recalc_emitter_field(depsgraph, ob, edit->psys);
5284   }
5285 
5286   toggle_particle_cursor(scene, true);
5287   DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE);
5288   WM_main_add_notifier(NC_SCENE | ND_MODE | NS_MODE_PARTICLE, NULL);
5289 }
5290 
ED_object_particle_edit_mode_enter(bContext * C)5291 void ED_object_particle_edit_mode_enter(bContext *C)
5292 {
5293   Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
5294   Scene *scene = CTX_data_scene(C);
5295   Object *ob = CTX_data_active_object(C);
5296   ED_object_particle_edit_mode_enter_ex(depsgraph, scene, ob);
5297 }
5298 
ED_object_particle_edit_mode_exit_ex(Scene * scene,Object * ob)5299 void ED_object_particle_edit_mode_exit_ex(Scene *scene, Object *ob)
5300 {
5301   ob->mode &= ~OB_MODE_PARTICLE_EDIT;
5302   toggle_particle_cursor(scene, false);
5303   free_all_psys_edit(ob);
5304 
5305   DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE);
5306   WM_main_add_notifier(NC_SCENE | ND_MODE | NS_MODE_OBJECT, NULL);
5307 }
5308 
ED_object_particle_edit_mode_exit(bContext * C)5309 void ED_object_particle_edit_mode_exit(bContext *C)
5310 {
5311   Scene *scene = CTX_data_scene(C);
5312   Object *ob = CTX_data_active_object(C);
5313   ED_object_particle_edit_mode_exit_ex(scene, ob);
5314 }
5315 
particle_edit_toggle_exec(bContext * C,wmOperator * op)5316 static int particle_edit_toggle_exec(bContext *C, wmOperator *op)
5317 {
5318   struct wmMsgBus *mbus = CTX_wm_message_bus(C);
5319   Scene *scene = CTX_data_scene(C);
5320   Object *ob = CTX_data_active_object(C);
5321   const int mode_flag = OB_MODE_PARTICLE_EDIT;
5322   const bool is_mode_set = (ob->mode & mode_flag) != 0;
5323 
5324   if (!is_mode_set) {
5325     if (!ED_object_mode_compat_set(C, ob, mode_flag, op->reports)) {
5326       return OPERATOR_CANCELLED;
5327     }
5328   }
5329 
5330   if (!is_mode_set) {
5331     Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
5332     ED_object_particle_edit_mode_enter_ex(depsgraph, scene, ob);
5333   }
5334   else {
5335     ED_object_particle_edit_mode_exit_ex(scene, ob);
5336   }
5337 
5338   WM_msg_publish_rna_prop(mbus, &ob->id, ob, Object, mode);
5339 
5340   WM_toolsystem_update_from_context_view3d(C);
5341 
5342   return OPERATOR_FINISHED;
5343 }
5344 
PARTICLE_OT_particle_edit_toggle(wmOperatorType * ot)5345 void PARTICLE_OT_particle_edit_toggle(wmOperatorType *ot)
5346 {
5347   /* identifiers */
5348   ot->name = "Particle Edit Toggle";
5349   ot->idname = "PARTICLE_OT_particle_edit_toggle";
5350   ot->description = "Toggle particle edit mode";
5351 
5352   /* api callbacks */
5353   ot->exec = particle_edit_toggle_exec;
5354   ot->poll = particle_edit_toggle_poll;
5355 
5356   /* flags */
5357   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
5358 }
5359 
5360 /************************ set editable operator ************************/
5361 
clear_edited_exec(bContext * C,wmOperator * UNUSED (op))5362 static int clear_edited_exec(bContext *C, wmOperator *UNUSED(op))
5363 {
5364   Object *ob = CTX_data_active_object(C);
5365   ParticleSystem *psys = psys_get_current(ob);
5366 
5367   if (psys->edit) {
5368     if (psys->edit->edited || 1) {
5369       PE_free_ptcache_edit(psys->edit);
5370 
5371       psys->edit = NULL;
5372       psys->free_edit = NULL;
5373 
5374       psys->recalc |= ID_RECALC_PSYS_RESET;
5375       psys->flag &= ~PSYS_GLOBAL_HAIR;
5376       psys->flag &= ~PSYS_EDITED;
5377 
5378       psys_reset(psys, PSYS_RESET_DEPSGRAPH);
5379       WM_event_add_notifier(C, NC_OBJECT | ND_PARTICLE | NA_EDITED, ob);
5380       BKE_particle_batch_cache_dirty_tag(psys, BKE_PARTICLE_BATCH_DIRTY_ALL);
5381       DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
5382     }
5383   }
5384   else { /* some operation might have protected hair from editing so let's clear the flag */
5385     psys->recalc |= ID_RECALC_PSYS_RESET;
5386     psys->flag &= ~PSYS_GLOBAL_HAIR;
5387     psys->flag &= ~PSYS_EDITED;
5388     WM_event_add_notifier(C, NC_OBJECT | ND_PARTICLE | NA_EDITED, ob);
5389     DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
5390   }
5391 
5392   return OPERATOR_FINISHED;
5393 }
5394 
PARTICLE_OT_edited_clear(wmOperatorType * ot)5395 void PARTICLE_OT_edited_clear(wmOperatorType *ot)
5396 {
5397   /* identifiers */
5398   ot->name = "Clear Edited";
5399   ot->idname = "PARTICLE_OT_edited_clear";
5400   ot->description = "Undo all edition performed on the particle system";
5401 
5402   /* api callbacks */
5403   ot->exec = clear_edited_exec;
5404   ot->poll = particle_edit_toggle_poll;
5405 
5406   /* flags */
5407   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
5408 }
5409 
5410 /************************ Unify length operator ************************/
5411 
calculate_point_length(PTCacheEditPoint * point)5412 static float calculate_point_length(PTCacheEditPoint *point)
5413 {
5414   float length = 0.0f;
5415   KEY_K;
5416   LOOP_KEYS {
5417     if (k > 0) {
5418       length += len_v3v3((key - 1)->co, key->co);
5419     }
5420   }
5421   return length;
5422 }
5423 
calculate_average_length(PTCacheEdit * edit)5424 static float calculate_average_length(PTCacheEdit *edit)
5425 {
5426   int num_selected = 0;
5427   float total_length = 0;
5428   POINT_P;
5429   LOOP_SELECTED_POINTS {
5430     total_length += calculate_point_length(point);
5431     num_selected++;
5432   }
5433   if (num_selected == 0) {
5434     return 0.0f;
5435   }
5436   return total_length / num_selected;
5437 }
5438 
scale_point_factor(PTCacheEditPoint * point,float factor)5439 static void scale_point_factor(PTCacheEditPoint *point, float factor)
5440 {
5441   float orig_prev_co[3], prev_co[3];
5442   KEY_K;
5443   LOOP_KEYS {
5444     if (k == 0) {
5445       copy_v3_v3(orig_prev_co, key->co);
5446       copy_v3_v3(prev_co, key->co);
5447     }
5448     else {
5449       float new_co[3];
5450       float delta[3];
5451 
5452       sub_v3_v3v3(delta, key->co, orig_prev_co);
5453       mul_v3_fl(delta, factor);
5454       add_v3_v3v3(new_co, prev_co, delta);
5455 
5456       copy_v3_v3(orig_prev_co, key->co);
5457       copy_v3_v3(key->co, new_co);
5458       copy_v3_v3(prev_co, key->co);
5459     }
5460   }
5461   point->flag |= PEP_EDIT_RECALC;
5462 }
5463 
scale_point_to_length(PTCacheEditPoint * point,float length)5464 static void scale_point_to_length(PTCacheEditPoint *point, float length)
5465 {
5466   const float point_length = calculate_point_length(point);
5467   if (point_length != 0.0f) {
5468     const float factor = length / point_length;
5469     scale_point_factor(point, factor);
5470   }
5471 }
5472 
scale_points_to_length(PTCacheEdit * edit,float length)5473 static void scale_points_to_length(PTCacheEdit *edit, float length)
5474 {
5475   POINT_P;
5476   LOOP_SELECTED_POINTS {
5477     scale_point_to_length(point, length);
5478   }
5479   recalc_lengths(edit);
5480 }
5481 
unify_length_exec(bContext * C,wmOperator * UNUSED (op))5482 static int unify_length_exec(bContext *C, wmOperator *UNUSED(op))
5483 {
5484   Object *ob = CTX_data_active_object(C);
5485   Scene *scene = CTX_data_scene(C);
5486   Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
5487 
5488   PTCacheEdit *edit = PE_get_current(depsgraph, scene, ob);
5489   float average_length = calculate_average_length(edit);
5490 
5491   if (average_length == 0.0f) {
5492     return OPERATOR_CANCELLED;
5493   }
5494   scale_points_to_length(edit, average_length);
5495 
5496   PE_update_object(depsgraph, scene, ob, 1);
5497   if (edit->psys) {
5498     WM_event_add_notifier(C, NC_OBJECT | ND_PARTICLE | NA_EDITED, ob);
5499   }
5500   else {
5501     DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
5502     WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
5503   }
5504 
5505   return OPERATOR_FINISHED;
5506 }
5507 
PARTICLE_OT_unify_length(struct wmOperatorType * ot)5508 void PARTICLE_OT_unify_length(struct wmOperatorType *ot)
5509 {
5510   /* identifiers */
5511   ot->name = "Unify Length";
5512   ot->idname = "PARTICLE_OT_unify_length";
5513   ot->description = "Make selected hair the same length";
5514 
5515   /* api callbacks */
5516   ot->exec = unify_length_exec;
5517   ot->poll = PE_poll_view3d;
5518 
5519   /* flags */
5520   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
5521 }
5522