Blender V4.3
particle_object.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2009 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
9#include <cstdlib>
10#include <cstring>
11
12#include "MEM_guardedalloc.h"
13
14#include "DNA_mesh_types.h"
15#include "DNA_meshdata_types.h"
16#include "DNA_modifier_types.h"
17#include "DNA_scene_types.h"
18
19#include "BLI_listbase.h"
20#include "BLI_math_geom.h"
21#include "BLI_math_matrix.h"
22#include "BLI_math_vector.h"
23#include "BLI_string.h"
24#include "BLI_utildefines.h"
25
26#include "BKE_bvhutils.hh"
27#include "BKE_context.hh"
28#include "BKE_customdata.hh"
29#include "BKE_global.hh"
30#include "BKE_layer.hh"
31#include "BKE_lib_id.hh"
32#include "BKE_mesh.hh"
34#include "BKE_modifier.hh"
35#include "BKE_object.hh"
36#include "BKE_particle.h"
37#include "BKE_pointcache.h"
38#include "BKE_report.hh"
39
40#include "DEG_depsgraph.hh"
43
44#include "RNA_access.hh"
45#include "RNA_define.hh"
46#include "RNA_prototypes.hh"
47
48#include "WM_api.hh"
49#include "WM_types.hh"
50
51#include "ED_object.hh"
52#include "ED_particle.hh"
53#include "ED_screen.hh"
54
56
57#include "physics_intern.hh"
58
59static float I[4][4] = {
60 {1.0f, 0.0f, 0.0f, 0.0f},
61 {0.0f, 1.0f, 0.0f, 0.0f},
62 {0.0f, 0.0f, 1.0f, 0.0f},
63 {0.0f, 0.0f, 0.0f, 1.0f},
64};
65
66/********************** particle system slot operators *********************/
67
69{
70 Main *bmain = CTX_data_main(C);
72 Scene *scene = CTX_data_scene(C);
73
74 if (!scene || !ob) {
75 return OPERATOR_CANCELLED;
76 }
77
78 object_add_particle_system(bmain, scene, ob, nullptr);
79
82
83 return OPERATOR_FINISHED;
84}
85
87{
88 /* identifiers */
89 ot->name = "Add Particle System Slot";
90 ot->idname = "OBJECT_OT_particle_system_add";
91 ot->description = "Add a particle system";
92
93 /* api callbacks */
96
97 /* flags */
99}
100
102{
103 Main *bmain = CTX_data_main(C);
105 Scene *scene = CTX_data_scene(C);
106 ViewLayer *view_layer = CTX_data_view_layer(C);
107 int mode_orig;
108
109 if (!scene || !ob) {
110 return OPERATOR_CANCELLED;
111 }
112
113 mode_orig = ob->mode;
115 object_remove_particle_system(bmain, scene, ob, psys);
116
117 /* possible this isn't the active object
118 * object_remove_particle_system() clears the mode on the last psys
119 */
120 if (mode_orig & OB_MODE_PARTICLE_EDIT) {
121 if ((ob->mode & OB_MODE_PARTICLE_EDIT) == 0) {
122 BKE_view_layer_synced_ensure(scene, view_layer);
123 if (BKE_view_layer_active_object_get(view_layer) == ob) {
125 }
126 }
127 }
128
131
132 return OPERATOR_FINISHED;
133}
134
136{
137 /* identifiers */
138 ot->name = "Remove Particle System Slot";
139 ot->idname = "OBJECT_OT_particle_system_remove";
140 ot->description = "Remove the selected particle system";
141
142 /* api callbacks */
145
146 /* flags */
148}
149
150/********************** new particle settings operator *********************/
151
152static bool psys_poll(bContext *C)
153{
154 PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem);
155 return (ptr.data != nullptr);
156}
157
159{
160 Main *bmain = CTX_data_main(C);
161 ParticleSystem *psys;
162 ParticleSettings *part = nullptr;
163 Object *ob;
165
166 ptr = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem);
167
168 psys = static_cast<ParticleSystem *>(ptr.data);
169
170 /* add or copy particle setting */
171 if (psys->part) {
172 part = reinterpret_cast<ParticleSettings *>(BKE_id_copy_ex(
173 bmain, &psys->part->id, nullptr, LIB_ID_COPY_DEFAULT | LIB_ID_COPY_ACTIONS));
174 }
175 else {
176 part = BKE_particlesettings_add(bmain, "ParticleSettings");
177 }
178
179 ob = (Object *)ptr.owner_id;
180
181 if (psys->part) {
182 id_us_min(&psys->part->id);
183 }
184
185 psys->part = part;
186
188
191
193
194 return OPERATOR_FINISHED;
195}
196
198{
199 /* identifiers */
200 ot->name = "New Particle Settings";
201 ot->idname = "PARTICLE_OT_new";
202 ot->description = "Add new particle settings";
203
204 /* api callbacks */
206 ot->poll = psys_poll;
207
208 /* flags */
210}
211
212/********************** keyed particle target operators *********************/
213
215{
216 Main *bmain = CTX_data_main(C);
217 PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem);
218 ParticleSystem *psys = static_cast<ParticleSystem *>(ptr.data);
219 Object *ob = (Object *)ptr.owner_id;
220
221 ParticleTarget *pt;
222
223 if (!psys) {
224 return OPERATOR_CANCELLED;
225 }
226
227 pt = static_cast<ParticleTarget *>(psys->targets.first);
228 for (; pt; pt = pt->next) {
229 pt->flag &= ~PTARGET_CURRENT;
230 }
231
232 pt = static_cast<ParticleTarget *>(MEM_callocN(sizeof(ParticleTarget), "keyed particle target"));
233
234 pt->flag |= PTARGET_CURRENT;
235 pt->psys = 1;
236
237 BLI_addtail(&psys->targets, pt);
238
241
243
244 return OPERATOR_FINISHED;
245}
246
248{
249 /* identifiers */
250 ot->name = "New Particle Target";
251 ot->idname = "PARTICLE_OT_new_target";
252 ot->description = "Add a new particle target";
253
254 /* api callbacks */
256
257 /* flags */
259}
260
262{
263 Main *bmain = CTX_data_main(C);
264 PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem);
265 ParticleSystem *psys = static_cast<ParticleSystem *>(ptr.data);
266 Object *ob = (Object *)ptr.owner_id;
267
268 ParticleTarget *pt;
269
270 if (!psys) {
271 return OPERATOR_CANCELLED;
272 }
273
274 pt = static_cast<ParticleTarget *>(psys->targets.first);
275 for (; pt; pt = pt->next) {
276 if (pt->flag & PTARGET_CURRENT) {
277 BLI_remlink(&psys->targets, pt);
278 MEM_freeN(pt);
279 break;
280 }
281 }
282 pt = static_cast<ParticleTarget *>(psys->targets.last);
283
284 if (pt) {
285 pt->flag |= PTARGET_CURRENT;
286 }
287
290
292
293 return OPERATOR_FINISHED;
294}
295
297{
298 /* identifiers */
299 ot->name = "Remove Particle Target";
300 ot->idname = "PARTICLE_OT_target_remove";
301 ot->description = "Remove the selected particle target";
302
303 /* api callbacks */
305
306 /* flags */
308}
309
310/************************ move up particle target operator *********************/
311
313{
314 PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem);
315 ParticleSystem *psys = static_cast<ParticleSystem *>(ptr.data);
316 Object *ob = (Object *)ptr.owner_id;
317 ParticleTarget *pt;
318
319 if (!psys) {
320 return OPERATOR_CANCELLED;
321 }
322
323 pt = static_cast<ParticleTarget *>(psys->targets.first);
324 for (; pt; pt = pt->next) {
325 if (pt->flag & PTARGET_CURRENT && pt->prev) {
326 BLI_remlink(&psys->targets, pt);
327 BLI_insertlinkbefore(&psys->targets, pt->prev, pt);
328
331 break;
332 }
333 }
334
335 return OPERATOR_FINISHED;
336}
337
339{
340 ot->name = "Move Up Target";
341 ot->idname = "PARTICLE_OT_target_move_up";
342 ot->description = "Move particle target up in the list";
343
345
346 /* flags */
348}
349
350/************************ move down particle target operator *********************/
351
353{
354 PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem);
355 ParticleSystem *psys = static_cast<ParticleSystem *>(ptr.data);
356 Object *ob = (Object *)ptr.owner_id;
357 ParticleTarget *pt;
358
359 if (!psys) {
360 return OPERATOR_CANCELLED;
361 }
362 pt = static_cast<ParticleTarget *>(psys->targets.first);
363 for (; pt; pt = pt->next) {
364 if (pt->flag & PTARGET_CURRENT && pt->next) {
365 BLI_remlink(&psys->targets, pt);
366 BLI_insertlinkafter(&psys->targets, pt->next, pt);
367
370 break;
371 }
372 }
373
374 return OPERATOR_FINISHED;
375}
376
378{
379 ot->name = "Move Down Target";
380 ot->idname = "PARTICLE_OT_target_move_down";
381 ot->description = "Move particle target down in the list";
382
384
385 /* flags */
387}
388
389/************************ refresh dupli objects *********************/
390
392{
393 PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem);
394 ParticleSystem *psys = static_cast<ParticleSystem *>(ptr.data);
395
396 if (!psys) {
397 return OPERATOR_CANCELLED;
398 }
399
403
404 return OPERATOR_FINISHED;
405}
406
408{
409 ot->name = "Refresh Instance Objects";
410 ot->idname = "PARTICLE_OT_dupliob_refresh";
411 ot->description = "Refresh list of instance objects and their weights";
412
414
415 /* flags */
417}
418
419/************************ move up particle dupli-weight operator *********************/
420
422{
423 PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem);
424 ParticleSystem *psys = static_cast<ParticleSystem *>(ptr.data);
425 ParticleSettings *part;
426
427 if (!psys) {
428 return OPERATOR_CANCELLED;
429 }
430
431 part = psys->part;
432 LISTBASE_FOREACH (ParticleDupliWeight *, dw, &part->instance_weights) {
433 if (dw->flag & PART_DUPLIW_CURRENT && dw->prev) {
434 BLI_remlink(&part->instance_weights, dw);
435 BLI_insertlinkbefore(&part->instance_weights, dw->prev, dw);
436
439 break;
440 }
441 }
442
443 return OPERATOR_FINISHED;
444}
445
447{
448 ot->name = "Move Up Instance Object";
449 ot->idname = "PARTICLE_OT_dupliob_move_up";
450 ot->description = "Move instance object up in the list";
451
453
454 /* flags */
456}
457
458/********************** particle dupliweight operators *********************/
459
461{
462 PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem);
463 ParticleSystem *psys = static_cast<ParticleSystem *>(ptr.data);
464 ParticleSettings *part;
465
466 if (!psys) {
467 return OPERATOR_CANCELLED;
468 }
469 part = psys->part;
470 LISTBASE_FOREACH (ParticleDupliWeight *, dw, &part->instance_weights) {
471 if (dw->flag & PART_DUPLIW_CURRENT) {
472 dw->flag &= ~PART_DUPLIW_CURRENT;
473 dw = static_cast<ParticleDupliWeight *>(MEM_dupallocN(dw));
475 BLI_addhead(&part->instance_weights, dw);
476
479 break;
480 }
481 }
482
483 return OPERATOR_FINISHED;
484}
485
487{
488 /* identifiers */
489 ot->name = "Copy Particle Instance Object";
490 ot->idname = "PARTICLE_OT_dupliob_copy";
491 ot->description = "Duplicate the current instance object";
492
493 /* api callbacks */
495
496 /* flags */
498}
499
501{
502 PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem);
503 ParticleSystem *psys = static_cast<ParticleSystem *>(ptr.data);
504 ParticleSettings *part;
505
506 if (!psys) {
507 return OPERATOR_CANCELLED;
508 }
509
510 part = psys->part;
511 LISTBASE_FOREACH (ParticleDupliWeight *, dw, &part->instance_weights) {
512 if (dw->flag & PART_DUPLIW_CURRENT) {
513 BLI_remlink(&part->instance_weights, dw);
514 MEM_freeN(dw);
515 break;
516 }
517 }
518
519 ParticleDupliWeight *dw = static_cast<ParticleDupliWeight *>(part->instance_weights.last);
520 if (dw) {
522 }
523
526
527 return OPERATOR_FINISHED;
528}
529
531{
532 /* identifiers */
533 ot->name = "Remove Particle Instance Object";
534 ot->idname = "PARTICLE_OT_dupliob_remove";
535 ot->description = "Remove the selected instance object";
536
537 /* api callbacks */
539
540 /* flags */
542}
543
544/************************ move down particle dupliweight operator *********************/
545
547{
548 PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem);
549 ParticleSystem *psys = static_cast<ParticleSystem *>(ptr.data);
550 ParticleSettings *part;
551
552 if (!psys) {
553 return OPERATOR_CANCELLED;
554 }
555
556 part = psys->part;
557 LISTBASE_FOREACH (ParticleDupliWeight *, dw, &part->instance_weights) {
558 if (dw->flag & PART_DUPLIW_CURRENT && dw->next) {
559 BLI_remlink(&part->instance_weights, dw);
560 BLI_insertlinkafter(&part->instance_weights, dw->next, dw);
561
564 break;
565 }
566 }
567
568 return OPERATOR_FINISHED;
569}
570
572{
573 ot->name = "Move Down Instance Object";
574 ot->idname = "PARTICLE_OT_dupliob_move_down";
575 ot->description = "Move instance object down in the list";
576
578
579 /* flags */
581}
582
583/************************ connect/disconnect hair operators *********************/
584
585static void disconnect_hair(Depsgraph *depsgraph, Scene *scene, Object *ob, ParticleSystem *psys)
586{
587 Object *object_eval = DEG_get_evaluated_object(depsgraph, ob);
588 ParticleSystem *psys_eval = psys_eval_get(depsgraph, ob, psys);
589 ParticleSystemModifierData *psmd_eval = psys_get_modifier(object_eval, psys_eval);
590 ParticleEditSettings *pset = PE_settings(scene);
591 ParticleData *pa;
592 PTCacheEdit *edit;
594 PTCacheEditKey *ekey = nullptr;
595 HairKey *key;
596 int i, k;
597 float hairmat[4][4];
598
599 if (!ob || !psys || psys->flag & PSYS_GLOBAL_HAIR) {
600 return;
601 }
602
603 if (!psys->part || psys->part->type != PART_HAIR) {
604 return;
605 }
606
607 edit = psys->edit;
608 point = edit ? edit->points : nullptr;
609
610 for (i = 0, pa = psys->particles; i < psys->totpart; i++, pa++) {
611 if (point) {
612 ekey = point->keys;
613 point++;
614 }
615
616 psys_mat_hair_to_global(ob, psmd_eval->mesh_final, psys->part->from, pa, hairmat);
617
618 for (k = 0, key = pa->hair; k < pa->totkey; k++, key++) {
619 mul_m4_v3(hairmat, key->co);
620
621 if (ekey) {
622 ekey->flag &= ~PEK_USE_WCO;
623 ekey++;
624 }
625 }
626 }
627
628 psys_free_path_cache(psys, psys->edit);
629
630 psys->flag |= PSYS_GLOBAL_HAIR;
631
633 pset->brushtype = PE_BRUSH_COMB;
634 }
635
636 PE_update_object(depsgraph, scene, ob, 0);
637}
638
640{
642 Scene *scene = CTX_data_scene(C);
644 ParticleSystem *psys = nullptr;
645 const bool all = RNA_boolean_get(op->ptr, "all");
646
647 if (!ob) {
648 return OPERATOR_CANCELLED;
649 }
650
651 if (all) {
653 disconnect_hair(depsgraph, scene, ob, psys);
654 }
655 }
656 else {
657 psys = psys_get_current(ob);
658 disconnect_hair(depsgraph, scene, ob, psys);
659 }
660
663
664 return OPERATOR_FINISHED;
665}
666
668{
669 ot->name = "Disconnect Hair";
670 ot->description = "Disconnect hair from the emitter mesh";
671 ot->idname = "PARTICLE_OT_disconnect_hair";
672
674
675 /* flags */
676 /* No REGISTER, redo does not work due to missing update, see #47750. */
678
680 ot->srna, "all", false, "All Hair", "Disconnect all hair systems from the emitter mesh");
681}
682
683/* from/to_world_space : whether from/to particles are in world or hair space
684 * from/to_mat : additional transform for from/to particles (e.g. for using object space copying)
685 */
686static bool remap_hair_emitter(Depsgraph *depsgraph,
687 Scene *scene,
688 Object *ob,
689 ParticleSystem *psys,
690 Object *target_ob,
691 ParticleSystem *target_psys,
692 PTCacheEdit *target_edit,
693 const float from_mat[4][4],
694 const float to_mat[4][4],
695 bool from_global,
696 bool to_global)
697{
698 Object *object_eval = DEG_get_evaluated_object(depsgraph, ob);
699 ParticleSystem *psys_eval = psys_eval_get(depsgraph, ob, psys);
700 ParticleSystemModifierData *target_psmd = psys_get_modifier(object_eval, psys_eval);
701 ParticleData *pa, *tpa;
702 PTCacheEditPoint *edit_point;
703 PTCacheEditKey *ekey;
704 BVHTreeFromMesh bvhtree = {nullptr};
705 const MFace *mface = nullptr, *mf;
706 const blender::int2 *edges = nullptr, *edge;
707 Mesh *mesh, *target_mesh;
708 int numverts;
709 int k;
710 float from_ob_imat[4][4], to_ob_imat[4][4];
711 float from_imat[4][4], to_imat[4][4];
712
713 if (!target_psmd->mesh_final) {
714 return false;
715 }
716 if (!psys->part || psys->part->type != PART_HAIR) {
717 return false;
718 }
719 if (!target_psys->part || target_psys->part->type != PART_HAIR) {
720 return false;
721 }
722
723 edit_point = target_edit ? target_edit->points : nullptr;
724
725 invert_m4_m4(from_ob_imat, ob->object_to_world().ptr());
726 invert_m4_m4(to_ob_imat, target_ob->object_to_world().ptr());
727 invert_m4_m4(from_imat, from_mat);
728 invert_m4_m4(to_imat, to_mat);
729
730 const bool use_dm_final_indices = (target_psys->part->use_modifier_stack &&
731 !target_psmd->mesh_final->runtime->deformed_only);
732
733 if (use_dm_final_indices || !target_psmd->mesh_original) {
734 mesh = target_psmd->mesh_final;
735 }
736 else {
737 mesh = target_psmd->mesh_original;
738 }
739 target_mesh = target_psmd->mesh_final;
740 if (mesh == nullptr) {
741 return false;
742 }
743 /* don't modify the original vertices */
744 /* we don't want to mess up target_psmd->dm when converting to global coordinates below */
745 mesh = (Mesh *)BKE_id_copy_ex(nullptr, &mesh->id, nullptr, LIB_ID_COPY_LOCALIZE);
746
747 /* BMESH_ONLY, deform dm may not have tessface */
749
750 numverts = mesh->verts_num;
751 blender::MutableSpan<blender::float3> positions = mesh->vert_positions_for_write();
752
753 /* convert to global coordinates */
754 for (int i = 0; i < numverts; i++) {
755 mul_m4_v3(to_mat, positions[i]);
756 }
757
758 if (mesh->totface_legacy != 0) {
759 mface = static_cast<const MFace *>(CustomData_get_layer(&mesh->fdata_legacy, CD_MFACE));
761 }
762 else if (mesh->edges_num != 0) {
763 edges = static_cast<const blender::int2 *>(
764 CustomData_get_layer_named(&mesh->edge_data, CD_PROP_INT32_2D, ".edge_verts"));
766 }
767 else {
768 BKE_id_free(nullptr, mesh);
769 return false;
770 }
771
772 int i;
773 for (i = 0, tpa = target_psys->particles, pa = psys->particles; i < target_psys->totpart;
774 i++, tpa++, pa++)
775 {
776 float from_co[3];
777 BVHTreeNearest nearest;
778
779 if (from_global) {
780 mul_v3_m4v3(from_co, from_ob_imat, pa->hair[0].co);
781 }
782 else {
783 mul_v3_m4v3(from_co, from_ob_imat, pa->hair[0].world_co);
784 }
785 mul_m4_v3(from_mat, from_co);
786
787 nearest.index = -1;
788 nearest.dist_sq = FLT_MAX;
789
790 BLI_bvhtree_find_nearest(bvhtree.tree, from_co, &nearest, bvhtree.nearest_callback, &bvhtree);
791
792 if (nearest.index == -1) {
793 if (G.debug & G_DEBUG) {
794 printf("No nearest point found for hair root!");
795 }
796 continue;
797 }
798
799 if (mface) {
800 float v[4][3];
801
802 mf = &mface[nearest.index];
803
804 copy_v3_v3(v[0], positions[mf->v1]);
805 copy_v3_v3(v[1], positions[mf->v2]);
806 copy_v3_v3(v[2], positions[mf->v3]);
807 if (mf->v4) {
808 copy_v3_v3(v[3], positions[mf->v4]);
809 interp_weights_poly_v3(tpa->fuv, v, 4, nearest.co);
810 }
811 else {
812 interp_weights_poly_v3(tpa->fuv, v, 3, nearest.co);
813 }
814 tpa->foffset = 0.0f;
815
816 tpa->num = nearest.index;
817 if (use_dm_final_indices) {
819 }
820 else {
822 target_psmd->mesh_final, target_psmd->mesh_original, tpa->num, tpa->fuv, nullptr);
823 }
824 }
825 else {
826 edge = &edges[nearest.index];
827
828 tpa->fuv[1] = line_point_factor_v3(nearest.co, positions[edge->x], positions[edge->y]);
829 tpa->fuv[0] = 1.0f - tpa->fuv[1];
830 tpa->fuv[2] = tpa->fuv[3] = 0.0f;
831 tpa->foffset = 0.0f;
832
833 tpa->num = nearest.index;
834 tpa->num_dmcache = -1;
835 }
836
837 /* translate hair keys */
838 {
839 HairKey *key, *tkey;
840 float hairmat[4][4], imat[4][4];
841 float offset[3];
842
843 if (to_global) {
844 copy_m4_m4(imat, target_ob->object_to_world().ptr());
845 }
846 else {
847 /* NOTE: using target_dm here, which is in target_ob object space and has full modifiers.
848 */
849 psys_mat_hair_to_object(target_ob, target_mesh, target_psys->part->from, tpa, hairmat);
850 invert_m4_m4(imat, hairmat);
851 }
852 mul_m4_m4m4(imat, imat, to_imat);
853
854 /* offset in world space */
855 sub_v3_v3v3(offset, nearest.co, from_co);
856
857 if (edit_point) {
858 for (k = 0, key = pa->hair, tkey = tpa->hair, ekey = edit_point->keys; k < tpa->totkey;
859 k++, key++, tkey++, ekey++)
860 {
861 float co_orig[3];
862
863 if (from_global) {
864 mul_v3_m4v3(co_orig, from_ob_imat, key->co);
865 }
866 else {
867 mul_v3_m4v3(co_orig, from_ob_imat, key->world_co);
868 }
869 mul_m4_v3(from_mat, co_orig);
870
871 add_v3_v3v3(tkey->co, co_orig, offset);
872
873 mul_m4_v3(imat, tkey->co);
874
875 ekey->flag |= PEK_USE_WCO;
876 }
877
878 edit_point++;
879 }
880 else {
881 for (k = 0, key = pa->hair, tkey = tpa->hair; k < tpa->totkey; k++, key++, tkey++) {
882 float co_orig[3];
883
884 if (from_global) {
885 mul_v3_m4v3(co_orig, from_ob_imat, key->co);
886 }
887 else {
888 mul_v3_m4v3(co_orig, from_ob_imat, key->world_co);
889 }
890 mul_m4_v3(from_mat, co_orig);
891
892 add_v3_v3v3(tkey->co, co_orig, offset);
893
894 mul_m4_v3(imat, tkey->co);
895 }
896 }
897 }
898 }
899
900 free_bvhtree_from_mesh(&bvhtree);
901 BKE_id_free(nullptr, mesh);
902
903 psys_free_path_cache(target_psys, target_edit);
904
905 PE_update_object(depsgraph, scene, target_ob, 0);
906
907 return true;
908}
909
910static bool connect_hair(Depsgraph *depsgraph, Scene *scene, Object *ob, ParticleSystem *psys)
911{
912 bool ok;
913
914 if (!psys) {
915 return false;
916 }
917
919 scene,
920 ob,
921 psys,
922 ob,
923 psys,
924 psys->edit,
925 ob->object_to_world().ptr(),
926 ob->object_to_world().ptr(),
927 psys->flag & PSYS_GLOBAL_HAIR,
928 false);
929 if (ok) {
930 psys->flag &= ~PSYS_GLOBAL_HAIR;
931 }
932
933 return ok;
934}
935
937{
939 Scene *scene = CTX_data_scene(C);
941 ParticleSystem *psys = nullptr;
942 const bool all = RNA_boolean_get(op->ptr, "all");
943 bool any_connected = false;
944
945 if (!ob) {
946 return OPERATOR_CANCELLED;
947 }
948
949 if (all) {
951 any_connected |= connect_hair(depsgraph, scene, ob, psys);
952 }
953 }
954 else {
955 psys = psys_get_current(ob);
956 any_connected |= connect_hair(depsgraph, scene, ob, psys);
957 }
958
959 if (!any_connected) {
962 "No hair connected (can't connect hair if particle system modifier is disabled)");
963 return OPERATOR_CANCELLED;
964 }
965
968
969 return OPERATOR_FINISHED;
970}
971
973{
974 ot->name = "Connect Hair";
975 ot->description = "Connect hair to the emitter mesh";
976 ot->idname = "PARTICLE_OT_connect_hair";
977
979
980 /* flags */
981 /* No REGISTER, redo does not work due to missing update, see #47750. */
983
985 ot->srna, "all", false, "All Hair", "Connect all hair systems to the emitter mesh");
986}
987
988/************************ particle system copy operator *********************/
989
994
995static void copy_particle_edit(Depsgraph *depsgraph,
996 Scene *scene,
997 Object *ob,
998 ParticleSystem *psys,
999 ParticleSystem *psys_from)
1000{
1001 PTCacheEdit *edit_from = psys_from->edit, *edit;
1002 ParticleData *pa;
1003 KEY_K;
1004 POINT_P;
1005
1006 if (!edit_from) {
1007 return;
1008 }
1009
1010 edit = static_cast<PTCacheEdit *>(MEM_dupallocN(edit_from));
1011 edit->psys = psys;
1012 psys->edit = edit;
1013
1014 edit->pathcache = nullptr;
1015 edit->mirror_cache = nullptr;
1016 BLI_listbase_clear(&edit->pathcachebufs);
1017
1018 edit->emitter_field = nullptr;
1019 edit->emitter_cosnos = nullptr;
1020
1021 edit->points = static_cast<PTCacheEditPoint *>(MEM_dupallocN(edit_from->points));
1022 pa = psys->particles;
1023 LOOP_POINTS {
1024 HairKey *hkey = pa->hair;
1025
1026 point->keys = static_cast<PTCacheEditKey *>(MEM_dupallocN(point->keys));
1027 LOOP_KEYS {
1028 key->co = hkey->co;
1029 key->time = &hkey->time;
1030 key->flag = hkey->editflag;
1031 if (!(psys->flag & PSYS_GLOBAL_HAIR)) {
1032 key->flag |= PEK_USE_WCO;
1033 hkey->editflag |= PEK_USE_WCO;
1034 }
1035
1036 hkey++;
1037 }
1038
1039 pa++;
1040 }
1041 update_world_cos(ob, edit);
1042
1043 recalc_lengths(edit);
1045 PE_update_object(depsgraph, scene, ob, true);
1046}
1047
1049{
1050 ModifierData *md, *md_next;
1051
1052 if (ob_to->type != OB_MESH) {
1053 return;
1054 }
1055 if (!ob_to->data || !ID_IS_EDITABLE(ob_to->data) || ID_IS_OVERRIDE_LIBRARY(ob_to->data)) {
1056 return;
1057 }
1058
1059 for (md = static_cast<ModifierData *>(ob_to->modifiers.first); md; md = md_next) {
1060 md_next = md->next;
1061
1062 /* remove all particle system modifiers as well,
1063 * these need to sync to the particle system list
1064 */
1065 if (ELEM(md->type,
1069 {
1070 BLI_remlink(&ob_to->modifiers, md);
1072 }
1073 }
1074
1076}
1077
1078/* single_psys_from is optional, if nullptr all psys of ob_from are copied */
1080 Scene *scene,
1081 Object *ob_from,
1082 ParticleSystem *single_psys_from,
1083 Object *ob_to,
1084 int space,
1085 bool duplicate_settings)
1086{
1087 Main *bmain = CTX_data_main(C);
1089 ModifierData *md;
1090 ParticleSystem *psys_start = nullptr, *psys, *psys_from;
1091 ParticleSystem **tmp_psys;
1092 CustomData_MeshMasks cdmask = {0};
1093 int i, totpsys;
1094
1095 if (ob_to->type != OB_MESH) {
1096 return false;
1097 }
1098 if (!ob_to->data || !BKE_id_is_editable(bmain, static_cast<const ID *>(ob_to->data))) {
1099 return false;
1100 }
1101
1102/* For remapping we need a valid DM.
1103 * Because the modifiers are appended at the end it's safe to use
1104 * the final DM of the object without particles.
1105 * However, when evaluating the DM all the particle modifiers must be valid,
1106 * i.e. have the psys assigned already.
1107 *
1108 * To break this hen/egg problem we create all psys separately first
1109 * (to collect required customdata masks),
1110 * then create the DM, then add them to the object and make the psys modifiers.
1111 */
1112#define PSYS_FROM_FIRST \
1113 static_cast<ParticleSystem *>( \
1114 (single_psys_from ? single_psys_from : ob_from->particlesystem.first))
1115#define PSYS_FROM_NEXT(cur) (single_psys_from ? nullptr : (cur)->next)
1116 totpsys = single_psys_from ? 1 : BLI_listbase_count(&ob_from->particlesystem);
1117
1118 tmp_psys = static_cast<ParticleSystem **>(
1119 MEM_mallocN(sizeof(ParticleSystem *) * totpsys, "temporary particle system array"));
1120
1121 for (psys_from = PSYS_FROM_FIRST, i = 0; psys_from; psys_from = PSYS_FROM_NEXT(psys_from), i++) {
1122 psys = BKE_object_copy_particlesystem(psys_from, 0);
1123 tmp_psys[i] = psys;
1124
1125 if (psys_start == nullptr) {
1126 psys_start = psys;
1127 }
1128
1129 psys_emitter_customdata_mask(psys, &cdmask);
1130 }
1131 /* to iterate source and target psys in sync,
1132 * we need to know where the newly added psys start
1133 */
1134 psys_start = totpsys > 0 ? tmp_psys[0] : nullptr;
1135
1136 /* now append psys to the object and make modifiers */
1137 for (i = 0, psys_from = PSYS_FROM_FIRST; i < totpsys; ++i, psys_from = PSYS_FROM_NEXT(psys_from))
1138 {
1140
1141 psys = tmp_psys[i];
1142
1143 /* append to the object */
1144 BLI_addtail(&ob_to->particlesystem, psys);
1145 psys_unique_name(ob_to, psys, psys->name);
1146
1147 /* add a particle system modifier for each system */
1149 psmd = (ParticleSystemModifierData *)md;
1150 /* push on top of the stack, no use trying to reproduce old stack order */
1151 BLI_addtail(&ob_to->modifiers, md);
1153
1154 SNPRINTF(md->name, "ParticleSystem %i", i);
1156
1157 psmd->psys = psys;
1158
1159 if (psys_from->edit) {
1160 copy_particle_edit(depsgraph, scene, ob_to, psys, psys_from);
1161 }
1162
1163 if (duplicate_settings) {
1164 id_us_min(&psys->part->id);
1165 psys->part = (ParticleSettings *)BKE_id_copy(bmain, &psys->part->id);
1166 }
1167 }
1168 MEM_freeN(tmp_psys);
1169
1170 /* NOTE: do this after creating DM copies for all the particle system modifiers,
1171 * the remapping otherwise makes final_dm invalid!
1172 */
1173 for (psys = psys_start, psys_from = PSYS_FROM_FIRST, i = 0; psys;
1174 psys = psys->next, psys_from = PSYS_FROM_NEXT(psys_from), i++)
1175 {
1176 const float(*from_mat)[4], (*to_mat)[4];
1177
1178 switch (space) {
1180 from_mat = I;
1181 to_mat = I;
1182 break;
1184 from_mat = ob_from->object_to_world().ptr();
1185 to_mat = ob_to->object_to_world().ptr();
1186 break;
1187 default:
1188 /* should not happen */
1189 from_mat = to_mat = nullptr;
1190 BLI_assert(false);
1191 break;
1192 }
1193 if (ob_from != ob_to) {
1195 scene,
1196 ob_from,
1197 psys_from,
1198 ob_to,
1199 psys,
1200 psys->edit,
1201 from_mat,
1202 to_mat,
1203 psys_from->flag & PSYS_GLOBAL_HAIR,
1204 psys->flag & PSYS_GLOBAL_HAIR);
1205 }
1206
1207 /* tag for recalc */
1208 // psys->recalc |= ID_RECALC_PSYS_RESET;
1209 }
1210
1211#undef PSYS_FROM_FIRST
1212#undef PSYS_FROM_NEXT
1213
1217 return true;
1218}
1219
1221{
1222 Object *ob;
1224 return false;
1225 }
1226
1229 return false;
1230 }
1231
1232 return true;
1233}
1234
1236{
1237 const int space = RNA_enum_get(op->ptr, "space");
1238 const bool remove_target_particles = RNA_boolean_get(op->ptr, "remove_target_particles");
1239 const bool use_active = RNA_boolean_get(op->ptr, "use_active");
1240 Scene *scene = CTX_data_scene(C);
1242
1243 ParticleSystem *psys_from = nullptr;
1244 if (use_active) {
1245 psys_from = static_cast<ParticleSystem *>(
1246 CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem).data);
1247 if (psys_from == nullptr) {
1248 /* Particle System context pointer is only valid in the Properties Editor. */
1249 psys_from = psys_get_current(ob_from);
1250 }
1251 }
1252
1253 int changed_tot = 0;
1254 int fail = 0;
1255
1256 CTX_DATA_BEGIN (C, Object *, ob_to, selected_editable_objects) {
1257 if (ob_from != ob_to) {
1258 bool changed = false;
1259 if (remove_target_particles) {
1261 changed = true;
1262 }
1263 if (copy_particle_systems_to_object(C, scene, ob_from, psys_from, ob_to, space, false)) {
1264 changed = true;
1265 }
1266 else {
1267 fail++;
1268 }
1269
1270 if (changed) {
1271 changed_tot++;
1272 }
1273 }
1274 }
1276
1277 if (changed_tot > 0) {
1279 }
1280
1281 if ((changed_tot == 0 && fail == 0) || fail) {
1282 BKE_reportf(op->reports,
1283 RPT_ERROR,
1284 "Copy particle systems to selected: %d done, %d failed",
1285 changed_tot,
1286 fail);
1287 }
1288
1289 return OPERATOR_FINISHED;
1290}
1291
1293{
1294 static const EnumPropertyItem space_items[] = {
1295 {PAR_COPY_SPACE_OBJECT, "OBJECT", 0, "Object", "Copy inside each object's local space"},
1296 {PAR_COPY_SPACE_WORLD, "WORLD", 0, "World", "Copy in world space"},
1297 {0, nullptr, 0, nullptr, nullptr},
1298 };
1299
1300 ot->name = "Copy Particle Systems";
1301 ot->description = "Copy particle systems from the active object to selected objects";
1302 ot->idname = "PARTICLE_OT_copy_particle_systems";
1303
1306
1307 /* flags */
1309
1311 "space",
1314 "Space",
1315 "Space transform for copying from one object to another");
1317 "remove_target_particles",
1318 true,
1319 "Remove Target Particles",
1320 "Remove particle systems on the target objects");
1322 "use_active",
1323 false,
1324 "Use Active",
1325 "Use the active particle system from the context");
1326}
1327
1329{
1331 return false;
1332 }
1335 return false;
1336 }
1337 if (ob->mode != OB_MODE_OBJECT) {
1338 CTX_wm_operator_poll_msg_set(C, "Object must be in object mode");
1339 return false;
1340 }
1341 return true;
1342}
1343
1345{
1346 const bool duplicate_settings = RNA_boolean_get(op->ptr, "use_duplicate_settings");
1347 Scene *scene = CTX_data_scene(C);
1349 /* Context pointer is only valid in the Properties Editor. */
1350 ParticleSystem *psys = static_cast<ParticleSystem *>(
1351 CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem).data);
1352 if (psys == nullptr) {
1353 psys = psys_get_current(ob);
1354 }
1355
1357 C, scene, ob, psys, ob, PAR_COPY_SPACE_OBJECT, duplicate_settings);
1358 return OPERATOR_FINISHED;
1359}
1360
1362{
1363 ot->name = "Duplicate Particle System";
1364 ot->description = "Duplicate particle system within the active object";
1365 ot->idname = "PARTICLE_OT_duplicate_particle_system";
1366
1369
1370 /* flags */
1372
1374 "use_duplicate_settings",
1375 false,
1376 "Duplicate Settings",
1377 "Duplicate settings as well, so the new particle system uses its own settings");
1378}
void free_bvhtree_from_mesh(BVHTreeFromMesh *data)
Definition bvhutils.cc:1160
BVHTree * BKE_bvhtree_from_mesh_get(BVHTreeFromMesh *data, const Mesh *mesh, BVHCacheType bvh_cache_type, int tree_type)
Definition bvhutils.cc:899
@ BVHTREE_FROM_EDGES
@ BVHTREE_FROM_FACES
#define CTX_DATA_BEGIN(C, Type, instance, member)
PointerRNA CTX_data_pointer_get_type(const bContext *C, const char *member, StructRNA *type)
void CTX_wm_operator_poll_msg_set(bContext *C, const char *msg)
Depsgraph * CTX_data_ensure_evaluated_depsgraph(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
Main * CTX_data_main(const bContext *C)
#define CTX_DATA_END
ViewLayer * CTX_data_view_layer(const bContext *C)
CustomData interface, see also DNA_customdata_types.h.
const void * CustomData_get_layer(const CustomData *data, eCustomDataType type)
const void * CustomData_get_layer_named(const CustomData *data, eCustomDataType type, blender::StringRef name)
@ G_DEBUG
void BKE_view_layer_synced_ensure(const Scene *scene, ViewLayer *view_layer)
Object * BKE_view_layer_active_object_get(const ViewLayer *view_layer)
bool BKE_id_is_editable(const Main *bmain, const ID *id)
Definition lib_id.cc:2456
@ LIB_ID_COPY_ACTIONS
@ LIB_ID_COPY_LOCALIZE
@ LIB_ID_COPY_DEFAULT
void BKE_id_free(Main *bmain, void *idv)
ID * BKE_id_copy_ex(Main *bmain, const ID *id, ID **new_id_p, int flag)
Definition lib_id.cc:760
ID * BKE_id_copy(Main *bmain, const ID *id)
Definition lib_id.cc:765
void id_us_min(ID *id)
Definition lib_id.cc:359
void BKE_mesh_tessface_ensure(Mesh *mesh)
void BKE_modifiers_persistent_uid_init(const Object &object, ModifierData &md)
void BKE_modifier_unique_name(ListBase *modifiers, ModifierData *md)
void BKE_modifier_free(ModifierData *md)
ModifierData * BKE_modifier_new(int type)
General operations, lookup, etc. for blender objects.
void BKE_object_free_particlesystems(Object *ob)
ParticleSystem * BKE_object_copy_particlesystem(ParticleSystem *psys, int flag)
struct ModifierData * object_add_particle_system(struct Main *bmain, const struct Scene *scene, struct Object *ob, const char *name)
struct ParticleSystemModifierData * psys_get_modifier(struct Object *ob, struct ParticleSystem *psys)
Definition particle.cc:2150
void psys_unique_name(struct Object *object, struct ParticleSystem *psys, const char *defname) ATTR_NONNULL(1
void psys_mat_hair_to_global(struct Object *ob, struct Mesh *mesh, short from, struct ParticleData *pa, float hairmat[4][4])
Definition particle.cc:3907
void psys_check_group_weights(struct ParticleSettings *part)
Definition particle.cc:762
void psys_mat_hair_to_object(struct Object *ob, struct Mesh *mesh, short from, struct ParticleData *pa, float hairmat[4][4])
Definition particle.cc:3851
void psys_emitter_customdata_mask(struct ParticleSystem *psys, struct CustomData_MeshMasks *r_cddata_masks)
Definition particle.cc:2202
void psys_check_boid_data(struct ParticleSystem *psys)
void object_remove_particle_system(struct Main *bmain, struct Scene *scene, struct Object *ob, struct ParticleSystem *psys)
Definition particle.cc:3991
struct ParticleSystem * psys_get_current(struct Object *ob)
Definition particle.cc:534
struct ParticleSystem * psys_eval_get(struct Depsgraph *depsgraph, struct Object *object, struct ParticleSystem *psys)
Definition particle.cc:664
void psys_free_path_cache(struct ParticleSystem *psys, struct PTCacheEdit *edit)
Definition particle.cc:904
#define DMCACHE_ISCHILD
int psys_particle_dm_face_lookup(struct Mesh *mesh_final, struct Mesh *mesh_original, int findex_orig, const float fw[4], struct LinkNode **poly_nodes)
Definition particle.cc:1830
struct ParticleSettings * BKE_particlesettings_add(struct Main *bmain, const char *name)
Definition particle.cc:4095
#define PEK_USE_WCO
void BKE_reportf(ReportList *reports, eReportType type, const char *format,...) ATTR_PRINTF_FORMAT(3
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:125
#define BLI_assert(a)
Definition BLI_assert.h:50
int BLI_bvhtree_find_nearest(const BVHTree *tree, const float co[3], BVHTreeNearest *nearest, BVHTree_NearestPointCallback callback, void *userdata)
BLI_INLINE bool BLI_listbase_is_empty(const struct ListBase *lb)
void BLI_addhead(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:90
#define LISTBASE_FOREACH(type, var, list)
BLI_INLINE void BLI_listbase_clear(struct ListBase *lb)
void BLI_insertlinkafter(struct ListBase *listbase, void *vprevlink, void *vnewlink) ATTR_NONNULL(1)
Definition listbase.cc:331
void BLI_addtail(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:110
void BLI_remlink(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:130
void BLI_insertlinkbefore(struct ListBase *listbase, void *vnextlink, void *vnewlink) ATTR_NONNULL(1)
Definition listbase.cc:370
int BLI_listbase_count(const struct ListBase *listbase) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
float line_point_factor_v3(const float p[3], const float l1[3], const float l2[3])
void interp_weights_poly_v3(float w[], float v[][3], int n, const float co[3])
void mul_m4_m4m4(float R[4][4], const float A[4][4], const float B[4][4])
void mul_m4_v3(const float M[4][4], float r[3])
void copy_m4_m4(float m1[4][4], const float m2[4][4])
void mul_v3_m4v3(float r[3], const float mat[4][4], const float vec[3])
bool invert_m4_m4(float inverse[4][4], const float mat[4][4])
MINLINE void sub_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void copy_v3_v3(float r[3], const float a[3])
MINLINE void add_v3_v3v3(float r[3], const float a[3], const float b[3])
#define SNPRINTF(dst, format,...)
Definition BLI_string.h:597
#define ELEM(...)
void DEG_id_tag_update(ID *id, unsigned int flags)
void DEG_relations_tag_update(Main *bmain)
Object * DEG_get_evaluated_object(const Depsgraph *depsgraph, Object *object)
@ ID_RECALC_PSYS_REDO
Definition DNA_ID.h:1048
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:1041
#define ID_IS_EDITABLE(_id)
Definition DNA_ID.h:658
#define ID_IS_OVERRIDE_LIBRARY(_id)
Definition DNA_ID.h:683
@ CD_PROP_INT32_2D
@ eModifierType_ParticleSystem
@ eModifierType_Fluid
@ eModifierType_DynamicPaint
@ OB_MODE_PARTICLE_EDIT
@ OB_MODE_OBJECT
@ OB_MESH
@ PART_DUPLIW_CURRENT
@ PTARGET_CURRENT
@ PART_HAIR
@ PSYS_GLOBAL_HAIR
@ PE_BRUSH_COMB
@ PE_BRUSH_PUFF
@ PE_BRUSH_ADD
void PE_update_object(Depsgraph *depsgraph, Scene *scene, Object *ob, int useflag)
ParticleEditSettings * PE_settings(Scene *scene)
bool ED_operator_object_active_local_editable(bContext *C)
Read Guarded memory(de)allocation.
in reality light always falls off quadratically Particle Retrieve the data of the particle that spawned the object for example to give variation to multiple instances of an object Point Retrieve information about points in a point cloud Retrieve the edges of an object as it appears to Cycles topology will always appear triangulated Convert a blackbody temperature to an RGB value Normal Generate a perturbed normal from an RGB normal map image Typically used for faking highly detailed surfaces Generate an OSL shader from a file or text data block Image Sample an image file as a texture Gabor Generate Gabor noise Gradient Generate interpolated color and intensity values based on the input vector Magic Generate a psychedelic color texture Voronoi Generate Worley noise based on the distance to random points Typically used to generate textures such as or biological cells Brick Generate a procedural texture producing bricks Texture Retrieve multiple types of texture coordinates nTypically used as inputs for texture nodes Vector Convert a point
@ OPTYPE_UNDO
Definition WM_types.hh:162
@ OPTYPE_REGISTER
Definition WM_types.hh:160
#define ND_MODE
Definition WM_types.hh:412
#define NC_SCENE
Definition WM_types.hh:345
#define NA_EDITED
Definition WM_types.hh:550
#define ND_PARTICLE
Definition WM_types.hh:432
#define NS_MODE_OBJECT
Definition WM_types.hh:526
#define ND_POINTCACHE
Definition WM_types.hh:433
#define NC_OBJECT
Definition WM_types.hh:346
ATTR_WARN_UNUSED_RESULT const BMVert * v
#define printf
const Depsgraph * depsgraph
draw_view in_light_buf[] float
void *(* MEM_mallocN)(size_t len, const char *str)
Definition mallocn.cc:44
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
void *(* MEM_callocN)(size_t len, const char *str)
Definition mallocn.cc:42
void *(* MEM_dupallocN)(const void *vmemh)
Definition mallocn.cc:39
#define G(x, y, z)
Object * context_object(const bContext *C)
Object * context_active_object(const bContext *C)
void recalc_emitter_field(Depsgraph *, Object *, ParticleSystem *psys)
void update_world_cos(Object *ob, PTCacheEdit *edit)
void recalc_lengths(PTCacheEdit *edit)
#define POINT_P
#define LOOP_KEYS
#define KEY_K
#define LOOP_POINTS
static void disconnect_hair(Depsgraph *depsgraph, Scene *scene, Object *ob, ParticleSystem *psys)
static int connect_hair_exec(bContext *C, wmOperator *op)
static bool duplicate_particle_systems_poll(bContext *C)
static int copy_particle_systems_exec(bContext *C, wmOperator *op)
static int remove_particle_dupliob_exec(bContext *C, wmOperator *)
void PARTICLE_OT_target_move_up(wmOperatorType *ot)
void PARTICLE_OT_dupliob_copy(wmOperatorType *ot)
void PARTICLE_OT_dupliob_refresh(wmOperatorType *ot)
static int dupliob_move_down_exec(bContext *C, wmOperator *)
static bool remap_hair_emitter(Depsgraph *depsgraph, Scene *scene, Object *ob, ParticleSystem *psys, Object *target_ob, ParticleSystem *target_psys, PTCacheEdit *target_edit, const float from_mat[4][4], const float to_mat[4][4], bool from_global, bool to_global)
static float I[4][4]
void PARTICLE_OT_new(wmOperatorType *ot)
eCopyParticlesSpace
@ PAR_COPY_SPACE_OBJECT
@ PAR_COPY_SPACE_WORLD
void PARTICLE_OT_new_target(wmOperatorType *ot)
static int new_particle_settings_exec(bContext *C, wmOperator *)
static int copy_particle_dupliob_exec(bContext *C, wmOperator *)
void PARTICLE_OT_disconnect_hair(wmOperatorType *ot)
void PARTICLE_OT_dupliob_move_down(wmOperatorType *ot)
static bool psys_poll(bContext *C)
void PARTICLE_OT_target_remove(wmOperatorType *ot)
static void copy_particle_edit(Depsgraph *depsgraph, Scene *scene, Object *ob, ParticleSystem *psys, ParticleSystem *psys_from)
static bool copy_particle_systems_poll(bContext *C)
void PARTICLE_OT_connect_hair(wmOperatorType *ot)
void OBJECT_OT_particle_system_remove(wmOperatorType *ot)
void PARTICLE_OT_copy_particle_systems(wmOperatorType *ot)
static int target_move_up_exec(bContext *C, wmOperator *)
void PARTICLE_OT_dupliob_remove(wmOperatorType *ot)
#define PSYS_FROM_FIRST
static int new_particle_target_exec(bContext *C, wmOperator *)
static bool copy_particle_systems_to_object(const bContext *C, Scene *scene, Object *ob_from, ParticleSystem *single_psys_from, Object *ob_to, int space, bool duplicate_settings)
static void remove_particle_systems_from_object(Object *ob_to)
static int disconnect_hair_exec(bContext *C, wmOperator *op)
static int target_move_down_exec(bContext *C, wmOperator *)
static int duplicate_particle_systems_exec(bContext *C, wmOperator *op)
static int remove_particle_target_exec(bContext *C, wmOperator *)
void PARTICLE_OT_dupliob_move_up(wmOperatorType *ot)
#define PSYS_FROM_NEXT(cur)
static int particle_system_add_exec(bContext *C, wmOperator *)
static int dupliob_move_up_exec(bContext *C, wmOperator *)
static int dupliob_refresh_exec(bContext *C, wmOperator *)
void OBJECT_OT_particle_system_add(wmOperatorType *ot)
void PARTICLE_OT_target_move_down(wmOperatorType *ot)
static int particle_system_remove_exec(bContext *C, wmOperator *)
void PARTICLE_OT_duplicate_particle_system(wmOperatorType *ot)
static bool connect_hair(Depsgraph *depsgraph, Scene *scene, Object *ob, ParticleSystem *psys)
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
int RNA_enum_get(PointerRNA *ptr, const char *name)
PropertyRNA * RNA_def_enum(StructOrFunctionRNA *cont_, const char *identifier, const EnumPropertyItem *items, const int default_value, const char *ui_name, const char *ui_description)
PropertyRNA * RNA_def_boolean(StructOrFunctionRNA *cont_, const char *identifier, const bool default_value, const char *ui_name, const char *ui_description)
static const EnumPropertyItem space_items[]
#define FLT_MAX
Definition stdcycles.h:14
float co[3]
BVHTree_NearestPointCallback nearest_callback
float world_co[3]
Definition DNA_ID.h:413
void * last
void * first
MeshRuntimeHandle * runtime
struct ModifierData * next
ustring name
Definition graph/node.h:177
ListBase particlesystem
ListBase modifiers
struct PTCacheEditKey * keys
struct ParticleCacheKey ** pathcache
PTCacheEditPoint * points
struct ParticleSystem * psys
struct ParticleSystem * psys
struct PTCacheEdit * edit
ParticleData * particles
struct ListBase targets
ParticleSettings * part
struct ParticleSystem * next
struct ParticleTarget * prev
struct ParticleTarget * next
ID * owner_id
Definition RNA_types.hh:40
void * data
Definition RNA_types.hh:42
const char * name
Definition WM_types.hh:990
bool(* poll)(bContext *C) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1042
const char * idname
Definition WM_types.hh:992
int(* exec)(bContext *C, wmOperator *op) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1006
const char * description
Definition WM_types.hh:996
StructRNA * srna
Definition WM_types.hh:1080
struct ReportList * reports
struct PointerRNA * ptr
void WM_main_add_notifier(uint type, void *reference)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
PointerRNA * ptr
Definition wm_files.cc:4126
wmOperatorType * ot
Definition wm_files.cc:4125