Blender V5.0
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) {
3415 immVertexFormat(), "pos", blender::gpu::VertAttrType::SFLOAT_32_32);
3417
3418 immUniformColor4ub(255, 255, 255, 128);
3419
3420 GPU_line_smooth(true);
3422
3423 imm_draw_circle_wire_2d(pos, float(xy.x), float(xy.y), pe_brush_size_get(scene, brush), 40);
3424
3426 GPU_line_smooth(false);
3427
3429 }
3430}
3431
3432static void toggle_particle_cursor(Scene *scene, bool enable)
3433{
3434 ParticleEditSettings *pset = PE_settings(scene);
3435
3436 if (pset->paintcursor && !enable) {
3437 WM_paint_cursor_end(static_cast<wmPaintCursor *>(pset->paintcursor));
3438 pset->paintcursor = nullptr;
3439 }
3440 else if (enable) {
3443 }
3444}
3445
3447
3448/* -------------------------------------------------------------------- */
3451
3453
3455 {DEL_PARTICLE, "PARTICLE", 0, "Particle", ""},
3456 {DEL_KEY, "KEY", 0, "Key", ""},
3457 {0, nullptr, 0, nullptr, nullptr},
3458};
3459
3460static void set_delete_particle(PEData *data, int pa_index)
3461{
3462 PTCacheEdit *edit = data->edit;
3463
3464 edit->points[pa_index].flag |= PEP_TAG;
3465}
3466
3467static void set_delete_particle_key(PEData *data, int pa_index, int key_index, bool /*is_inside*/)
3468{
3469 PTCacheEdit *edit = data->edit;
3470
3471 edit->points[pa_index].keys[key_index].flag |= PEK_TAG;
3472}
3473
3475{
3476 PEData data;
3477 int type = RNA_enum_get(op->ptr, "type");
3478
3479 PE_set_data(C, &data);
3480
3481 if (type == DEL_KEY) {
3483 remove_tagged_keys(data.depsgraph, data.ob, data.edit->psys);
3484 recalc_lengths(data.edit);
3485 }
3486 else if (type == DEL_PARTICLE) {
3488 remove_tagged_particles(data.ob, data.edit->psys, pe_x_mirror(data.ob));
3489 recalc_lengths(data.edit);
3490 }
3491
3495
3496 return OPERATOR_FINISHED;
3497}
3498
3500{
3501 /* identifiers */
3502 ot->name = "Delete";
3503 ot->idname = "PARTICLE_OT_delete";
3504 ot->description = "Delete selected particles or keys";
3505
3506 /* API callbacks. */
3507 ot->exec = delete_exec;
3508 ot->invoke = WM_menu_invoke;
3509 ot->poll = PE_hair_poll;
3510
3511 /* flags */
3512 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3513
3514 /* properties */
3515 ot->prop = RNA_def_enum(ot->srna,
3516 "type",
3519 "Type",
3520 "Delete a full particle or only keys");
3521}
3522
3524
3525/* -------------------------------------------------------------------- */
3528
3529static void PE_mirror_x(Depsgraph *depsgraph, Scene *scene, Object *ob, int tagged)
3530{
3531 Mesh *mesh = (Mesh *)(ob->data);
3532 ParticleSystemModifierData *psmd_eval;
3533 PTCacheEdit *edit = PE_get_current(depsgraph, scene, ob);
3534 ParticleSystem *psys = edit->psys;
3535 ParticleData *pa, *newpa, *new_pars;
3536 PTCacheEditPoint *newpoint, *new_points;
3537 POINT_P;
3538 KEY_K;
3539 HairKey *hkey;
3540 int *mirrorfaces = nullptr;
3541 int rotation, totpart, newtotpart;
3542
3543 if (psys->flag & PSYS_GLOBAL_HAIR) {
3544 return;
3545 }
3546
3547 psmd_eval = edit->psmd_eval;
3548 if (!psmd_eval->mesh_final) {
3549 return;
3550 }
3551
3552 const bool use_dm_final_indices = (psys->part->use_modifier_stack &&
3553 !psmd_eval->mesh_final->runtime->deformed_only);
3554
3555 /* NOTE: this is not nice to use tessfaces but hard to avoid since pa->num uses tessfaces */
3557
3558 /* NOTE: In case psys uses Mesh tessface indices, we mirror final Mesh itself, not orig mesh.
3559 * Avoids an (impossible) mesh -> orig -> mesh tessface indices conversion. */
3560 mirrorfaces = mesh_get_x_mirror_faces(
3561 ob, nullptr, use_dm_final_indices ? psmd_eval->mesh_final : nullptr);
3562
3563 if (!edit->mirror_cache) {
3564 PE_update_mirror_cache(ob, psys);
3565 }
3566
3567 totpart = psys->totpart;
3568 newtotpart = psys->totpart;
3570 pa = psys->particles + p;
3571
3572 if (!tagged) {
3573 if (point_is_selected(point)) {
3574 if (edit->mirror_cache[p] != -1) {
3575 /* already has a mirror, don't need to duplicate */
3576 PE_mirror_particle(ob, psmd_eval->mesh_final, psys, pa, nullptr);
3577 continue;
3578 }
3579 point->flag |= PEP_TAG;
3580 }
3581 }
3582
3583 if ((point->flag & PEP_TAG) && mirrorfaces[pa->num * 2] != -1) {
3584 newtotpart++;
3585 }
3586 }
3587
3588 if (newtotpart != psys->totpart) {
3589 const MFace *mtessface = use_dm_final_indices ?
3590 (const MFace *)CustomData_get_layer(
3591 &psmd_eval->mesh_final->fdata_legacy, CD_MFACE) :
3592 (const MFace *)CustomData_get_layer(&mesh->fdata_legacy,
3593 CD_MFACE);
3594
3595 /* allocate new arrays and copy existing */
3596 new_pars = MEM_calloc_arrayN<ParticleData>(newtotpart, "ParticleData new");
3597 new_points = MEM_calloc_arrayN<PTCacheEditPoint>(newtotpart, "PTCacheEditPoint new");
3598
3599 if (psys->particles) {
3600 memcpy(new_pars, psys->particles, totpart * sizeof(ParticleData));
3601 MEM_freeN(psys->particles);
3602 }
3603 psys->particles = new_pars;
3604
3605 if (edit->points) {
3606 memcpy(new_points, edit->points, totpart * sizeof(PTCacheEditPoint));
3607 MEM_freeN(edit->points);
3608 }
3609 edit->points = new_points;
3610
3612
3613 edit->totpoint = psys->totpart = newtotpart;
3614
3615 /* create new elements */
3616 newpa = psys->particles + totpart;
3617 newpoint = edit->points + totpart;
3618
3619 for (p = 0, point = edit->points; p < totpart; p++, point++) {
3620 pa = psys->particles + p;
3621 const int pa_num = pa->num;
3622
3623 if (point->flag & PEP_HIDE) {
3624 continue;
3625 }
3626
3627 if (!(point->flag & PEP_TAG) || mirrorfaces[pa_num * 2] == -1) {
3628 continue;
3629 }
3630
3631 /* duplicate */
3632 *newpa = *pa;
3633 *newpoint = *point;
3634 if (pa->hair) {
3635 newpa->hair = static_cast<HairKey *>(MEM_dupallocN(pa->hair));
3636 }
3637 if (point->keys) {
3638 newpoint->keys = static_cast<PTCacheEditKey *>(MEM_dupallocN(point->keys));
3639 }
3640
3641 /* rotate weights according to vertex index rotation */
3642 rotation = mirrorfaces[pa_num * 2 + 1];
3643 newpa->fuv[0] = pa->fuv[2];
3644 newpa->fuv[1] = pa->fuv[1];
3645 newpa->fuv[2] = pa->fuv[0];
3646 newpa->fuv[3] = pa->fuv[3];
3647 while (rotation--) {
3648 if (mtessface[pa_num].v4) {
3649 SHIFT4(float, newpa->fuv[0], newpa->fuv[1], newpa->fuv[2], newpa->fuv[3]);
3650 }
3651 else {
3652 SHIFT3(float, newpa->fuv[0], newpa->fuv[1], newpa->fuv[2]);
3653 }
3654 }
3655
3656 /* assign face index */
3657 /* NOTE: mesh_get_x_mirror_faces generates -1 for non-found mirror,
3658 * same as DMCACHE_NOTFOUND. */
3659 newpa->num = mirrorfaces[pa_num * 2];
3660
3661 if (use_dm_final_indices) {
3663 }
3664 else {
3666 psmd_eval->mesh_final, psmd_eval->mesh_original, newpa->num, newpa->fuv, nullptr);
3667 }
3668
3669 /* update edit key pointers */
3670 key = newpoint->keys;
3671 for (k = 0, hkey = newpa->hair; k < newpa->totkey; k++, hkey++, key++) {
3672 key->co = hkey->co;
3673 key->time = &hkey->time;
3674 }
3675
3676 /* map key positions as mirror over x axis */
3677 PE_mirror_particle(ob, psmd_eval->mesh_final, psys, pa, newpa);
3678
3679 newpa++;
3680 newpoint++;
3681 }
3682 }
3683
3684 LOOP_POINTS {
3685 point->flag &= ~PEP_TAG;
3686 }
3687
3688 MEM_freeN(mirrorfaces);
3689}
3690
3692{
3694 Scene *scene = CTX_data_scene(C);
3696 PTCacheEdit *edit = PE_get_current(depsgraph, scene, ob);
3697
3698 PE_mirror_x(depsgraph, scene, ob, 0);
3699
3700 update_world_cos(ob, edit);
3701 psys_free_path_cache(nullptr, edit);
3702
3706
3707 return OPERATOR_FINISHED;
3708}
3709
3711{
3712 if (!PE_hair_poll(C)) {
3713 return false;
3714 }
3715
3717 Scene *scene = CTX_data_scene(C);
3719 PTCacheEdit *edit = PE_get_current(depsgraph, scene, ob);
3720
3721 /* The operator only works for hairs emitted from faces. */
3722 return edit->psys->part->from == PART_FROM_FACE;
3723}
3724
3726{
3727 /* identifiers */
3728 ot->name = "Mirror";
3729 /* Using default context for 'flipping along axis', to differentiate from 'symmetrizing' (i.e.
3730 * 'mirrored copy').
3731 * See https://projects.blender.org/blender/blender/issues/43295#issuecomment-1400465 */
3732 ot->translation_context = BLT_I18NCONTEXT_DEFAULT;
3733 ot->idname = "PARTICLE_OT_mirror";
3734 ot->description = "Duplicate and mirror the selected particles along the local X axis";
3735
3736 /* API callbacks. */
3737 ot->exec = mirror_exec;
3738 ot->poll = mirror_poll;
3739
3740 /* flags */
3741 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3742}
3743
3745
3746/* -------------------------------------------------------------------- */
3749
3751 float /*mat*/[4][4],
3752 float imat[4][4],
3753 int point_index,
3754 int key_index,
3755 PTCacheEditKey *key,
3756 float mouse_distance)
3757{
3758 ParticleEditSettings *pset = PE_settings(data->scene);
3759 float cvec[3], fac;
3760
3761 if (pset->flag & PE_LOCK_FIRST && key_index == 0) {
3762 return;
3763 }
3764
3765 fac = float(pow(double(1.0f - mouse_distance / data->rad), double(data->combfac)));
3766
3767 copy_v3_v3(cvec, data->dvec);
3768 mul_mat3_m4_v3(imat, cvec);
3769 mul_v3_fl(cvec, fac);
3770 add_v3_v3(key->co, cvec);
3771
3772 (data->edit->points + point_index)->flag |= PEP_EDIT_RECALC;
3773}
3774
3775static void brush_cut(PEData *data, int pa_index)
3776{
3777 PTCacheEdit *edit = data->edit;
3778 ARegion *region = data->vc.region;
3779 Object *ob = data->ob;
3780 ParticleEditSettings *pset = PE_settings(data->scene);
3781 ParticleCacheKey *key = edit->pathcache[pa_index];
3782 float rad2, cut_time = 1.0;
3783 float x0, x1, v0, v1, o0, o1, xo0, xo1, d, dv;
3784 int k, cut, keys = int(pow(2.0, double(pset->draw_step)));
3785 int screen_co[2];
3786
3787 BLI_assert(data->rng != nullptr);
3788 /* blunt scissors */
3789 if (BLI_rng_get_float(data->rng) > data->cutfac) {
3790 return;
3791 }
3792
3793 /* don't cut hidden */
3794 if (edit->points[pa_index].flag & PEP_HIDE) {
3795 return;
3796 }
3797
3798 if (ED_view3d_project_int_global(region, key->co, screen_co, V3D_PROJ_TEST_CLIP_NEAR) !=
3800 {
3801 return;
3802 }
3803
3804 rad2 = data->rad * data->rad;
3805
3806 cut = 0;
3807
3808 x0 = float(screen_co[0]);
3809 x1 = float(screen_co[1]);
3810
3811 o0 = float(data->mval[0]);
3812 o1 = float(data->mval[1]);
3813
3814 xo0 = x0 - o0;
3815 xo1 = x1 - o1;
3816
3817 /* check if root is inside circle */
3818 if (xo0 * xo0 + xo1 * xo1 < rad2 && key_test_depth(data, key->co, screen_co)) {
3819 cut_time = -1.0f;
3820 cut = 1;
3821 }
3822 else {
3823 /* calculate path time closest to root that was inside the circle */
3824 for (k = 1, key++; k <= keys; k++, key++) {
3825
3826 if ((ED_view3d_project_int_global(region, key->co, screen_co, V3D_PROJ_TEST_CLIP_NEAR) !=
3827 V3D_PROJ_RET_OK) ||
3828 key_test_depth(data, key->co, screen_co) == 0)
3829 {
3830 x0 = float(screen_co[0]);
3831 x1 = float(screen_co[1]);
3832
3833 xo0 = x0 - o0;
3834 xo1 = x1 - o1;
3835 continue;
3836 }
3837
3838 v0 = float(screen_co[0]) - x0;
3839 v1 = float(screen_co[1]) - x1;
3840
3841 dv = v0 * v0 + v1 * v1;
3842
3843 d = (v0 * xo1 - v1 * xo0);
3844
3845 d = dv * rad2 - d * d;
3846
3847 if (d > 0.0f) {
3848 d = sqrtf(d);
3849
3850 cut_time = -(v0 * xo0 + v1 * xo1 + d);
3851
3852 if (cut_time > 0.0f) {
3853 cut_time /= dv;
3854
3855 if (cut_time < 1.0f) {
3856 cut_time += float(k - 1);
3857 cut_time /= float(keys);
3858 cut = 1;
3859 break;
3860 }
3861 }
3862 }
3863
3864 x0 = float(screen_co[0]);
3865 x1 = float(screen_co[1]);
3866
3867 xo0 = x0 - o0;
3868 xo1 = x1 - o1;
3869 }
3870 }
3871
3872 if (cut) {
3873 if (cut_time < 0.0f) {
3874 edit->points[pa_index].flag |= PEP_TAG;
3875 }
3876 else {
3877 rekey_particle_to_time(data->context, data->scene, ob, pa_index, cut_time);
3878 edit->points[pa_index].flag |= PEP_EDIT_RECALC;
3879 }
3880 }
3881}
3882
3883static void brush_length(PEData *data, int point_index, float /*mouse_distance*/)
3884{
3885 PTCacheEdit *edit = data->edit;
3886 PTCacheEditPoint *point = edit->points + point_index;
3887 KEY_K;
3888 float dvec[3], pvec[3] = {0.0f, 0.0f, 0.0f};
3889
3890 LOOP_KEYS {
3891 if (k == 0) {
3892 copy_v3_v3(pvec, key->co);
3893 }
3894 else {
3895 sub_v3_v3v3(dvec, key->co, pvec);
3896 copy_v3_v3(pvec, key->co);
3897 mul_v3_fl(dvec, data->growfac);
3898 add_v3_v3v3(key->co, (key - 1)->co, dvec);
3899 }
3900 }
3901
3902 point->flag |= PEP_EDIT_RECALC;
3903}
3904
3905static void brush_puff(PEData *data, int point_index, float mouse_distance)
3906{
3907 PTCacheEdit *edit = data->edit;
3908 ParticleSystem *psys = edit->psys;
3909 PTCacheEditPoint *point = edit->points + point_index;
3910 KEY_K;
3911 float mat[4][4], imat[4][4];
3912
3913 float onor_prev[3]; /* previous normal (particle-space) */
3914 float ofs_prev[3]; /* accumulate offset for puff_volume (particle-space) */
3915 float co_root[3], no_root[3]; /* root location and normal (global-space) */
3916 float co_prev[3], co[3]; /* track key coords as we loop (global-space) */
3917 float fac = 0.0f, length_accum = 0.0f;
3918 bool puff_volume = false;
3919 bool changed = false;
3920
3921 zero_v3(ofs_prev);
3922
3923 {
3924 ParticleEditSettings *pset = PE_settings(data->scene);
3925 ParticleBrushData *brush = &pset->brush[pset->brushtype];
3926 puff_volume = (brush->flag & PE_BRUSH_DATA_PUFF_VOLUME) != 0;
3927 }
3928
3929 if (psys && !(psys->flag & PSYS_GLOBAL_HAIR)) {
3931 data->ob, data->mesh, psys->part->from, psys->particles + point_index, mat);
3932 invert_m4_m4(imat, mat);
3933 }
3934 else {
3935 unit_m4(mat);
3936 unit_m4(imat);
3937 }
3938
3939 LOOP_KEYS {
3940 float kco[3];
3941
3942 if (k == 0) {
3943 /* find root coordinate and normal on emitter */
3944 copy_v3_v3(co, key->co);
3945 mul_m4_v3(mat, co);
3946
3947 /* Use `kco` as the object space version of world-space `co`,
3948 * `ob->world_to_object` is set before calling. */
3949 mul_v3_m4v3(kco, data->ob->world_to_object().ptr(), co);
3950
3951 point_index = BLI_kdtree_3d_find_nearest(edit->emitter_field, kco, nullptr);
3952 if (point_index == -1) {
3953 return;
3954 }
3955
3956 copy_v3_v3(co_root, co);
3957 copy_v3_v3(no_root, &edit->emitter_cosnos[point_index * 6 + 3]);
3958 mul_mat3_m4_v3(data->ob->object_to_world().ptr(), no_root); /* normal into global-space */
3959 normalize_v3(no_root);
3960
3961 if (puff_volume) {
3962 copy_v3_v3(onor_prev, no_root);
3963 mul_mat3_m4_v3(imat, onor_prev); /* global-space into particle space */
3964 normalize_v3(onor_prev);
3965 }
3966
3967 fac = float(pow(double(1.0f - mouse_distance / data->rad), double(data->pufffac)));
3968 fac *= 0.025f;
3969 if (data->invert) {
3970 fac = -fac;
3971 }
3972 }
3973 else {
3974 /* Compute position as if hair was standing up straight. */
3975 float length;
3976 copy_v3_v3(co_prev, co);
3977 copy_v3_v3(co, key->co);
3978 mul_m4_v3(mat, co);
3979 length = len_v3v3(co_prev, co);
3980 length_accum += length;
3981
3982 if ((data->select == 0 || (key->flag & PEK_SELECT)) && !(key->flag & PEK_HIDE)) {
3983 float dco[3]; /* delta temp var */
3984
3985 madd_v3_v3v3fl(kco, co_root, no_root, length_accum);
3986
3987 /* blend between the current and straight position */
3988 sub_v3_v3v3(dco, kco, co);
3989 madd_v3_v3fl(co, dco, fac);
3990 /* keep the same distance from the root or we get glitches #35406. */
3991 dist_ensure_v3_v3fl(co, co_root, length_accum);
3992
3993 /* Re-use dco to compare before and after translation and add to the offset. */
3994 copy_v3_v3(dco, key->co);
3995
3996 mul_v3_m4v3(key->co, imat, co);
3997
3998 if (puff_volume) {
3999 /* accumulate the total distance moved to apply to unselected
4000 * keys that come after */
4001 sub_v3_v3v3(ofs_prev, key->co, dco);
4002 }
4003 changed = true;
4004 }
4005 else {
4006
4007 if (puff_volume) {
4008#if 0
4009 /* this is simple but looks bad, adds annoying kinks */
4010 add_v3_v3(key->co, ofs);
4011#else
4012 /* Translate (not rotate) the rest of the hair if its not selected. */
4013 {
4014/* NOLINTNEXTLINE: readability-redundant-preprocessor */
4015# if 0 /* Kind of works but looks worse than what's below. */
4016
4017 /* Move the unselected point on a vector based on the
4018 * hair direction and the offset */
4019 float c1[3], c2[3];
4020 sub_v3_v3v3(dco, lastco, co);
4021 mul_mat3_m4_v3(imat, dco); /* into particle space */
4022
4023 /* move the point along a vector perpendicular to the
4024 * hairs direction, reduces odd kinks, */
4025 cross_v3_v3v3(c1, ofs, dco);
4026 cross_v3_v3v3(c2, c1, dco);
4027 normalize_v3(c2);
4028 mul_v3_fl(c2, len_v3(ofs));
4029 add_v3_v3(key->co, c2);
4030# else
4031 /* Move the unselected point on a vector based on the
4032 * the normal of the closest geometry */
4033 float oco[3], onor[3];
4034 copy_v3_v3(oco, key->co);
4035 mul_m4_v3(mat, oco);
4036
4037 /* Use `kco` as the object space version of world-space `co`,
4038 * `ob->world_to_object` is set before calling. */
4039 mul_v3_m4v3(kco, data->ob->world_to_object().ptr(), oco);
4040
4041 point_index = BLI_kdtree_3d_find_nearest(edit->emitter_field, kco, nullptr);
4042 if (point_index != -1) {
4043 copy_v3_v3(onor, &edit->emitter_cosnos[point_index * 6 + 3]);
4044 mul_mat3_m4_v3(data->ob->object_to_world().ptr(),
4045 onor); /* Normal into world-space. */
4046 mul_mat3_m4_v3(imat, onor); /* World-space into particle-space. */
4047 normalize_v3(onor);
4048 }
4049 else {
4050 copy_v3_v3(onor, onor_prev);
4051 }
4052
4053 if (!is_zero_v3(ofs_prev)) {
4054 mul_v3_fl(onor, len_v3(ofs_prev));
4055
4056 add_v3_v3(key->co, onor);
4057 }
4058
4059 copy_v3_v3(onor_prev, onor);
4060# endif
4061 }
4062#endif
4063 }
4064 }
4065 }
4066 }
4067
4068 if (changed) {
4069 point->flag |= PEP_EDIT_RECALC;
4070 }
4071}
4072
4074 float /*mat*/[4][4],
4075 float /*imat*/[4][4],
4076 int point_index,
4077 int key_index,
4078 PTCacheEditKey * /*key*/,
4079 float /*mouse_distance*/)
4080{
4081 /* roots have full weight always */
4082 if (key_index) {
4083 PTCacheEdit *edit = data->edit;
4084 ParticleSystem *psys = edit->psys;
4085
4086 ParticleData *pa = psys->particles + point_index;
4087 pa->hair[key_index].weight = data->weightfac;
4088
4089 (data->edit->points + point_index)->flag |= PEP_EDIT_RECALC;
4090 }
4091}
4092
4094 float mat[4][4],
4095 float /*imat*/[4][4],
4096 int /*point_index*/,
4097 int key_index,
4098 PTCacheEditKey *key,
4099 float /*mouse_distance*/)
4100{
4101 if (key_index) {
4102 float dvec[3];
4103
4104 sub_v3_v3v3(dvec, key->co, (key - 1)->co);
4105 mul_mat3_m4_v3(mat, dvec);
4106 add_v3_v3(data->vec, dvec);
4107 data->tot++;
4108 }
4109}
4110
4112 float /*mat*/[4][4],
4113 float imat[4][4],
4114 int point_index,
4115 int key_index,
4116 PTCacheEditKey *key,
4117 float /*mouse_distance*/)
4118{
4119 float vec[3], dvec[3];
4120
4121 if (key_index) {
4122 copy_v3_v3(vec, data->vec);
4123 mul_mat3_m4_v3(imat, vec);
4124
4125 sub_v3_v3v3(dvec, key->co, (key - 1)->co);
4126
4127 sub_v3_v3v3(dvec, vec, dvec);
4128 mul_v3_fl(dvec, data->smoothfac);
4129
4130 add_v3_v3(key->co, dvec);
4131 }
4132
4133 (data->edit->points + point_index)->flag |= PEP_EDIT_RECALC;
4134}
4135
4136/* convert from triangle barycentric weights to quad mean value weights */
4138 const float v1[3], const float v2[3], const float v3[3], const float v4[3], float w[4])
4139{
4140 float co[3], vert[4][3];
4141
4142 copy_v3_v3(vert[0], v1);
4143 copy_v3_v3(vert[1], v2);
4144 copy_v3_v3(vert[2], v3);
4145 copy_v3_v3(vert[3], v4);
4146
4147 co[0] = v1[0] * w[0] + v2[0] * w[1] + v3[0] * w[2] + v4[0] * w[3];
4148 co[1] = v1[1] * w[0] + v2[1] * w[1] + v3[1] * w[2] + v4[1] * w[3];
4149 co[2] = v1[2] * w[0] + v2[2] * w[1] + v3[2] * w[2] + v4[2] * w[3];
4150
4151 interp_weights_poly_v3(w, vert, 4, co);
4152}
4153
4156 Scene * /*scene*/,
4157 Object *ob,
4158 Mesh *mesh,
4159 float *vert_cos,
4160 const float co1[3],
4161 const float co2[3],
4162 float *min_d,
4163 int *min_face,
4164 float *min_w,
4165 float *face_minmax,
4166 float *pa_minmax,
4167 float radius,
4168 float *ipoint)
4169{
4170 const MFace *mface = nullptr;
4171 int i, totface, intersect = 0;
4172 float cur_d;
4173 blender::float2 cur_uv;
4174 blender::float3 v1, v2, v3, v4, min, max, p_min, p_max;
4175 float cur_ipoint[3];
4176
4177 if (mesh == nullptr) {
4178 psys_disable_all(ob);
4179
4180 Object *ob_eval = DEG_get_evaluated(depsgraph, ob);
4181 mesh = BKE_object_get_evaluated_mesh(ob_eval);
4182 if (mesh == nullptr) {
4183 return 0;
4184 }
4185
4186 psys_enable_all(ob);
4187
4188 if (mesh == nullptr) {
4189 return 0;
4190 }
4191 }
4192
4193 /* BMESH_ONLY, deform dm may not have tessface */
4195
4196 if (pa_minmax == nullptr) {
4197 INIT_MINMAX(p_min, p_max);
4198 minmax_v3v3_v3(p_min, p_max, co1);
4199 minmax_v3v3_v3(p_min, p_max, co2);
4200 }
4201 else {
4202 copy_v3_v3(p_min, pa_minmax);
4203 copy_v3_v3(p_max, pa_minmax + 3);
4204 }
4205
4206 totface = mesh->totface_legacy;
4207 mface = (const MFace *)CustomData_get_layer(&mesh->fdata_legacy, CD_MFACE);
4208 blender::Span<blender::float3> positions = mesh->vert_positions();
4209
4210 /* lets intersect the faces */
4211 for (i = 0; i < totface; i++, mface++) {
4212 if (vert_cos) {
4213 copy_v3_v3(v1, vert_cos + 3 * mface->v1);
4214 copy_v3_v3(v2, vert_cos + 3 * mface->v2);
4215 copy_v3_v3(v3, vert_cos + 3 * mface->v3);
4216 if (mface->v4) {
4217 copy_v3_v3(v4, vert_cos + 3 * mface->v4);
4218 }
4219 }
4220 else {
4221 copy_v3_v3(v1, positions[mface->v1]);
4222 copy_v3_v3(v2, positions[mface->v2]);
4223 copy_v3_v3(v3, positions[mface->v3]);
4224 if (mface->v4) {
4225 copy_v3_v3(v4, positions[mface->v4]);
4226 }
4227 }
4228
4229 if (face_minmax == nullptr) {
4234 if (mface->v4) {
4236 }
4237 if (isect_aabb_aabb_v3(min, max, p_min, p_max) == 0) {
4238 continue;
4239 }
4240 }
4241 else {
4242 copy_v3_v3(min, face_minmax + 6 * i);
4243 copy_v3_v3(max, face_minmax + 6 * i + 3);
4244 if (isect_aabb_aabb_v3(min, max, p_min, p_max) == 0) {
4245 continue;
4246 }
4247 }
4248
4249 if (radius > 0.0f) {
4250 if (isect_sweeping_sphere_tri_v3(co1, co2, radius, v2, v3, v1, &cur_d, cur_ipoint)) {
4251 if (cur_d < *min_d) {
4252 *min_d = cur_d;
4253 copy_v3_v3(ipoint, cur_ipoint);
4254 *min_face = i;
4255 intersect = 1;
4256 }
4257 }
4258 if (mface->v4) {
4259 if (isect_sweeping_sphere_tri_v3(co1, co2, radius, v4, v1, v3, &cur_d, cur_ipoint)) {
4260 if (cur_d < *min_d) {
4261 *min_d = cur_d;
4262 copy_v3_v3(ipoint, cur_ipoint);
4263 *min_face = i;
4264 intersect = 1;
4265 }
4266 }
4267 }
4268 }
4269 else {
4270 if (isect_line_segment_tri_v3(co1, co2, v1, v2, v3, &cur_d, cur_uv)) {
4271 if (cur_d < *min_d) {
4272 *min_d = cur_d;
4273 min_w[0] = 1.0f - cur_uv[0] - cur_uv[1];
4274 min_w[1] = cur_uv[0];
4275 min_w[2] = cur_uv[1];
4276 min_w[3] = 0.0f;
4277 if (mface->v4) {
4278 intersect_dm_quad_weights(v1, v2, v3, v4, min_w);
4279 }
4280 *min_face = i;
4281 intersect = 1;
4282 }
4283 }
4284 if (mface->v4) {
4285 if (isect_line_segment_tri_v3(co1, co2, v1, v3, v4, &cur_d, cur_uv)) {
4286 if (cur_d < *min_d) {
4287 *min_d = cur_d;
4288 min_w[0] = 1.0f - cur_uv[0] - cur_uv[1];
4289 min_w[1] = 0.0f;
4290 min_w[2] = cur_uv[0];
4291 min_w[3] = cur_uv[1];
4292 intersect_dm_quad_weights(v1, v2, v3, v4, min_w);
4293 *min_face = i;
4294 intersect = 1;
4295 }
4296 }
4297 }
4298 }
4299 }
4300 return intersect;
4301}
4302
4314
4319
4320static void brush_add_count_iter(void *__restrict iter_data_v,
4321 const int iter,
4322 const TaskParallelTLS *__restrict tls_v)
4323{
4324 BrushAddCountIterData *iter_data = (BrushAddCountIterData *)iter_data_v;
4325 Depsgraph *depsgraph = iter_data->depsgraph;
4326 PEData *data = iter_data->data;
4327 PTCacheEdit *edit = data->edit;
4328 ParticleSystem *psys = edit->psys;
4329 ParticleSystemModifierData *psmd_eval = edit->psmd_eval;
4330 ParticleData *add_pars = iter_data->add_pars;
4331 BrushAddCountIterTLSData *tls = static_cast<BrushAddCountIterTLSData *>(tls_v->userdata_chunk);
4332 const int number = iter_data->number;
4333 const short size = iter_data->size;
4334 const int size2 = size * size;
4335 float dmx, dmy;
4336 if (number > 1) {
4337 dmx = size;
4338 dmy = size;
4339 if (tls->rng == nullptr) {
4340 tls->rng = BLI_rng_new_srandom(psys->seed + data->mval[0] + data->mval[1] +
4342 }
4343 /* rejection sampling to get points in circle */
4344 while (dmx * dmx + dmy * dmy > size2) {
4345 dmx = (2.0f * BLI_rng_get_float(tls->rng) - 1.0f) * size;
4346 dmy = (2.0f * BLI_rng_get_float(tls->rng) - 1.0f) * size;
4347 }
4348 }
4349 else {
4350 dmx = 0.0f;
4351 dmy = 0.0f;
4352 }
4353
4354 float mco[2];
4355 mco[0] = data->mval[0] + dmx;
4356 mco[1] = data->mval[1] + dmy;
4357
4358 float co1[3], co2[3];
4359 ED_view3d_win_to_segment_clipped(depsgraph, data->vc.region, data->vc.v3d, mco, co1, co2, true);
4360
4361 mul_m4_v3(iter_data->imat, co1);
4362 mul_m4_v3(iter_data->imat, co2);
4363 float min_d = 2.0;
4364
4365 /* warning, returns the derived mesh face */
4366 BLI_assert(iter_data->mesh != nullptr);
4368 iter_data->scene,
4369 iter_data->object,
4370 iter_data->mesh,
4371 nullptr,
4372 co1,
4373 co2,
4374 &min_d,
4375 &add_pars[iter].num_dmcache,
4376 add_pars[iter].fuv,
4377 nullptr,
4378 nullptr,
4379 0,
4380 nullptr))
4381 {
4382 if (psys->part->use_modifier_stack && !psmd_eval->mesh_final->runtime->deformed_only) {
4383 add_pars[iter].num = add_pars[iter].num_dmcache;
4384 add_pars[iter].num_dmcache = DMCACHE_ISCHILD;
4385 }
4386 else if (iter_data->mesh == psmd_eval->mesh_original) {
4387 /* Final DM is not same topology as orig mesh,
4388 * we have to map num_dmcache to real final dm. */
4389 add_pars[iter].num = add_pars[iter].num_dmcache;
4390 add_pars[iter].num_dmcache = psys_particle_dm_face_lookup(psmd_eval->mesh_final,
4391 psmd_eval->mesh_original,
4392 add_pars[iter].num,
4393 add_pars[iter].fuv,
4394 nullptr);
4395 }
4396 else {
4397 add_pars[iter].num = add_pars[iter].num_dmcache;
4398 }
4399 if (add_pars[iter].num != DMCACHE_NOTFOUND) {
4400 tls->num_added++;
4401 }
4402 }
4403}
4404
4405static void brush_add_count_iter_reduce(const void *__restrict /*userdata*/,
4406 void *__restrict join_v,
4407 void *__restrict chunk_v)
4408{
4411 join->num_added += tls->num_added;
4412}
4413
4414static void brush_add_count_iter_free(const void *__restrict /*userdata_v*/,
4415 void *__restrict chunk_v)
4416{
4418 if (tls->rng != nullptr) {
4419 BLI_rng_free(tls->rng);
4420 }
4421}
4422
4423static int brush_add(const bContext *C, PEData *data, short number)
4424{
4426 Scene *scene = data->scene;
4427 Object *ob = data->ob;
4428 Mesh *mesh;
4429 PTCacheEdit *edit = data->edit;
4430 ParticleSystem *psys = edit->psys;
4431 ParticleData *add_pars;
4432 ParticleSystemModifierData *psmd_eval = edit->psmd_eval;
4433 ParticleSimulationData sim = {nullptr};
4434 ParticleEditSettings *pset = PE_settings(scene);
4435 int i, k, n = 0, totpart = psys->totpart;
4436 float co1[3], imat[4][4];
4437 float framestep, timestep;
4438 short size = pset->brush[PE_BRUSH_ADD].size;
4439 RNG *rng;
4440
4441 invert_m4_m4(imat, ob->object_to_world().ptr());
4442
4443 if (psys->flag & PSYS_GLOBAL_HAIR) {
4444 return 0;
4445 }
4446
4447 add_pars = MEM_calloc_arrayN<ParticleData>(number, "ParticleData add");
4448
4449 rng = BLI_rng_new_srandom(psys->seed + data->mval[0] + data->mval[1]);
4450
4451 sim.depsgraph = depsgraph;
4452 sim.scene = scene;
4453 sim.ob = ob;
4454 sim.psys = psys;
4455 sim.psmd = psmd_eval;
4456
4457 timestep = psys_get_timestep(&sim);
4458
4459 if (psys->part->use_modifier_stack || psmd_eval->mesh_final->runtime->deformed_only) {
4460 mesh = psmd_eval->mesh_final;
4461 }
4462 else {
4463 mesh = psmd_eval->mesh_original;
4464 }
4465 BLI_assert(mesh);
4466
4467 /* Calculate positions of new particles to add, based on brush intersection
4468 * with object. New particle data is assigned to a corresponding to check
4469 * index element of add_pars array. This means, that add_pars is a sparse
4470 * array.
4471 */
4472 BrushAddCountIterData iter_data;
4473 iter_data.depsgraph = depsgraph;
4474 iter_data.scene = scene;
4475 iter_data.object = ob;
4476 iter_data.mesh = mesh;
4477 iter_data.data = data;
4478 iter_data.number = number;
4479 iter_data.size = size;
4480 iter_data.add_pars = add_pars;
4481 copy_m4_m4(iter_data.imat, imat);
4482
4483 BrushAddCountIterTLSData tls = {nullptr};
4484
4485 TaskParallelSettings settings;
4487 settings.userdata_chunk = &tls;
4491 BLI_task_parallel_range(0, number, &iter_data, brush_add_count_iter, &settings);
4492
4493 /* Convert add_parse to a dense array, where all new particles are in the
4494 * beginning of the array.
4495 */
4496 n = tls.num_added;
4497 for (int current_iter = 0, new_index = 0; current_iter < number; current_iter++) {
4498 if (add_pars[current_iter].num == DMCACHE_NOTFOUND) {
4499 continue;
4500 }
4501 if (new_index != current_iter) {
4502 new_index++;
4503 continue;
4504 }
4505 memcpy(add_pars + new_index, add_pars + current_iter, sizeof(ParticleData));
4506 new_index++;
4507 }
4508
4509 /* TODO(sergey): Consider multi-threading this part as well. */
4510 if (n) {
4511 int newtotpart = totpart + n;
4512 float hairmat[4][4], cur_co[3];
4513 KDTree_3d *tree = nullptr;
4514 ParticleData *pa, *new_pars = MEM_calloc_arrayN<ParticleData>(newtotpart, "ParticleData new");
4516 newtotpart, "PTCacheEditPoint array new");
4517 PTCacheEditKey *key;
4518 HairKey *hkey;
4519
4520 /* save existing elements */
4521 memcpy(new_pars, psys->particles, totpart * sizeof(ParticleData));
4522 memcpy(new_points, edit->points, totpart * sizeof(PTCacheEditPoint));
4523
4524 /* change old arrays to new ones */
4525 if (psys->particles) {
4526 MEM_freeN(psys->particles);
4527 }
4528 psys->particles = new_pars;
4529
4530 if (edit->points) {
4531 MEM_freeN(edit->points);
4532 }
4533 edit->points = new_points;
4534
4536
4537 /* create tree for interpolation */
4538 if (pset->flag & PE_INTERPOLATE_ADDED && psys->totpart) {
4539 tree = BLI_kdtree_3d_new(psys->totpart);
4540
4541 for (i = 0, pa = psys->particles; i < totpart; i++, pa++) {
4543 psys->part->from,
4544 pa->num,
4545 pa->num_dmcache,
4546 pa->fuv,
4547 pa->foffset,
4548 cur_co,
4549 nullptr,
4550 nullptr,
4551 nullptr,
4552 nullptr);
4553 BLI_kdtree_3d_insert(tree, i, cur_co);
4554 }
4555
4556 BLI_kdtree_3d_balance(tree);
4557 }
4558
4559 edit->totpoint = psys->totpart = newtotpart;
4560
4561 /* create new elements */
4562 pa = psys->particles + totpart;
4563 point = edit->points + totpart;
4564
4565 for (i = totpart; i < newtotpart; i++, pa++, point++) {
4566 memcpy(pa, add_pars + i - totpart, sizeof(ParticleData));
4567 pa->hair = MEM_calloc_arrayN<HairKey>(pset->totaddkey, "BakeKey key add");
4568 key = point->keys = MEM_calloc_arrayN<PTCacheEditKey>(pset->totaddkey, "PTCacheEditKey add");
4569 point->totkey = pa->totkey = pset->totaddkey;
4570
4571 for (k = 0, hkey = pa->hair; k < pa->totkey; k++, hkey++, key++) {
4572 key->co = hkey->co;
4573 key->time = &hkey->time;
4574
4575 if (!(psys->flag & PSYS_GLOBAL_HAIR)) {
4576 key->flag |= PEK_USE_WCO;
4577 }
4578 }
4579
4580 pa->size = 1.0f;
4581 init_particle(&sim, pa);
4582 reset_particle(&sim, pa, 0.0, 1.0);
4583 point->flag |= PEP_EDIT_RECALC;
4584 if (pe_x_mirror(ob)) {
4585 point->flag |= PEP_TAG; /* signal for duplicate */
4586 }
4587
4588 framestep = pa->lifetime / float(pset->totaddkey - 1);
4589
4590 if (tree) {
4591 ParticleData *ppa;
4592 HairKey *thkey;
4593 ParticleKey key3[3];
4594 KDTreeNearest_3d ptn[3];
4595 int w, maxw;
4596 float maxd, totw = 0.0, weight[3];
4597
4599 psys->part->from,
4600 pa->num,
4601 pa->num_dmcache,
4602 pa->fuv,
4603 pa->foffset,
4604 co1,
4605 nullptr,
4606 nullptr,
4607 nullptr,
4608 nullptr);
4609 maxw = BLI_kdtree_3d_find_nearest_n(tree, co1, ptn, 3);
4610
4611 maxd = ptn[maxw - 1].dist;
4612
4613 for (w = 0; w < maxw; w++) {
4614 weight[w] = float(pow(2.0, double(-6.0f * ptn[w].dist / maxd)));
4615 totw += weight[w];
4616 }
4617 for (; w < 3; w++) {
4618 weight[w] = 0.0f;
4619 }
4620
4621 if (totw > 0.0f) {
4622 for (w = 0; w < maxw; w++) {
4623 weight[w] /= totw;
4624 }
4625 }
4626 else {
4627 for (w = 0; w < maxw; w++) {
4628 weight[w] = 1.0f / maxw;
4629 }
4630 }
4631
4632 ppa = psys->particles + ptn[0].index;
4633
4634 for (k = 0; k < pset->totaddkey; k++) {
4635 thkey = pa->hair + k;
4636 thkey->time = pa->time + k * framestep;
4637
4638 key3[0].time = thkey->time / 100.0f;
4639 psys_get_particle_on_path(&sim, ptn[0].index, key3, false);
4640 mul_v3_fl(key3[0].co, weight[0]);
4641
4642 /* TODO: interpolating the weight would be nicer */
4643 thkey->weight = (ppa->hair + std::min(k, ppa->totkey - 1))->weight;
4644
4645 if (maxw > 1) {
4646 key3[1].time = key3[0].time;
4647 psys_get_particle_on_path(&sim, ptn[1].index, &key3[1], false);
4648 mul_v3_fl(key3[1].co, weight[1]);
4649 add_v3_v3(key3[0].co, key3[1].co);
4650
4651 if (maxw > 2) {
4652 key3[2].time = key3[0].time;
4653 psys_get_particle_on_path(&sim, ptn[2].index, &key3[2], false);
4654 mul_v3_fl(key3[2].co, weight[2]);
4655 add_v3_v3(key3[0].co, key3[2].co);
4656 }
4657 }
4658
4659 if (k == 0) {
4660 sub_v3_v3v3(co1, pa->state.co, key3[0].co);
4661 }
4662
4663 add_v3_v3v3(thkey->co, key3[0].co, co1);
4664
4665 thkey->time = key3[0].time;
4666 }
4667 }
4668 else {
4669 for (k = 0, hkey = pa->hair; k < pset->totaddkey; k++, hkey++) {
4670 madd_v3_v3v3fl(hkey->co, pa->state.co, pa->state.vel, k * framestep * timestep);
4671 hkey->time += k * framestep;
4672 hkey->weight = 1.0f - float(k) / float(pset->totaddkey - 1);
4673 }
4674 }
4675 for (k = 0, hkey = pa->hair; k < pset->totaddkey; k++, hkey++) {
4676 psys_mat_hair_to_global(ob, psmd_eval->mesh_final, psys->part->from, pa, hairmat);
4677 invert_m4_m4(imat, hairmat);
4678 mul_m4_v3(imat, hkey->co);
4679 }
4680 }
4681
4682 if (tree) {
4683 BLI_kdtree_3d_free(tree);
4684 }
4685 }
4686
4687 MEM_freeN(add_pars);
4688
4689 BLI_rng_free(rng);
4690
4691 return n;
4692}
4693
4695
4696/* -------------------------------------------------------------------- */
4699
4713
4715{
4717 Scene *scene = CTX_data_scene(C);
4718 ViewLayer *view_layer = CTX_data_view_layer(C);
4720 PTCacheEdit *edit = PE_get_current(depsgraph, scene, ob);
4721 ARegion *region = CTX_wm_region(C);
4722 BrushEdit *bedit;
4724
4725 /* set the 'distance factor' for grabbing (used in comb etc) */
4727 PE_minmax(depsgraph, scene, view_layer, min, max);
4729
4730 bedit = MEM_callocN<BrushEdit>("BrushEdit");
4731 bedit->first = 1;
4732 op->customdata = bedit;
4733
4734 bedit->scene = scene;
4735 bedit->view_layer = view_layer;
4736 bedit->ob = ob;
4737 bedit->edit = edit;
4738
4739 bedit->zfac = ED_view3d_calc_zfac(static_cast<const RegionView3D *>(region->regiondata), min);
4740
4741 /* cache view depths and settings for re-use */
4742 PE_set_view3d_data(C, &bedit->data);
4744
4745 return 1;
4746}
4747
4749{
4750 BrushEdit *bedit = static_cast<BrushEdit *>(op->customdata);
4752 Scene *scene = bedit->scene;
4753 Object *ob = bedit->ob;
4754 PTCacheEdit *edit = bedit->edit;
4755 ParticleEditSettings *pset = PE_settings(scene);
4756 ParticleSystemModifierData *psmd_eval = edit->psmd_eval;
4757 ParticleBrushData *brush = &pset->brush[pset->brushtype];
4758 ARegion *region = CTX_wm_region(C);
4759 float vec[3], mousef[2];
4760 int mval[2];
4761 int flip, mouse[2], removed = 0, added = 0, selected = 0, tot_steps = 1, step = 1;
4762 float dx, dy, dmax;
4763 int lock_root = pset->flag & PE_LOCK_FIRST;
4764
4765 if (!PE_start_edit(edit)) {
4766 return;
4767 }
4768
4769 RNA_float_get_array(itemptr, "mouse", mousef);
4770 mouse[0] = mousef[0];
4771 mouse[1] = mousef[1];
4772 flip = RNA_boolean_get(op->ptr, "pen_flip");
4773
4774 if (bedit->first) {
4775 bedit->lastmouse[0] = mouse[0];
4776 bedit->lastmouse[1] = mouse[1];
4777 }
4778
4779 dx = mouse[0] - bedit->lastmouse[0];
4780 dy = mouse[1] - bedit->lastmouse[1];
4781
4782 mval[0] = mouse[0];
4783 mval[1] = mouse[1];
4784
4785 /* Disable locking temporarily for disconnected hair. */
4786 if (edit->psys && edit->psys->flag & PSYS_GLOBAL_HAIR) {
4787 pset->flag &= ~PE_LOCK_FIRST;
4788 }
4789
4790 if (((pset->brushtype == PE_BRUSH_ADD) ?
4791 (sqrtf(dx * dx + dy * dy) > pset->brush[PE_BRUSH_ADD].step) :
4792 (dx != 0 || dy != 0)) ||
4793 bedit->first)
4794 {
4795 PEData data = bedit->data;
4796 data.context = C; /* TODO(mai): why isn't this set in bedit->data? */
4797
4799 selected = short(count_selected_keys(scene, edit));
4800
4801 dmax = max_ff(fabsf(dx), fabsf(dy));
4802 tot_steps = dmax / (0.2f * pe_brush_size_get(scene, brush)) + 1;
4803
4804 dx /= float(tot_steps);
4805 dy /= float(tot_steps);
4806
4807 for (step = 1; step <= tot_steps; step++) {
4808 mval[0] = bedit->lastmouse[0] + step * dx;
4809 mval[1] = bedit->lastmouse[1] + step * dy;
4810
4811 switch (pset->brushtype) {
4812 case PE_BRUSH_COMB: {
4813 const float xy_delta[2] = {dx, dy};
4814 data.mval = mval;
4815 data.rad = pe_brush_size_get(scene, brush);
4816
4817 data.combfac = (brush->strength - 0.5f) * 2.0f;
4818 if (data.combfac < 0.0f) {
4819 data.combfac = 1.0f - 9.0f * data.combfac;
4820 }
4821 else {
4822 data.combfac = 1.0f - data.combfac;
4823 }
4824
4825 invert_m4_m4(ob->runtime->world_to_object.ptr(), ob->object_to_world().ptr());
4826
4827 ED_view3d_win_to_delta(region, xy_delta, bedit->zfac, vec);
4828 data.dvec = vec;
4829
4831 break;
4832 }
4833 case PE_BRUSH_CUT: {
4834 if (edit->psys && edit->pathcache) {
4835 data.mval = mval;
4836 data.rad = pe_brush_size_get(scene, brush);
4837 data.cutfac = brush->strength;
4838
4839 if (selected) {
4841 }
4842 else {
4844 }
4845
4846 removed = remove_tagged_particles(ob, edit->psys, pe_x_mirror(ob));
4847 if (pset->flag & PE_KEEP_LENGTHS) {
4848 recalc_lengths(edit);
4849 }
4850 }
4851 else {
4852 removed = 0;
4853 }
4854
4855 break;
4856 }
4857 case PE_BRUSH_LENGTH: {
4858 data.mval = mval;
4859
4860 data.rad = pe_brush_size_get(scene, brush);
4861 data.growfac = brush->strength / 50.0f;
4862
4863 if (brush->invert ^ flip) {
4864 data.growfac = 1.0f - data.growfac;
4865 }
4866 else {
4867 data.growfac = 1.0f + data.growfac;
4868 }
4869
4871
4872 if (pset->flag & PE_KEEP_LENGTHS) {
4873 recalc_lengths(edit);
4874 }
4875 break;
4876 }
4877 case PE_BRUSH_PUFF: {
4878 if (edit->psys) {
4879 data.mesh = psmd_eval->mesh_final;
4880 data.mval = mval;
4881 data.rad = pe_brush_size_get(scene, brush);
4882 data.select = selected;
4883
4884 data.pufffac = (brush->strength - 0.5f) * 2.0f;
4885 if (data.pufffac < 0.0f) {
4886 data.pufffac = 1.0f - 9.0f * data.pufffac;
4887 }
4888 else {
4889 data.pufffac = 1.0f - data.pufffac;
4890 }
4891
4892 data.invert = (brush->invert ^ flip);
4893 invert_m4_m4(ob->runtime->world_to_object.ptr(), ob->object_to_world().ptr());
4894
4896 }
4897 break;
4898 }
4899 case PE_BRUSH_ADD: {
4900 if (edit->psys && edit->psys->part->from == PART_FROM_FACE) {
4901 data.mval = mval;
4902
4903 added = brush_add(C, &data, brush->count);
4904
4905 if (pset->flag & PE_KEEP_LENGTHS) {
4906 recalc_lengths(edit);
4907 }
4908 }
4909 else {
4910 added = 0;
4911 }
4912 break;
4913 }
4914 case PE_BRUSH_SMOOTH: {
4915 data.mval = mval;
4916 data.rad = pe_brush_size_get(scene, brush);
4917
4918 data.vec[0] = data.vec[1] = data.vec[2] = 0.0f;
4919 data.tot = 0;
4920
4921 data.smoothfac = brush->strength;
4922
4923 invert_m4_m4(ob->runtime->world_to_object.ptr(), ob->object_to_world().ptr());
4924
4926
4927 if (data.tot) {
4928 mul_v3_fl(data.vec, 1.0f / float(data.tot));
4930 }
4931
4932 break;
4933 }
4934 case PE_BRUSH_WEIGHT: {
4935 if (edit->psys) {
4936 data.mesh = psmd_eval->mesh_final;
4937 data.mval = mval;
4938 data.rad = pe_brush_size_get(scene, brush);
4939
4940 data.weightfac = brush->strength; /* note that this will never be zero */
4941
4943 }
4944
4945 break;
4946 }
4947 }
4948 if ((pset->flag & PE_KEEP_LENGTHS) == 0) {
4949 recalc_lengths(edit);
4950 }
4951
4952 if (ELEM(pset->brushtype, PE_BRUSH_ADD, PE_BRUSH_CUT) && (added || removed)) {
4953 if (pset->brushtype == PE_BRUSH_ADD && pe_x_mirror(ob)) {
4954 PE_mirror_x(depsgraph, scene, ob, 1);
4955 }
4956
4957 update_world_cos(ob, edit);
4958 psys_free_path_cache(nullptr, edit);
4960 }
4961 else {
4962 PE_update_object(depsgraph, scene, ob, 1);
4963 }
4964 }
4965
4966 if (edit->psys) {
4970 }
4971 else {
4974 }
4975
4976 bedit->lastmouse[0] = mouse[0];
4977 bedit->lastmouse[1] = mouse[1];
4978 bedit->first = 0;
4979 }
4980
4981 pset->flag |= lock_root;
4982}
4983
4985{
4986 BrushEdit *bedit = static_cast<BrushEdit *>(op->customdata);
4987
4988 PE_data_free(&bedit->data);
4989 MEM_freeN(bedit);
4990}
4991
4993{
4994 if (!brush_edit_init(C, op)) {
4995 return OPERATOR_CANCELLED;
4996 }
4997
4998 RNA_BEGIN (op->ptr, itemptr, "stroke") {
4999 brush_edit_apply(C, op, &itemptr);
5000 }
5001 RNA_END;
5002
5003 brush_edit_exit(op);
5004
5005 return OPERATOR_FINISHED;
5006}
5007
5008static void brush_edit_apply_event(bContext *C, wmOperator *op, const wmEvent *event)
5009{
5010 PointerRNA itemptr;
5011 float mouse[2];
5012
5013 copy_v2fl_v2i(mouse, event->mval);
5014
5015 /* fill in stroke */
5016 RNA_collection_add(op->ptr, "stroke", &itemptr);
5017
5018 RNA_float_set_array(&itemptr, "mouse", mouse);
5019
5020 /* apply */
5021 brush_edit_apply(C, op, &itemptr);
5022}
5023
5025{
5026 if (!brush_edit_init(C, op)) {
5027 return OPERATOR_CANCELLED;
5028 }
5029
5030 RNA_boolean_set(op->ptr, "pen_flip", event->modifier & KM_SHIFT); /* XXX hardcoded */
5031
5032 brush_edit_apply_event(C, op, event);
5033
5035
5037}
5038
5040{
5041 switch (event->type) {
5042 case LEFTMOUSE:
5043 case MIDDLEMOUSE:
5044 case RIGHTMOUSE: /* XXX hardcoded */
5045 if (event->val == KM_RELEASE) {
5046 brush_edit_exit(op);
5047 return OPERATOR_FINISHED;
5048 }
5049 break;
5050 case MOUSEMOVE:
5051 brush_edit_apply_event(C, op, event);
5052 break;
5053 default: {
5054 break;
5055 }
5056 }
5057
5059}
5060
5061static void brush_edit_cancel(bContext * /*C*/, wmOperator *op)
5062{
5063 brush_edit_exit(op);
5064}
5065
5070
5072{
5073 /* identifiers */
5074 ot->name = "Brush Edit";
5075 ot->idname = "PARTICLE_OT_brush_edit";
5076 ot->description = "Apply a stroke of brush to the particles";
5077
5078 /* API callbacks. */
5079 ot->exec = brush_edit_exec;
5080 ot->invoke = brush_edit_invoke;
5081 ot->modal = brush_edit_modal;
5082 ot->cancel = brush_edit_cancel;
5083 ot->poll = brush_edit_poll;
5084
5085 /* flags */
5087
5088 /* properties */
5089 PropertyRNA *prop;
5090 prop = RNA_def_collection_runtime(ot->srna, "stroke", &RNA_OperatorStrokeElement, "Stroke", "");
5092 prop = RNA_def_boolean(
5093 ot->srna, "pen_flip", false, "Pen Flip", "Whether a tablet's eraser mode is being used");
5095}
5096
5098
5099/* -------------------------------------------------------------------- */
5102
5104{
5105 if (PE_hair_poll(C)) {
5106 Scene *scene = CTX_data_scene(C);
5107 ParticleEditSettings *pset = PE_settings(scene);
5108
5109 if (pset->shape_object && (pset->shape_object->type == OB_MESH)) {
5110 return true;
5111 }
5112 }
5113
5114 return false;
5115}
5116
5121
5122static void point_inside_bvh_cb(void *userdata,
5123 int index,
5124 const BVHTreeRay *ray,
5125 BVHTreeRayHit *hit)
5126{
5127 PointInsideBVH *data = static_cast<PointInsideBVH *>(userdata);
5128
5129 data->bvhdata->raycast_callback(data->bvhdata, index, ray, hit);
5130
5131 if (hit->index != -1) {
5132 ++data->num_hits;
5133 }
5134}
5135
5136/* true if the point is inside the shape mesh */
5138{
5139 blender::bke::BVHTreeFromMesh *shape_bvh = data->shape_bvh;
5140 const float dir[3] = {1.0f, 0.0f, 0.0f};
5141 PointInsideBVH userdata;
5142
5143 userdata.bvhdata = data->shape_bvh;
5144 userdata.num_hits = 0;
5145
5146 float co_shape[3];
5147 mul_v3_m4v3(co_shape, pset->shape_object->world_to_object().ptr(), key->co);
5148
5150 shape_bvh->tree, co_shape, dir, 0.0f, BVH_RAYCAST_DIST_MAX, point_inside_bvh_cb, &userdata);
5151
5152 /* for any point inside a watertight mesh the number of hits is uneven */
5153 return (userdata.num_hits % 2) == 1;
5154}
5155
5156static void shape_cut(PEData *data, int pa_index)
5157{
5158 PTCacheEdit *edit = data->edit;
5159 Object *ob = data->ob;
5160 ParticleEditSettings *pset = PE_settings(data->scene);
5161 ParticleCacheKey *key;
5162
5163 bool cut;
5164 float cut_time = 1.0;
5165 int k, totkeys = 1 << pset->draw_step;
5166
5167 /* don't cut hidden */
5168 if (edit->points[pa_index].flag & PEP_HIDE) {
5169 return;
5170 }
5171
5172 cut = false;
5173
5174 /* check if root is inside the cut shape */
5175 key = edit->pathcache[pa_index];
5176 if (!shape_cut_test_point(data, pset, key)) {
5177 cut_time = -1.0f;
5178 cut = true;
5179 }
5180 else {
5181 for (k = 0; k < totkeys; k++, key++) {
5182 BVHTreeRayHit hit;
5183
5184 float co_curr_shape[3], co_next_shape[3];
5185 float dir_shape[3];
5186 float len_shape;
5187
5188 mul_v3_m4v3(co_curr_shape, pset->shape_object->world_to_object().ptr(), key->co);
5189 mul_v3_m4v3(co_next_shape, pset->shape_object->world_to_object().ptr(), (key + 1)->co);
5190
5191 sub_v3_v3v3(dir_shape, co_next_shape, co_curr_shape);
5192 len_shape = normalize_v3(dir_shape);
5193
5194 memset(&hit, 0, sizeof(hit));
5195 hit.index = -1;
5196 hit.dist = len_shape;
5197 BLI_bvhtree_ray_cast(data->shape_bvh->tree,
5198 co_curr_shape,
5199 dir_shape,
5200 0.0f,
5201 &hit,
5202 data->shape_bvh->raycast_callback,
5203 data->shape_bvh);
5204 if (hit.index >= 0) {
5205 if (hit.dist < len_shape) {
5206 cut_time = ((hit.dist / len_shape) + float(k)) / float(totkeys);
5207 cut = true;
5208 break;
5209 }
5210 }
5211 }
5212 }
5213
5214 if (cut) {
5215 if (cut_time < 0.0f) {
5216 edit->points[pa_index].flag |= PEP_TAG;
5217 }
5218 else {
5219 rekey_particle_to_time(data->context, data->scene, ob, pa_index, cut_time);
5220 edit->points[pa_index].flag |= PEP_EDIT_RECALC;
5221 }
5222 }
5223}
5224
5226{
5228 Scene *scene = CTX_data_scene(C);
5230 ParticleEditSettings *pset = PE_settings(scene);
5231 PTCacheEdit *edit = PE_get_current(depsgraph, scene, ob);
5232 Object *shapeob = pset->shape_object;
5233 int selected = count_selected_keys(scene, edit);
5234 int lock_root = pset->flag & PE_LOCK_FIRST;
5235
5236 if (!PE_start_edit(edit)) {
5237 return OPERATOR_CANCELLED;
5238 }
5239
5240 /* Disable locking temporarily for disconnected hair. */
5241 if (edit->psys && edit->psys->flag & PSYS_GLOBAL_HAIR) {
5242 pset->flag &= ~PE_LOCK_FIRST;
5243 }
5244
5245 if (edit->psys && edit->pathcache) {
5246 PEData data;
5247 int removed;
5248
5249 PE_set_data(C, &data);
5250 if (!PE_create_shape_tree(&data, shapeob)) {
5251 /* shapeob may not have faces... */
5252 return OPERATOR_CANCELLED;
5253 }
5254
5255 if (selected) {
5257 }
5258 else {
5260 }
5261
5262 removed = remove_tagged_particles(ob, edit->psys, pe_x_mirror(ob));
5263 recalc_lengths(edit);
5264
5265 if (removed) {
5266 update_world_cos(ob, edit);
5267 psys_free_path_cache(nullptr, edit);
5269 }
5270 else {
5271 PE_update_object(data.depsgraph, scene, ob, 1);
5272 }
5273
5274 if (edit->psys) {
5278 }
5279 else {
5282 }
5283
5285 }
5286
5287 pset->flag |= lock_root;
5288
5289 return OPERATOR_FINISHED;
5290}
5291
5293{
5294 /* identifiers */
5295 ot->name = "Shape Cut";
5296 ot->idname = "PARTICLE_OT_shape_cut";
5297 ot->description = "Cut hair to conform to the set shape object";
5298
5299 /* API callbacks. */
5300 ot->exec = shape_cut_exec;
5301 ot->poll = shape_cut_poll;
5302
5303 /* flags */
5304 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
5305}
5306
5308
5309/* -------------------------------------------------------------------- */
5312
5314 Depsgraph *depsgraph, Scene *scene, Object *ob, PointCache *cache, ParticleSystem *psys)
5315{
5316 Object *ob_eval = DEG_get_evaluated(depsgraph, ob);
5317 PTCacheEdit *edit;
5318 ParticleSystemModifierData *psmd = (psys) ? psys_get_modifier(ob, psys) : nullptr;
5319 ParticleSystemModifierData *psmd_eval = nullptr;
5320 POINT_P;
5321 KEY_K;
5322 ParticleData *pa = nullptr;
5323 HairKey *hkey;
5324 int totpoint;
5325
5326 if (psmd != nullptr) {
5328 psmd->modifier.name);
5329 }
5330
5331 /* no psmd->dm happens in case particle system modifier is not enabled */
5332 if (!(psys && psmd && psmd_eval->mesh_final) && !cache) {
5333 return;
5334 }
5335
5336 if (cache && cache->flag & PTCACHE_DISK_CACHE) {
5337 return;
5338 }
5339
5340 if (psys == nullptr && (cache && BLI_listbase_is_empty(&cache->mem_cache))) {
5341 return;
5342 }
5343
5344 edit = (psys) ? psys->edit : cache->edit;
5345
5346 if (!edit) {
5347 ParticleSystem *psys_eval = nullptr;
5348 if (psys) {
5349 psys_eval = psys_eval_get(depsgraph, ob, psys);
5350 psys_copy_particles(psys, psys_eval);
5351 }
5352
5353 totpoint = psys ? psys->totpart : int(((PTCacheMem *)cache->mem_cache.first)->totpoint);
5354
5355 edit = MEM_callocN<PTCacheEdit>("PE_create_particle_edit");
5356 edit->points = MEM_calloc_arrayN<PTCacheEditPoint>(totpoint, "PTCacheEditPoints");
5357 edit->totpoint = totpoint;
5358
5359 if (psys && !cache) {
5360 edit->psmd = psmd;
5361 edit->psmd_eval = psmd_eval;
5362 psys->edit = edit;
5363 edit->psys = psys;
5364 edit->psys_eval = psys_eval;
5365
5367
5368 edit->pathcache = nullptr;
5370
5371 pa = psys->particles;
5372 LOOP_POINTS {
5373 point->totkey = pa->totkey;
5374 point->keys = MEM_calloc_arrayN<PTCacheEditKey>(point->totkey, "ParticleEditKeys");
5375 point->flag |= PEP_EDIT_RECALC;
5376
5377 hkey = pa->hair;
5378 LOOP_KEYS {
5379 key->co = hkey->co;
5380 key->time = &hkey->time;
5381 key->flag = hkey->editflag;
5382 if (!(psys->flag & PSYS_GLOBAL_HAIR)) {
5383 key->flag |= PEK_USE_WCO;
5384 hkey->editflag |= PEK_USE_WCO;
5385 }
5386
5387 hkey++;
5388 }
5389 pa++;
5390 }
5391 update_world_cos(ob, edit);
5392 }
5393 else {
5394 int totframe = 0;
5395
5396 cache->edit = edit;
5398 edit->psys = nullptr;
5399
5400 LISTBASE_FOREACH (PTCacheMem *, pm, &cache->mem_cache) {
5401 totframe++;
5402 }
5403
5404 LISTBASE_FOREACH (PTCacheMem *, pm, &cache->mem_cache) {
5405 LOOP_POINTS {
5406 void *cur[BPHYS_TOT_DATA];
5407 if (BKE_ptcache_mem_pointers_seek(p, pm, cur) == 0) {
5408 continue;
5409 }
5410
5411 if (!point->totkey) {
5412 key = point->keys = MEM_calloc_arrayN<PTCacheEditKey>(totframe, "ParticleEditKeys");
5413 point->flag |= PEP_EDIT_RECALC;
5414 }
5415 else {
5416 key = point->keys + point->totkey;
5417 }
5418
5419 key->co = static_cast<float *>(cur[BPHYS_DATA_LOCATION]);
5420 key->vel = static_cast<float *>(cur[BPHYS_DATA_VELOCITY]);
5421 key->rot = static_cast<float *>(cur[BPHYS_DATA_ROTATION]);
5422 key->ftime = float(pm->frame);
5423 key->time = &key->ftime;
5425
5426 point->totkey++;
5427 }
5428 }
5429 psys = nullptr;
5430 }
5431
5432 recalc_lengths(edit);
5433 if (psys && !cache) {
5435 }
5436
5437 PE_update_object(depsgraph, scene, ob, 1);
5438 }
5439}
5440
5442{
5444
5445 if (ob == nullptr || ob->type != OB_MESH) {
5446 return false;
5447 }
5448 if (!ob->data || !ID_IS_EDITABLE(ob->data) || ID_IS_OVERRIDE_LIBRARY(ob->data)) {
5449 return false;
5450 }
5451
5453}
5454
5455static void free_all_psys_edit(Object *object)
5456{
5457 for (ParticleSystem *psys = static_cast<ParticleSystem *>(object->particlesystem.first);
5458 psys != nullptr;
5459 psys = psys->next)
5460 {
5461 if (psys->edit != nullptr) {
5462 BLI_assert(psys->free_edit != nullptr);
5463 psys->free_edit(psys->edit);
5464 psys->free_edit = nullptr;
5465 psys->edit = nullptr;
5466 }
5467 }
5468}
5469
5475
5477{
5478 /* Needed so #ParticleSystemModifierData.mesh_final is set. */
5480
5481 PTCacheEdit *edit;
5482
5484
5485 edit = PE_create_current(depsgraph, scene, ob);
5486
5487 /* Mesh may have changed since last entering editmode.
5488 * NOTE: this may have run before if the edit data was just created,
5489 * so could avoid this and speed up a little. */
5490 if (edit && edit->psys) {
5491 /* Make sure pointer to the evaluated modifier data is up to date,
5492 * with possible changes applied when object was outside of the
5493 * edit mode. */
5494 Object *object_eval = DEG_get_evaluated(depsgraph, ob);
5496 object_eval, edit->psmd->modifier.name);
5498 }
5499
5500 toggle_particle_cursor(scene, true);
5503}
5504
5512
5522
5529
5531{
5532 wmMsgBus *mbus = CTX_wm_message_bus(C);
5533 Scene *scene = CTX_data_scene(C);
5535 const int mode_flag = OB_MODE_PARTICLE_EDIT;
5536 const bool is_mode_set = (ob->mode & mode_flag) != 0;
5537
5538 if (!is_mode_set) {
5539 if (!blender::ed::object::mode_compat_set(C, ob, eObjectMode(mode_flag), op->reports)) {
5540 return OPERATOR_CANCELLED;
5541 }
5542 }
5543
5544 if (!is_mode_set) {
5547 }
5548 else {
5550 }
5551
5552 WM_msg_publish_rna_prop(mbus, &ob->id, ob, Object, mode);
5553
5555
5556 return OPERATOR_FINISHED;
5557}
5558
5560{
5561 /* identifiers */
5562 ot->name = "Particle Edit Toggle";
5563 ot->idname = "PARTICLE_OT_particle_edit_toggle";
5564 ot->description = "Toggle particle edit mode";
5565
5566 /* API callbacks. */
5569
5570 /* flags */
5571 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
5572}
5573
5575
5576/* -------------------------------------------------------------------- */
5579
5581{
5583 ParticleSystem *psys = psys_get_current(ob);
5584
5585 if (psys->edit) {
5586 if (/*psys->edit->edited ||*/ true) {
5588
5589 psys->edit = nullptr;
5590 psys->free_edit = nullptr;
5591
5593 psys->flag &= ~PSYS_GLOBAL_HAIR;
5594 psys->flag &= ~PSYS_EDITED;
5595
5600 }
5601 }
5602 else { /* some operation might have protected hair from editing so let's clear the flag */
5604 psys->flag &= ~PSYS_GLOBAL_HAIR;
5605 psys->flag &= ~PSYS_EDITED;
5608 }
5609
5610 return OPERATOR_FINISHED;
5611}
5612
5614{
5615 /* identifiers */
5616 ot->name = "Clear Edited";
5617 ot->idname = "PARTICLE_OT_edited_clear";
5618 ot->description = "Undo all edition performed on the particle system";
5619
5620 /* API callbacks. */
5621 ot->exec = clear_edited_exec;
5623
5624 /* flags */
5625 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
5626}
5627
5629
5630/* -------------------------------------------------------------------- */
5633
5635{
5636 float length = 0.0f;
5637 KEY_K;
5638 LOOP_KEYS {
5639 if (k > 0) {
5640 length += len_v3v3((key - 1)->co, key->co);
5641 }
5642 }
5643 return length;
5644}
5645
5647{
5648 int num_selected = 0;
5649 float total_length = 0;
5650 POINT_P;
5652 total_length += calculate_point_length(point);
5653 num_selected++;
5654 }
5655 if (num_selected == 0) {
5656 return 0.0f;
5657 }
5658 return total_length / num_selected;
5659}
5660
5661static void scale_point_factor(PTCacheEditPoint *point, float factor)
5662{
5663 float orig_prev_co[3], prev_co[3];
5664 KEY_K;
5665 LOOP_KEYS {
5666 if (k == 0) {
5667 copy_v3_v3(orig_prev_co, key->co);
5668 copy_v3_v3(prev_co, key->co);
5669 }
5670 else {
5671 float new_co[3];
5672 float delta[3];
5673
5674 sub_v3_v3v3(delta, key->co, orig_prev_co);
5675 mul_v3_fl(delta, factor);
5676 add_v3_v3v3(new_co, prev_co, delta);
5677
5678 copy_v3_v3(orig_prev_co, key->co);
5679 copy_v3_v3(key->co, new_co);
5680 copy_v3_v3(prev_co, key->co);
5681 }
5682 }
5683 point->flag |= PEP_EDIT_RECALC;
5684}
5685
5687{
5688 const float point_length = calculate_point_length(point);
5689 if (point_length != 0.0f) {
5690 const float factor = length / point_length;
5691 scale_point_factor(point, factor);
5692 }
5693}
5694
5696{
5697 POINT_P;
5700 }
5701 recalc_lengths(edit);
5702}
5703
5705{
5707 Scene *scene = CTX_data_scene(C);
5709
5710 PTCacheEdit *edit = PE_get_current(depsgraph, scene, ob);
5711 float average_length = calculate_average_length(edit);
5712
5713 if (average_length == 0.0f) {
5714 return OPERATOR_CANCELLED;
5715 }
5716 scale_points_to_length(edit, average_length);
5717
5718 PE_update_object(depsgraph, scene, ob, 1);
5719 if (edit->psys) {
5721 }
5722 else {
5725 }
5726
5727 return OPERATOR_FINISHED;
5728}
5729
5731{
5732 /* identifiers */
5733 ot->name = "Unify Length";
5734 ot->idname = "PARTICLE_OT_unify_length";
5735 ot->description = "Make selected hair the same length";
5736
5737 /* API callbacks. */
5738 ot->exec = unify_length_exec;
5739 ot->poll = PE_poll_view3d;
5740
5741 /* flags */
5742 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
5743}
5744
float BKE_brush_weight_get(const Paint *paint, const Brush *brush)
Definition brush.cc:1364
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:4592
struct ParticleSystemModifierData * psys_get_modifier(struct Object *ob, struct ParticleSystem *psys)
Definition particle.cc:2155
#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:3870
void psys_disable_all(struct Object *ob)
Definition particle.cc:643
void psys_mat_hair_to_global(struct Object *ob, struct Mesh *mesh, short from, struct ParticleData *pa, float hairmat[4][4])
Definition particle.cc:3900
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:3655
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:3844
float psys_get_timestep(struct ParticleSimulationData *sim)
Definition particle.cc:4467
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:2021
void psys_enable_all(struct Object *ob)
Definition particle.cc:651
#define LOOP_PARTICLES
void init_particle(struct ParticleSimulationData *sim, struct ParticleData *pa)
struct ParticleSystem * psys_get_current(struct Object *ob)
Definition particle.cc:538
struct ParticleSystem * psys_eval_get(struct Depsgraph *depsgraph, struct Object *object, struct ParticleSystem *psys)
Definition particle.cc:668
void psys_free_path_cache(struct ParticleSystem *psys, struct PTCacheEdit *edit)
Definition particle.cc:907
#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:1834
void BKE_particle_batch_cache_dirty_tag(struct ParticleSystem *psys, int mode)
Definition particle.cc:5288
#define PSYS_RESET_DEPSGRAPH
@ BKE_PARTICLE_BATCH_DIRTY_ALL
void psys_copy_particles(struct ParticleSystem *psys_dst, struct ParticleSystem *psys_src)
Definition particle.cc:1056
#define PARTICLE_P
void BKE_ptcache_mem_pointers_incr(void *cur[BPHYS_TOT_DATA])
void BKE_ptcache_ids_from_object(struct ListBase *lb, struct Object *ob, struct Scene *scene, int duplis)
#define PTCACHE_VEL_PER_SEC
#define PEK_HIDE
#define PEP_EDIT_RECALC
#define PTCACHE_TYPE_CLOTH
#define PEP_HIDE
int BKE_ptcache_mem_pointers_seek(int point_index, struct PTCacheMem *pm, void *cur[BPHYS_TOT_DATA])
#define PTCACHE_TYPE_PARTICLES
#define PTCACHE_TYPE_SOFTBODY
#define PEP_TAG
@ PT_CACHE_EDIT_UPDATE_PARTICLE_FROM_EVAL
#define PEK_SELECT
#define PEK_USE_WCO
#define PEK_TAG
void BKE_reportf(ReportList *reports, eReportType type, const char *format,...) ATTR_PRINTF_FORMAT(3
@ RPT_INFO
Definition BKE_report.hh:35
void BKE_scene_graph_evaluated_ensure(Depsgraph *depsgraph, Main *bmain)
Definition scene.cc:2626
#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:123
#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:1101
@ ID_RECALC_PSYS_REDO
Definition DNA_ID.h:1081
@ ID_RECALC_SYNC_TO_EVAL
Definition DNA_ID.h:1118
@ ID_RECALC_PSYS_RESET
Definition DNA_ID.h:1083
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:1074
#define ID_IS_EDITABLE(_id)
Definition DNA_ID.h:705
#define ID_IS_OVERRIDE_LIBRARY(_id)
Definition DNA_ID.h:730
@ ME_SYMMETRY_X
@ eModifierType_Cloth
@ eModifierType_Softbody
eObjectMode
@ OB_MODE_PARTICLE_EDIT
@ OB_MESH
@ PSYS_CURRENT
@ PSYS_HAIR_DYNAMICS
@ PSYS_EDITED
@ PSYS_HAIR_DONE
@ PSYS_GLOBAL_HAIR
@ PSYS_HAIR_UPDATED
@ PART_EMITTER
@ PART_HAIR
@ PARS_REKEY
@ PART_FROM_FACE
@ BPHYS_DATA_VELOCITY
@ BPHYS_DATA_LOCATION
@ BPHYS_DATA_ROTATION
#define BPHYS_TOT_DATA
@ PTCACHE_BAKED
@ PTCACHE_DISK_CACHE
@ UNIFIED_PAINT_SIZE
@ PE_LOCK_FIRST
@ PE_FADE_TIME
@ PE_INTERPOLATE_ADDED
@ PE_DEFLECT_EMITTER
@ PE_KEEP_LENGTHS
@ PE_AUTO_VELOCITY
@ PE_BRUSH_DATA_PUFF_VOLUME
@ PE_TYPE_CLOTH
@ PE_TYPE_PARTICLES
@ PE_TYPE_SOFTBODY
@ SCE_SELECT_PATH
@ SCE_SELECT_POINT
@ SCE_SELECT_END
@ PE_BRUSH_COMB
@ PE_BRUSH_CUT
@ PE_BRUSH_PUFF
@ PE_BRUSH_LENGTH
@ PE_BRUSH_WEIGHT
@ PE_BRUSH_ADD
@ PE_BRUSH_SMOOTH
@ 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)
Definition meshtools.cc:500
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)
@ SEL_SELECT
@ SEL_INVERT
@ SEL_DESELECT
@ SEL_TOGGLE
#define SEL_OP_USE_PRE_DESELECT(sel_op)
float ED_view3d_select_dist_px()
#define XRAY_ENABLED(v3d)
void ED_view3d_win_to_delta(const ARegion *region, const float xy_delta[2], float zfac, float r_out[3], bool precise=false)
@ 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)
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(GPUBuiltinShader 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
void GPU_line_smooth(bool enable)
Definition gpu_state.cc:78
@ GPU_BLEND_NONE
Definition GPU_state.hh:85
@ GPU_BLEND_ALPHA
Definition GPU_state.hh:87
void GPU_blend(GPUBlend blend)
Definition gpu_state.cc:42
uint GPU_vertformat_attr_add(GPUVertFormat *format, blender::StringRef name, blender::gpu::VertAttrType type)
Read Guarded memory(de)allocation.
#define MEM_SAFE_FREE(v)
#define RNA_BEGIN(sptr, itemptr, propname)
#define RNA_END
@ PROP_SKIP_SAVE
Definition RNA_types.hh:344
@ PROP_HIDDEN
Definition RNA_types.hh:338
#define C
Definition RandGen.cpp:29
@ KM_SHIFT
Definition WM_types.hh:278
@ KM_RELEASE
Definition WM_types.hh:312
#define NS_MODE_PARTICLE
Definition WM_types.hh:570
#define ND_MODE
Definition WM_types.hh:445
#define NC_SCENE
Definition WM_types.hh:378
@ OPTYPE_BLOCKING
Definition WM_types.hh:184
@ OPTYPE_UNDO
Definition WM_types.hh:182
@ OPTYPE_REGISTER
Definition WM_types.hh:180
#define ND_MODIFIER
Definition WM_types.hh:462
#define NA_EDITED
Definition WM_types.hh:584
#define ND_PARTICLE
Definition WM_types.hh:465
#define NS_MODE_OBJECT
Definition WM_types.hh:560
#define NC_OBJECT
Definition WM_types.hh:379
#define NA_SELECTED
Definition WM_types.hh:589
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
nullptr float
dot(value.rgb, luminance_coefficients)") DEFINE_VALUE("REDUCE(lhs
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:1717
static const EnumPropertyItem delete_type_items[]
Definition editfont.cc:1706
KDTree_3d * tree
static bool is_inside(int x, int y, int cols, int rows)
Definition filesel.cc:764
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 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)
void(*)(PEData *data, float mat[4][4], float imat[4][4], int point_index, int key_index, PTCacheEditKey *key, float mouse_distance) ForHitKeyMatFunc
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)
@ RAN_HAIR
@ RAN_POINTS
void PARTICLE_OT_mirror(wmOperatorType *ot)
static bool point_is_selected(PTCacheEditPoint *point)
static void pe_iterate_lengths(Scene *scene, PTCacheEdit *edit)
@ DEL_KEY
@ DEL_PARTICLE
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)
void(*)(PEData *data, const float mat[4][4], const float imat[4][4], int point_index, int key_index, PTCacheEditKey *key) ForKeyMatFunc
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)
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)
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)
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
#define fabsf
#define sqrtf
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:774
wmEventType type
Definition WM_types.hh:757
short val
Definition WM_types.hh:759
int mval[2]
Definition WM_types.hh:763
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:178
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:4237
#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:145