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