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