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