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