Blender V4.3
object_constraint.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
9#include <cstdio>
10#include <cstring>
11
12#include "MEM_guardedalloc.h"
13
14#include "BLI_blenlib.h"
15#include "BLI_dynstr.h"
16#include "BLI_math_matrix.h"
17#include "BLI_math_vector.h"
18#include "BLI_utildefines.h"
19
20#include "BLT_translation.hh"
21
22#include "DNA_anim_types.h"
23#include "DNA_armature_types.h"
25#include "DNA_curve_types.h"
26#include "DNA_object_types.h"
27#include "DNA_scene_types.h"
28#include "DNA_text_types.h"
29
30#include "BIK_api.h"
31#include "BKE_action.hh"
32#include "BKE_armature.hh"
33#include "BKE_constraint.h"
34#include "BKE_context.hh"
35#include "BKE_fcurve.hh"
36#include "BKE_layer.hh"
37#include "BKE_main.hh"
38#include "BKE_object.hh"
39#include "BKE_report.hh"
40#include "BKE_tracking.h"
41
42#include "DEG_depsgraph.hh"
45
46#ifdef WITH_PYTHON
47# include "BPY_extern.hh"
48#endif
49
50#include "WM_api.hh"
51#include "WM_types.hh"
52
53#include "RNA_access.hh"
54#include "RNA_define.hh"
55#include "RNA_enum_types.hh"
56#include "RNA_path.hh"
57#include "RNA_prototypes.hh"
58
59#include "ED_object.hh"
60#include "ED_screen.hh"
61
62#include "ANIM_action.hh"
63#include "ANIM_action_legacy.hh"
64#include "ANIM_animdata.hh"
65
66#include "UI_interface.hh"
67#include "UI_resources.hh"
68
69#include "object_intern.hh"
70
71namespace blender::ed::object {
72
73/* ------------------------------------------------------------------- */
78{
79 if (ob == nullptr) {
80 return nullptr;
81 }
82
83 if (ob->mode & OB_MODE_POSE) {
84 bPoseChannel *pchan;
85
87 if (pchan) {
88 return &pchan->constraints;
89 }
90 }
91 else {
92 return &ob->constraints;
93 }
94
95 return nullptr;
96}
97
99{
100 bPoseChannel *pose_bone = static_cast<bPoseChannel *>(CTX_data_pointer_get(C, "pose_bone").data);
101 if (pose_bone == nullptr) {
102 pose_bone = static_cast<bPoseChannel *>(CTX_data_pointer_get(C, "active_pose_bone").data);
103 if (pose_bone == nullptr) {
104 return nullptr;
105 }
106 }
107
108 return &pose_bone->constraints;
109}
110
112{
113 if (r_pchan) {
114 *r_pchan = nullptr;
115 }
116
117 if (ELEM(nullptr, ob, con)) {
118 return nullptr;
119 }
120
121 /* try object constraints first */
122 if (BLI_findindex(&ob->constraints, con) != -1) {
123 return &ob->constraints;
124 }
125
126 /* if armature, try pose bones too */
127 if (ob->pose) {
128 /* try each bone in order
129 * NOTE: it's not possible to directly look up the active bone yet, so this will have to do
130 */
131 LISTBASE_FOREACH (bPoseChannel *, pchan, &ob->pose->chanbase) {
132 if (BLI_findindex(&pchan->constraints, con) != -1) {
133
134 if (r_pchan) {
135 *r_pchan = pchan;
136 }
137
138 return &pchan->constraints;
139 }
140 }
141 }
142
143 /* done */
144 return nullptr;
145}
146
151
154/* ------------------------------------------------------------------- */
158#ifdef WITH_PYTHON
159
160/* this callback sets the text-file to be used for selected menu item */
161static void validate_pyconstraint_cb(Main *bmain, void *arg1, void *arg2)
162{
163 bPythonConstraint *data = static_cast<bPythonConstraint *>(arg1);
164 Text *text = nullptr;
165 int index = *((int *)arg2);
166 int i;
167
168 /* exception for no script */
169 if (index) {
170 /* innovative use of a for...loop to search */
171 for (text = static_cast<Text *>(bmain->texts.first), i = 1; text && index != i;
172 i++, text = static_cast<Text *>(text->id.next))
173 {
174 /* pass */
175 }
176 }
177 data->text = text;
178}
179
180/* this returns a string for the list of usable pyconstraint script names */
181static char *buildmenu_pyconstraints(Main *bmain, Text *con_text, int *pyconindex)
182{
183 DynStr *pupds = BLI_dynstr_new();
184 Text *text;
185 char *str;
186 char buf[64];
187 int i;
188
189 /* add title first */
190 STRNCPY(buf, "Scripts: %t|[None]%x0|");
191 BLI_dynstr_append(pupds, buf);
192
193 /* init active-index first */
194 if (con_text == nullptr) {
195 *pyconindex = 0;
196 }
197
198 /* loop through markers, adding them */
199 for (text = static_cast<Text *>(bmain->texts.first), i = 1; text;
200 i++, text = static_cast<Text *>(text->id.next))
201 {
202 /* this is important to ensure that right script is shown as active */
203 if (text == con_text) {
204 *pyconindex = i;
205 }
206
207 /* only include valid pyconstraint scripts */
208 if (BPY_is_pyconstraint(text)) {
209 BLI_dynstr_append(pupds, text->id.name + 2);
210
211 SNPRINTF(buf, "%%x%d", i);
212 BLI_dynstr_append(pupds, buf);
213
214 if (text->id.next) {
215 BLI_dynstr_append(pupds, "|");
216 }
217 }
218 }
219
220 /* convert to normal MEM_malloc'd string */
222 BLI_dynstr_free(pupds);
223
224 return str;
225}
226#endif /* WITH_PYTHON */
227
228#if 0 /* UNUSED, until pyconstraints are added back. */
229/* this callback gets called when the 'refresh' button of a pyconstraint gets pressed */
230static void update_pyconstraint_cb(void *arg1, void *arg2)
231{
232# ifndef WITH_PYTHON
233 (void)arg1; /* unused */
234 (void)arg2; /* unused */
235# else
236 Object *owner = (Object *)arg1;
237 bConstraint *con = (bConstraint *)arg2;
238 if (owner && con) {
239 BPY_pyconstraint_update(owner, con);
240 }
241# endif /* WITH_PYTHON */
242}
243#endif /* UNUSED */
244
247/* ------------------------------------------------------------------- */
251/* helper function for add_constriant - sets the last target for the active constraint */
253 Object *target,
254 const char subtarget[],
255 int index)
256{
257 ListBase targets = {nullptr, nullptr};
259 int targets_num, i;
260
261 if (BKE_constraint_targets_get(con, &targets)) {
262 targets_num = BLI_listbase_count(&targets);
263
264 if (index < 0) {
265 if (abs(index) < targets_num) {
266 index = targets_num - abs(index);
267 }
268 else {
269 index = targets_num - 1;
270 }
271 }
272 else if (index >= targets_num) {
273 index = targets_num - 1;
274 }
275
276 for (ct = static_cast<bConstraintTarget *>(targets.first), i = 0; ct; ct = ct->next, i++) {
277 if (i == index) {
278 ct->tar = target;
279 STRNCPY(ct->subtarget, subtarget);
280 break;
281 }
282 }
283
284 BKE_constraint_targets_flush(con, &targets, false);
285 }
286}
287
290/* ------------------------------------------------------------------- */
294static void test_constraint(
295 Main *bmain, Object *owner, bPoseChannel *pchan, bConstraint *con, int type)
296{
297 ListBase targets = {nullptr, nullptr};
298 bool check_targets = true;
299
300 /* clear disabled-flag first */
301 con->flag &= ~CONSTRAINT_DISABLE;
302
303 if (con->type == CONSTRAINT_TYPE_KINEMATIC) {
304 bKinematicConstraint *data = static_cast<bKinematicConstraint *>(con->data);
305
306 /* Bad: we need a separate set of checks here as pole-target is optional...
307 * otherwise pole-target must exist too or else the constraint is deemed invalid. */
308
309 /* default IK check ... */
310 if (BKE_object_exists_check(bmain, data->tar) == 0) {
311 data->tar = nullptr;
312 con->flag |= CONSTRAINT_DISABLE;
313 }
314 else if (data->tar == owner) {
315 if (!BKE_armature_find_bone_name(BKE_armature_from_object(owner), data->subtarget)) {
316 con->flag |= CONSTRAINT_DISABLE;
317 }
318 }
319
320 if (data->poletar) {
321 if (BKE_object_exists_check(bmain, data->poletar) == 0) {
322 data->poletar = nullptr;
323 con->flag |= CONSTRAINT_DISABLE;
324 }
325 else if (data->poletar == owner) {
326 if (!BKE_armature_find_bone_name(BKE_armature_from_object(owner), data->polesubtarget)) {
327 con->flag |= CONSTRAINT_DISABLE;
328 }
329 }
330 }
331 /* ... can be overwritten here */
332 BIK_test_constraint(owner, con);
333 /* targets have already been checked for this */
334 check_targets = false;
335 }
336 else if (con->type == CONSTRAINT_TYPE_PIVOT) {
337 bPivotConstraint *data = static_cast<bPivotConstraint *>(con->data);
338
339 /* target doesn't have to exist, but if it is non-null, it must exist! */
340 if (data->tar && BKE_object_exists_check(bmain, data->tar) == 0) {
341 data->tar = nullptr;
342 con->flag |= CONSTRAINT_DISABLE;
343 }
344 else if (data->tar == owner) {
345 if (!BKE_armature_find_bone_name(BKE_armature_from_object(owner), data->subtarget)) {
346 con->flag |= CONSTRAINT_DISABLE;
347 }
348 }
349
350 /* targets have already been checked for this */
351 check_targets = false;
352 }
353 else if (con->type == CONSTRAINT_TYPE_ACTION) {
354 bActionConstraint *data = static_cast<bActionConstraint *>(con->data);
355
356 /* validate action */
357 if (data->act == nullptr) {
358 /* must have action */
359 con->flag |= CONSTRAINT_DISABLE;
360 }
361 else {
363 if (!ELEM(data->act->idroot, ID_OB, 0)) {
364 /* Only object-rooted actions can be used. */
365 data->act = nullptr;
366 con->flag |= CONSTRAINT_DISABLE;
367 }
368 }
369 else {
370 /* The slot was assigned, so assume that it is suitable to animate the
371 * owner (only suitable slots appear in the drop-down). */
372 animrig::Action &action = data->act->wrap();
373 animrig::Slot *slot = action.slot_for_handle(data->action_slot_handle);
374 if (!slot) {
375 con->flag |= CONSTRAINT_DISABLE;
376 }
377 }
378 }
379
380 /* Skip target checking if we're not using it */
381 if (data->flag & ACTCON_USE_EVAL_TIME) {
382 check_targets = false;
383 }
384 }
385 else if (con->type == CONSTRAINT_TYPE_FOLLOWPATH) {
386 bFollowPathConstraint *data = static_cast<bFollowPathConstraint *>(con->data);
387
388 /* don't allow track/up axes to be the same */
389 if (data->upflag == data->trackflag) {
390 con->flag |= CONSTRAINT_DISABLE;
391 }
392 if (data->upflag + 3 == data->trackflag) {
393 con->flag |= CONSTRAINT_DISABLE;
394 }
395 }
396 else if (con->type == CONSTRAINT_TYPE_TRACKTO) {
397 bTrackToConstraint *data = static_cast<bTrackToConstraint *>(con->data);
398
399 /* don't allow track/up axes to be the same */
400 if (data->reserved2 == data->reserved1) {
401 con->flag |= CONSTRAINT_DISABLE;
402 }
403 if (data->reserved2 + 3 == data->reserved1) {
404 con->flag |= CONSTRAINT_DISABLE;
405 }
406 }
407 else if (con->type == CONSTRAINT_TYPE_LOCKTRACK) {
408 bLockTrackConstraint *data = static_cast<bLockTrackConstraint *>(con->data);
409
410 if (data->lockflag == data->trackflag) {
411 con->flag |= CONSTRAINT_DISABLE;
412 }
413 if (data->lockflag + 3 == data->trackflag) {
414 con->flag |= CONSTRAINT_DISABLE;
415 }
416 }
417 else if (con->type == CONSTRAINT_TYPE_SPLINEIK) {
418 bSplineIKConstraint *data = static_cast<bSplineIKConstraint *>(con->data);
419
420 /* if the number of points does not match the amount required by the chain length,
421 * free the points array and request a rebind...
422 */
423 if ((data->points == nullptr) || (data->numpoints != data->chainlen + 1)) {
424 MEM_SAFE_FREE(data->points);
425 data->numpoints = 0;
426
427 /* clear the bound flag, forcing a rebind next time this is evaluated */
428 data->flag &= ~CONSTRAINT_SPLINEIK_BOUND;
429 }
430 }
431 else if (con->type == CONSTRAINT_TYPE_FOLLOWTRACK) {
432 bFollowTrackConstraint *data = static_cast<bFollowTrackConstraint *>(con->data);
433
434 if ((data->flag & CAMERASOLVER_ACTIVECLIP) == 0) {
435 if (data->clip != nullptr && data->track[0]) {
436 MovieTracking *tracking = &data->clip->tracking;
437 MovieTrackingObject *tracking_object;
438
439 if (data->object[0]) {
440 tracking_object = BKE_tracking_object_get_named(tracking, data->object);
441 }
442 else {
443 tracking_object = BKE_tracking_object_get_camera(tracking);
444 }
445
446 if (!tracking_object) {
447 con->flag |= CONSTRAINT_DISABLE;
448 }
449 else {
450 if (!BKE_tracking_object_find_track_with_name(tracking_object, data->track)) {
451 con->flag |= CONSTRAINT_DISABLE;
452 }
453 }
454 }
455 else {
456 con->flag |= CONSTRAINT_DISABLE;
457 }
458 }
459 }
460 else if (con->type == CONSTRAINT_TYPE_CAMERASOLVER) {
461 bCameraSolverConstraint *data = static_cast<bCameraSolverConstraint *>(con->data);
462
463 if ((data->flag & CAMERASOLVER_ACTIVECLIP) == 0 && (data->clip == nullptr)) {
464 con->flag |= CONSTRAINT_DISABLE;
465 }
466 }
467 else if (con->type == CONSTRAINT_TYPE_OBJECTSOLVER) {
468 bObjectSolverConstraint *data = static_cast<bObjectSolverConstraint *>(con->data);
469
470 if ((data->flag & CAMERASOLVER_ACTIVECLIP) == 0 && (data->clip == nullptr)) {
471 con->flag |= CONSTRAINT_DISABLE;
472 }
473 }
474 else if (con->type == CONSTRAINT_TYPE_TRANSFORM_CACHE) {
475 bTransformCacheConstraint *data = static_cast<bTransformCacheConstraint *>(con->data);
476
477 if ((data->cache_file == nullptr) || (data->object_path[0] == '\0')) {
478 con->flag |= CONSTRAINT_DISABLE;
479 }
480 }
481
482 /* Check targets for constraints */
483 if (check_targets && BKE_constraint_targets_get(con, &targets)) {
484 /* disable and clear constraints targets that are incorrect */
485 LISTBASE_FOREACH (bConstraintTarget *, ct, &targets) {
486 /* general validity checks (for those constraints that need this) */
487 if (BKE_object_exists_check(bmain, ct->tar) == 0) {
488 /* object doesn't exist, but constraint requires target */
489 ct->tar = nullptr;
490 con->flag |= CONSTRAINT_DISABLE;
491 }
492 else if (ct->tar == owner) {
493 if (type == CONSTRAINT_OBTYPE_BONE) {
494 if (!BKE_armature_find_bone_name(BKE_armature_from_object(owner), ct->subtarget)) {
495 /* bone must exist in armature... */
496 /* TODO: clear subtarget? */
497 con->flag |= CONSTRAINT_DISABLE;
498 }
499 else if (STREQ(pchan->name, ct->subtarget)) {
500 /* cannot target self */
501 ct->subtarget[0] = '\0';
502 con->flag |= CONSTRAINT_DISABLE;
503 }
504 }
505 else {
506 /* cannot use self as target */
507 ct->tar = nullptr;
508 con->flag |= CONSTRAINT_DISABLE;
509 }
510 }
511
512 /* target checks for specific constraints */
513 if (ELEM(con->type,
517 {
518 if (ct->tar) {
519 /* The object type check is only needed here in case we have a placeholder
520 * object assigned (because the library containing the curve is missing).
521 *
522 * In other cases it should be impossible to have a type mismatch.
523 */
524 if (ct->tar->type != OB_CURVES_LEGACY) {
525 con->flag |= CONSTRAINT_DISABLE;
526 }
527 else {
528 Curve *cu = static_cast<Curve *>(ct->tar->data);
529
530 /* auto-set 'Path' setting on curve so this works. */
531 cu->flag |= CU_PATH;
532 }
533 }
534 }
535 else if (con->type == CONSTRAINT_TYPE_ARMATURE) {
536 if (ct->tar) {
537 /* The object type check is only needed here in case we have a placeholder
538 * object assigned (because the library containing the armature is missing).
539 *
540 * In other cases it should be impossible to have a type mismatch.
541 */
542 if (ct->tar->type != OB_ARMATURE) {
543 con->flag |= CONSTRAINT_DISABLE;
544 }
545 else if (!BKE_armature_find_bone_name(BKE_armature_from_object(ct->tar), ct->subtarget))
546 {
547 /* bone must exist in armature... */
548 con->flag |= CONSTRAINT_DISABLE;
549 }
550 }
551 }
552 }
553
554 /* free any temporary targets */
555 BKE_constraint_targets_flush(con, &targets, false);
556 }
557 else if (check_targets) {
558 /* constraints with empty target list that actually require targets */
560 con->flag |= CONSTRAINT_DISABLE;
561 }
562 }
563}
564
565static int constraint_type_get(Object *owner, bPoseChannel *pchan)
566{
567 int type;
568 /* Check parents */
569 if (pchan) {
570 switch (owner->type) {
571 case OB_ARMATURE:
573 break;
574 default:
576 break;
577 }
578 }
579 else {
581 }
582 return type;
583}
584
585/* checks validity of object pointers, and NULLs,
586 * if Bone doesn't exist it sets the CONSTRAINT_DISABLE flag.
587 */
588static void test_constraints(Main *bmain, Object *ob, bPoseChannel *pchan)
589{
590 ListBase *conlist = nullptr;
591 int type;
592
593 if (ob == nullptr) {
594 return;
595 }
596
597 type = constraint_type_get(ob, pchan);
598
599 /* Get the constraint list for this object */
600 switch (type) {
602 conlist = &ob->constraints;
603 break;
605 conlist = &pchan->constraints;
606 break;
607 }
608
609 /* Check all constraints - is constraint valid? */
610 if (conlist) {
611 LISTBASE_FOREACH (bConstraint *, curcon, conlist) {
612 test_constraint(bmain, ob, pchan, curcon, type);
613 }
614 }
615}
616
618{
619 if (ob->constraints.first) {
620 test_constraints(bmain, ob, nullptr);
621 }
622
623 if (ob->type == OB_ARMATURE && ob->pose) {
624 LISTBASE_FOREACH (bPoseChannel *, pchan, &ob->pose->chanbase) {
625 if (pchan->constraints.first) {
626 test_constraints(bmain, ob, pchan);
627 }
628 }
629 }
630}
631
632static void object_test_constraint(Main *bmain, Object *ob, bConstraint *con)
633{
634 if (ob->type == OB_ARMATURE && ob->pose) {
635 if (BLI_findindex(&ob->constraints, con) != -1) {
636 test_constraint(bmain, ob, nullptr, con, CONSTRAINT_OBTYPE_OBJECT);
637 }
638 else {
639 LISTBASE_FOREACH (bPoseChannel *, pchan, &ob->pose->chanbase) {
640 if (BLI_findindex(&pchan->constraints, con) != -1) {
641 test_constraint(bmain, ob, pchan, con, CONSTRAINT_OBTYPE_BONE);
642 break;
643 }
644 }
645 }
646 }
647 else {
648 test_constraint(bmain, ob, nullptr, con, CONSTRAINT_OBTYPE_OBJECT);
649 }
650}
651
654/* ------------------------------------------------------------------- */
658#define EDIT_CONSTRAINT_OWNER_OBJECT 0
659#define EDIT_CONSTRAINT_OWNER_BONE 1
660
663 "OBJECT",
664 0,
665 "Object",
666 "Edit a constraint on the active object"},
667 {EDIT_CONSTRAINT_OWNER_BONE, "BONE", 0, "Bone", "Edit a constraint on the active bone"},
668 {0, nullptr, 0, nullptr, nullptr},
669};
670
672 StructRNA *rna_type,
673 const bool is_liboverride_allowed)
674{
675 PointerRNA ptr = CTX_data_pointer_get_type(C, "constraint", rna_type);
677 bConstraint *con = static_cast<bConstraint *>(ptr.data);
678
680 return false;
681 }
682
683 if (ptr.owner_id != nullptr && !ID_IS_EDITABLE(ptr.owner_id)) {
684 CTX_wm_operator_poll_msg_set(C, "Cannot edit library data");
685 return false;
686 }
687
688 if (!is_liboverride_allowed && BKE_constraint_is_nonlocal_in_liboverride(ob, con)) {
690 C, "Cannot edit constraints coming from linked data in a library override");
691 return false;
692 }
693
694 return true;
695}
696
698{
699 return edit_constraint_poll_generic(C, &RNA_Constraint, false);
700}
701
702/* Used by operators performing actions allowed also on constraints from the overridden linked
703 * object (not only from added 'local' ones). */
705{
706 return edit_constraint_poll_generic(C, &RNA_Constraint, true);
707}
708
710{
711 PropertyRNA *prop;
712 prop = RNA_def_string(
713 ot->srna, "constraint", nullptr, MAX_NAME, "Constraint", "Name of the constraint to edit");
715 prop = RNA_def_enum(
716 ot->srna, "owner", constraint_owner_items, 0, "Owner", "The owner of this constraint");
718}
719
721{
723 ot->srna, "report", false, "Report", "Create a notification after the operation");
725}
726
728 wmOperator *op,
729 const wmEvent *event,
730 int *r_retval)
731{
732 PointerRNA ptr = CTX_data_pointer_get_type(C, "constraint", &RNA_Constraint);
734 bConstraint *con;
735 ListBase *list;
736
737 if (RNA_struct_property_is_set(op->ptr, "constraint") &&
738 RNA_struct_property_is_set(op->ptr, "owner"))
739 {
740 return true;
741 }
742
743 if (ptr.data) {
744 con = static_cast<bConstraint *>(ptr.data);
745 RNA_string_set(op->ptr, "constraint", con->name);
746
747 list = constraint_list_from_constraint(ob, con, nullptr);
748
749 if (&ob->constraints == list) {
751 }
752 else {
754 }
755
756 return true;
757 }
758
759 /* Check the custom data of panels under the mouse for a modifier. */
760 if (event != nullptr) {
762
763 if (!(panel_ptr == nullptr || RNA_pointer_is_null(panel_ptr))) {
764 if (RNA_struct_is_a(panel_ptr->type, &RNA_Constraint)) {
765 con = static_cast<bConstraint *>(panel_ptr->data);
766 RNA_string_set(op->ptr, "constraint", con->name);
767 list = constraint_list_from_constraint(ob, con, nullptr);
768 RNA_enum_set(op->ptr,
769 "owner",
772
773 return true;
774 }
775
776 BLI_assert(r_retval != nullptr); /* We need the return value in this case. */
777 if (r_retval != nullptr) {
779 }
780 return false;
781 }
782 }
783
784 return false;
785}
786
788{
789 char constraint_name[MAX_NAME];
790 int owner = RNA_enum_get(op->ptr, "owner");
791 bConstraint *con;
792 ListBase *list = nullptr;
793
794 RNA_string_get(op->ptr, "constraint", constraint_name);
795
796 if (owner == EDIT_CONSTRAINT_OWNER_BONE) {
797 list = pose_constraint_list(C);
798 if (!list) {
799 return nullptr;
800 }
801 }
802 else {
803 list = &ob->constraints;
804 }
805
806 con = BKE_constraints_find_name(list, constraint_name);
807#if 0
808 if (G.debug & G_DEBUG) {
809 printf("constraint found = %p, %s\n", (void *)con, (con) ? con->name : "<Not found>");
810 }
811#endif
812
813 if (con && (type != 0) && (con->type != type)) {
814 con = nullptr;
815 }
816
817 return con;
818}
819
822/* ------------------------------------------------------------------- */
829{
830 Main *bmain = CTX_data_main(C);
833 bStretchToConstraint *data = (con) ? (bStretchToConstraint *)con->data : nullptr;
834
835 /* despite 3 layers of checks, we may still not be able to find a constraint */
836 if (data == nullptr) {
837 return OPERATOR_CANCELLED;
838 }
839
840 /* just set original length to 0.0, which will cause a reset on next recalc */
841 data->orglength = 0.0f;
842 constraint_update(bmain, ob);
843
845 return OPERATOR_FINISHED;
846}
847
848static int stretchto_reset_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/)
849{
850 if (edit_constraint_invoke_properties(C, op, nullptr, nullptr)) {
851 return stretchto_reset_exec(C, op);
852 }
853 return OPERATOR_CANCELLED;
854}
855
857{
858 /* identifiers */
859 ot->name = "Reset Original Length";
860 ot->idname = "CONSTRAINT_OT_stretchto_reset";
861 ot->description = "Reset original length of bone for Stretch To Constraint";
862
863 /* callbacks */
867
868 /* flags */
870
871 /* properties */
873}
874
877/* ------------------------------------------------------------------- */
884{
885 Main *bmain = CTX_data_main(C);
888 bDistLimitConstraint *data = (con) ? (bDistLimitConstraint *)con->data : nullptr;
889
890 /* despite 3 layers of checks, we may still not be able to find a constraint */
891 if (data == nullptr) {
892 return OPERATOR_CANCELLED;
893 }
894
895 /* just set original length to 0.0, which will cause a reset on next recalc */
896 data->dist = 0.0f;
897 constraint_update(bmain, ob);
898
900 return OPERATOR_FINISHED;
901}
902
903static int limitdistance_reset_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/)
904{
905 if (edit_constraint_invoke_properties(C, op, nullptr, nullptr)) {
906 return limitdistance_reset_exec(C, op);
907 }
908 return OPERATOR_CANCELLED;
909}
910
912{
913 /* identifiers */
914 ot->name = "Reset Distance";
915 ot->idname = "CONSTRAINT_OT_limitdistance_reset";
916 ot->description = "Reset limiting distance for Limit Distance Constraint";
917
918 /* callbacks */
922
923 /* flags */
925
926 /* properties */
928}
929
932/* ------------------------------------------------------------------- */
936/* Force evaluation so that the 'set inverse' flag is handled.
937 * No-op when the constraint is enabled, as in such cases the evaluation will happen anyway.
938 */
940{
941 if ((con->flag & (CONSTRAINT_DISABLE | CONSTRAINT_OFF)) == 0) {
942 return;
943 }
944
947
948 short flag_backup = con->flag;
951 con->flag = flag_backup;
952}
953
954/* ChildOf Constraint - set inverse callback */
956{
957 Main *bmain = CTX_data_main(C);
960 bChildOfConstraint *data = (con) ? (bChildOfConstraint *)con->data : nullptr;
961
962 /* despite 3 layers of checks, we may still not be able to find a constraint */
963 if (data == nullptr) {
964 printf("DEBUG: Child-Of Set Inverse - object = '%s'\n", (ob) ? ob->id.name + 2 : "<None>");
965 BKE_report(op->reports, RPT_ERROR, "Could not find constraint data for Child-Of Set Inverse");
966 return OPERATOR_CANCELLED;
967 }
968
969 /* Set a flag to request recalculation on next update. */
970 data->flag |= CHILDOF_SET_INVERSE;
971
972 /* Force constraint to run, it will perform the recalculation. */
974
975 constraint_update(bmain, ob);
977
978 return OPERATOR_FINISHED;
979}
980
981static int childof_set_inverse_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/)
982{
983 if (edit_constraint_invoke_properties(C, op, nullptr, nullptr)) {
984 return childof_set_inverse_exec(C, op);
985 }
986 return OPERATOR_CANCELLED;
987}
988
990{
991 /* identifiers */
992 ot->name = "Set Inverse";
993 ot->idname = "CONSTRAINT_OT_childof_set_inverse";
994 ot->description = "Set inverse correction for Child Of constraint";
995
996 /* callbacks */
1000
1001 /* flags */
1003
1004 /* properties */
1006}
1007
1008/* ChildOf Constraint - clear inverse callback */
1010{
1011 Main *bmain = CTX_data_main(C);
1014 bChildOfConstraint *data = (con) ? (bChildOfConstraint *)con->data : nullptr;
1015
1016 if (data == nullptr) {
1017 BKE_report(op->reports, RPT_ERROR, "Child Of constraint not found");
1018 return OPERATOR_CANCELLED;
1019 }
1020
1021 /* simply clear the matrix */
1022 unit_m4(data->invmat);
1023
1024 constraint_update(bmain, ob);
1026
1027 return OPERATOR_FINISHED;
1028}
1029
1030static int childof_clear_inverse_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/)
1031{
1032 if (edit_constraint_invoke_properties(C, op, nullptr, nullptr)) {
1033 return childof_clear_inverse_exec(C, op);
1034 }
1035 return OPERATOR_CANCELLED;
1036}
1037
1039{
1040 /* identifiers */
1041 ot->name = "Clear Inverse";
1042 ot->idname = "CONSTRAINT_OT_childof_clear_inverse";
1043 ot->description = "Clear inverse correction for Child Of constraint";
1044
1045 /* callbacks */
1049
1050 /* flags */
1052
1053 /* properties */
1055}
1056
1059/* ------------------------------------------------------------------- */
1064{
1065 Main *bmain = CTX_data_main(C);
1068 bFollowPathConstraint *data = (con) ? (bFollowPathConstraint *)con->data : nullptr;
1069
1070 bAction *act = nullptr;
1071 FCurve *fcu = nullptr;
1072 int sfra = RNA_int_get(op->ptr, "frame_start");
1073 int len = RNA_int_get(op->ptr, "length");
1074 float standardRange = 1.0;
1075
1076 /* nearly impossible sanity check */
1077 if (data == nullptr) {
1078 BKE_report(op->reports, RPT_ERROR, "Follow Path constraint not found");
1079 return OPERATOR_CANCELLED;
1080 }
1081
1082 /* add F-Curve as appropriate */
1083 if (data->tar) {
1084 Curve *cu = (Curve *)data->tar->data;
1085
1086 if (ELEM(nullptr, cu->adt, cu->adt->action) ||
1087 (animrig::fcurve_find_in_assigned_slot(*cu->adt, {"eval_time", 0}) == nullptr))
1088 {
1089 /* create F-Curve for path animation */
1090 act = animrig::id_action_ensure(bmain, &cu->id);
1091 PointerRNA id_ptr = RNA_id_pointer_create(&cu->id);
1092 fcu = animrig::action_fcurve_ensure(bmain, act, nullptr, &id_ptr, {"eval_time", 0});
1093
1094 /* standard vertical range - 1:1 = 100 frames */
1095 standardRange = 100.0f;
1096 }
1097 else {
1098 /* path anim exists already - abort for now as this may well be what was intended */
1099 BKE_report(op->reports, RPT_WARNING, "Path is already animated");
1100 return OPERATOR_CANCELLED;
1101 }
1102 }
1103 else {
1104 /* animate constraint's "fixed offset" */
1105 PropertyRNA *prop;
1106
1107 /* get RNA pointer to constraint's "offset_factor" property - to build RNA path */
1108 PointerRNA ptr = RNA_pointer_create(&ob->id, &RNA_FollowPathConstraint, con);
1109 prop = RNA_struct_find_property(&ptr, "offset_factor");
1110
1111 const std::optional<std::string> path = RNA_path_from_ID_to_property(&ptr, prop);
1112 BLI_assert(path.has_value());
1113
1114 /* create F-Curve for constraint */
1115 act = animrig::id_action_ensure(bmain, &ob->id);
1116 PointerRNA id_ptr = RNA_id_pointer_create(&ob->id);
1117 fcu = animrig::action_fcurve_ensure(bmain, act, nullptr, &id_ptr, {path->c_str(), 0});
1118
1119 /* standard vertical range - 0.0 to 1.0 */
1120 standardRange = 1.0f;
1121
1122 /* enable "Use Fixed Position" so that animating this has effect */
1123 data->followflag |= FOLLOWPATH_STATIC;
1124 }
1125
1126 /* setup dummy 'generator' modifier here to get 1-1 correspondence still working
1127 * and define basic slope of this curve based on the properties
1128 */
1129 if (!fcu->bezt && !fcu->fpt && !fcu->modifiers.first) {
1131 FMod_Generator *gen = static_cast<FMod_Generator *>(fcm->data);
1132
1133 /* Assume that we have the following equation:
1134 * y = Ax + B
1135 * 1 0 <-- coefficients array indices
1136 */
1137 float A = standardRange / float(len);
1138 float B = float(-sfra) * A;
1139
1140 gen->coefficients[1] = A;
1141 gen->coefficients[0] = B;
1142 }
1143
1144 /* updates... */
1146 return OPERATOR_FINISHED;
1147}
1148
1149static int followpath_path_animate_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/)
1150{
1151 /* hook up invoke properties for figuring out which constraint we're dealing with */
1152 if (edit_constraint_invoke_properties(C, op, nullptr, nullptr)) {
1153 return followpath_path_animate_exec(C, op);
1154 }
1155 return OPERATOR_CANCELLED;
1156}
1157
1159{
1160 /* identifiers */
1161 ot->name = "Auto Animate Path";
1162 ot->idname = "CONSTRAINT_OT_followpath_path_animate";
1163 ot->description =
1164 "Add default animation for path used by constraint if it isn't animated already";
1165
1166 /* callbacks */
1170
1171 /* flags */
1173
1174 /* props */
1177 "frame_start",
1178 1,
1179 MINAFRAME,
1180 MAXFRAME,
1181 "Start Frame",
1182 "First frame of path animation",
1183 MINAFRAME,
1184 MAXFRAME);
1186 "length",
1187 100,
1188 0,
1189 MAXFRAME,
1190 "Length",
1191 "Number of frames that path animation should take",
1192 0,
1193 MAXFRAME);
1194}
1195
1198/* ------------------------------------------------------------------- */
1203{
1204 Main *bmain = CTX_data_main(C);
1207 bObjectSolverConstraint *data = (con) ? (bObjectSolverConstraint *)con->data : nullptr;
1208
1209 /* despite 3 layers of checks, we may still not be able to find a constraint */
1210 if (data == nullptr) {
1211 printf("DEBUG: ObjectSolver Set Inverse - object = '%s'\n", (ob) ? ob->id.name + 2 : "<None>");
1212 BKE_report(
1213 op->reports, RPT_ERROR, "Could not find constraint data for ObjectSolver Set Inverse");
1214 return OPERATOR_CANCELLED;
1215 }
1216
1217 /* Set a flag to request recalculation on next update. */
1218 data->flag |= OBJECTSOLVER_SET_INVERSE;
1219
1220 /* Force constraint to run, it will perform the recalculation. */
1222
1223 constraint_update(bmain, ob);
1225
1226 return OPERATOR_FINISHED;
1227}
1228
1229static int objectsolver_set_inverse_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/)
1230{
1231 if (edit_constraint_invoke_properties(C, op, nullptr, nullptr)) {
1232 return objectsolver_set_inverse_exec(C, op);
1233 }
1234 return OPERATOR_CANCELLED;
1235}
1236
1238{
1239 /* identifiers */
1240 ot->name = "Set Inverse";
1241 ot->idname = "CONSTRAINT_OT_objectsolver_set_inverse";
1242 ot->description = "Set inverse correction for Object Solver constraint";
1243
1244 /* callbacks */
1248
1249 /* flags */
1251
1252 /* properties */
1254}
1255
1258/* ------------------------------------------------------------------- */
1263{
1264 Main *bmain = CTX_data_main(C);
1267 bObjectSolverConstraint *data = (con) ? (bObjectSolverConstraint *)con->data : nullptr;
1268
1269 if (data == nullptr) {
1270 BKE_report(op->reports, RPT_ERROR, "Child Of constraint not found");
1271 return OPERATOR_CANCELLED;
1272 }
1273
1274 /* simply clear the matrix */
1275 unit_m4(data->invmat);
1276
1277 constraint_update(bmain, ob);
1279
1280 return OPERATOR_FINISHED;
1281}
1282
1284 wmOperator *op,
1285 const wmEvent * /*event*/)
1286{
1287 if (edit_constraint_invoke_properties(C, op, nullptr, nullptr)) {
1288 return objectsolver_clear_inverse_exec(C, op);
1289 }
1290 return OPERATOR_CANCELLED;
1291}
1292
1294{
1295 /* identifiers */
1296 ot->name = "Clear Inverse";
1297 ot->idname = "CONSTRAINT_OT_objectsolver_clear_inverse";
1298 ot->description = "Clear inverse correction for Object Solver constraint";
1299
1300 /* callbacks */
1304
1305 /* flags */
1307
1308 /* properties */
1310}
1311
1314/* ------------------------------------------------------------------- */
1319{
1320 ListBase *lb = constraint_list_from_constraint(ob, con, nullptr);
1321
1322 /* lets be nice and escape if its active already */
1323 /* NOTE: this assumes that the stack doesn't have other active ones set... */
1324 if ((lb && con) && (con->flag & CONSTRAINT_ACTIVE)) {
1325 return;
1326 }
1327
1329}
1330
1332{
1333 if (ob->pose) {
1335 }
1336
1337 object_test_constraints(bmain, ob);
1338
1339 if (ob->type == OB_ARMATURE) {
1341 }
1342 else {
1344 }
1345}
1346
1347static void object_pose_tag_update(Main *bmain, Object *ob)
1348{
1349 BKE_pose_tag_recalc(bmain, ob->pose); /* Checks & sort pose channels. */
1350}
1351
1353{
1354 constraint_update(bmain, ob);
1355
1356 if (ob->pose) {
1357 object_pose_tag_update(bmain, ob);
1358 }
1360}
1361
1363{
1364 if (ob->pose) {
1366 }
1367
1368 if (con) {
1369 object_test_constraint(bmain, ob, con);
1370 }
1371
1372 if (ob->type == OB_ARMATURE) {
1374 }
1375 else {
1377 }
1378
1379 /* Do Copy-on-Write tag here too, otherwise constraint
1380 * influence/mute buttons in UI have no effect
1381 */
1383}
1384
1386{
1387 constraint_tag_update(bmain, ob, con);
1388
1389 if (ob->pose) {
1390 object_pose_tag_update(bmain, ob);
1391 }
1393}
1394
1395bool constraint_move_to_index(Object *ob, bConstraint *con, const int index)
1396{
1397 BLI_assert(con != nullptr);
1398 BLI_assert(index >= 0);
1399
1400 ListBase *conlist = constraint_list_from_constraint(ob, con, nullptr);
1401 int current_index = BLI_findindex(conlist, con);
1402 BLI_assert(current_index >= 0);
1403
1404 BLI_listbase_link_move(conlist, con, index - current_index);
1405
1407
1408 return true;
1409}
1410
1411void constraint_link(Main *bmain, Object *ob_dst, ListBase *dst, ListBase *src)
1412{
1414 BKE_constraints_copy(dst, src, true);
1415 LISTBASE_FOREACH (bConstraint *, con, dst) {
1416 constraint_dependency_tag_update(bmain, ob_dst, con);
1417 }
1419}
1420
1422{
1423 bConstraint *copy_con = BKE_constraint_copy_for_object(ob_dst, con);
1425
1426 constraint_dependency_tag_update(bmain, ob_dst, con);
1428}
1429
1431{
1432 bConstraint *copy_con = BKE_constraint_copy_for_pose(ob_dst, pchan, con);
1434
1435 constraint_dependency_tag_update(bmain, ob_dst, con);
1437}
1438
1441/* ------------------------------------------------------------------- */
1446{
1447 Main *bmain = CTX_data_main(C);
1449 bConstraint *con = edit_constraint_property_get(C, op, ob, 0);
1450
1451 if (con == nullptr) {
1452 return OPERATOR_CANCELLED;
1453 }
1454
1455 ListBase *lb = constraint_list_from_constraint(ob, con, nullptr);
1456
1457 /* Store name temporarily for report. */
1458 char name[MAX_NAME];
1459 STRNCPY(name, con->name);
1460
1461 /* free the constraint */
1462 if (BKE_constraint_remove_ex(lb, ob, con)) {
1463 /* Needed to set the flags on pose-bones correctly. */
1464 constraint_update(bmain, ob);
1465
1466 /* relations */
1468
1469 /* notifiers */
1471
1472 if (RNA_boolean_get(op->ptr, "report")) {
1473 BKE_reportf(op->reports, RPT_INFO, "Removed constraint: %s", name);
1474 }
1475
1476 return OPERATOR_FINISHED;
1477 }
1478 /* couldn't remove due to some invalid data */
1479 return OPERATOR_CANCELLED;
1480}
1481
1482static int constraint_delete_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1483{
1484 int retval;
1485 if (!edit_constraint_invoke_properties(C, op, event, &retval)) {
1486 return OPERATOR_CANCELLED;
1487 }
1488 return constraint_delete_exec(C, op);
1489}
1490
1492{
1493 /* identifiers */
1494 ot->name = "Delete Constraint";
1495 ot->idname = "CONSTRAINT_OT_delete";
1496 ot->description = "Remove constraint from constraint stack";
1497
1498 /* callbacks */
1502
1503 /* flags */
1507}
1508
1511/* ------------------------------------------------------------------- */
1516{
1517 Scene *scene = CTX_data_scene(C);
1519 Main *bmain = CTX_data_main(C);
1521 bConstraint *con = edit_constraint_property_get(C, op, ob, 0);
1522
1523 if (con == nullptr) {
1524 return OPERATOR_CANCELLED;
1525 }
1526
1527 bPoseChannel *pchan;
1529
1530 /* Store name temporarily for report. */
1531 char name[MAX_NAME];
1532 STRNCPY(name, con->name);
1533 const bool is_first_constraint = con != constraints->first;
1534
1535 /* Copy the constraint. */
1536 bool success;
1537 if (pchan) {
1539 depsgraph, scene, constraints, ob, con, pchan);
1540 }
1541 else {
1543 }
1544
1545 if (!success) {
1546 /* Couldn't remove due to some invalid data. */
1547 return OPERATOR_CANCELLED;
1548 }
1549
1550 /* Update for any children that may get moved. */
1552
1553 /* Needed to set the flags on pose-bones correctly. */
1554 constraint_update(bmain, ob);
1555
1558 if (pchan) {
1560 }
1561 else {
1563 }
1564
1565 if (RNA_boolean_get(op->ptr, "report")) {
1566 if (is_first_constraint) {
1567 BKE_report(op->reports,
1568 RPT_INFO,
1569 "Applied constraint was not first, result may not be as expected");
1570 }
1571 else {
1572 /* Only add this report if the operator didn't cause another one. The purpose here is
1573 * to alert that something happened, and the previous report will do that anyway. */
1574 BKE_reportf(op->reports, RPT_INFO, "Applied constraint: %s", name);
1575 }
1576 }
1577
1578 return OPERATOR_FINISHED;
1579}
1580
1581static int constraint_apply_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1582{
1583 int retval;
1584 if (!edit_constraint_invoke_properties(C, op, event, &retval)) {
1585 return OPERATOR_CANCELLED;
1586 }
1587 return constraint_apply_exec(C, op);
1588}
1589
1591{
1592 /* identifiers */
1593 ot->name = "Apply Constraint";
1594 ot->idname = "CONSTRAINT_OT_apply";
1595 ot->description = "Apply constraint and remove from the stack";
1596
1597 /* callbacks */
1601
1602 /* flags */
1606}
1607
1610/* ------------------------------------------------------------------- */
1615{
1616 Main *bmain = CTX_data_main(C);
1618 bConstraint *con = edit_constraint_property_get(C, op, ob, 0);
1619
1620 if (con == nullptr) {
1621 return OPERATOR_CANCELLED;
1622 }
1623
1624 bPoseChannel *pchan;
1626
1627 /* Store name temporarily for report. */
1628 char name[MAX_NAME];
1629 STRNCPY(name, con->name);
1630
1631 /* Copy the constraint. */
1632 bConstraint *copy_con;
1633 if (pchan) {
1634 copy_con = BKE_constraint_copy_for_pose(ob, pchan, con);
1635 }
1636 else {
1637 copy_con = BKE_constraint_copy_for_object(ob, con);
1638 }
1639
1640 if (!copy_con) {
1641 /* Couldn't remove due to some invalid data. */
1642 return OPERATOR_CANCELLED;
1643 }
1645
1646 /* Move constraint to correct position. */
1647 const int new_index = BLI_findindex(constraints, con) + 1;
1648 const int current_index = BLI_findindex(constraints, copy_con);
1649 BLI_assert(new_index >= 0);
1650 BLI_assert(current_index >= 0);
1651 BLI_listbase_link_move(constraints, copy_con, new_index - current_index);
1652
1653 /* Needed to set the flags on pose-bones correctly. */
1654 constraint_update(bmain, ob);
1655
1658
1659 if (RNA_boolean_get(op->ptr, "report")) {
1660 BKE_reportf(op->reports, RPT_INFO, "Copied constraint: %s", name);
1661 }
1662
1663 return OPERATOR_FINISHED;
1664}
1665
1666static int constraint_copy_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1667{
1668 int retval;
1669 if (!edit_constraint_invoke_properties(C, op, event, &retval)) {
1670 return OPERATOR_CANCELLED;
1671 }
1672 return constraint_copy_exec(C, op);
1673}
1674
1676{
1677 /* identifiers */
1678 ot->name = "Duplicate Constraint";
1679 ot->idname = "CONSTRAINT_OT_copy";
1680 ot->description = "Duplicate constraint at the same position in the stack";
1681
1682 /* callbacks */
1686
1687 /* flags */
1691}
1692
1695/* ------------------------------------------------------------------- */
1700{
1701 Main *bmain = CTX_data_main(C);
1702 Object *obact = context_active_object(C);
1703 bConstraint *con = edit_constraint_property_get(C, op, obact, 0);
1704
1705 if (con == nullptr) {
1706 return OPERATOR_CANCELLED;
1707 }
1708
1709 bPoseChannel *pchan;
1710 constraint_list_from_constraint(obact, con, &pchan);
1711
1712 if (pchan) {
1713 /* Don't do anything if bone doesn't exist or doesn't have any constraints. */
1714 if (pchan->constraints.first == nullptr) {
1715 BKE_report(op->reports, RPT_ERROR, "No constraints for copying");
1716 return OPERATOR_CANCELLED;
1717 }
1718
1719 Object *prev_ob = nullptr;
1720
1721 /* Copy all constraints from active pose-bone to all selected pose-bones. */
1722 CTX_DATA_BEGIN_WITH_ID (C, bPoseChannel *, chan, selected_pose_bones, Object *, ob) {
1723 /* If we're not handling the object we're copying from, copy all constraints over. */
1724 if (pchan == chan) {
1725 continue;
1726 }
1727
1728 bConstraint *copy_con = BKE_constraint_copy_for_pose(ob, chan, con);
1730
1731 /* Update flags (need to add here, not just copy). */
1732 chan->constflag |= pchan->constflag;
1733
1734 if (prev_ob == ob) {
1735 continue;
1736 }
1737
1738 BKE_pose_tag_recalc(bmain, ob->pose);
1740 prev_ob = ob;
1741 }
1743 }
1744 else {
1745 /* Copy all constraints from active object to all selected objects. */
1746 CTX_DATA_BEGIN (C, Object *, ob, selected_editable_objects) {
1747 /* If we're not handling the object we're copying from, copy all constraints over. */
1748 if (obact == ob) {
1749 continue;
1750 }
1751
1752 bConstraint *copy_con = BKE_constraint_copy_for_object(ob, con);
1754
1756 }
1758 }
1759
1760 /* Force depsgraph to get recalculated since new relationships added. */
1762
1764
1765 return OPERATOR_FINISHED;
1766}
1767
1769{
1770 int retval;
1771 if (!edit_constraint_invoke_properties(C, op, event, &retval)) {
1772 return retval;
1773 }
1775}
1776
1778{
1779 PointerRNA ptr = CTX_data_pointer_get_type(C, "constraint", &RNA_Constraint);
1781 bConstraint *con = static_cast<bConstraint *>(ptr.data);
1782 bPoseChannel *pchan;
1783 constraint_list_from_constraint(obact, con, &pchan);
1784
1785 if (pchan) {
1786 bool found = false;
1787 CTX_DATA_BEGIN_WITH_ID (C, bPoseChannel *, chan, selected_pose_bones, Object *, ob) {
1788 UNUSED_VARS(ob);
1789 if (pchan != chan) {
1792 found = true;
1793 break;
1794 }
1795 }
1797 if (found) {
1798 return true;
1799 }
1800
1801 CTX_wm_operator_poll_msg_set(C, "No other bones are selected");
1802 return false;
1803 }
1804
1805 if (!obact) {
1806 CTX_wm_operator_poll_msg_set(C, "No selected object to copy from");
1807 return false;
1808 }
1809
1810 bool found = false;
1811 CTX_DATA_BEGIN (C, Object *, ob, selected_objects) {
1812 if (ob != obact) {
1815 found = true;
1816 break;
1817 }
1818 }
1820 if (found) {
1821 return true;
1822 }
1823
1824 CTX_wm_operator_poll_msg_set(C, "No other objects are selected");
1825 return false;
1826}
1827
1829{
1830 /* identifiers */
1831 ot->name = "Copy Constraint To Selected";
1832 ot->idname = "CONSTRAINT_OT_copy_to_selected";
1833 ot->description = "Copy constraint to other selected objects/bones";
1834
1835 /* api callbacks */
1839
1840 /* flags */
1843}
1844
1847/* ------------------------------------------------------------------- */
1852{
1854 bConstraint *con = edit_constraint_property_get(C, op, ob, 0);
1855
1856 if (con && con->next) {
1857 ListBase *conlist = constraint_list_from_constraint(ob, con, nullptr);
1858 bConstraint *nextCon = con->next;
1859
1860 /* insert the nominated constraint after the one that used to be after it */
1861 BLI_remlink(conlist, con);
1862 BLI_insertlinkafter(conlist, nextCon, con);
1863
1866
1867 return OPERATOR_FINISHED;
1868 }
1869
1870 return OPERATOR_CANCELLED;
1871}
1872
1874{
1875 int retval;
1876 if (edit_constraint_invoke_properties(C, op, event, &retval)) {
1877 return constraint_move_down_exec(C, op);
1878 }
1879 return retval;
1880}
1881
1883{
1884 /* identifiers */
1885 ot->name = "Move Constraint Down";
1886 ot->idname = "CONSTRAINT_OT_move_down";
1887 ot->description = "Move constraint down in constraint stack";
1888
1889 /* callbacks */
1893
1894 /* flags */
1896
1897 /* properties */
1899}
1900
1903/* ------------------------------------------------------------------- */
1908{
1910 bConstraint *con = edit_constraint_property_get(C, op, ob, 0);
1911
1912 if (con && con->prev) {
1913 ListBase *conlist = constraint_list_from_constraint(ob, con, nullptr);
1914 bConstraint *prevCon = con->prev;
1915
1916 /* insert the nominated constraint before the one that used to be before it */
1917 BLI_remlink(conlist, con);
1918 BLI_insertlinkbefore(conlist, prevCon, con);
1919
1922
1923 return OPERATOR_FINISHED;
1924 }
1925
1926 return OPERATOR_CANCELLED;
1927}
1928
1929static int constraint_move_up_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1930{
1931 int retval;
1932 if (edit_constraint_invoke_properties(C, op, event, &retval)) {
1933 return constraint_move_up_exec(C, op);
1934 }
1935 return retval;
1936}
1937
1939{
1940 /* identifiers */
1941 ot->name = "Move Constraint Up";
1942 ot->idname = "CONSTRAINT_OT_move_up";
1943 ot->description = "Move constraint up in constraint stack";
1944
1945 /* callbacks */
1949
1950 /* flags */
1953}
1954
1957/* ------------------------------------------------------------------- */
1962{
1964 bConstraint *con = edit_constraint_property_get(C, op, ob, 0);
1965
1966 int new_index = RNA_int_get(op->ptr, "index");
1967 if (new_index < 0) {
1968 new_index = 0;
1969 }
1970
1971 if (con) {
1972 constraint_move_to_index(ob, con, new_index);
1973
1975
1976 return OPERATOR_FINISHED;
1977 }
1978
1979 return OPERATOR_CANCELLED;
1980}
1981
1983{
1984 int retval;
1985 if (edit_constraint_invoke_properties(C, op, event, &retval)) {
1986 return constraint_move_to_index_exec(C, op);
1987 }
1988 return retval;
1989}
1990
1992{
1993 /* identifiers */
1994 ot->name = "Move Constraint to Index";
1995 ot->idname = "CONSTRAINT_OT_move_to_index";
1996 ot->description =
1997 "Change the constraint's position in the list so it evaluates after the set number of "
1998 "others";
1999
2000 /* callbacks */
2004
2005 /* flags */
2009 "index",
2010 0,
2011 0,
2012 INT_MAX,
2013 "Index",
2014 "The index to move the constraint to",
2015 0,
2016 INT_MAX);
2017}
2018
2021/* ------------------------------------------------------------------- */
2026{
2027 Main *bmain = CTX_data_main(C);
2028 Object *prev_ob = nullptr;
2029
2030 /* free constraints for all selected bones */
2031 CTX_DATA_BEGIN_WITH_ID (C, bPoseChannel *, pchan, selected_pose_bones, Object *, ob) {
2032 BKE_constraints_free(&pchan->constraints);
2033 pchan->constflag = 0;
2034
2035 if (prev_ob != ob) {
2038 prev_ob = ob;
2039 }
2040 }
2042
2043 /* force depsgraph to get recalculated since relationships removed */
2045
2046 /* NOTE: calling BIK_clear_data() isn't needed here. */
2047
2048 return OPERATOR_FINISHED;
2049}
2050
2052{
2053 /* identifiers */
2054 ot->name = "Clear Pose Constraints";
2055 ot->idname = "POSE_OT_constraints_clear";
2056 ot->description = "Clear all constraints from the selected bones";
2057
2058 /* callbacks */
2060 /* XXX: do we want to ensure there are selected bones too? */
2062}
2063
2065{
2066 Main *bmain = CTX_data_main(C);
2067
2068 /* do freeing */
2069 CTX_DATA_BEGIN (C, Object *, ob, selected_editable_objects) {
2070 BKE_constraints_free(&ob->constraints);
2072 }
2074
2075 /* force depsgraph to get recalculated since relationships removed */
2077
2078 /* do updates */
2080
2081 return OPERATOR_FINISHED;
2082}
2083
2086/* ------------------------------------------------------------------- */
2091{
2092 /* identifiers */
2093 ot->name = "Clear Object Constraints";
2094 ot->idname = "OBJECT_OT_constraints_clear";
2095 ot->description = "Clear all constraints from the selected objects";
2096
2097 /* callbacks */
2100}
2101
2104/* ------------------------------------------------------------------- */
2109{
2110 Main *bmain = CTX_data_main(C);
2112
2113 /* don't do anything if bone doesn't exist or doesn't have any constraints */
2114 if (ELEM(nullptr, pchan, pchan->constraints.first)) {
2115 BKE_report(op->reports, RPT_ERROR, "No active bone with constraints for copying");
2116 return OPERATOR_CANCELLED;
2117 }
2118
2119 Object *prev_ob = nullptr;
2120
2121 /* Copy all constraints from active pose-bone to all selected pose-bones. */
2122 CTX_DATA_BEGIN_WITH_ID (C, bPoseChannel *, chan, selected_pose_bones, Object *, ob) {
2123 /* if we're not handling the object we're copying from, copy all constraints over */
2124 if (pchan != chan) {
2125 BKE_constraints_copy(&chan->constraints, &pchan->constraints, true);
2126 /* update flags (need to add here, not just copy) */
2127 chan->constflag |= pchan->constflag;
2128
2129 if (prev_ob != ob) {
2130 BKE_pose_tag_recalc(bmain, ob->pose);
2132 prev_ob = ob;
2133 }
2134 }
2135 }
2137
2138 /* force depsgraph to get recalculated since new relationships added */
2140
2142
2143 return OPERATOR_FINISHED;
2144}
2145
2147{
2148 /* identifiers */
2149 ot->name = "Copy Constraints to Selected Bones";
2150 ot->idname = "POSE_OT_constraints_copy";
2151 ot->description = "Copy constraints to other selected bones";
2152
2153 /* api callbacks */
2156
2157 /* flags */
2159}
2160
2163/* ------------------------------------------------------------------- */
2168{
2169 Main *bmain = CTX_data_main(C);
2170 Object *obact = context_active_object(C);
2171
2172 /* copy all constraints from active object to all selected objects */
2173 CTX_DATA_BEGIN (C, Object *, ob, selected_editable_objects) {
2174 /* if we're not handling the object we're copying from, copy all constraints over */
2175 if (obact != ob) {
2176 BKE_constraints_copy(&ob->constraints, &obact->constraints, true);
2178 }
2179 }
2181
2182 /* force depsgraph to get recalculated since new relationships added */
2184
2185 /* notifiers for updates */
2187
2188 return OPERATOR_FINISHED;
2189}
2190
2192{
2193 /* identifiers */
2194 ot->name = "Copy Constraints to Selected Objects";
2195 ot->idname = "OBJECT_OT_constraints_copy";
2196 ot->description = "Copy constraints to other selected objects";
2197
2198 /* api callbacks */
2201
2202 /* flags */
2204}
2205
2208/* ------------------------------------------------------------------- */
2212/* get the Object and/or PoseChannel to use as target */
2214 bContext *C, int con_type, Object **tar_ob, bPoseChannel **tar_pchan, bool add)
2215{
2216 Object *obact = context_active_object(C);
2218 bool only_curve = false, only_mesh = false, only_ob = false;
2219 bool found = false;
2220
2221 /* clear tar_ob and tar_pchan fields before use
2222 * - assume for now that both always exist...
2223 */
2224 *tar_ob = nullptr;
2225 *tar_pchan = nullptr;
2226
2227 /* check if constraint type doesn't requires a target
2228 * - if so, no need to get any targets
2229 */
2230 switch (con_type) {
2231 /* no-target constraints --------------------------- */
2232 /* null constraint - shouldn't even be added! */
2234 /* limit constraints - no targets needed */
2239 return false;
2240
2241 /* restricted target-type constraints -------------- */
2242 /* NOTE: for these, we cannot try to add a target object if no valid ones are found,
2243 * since that doesn't work */
2244 /* curve-based constraints - set the only_curve and only_ob flags */
2248 only_curve = true;
2249 only_ob = true;
2250 add = false;
2251 break;
2252
2253 /* mesh only? */
2255 only_mesh = true;
2256 only_ob = true;
2257 add = false;
2258 break;
2259 }
2260
2261 /* if the active Object is Armature, and we can search for bones, do so... */
2262 if ((obact->type == OB_ARMATURE) && (only_ob == false)) {
2263 /* search in list of selected Pose-Channels for target */
2264 CTX_DATA_BEGIN (C, bPoseChannel *, pchan, selected_pose_bones_from_active_object) {
2265 /* just use the first one that we encounter, as long as it is not the active one */
2266 if (pchan != pchanact) {
2267 *tar_ob = obact;
2268 *tar_pchan = pchan;
2269 found = true;
2270
2271 break;
2272 }
2273 }
2275 }
2276
2277 /* if not yet found, try selected Objects... */
2278 if (found == false) {
2279 /* search in selected objects context */
2280 CTX_DATA_BEGIN (C, Object *, ob, selected_objects) {
2281 /* just use the first object we encounter (that isn't the active object)
2282 * and which fulfills the criteria for the object-target that we've got
2283 */
2284 if (ob != obact) {
2285 /* for armatures in pose mode, look inside the armature for the active bone
2286 * so that we set up cross-armature constraints with less effort
2287 */
2288 if ((ob->type == OB_ARMATURE) && (ob->mode & OB_MODE_POSE) && (!only_curve && !only_mesh))
2289 {
2290
2291 /* Only use the object & bone if the bone is visible & selected
2292 * since we may have multiple objects in pose mode at once. */
2294 if (pchan != nullptr) {
2295 *tar_pchan = pchan;
2296 *tar_ob = ob;
2297 found = true;
2298 }
2299
2300 break;
2301 }
2302 if (((!only_curve) || (ob->type == OB_CURVES_LEGACY)) &&
2303 ((!only_mesh) || (ob->type == OB_MESH)))
2304 {
2305 /* set target */
2306 *tar_ob = ob;
2307 found = true;
2308
2309 /* perform some special operations on the target */
2310 if (only_curve) {
2311 /* Curve-Path option must be enabled for follow-path constraints to be able to work
2312 */
2313 Curve *cu = (Curve *)ob->data;
2314 cu->flag |= CU_PATH;
2315 }
2316
2317 break;
2318 }
2319 }
2320 }
2322 }
2323
2324 /* if still not found, add a new empty to act as a target (if allowed) */
2325 if ((found == false) && (add)) {
2326 Main *bmain = CTX_data_main(C);
2327 Scene *scene = CTX_data_scene(C);
2328 ViewLayer *view_layer = CTX_data_view_layer(C);
2329 BKE_view_layer_synced_ensure(scene, view_layer);
2330 Base *base = BKE_view_layer_active_base_get(view_layer);
2331 Object *obt;
2332
2333 /* add new target object */
2334 obt = BKE_object_add(bmain, scene, view_layer, OB_EMPTY, nullptr);
2335
2336 /* transform cent to global coords for loc */
2337 if (pchanact) {
2338 /* Since by default, IK targets the tip of the last bone,
2339 * use the tip of the active PoseChannel if adding a target for an IK Constraint. */
2340 if (con_type == CONSTRAINT_TYPE_KINEMATIC) {
2341 mul_v3_m4v3(obt->loc, obact->object_to_world().ptr(), pchanact->pose_tail);
2342 }
2343 else {
2344 mul_v3_m4v3(obt->loc, obact->object_to_world().ptr(), pchanact->pose_head);
2345 }
2346 }
2347 else {
2348 copy_v3_v3(obt->loc, obact->object_to_world().location());
2349 }
2350
2351 /* restore, BKE_object_add sets active */
2352 view_layer->basact = base;
2353 base_select(base, BA_SELECT);
2354
2355 /* make our new target the new object */
2356 *tar_ob = obt;
2357 found = true;
2358 }
2359
2360 /* return whether there's any target */
2361 return found;
2362}
2363
2364/* used by add constraint operators to add the constraint required */
2366 bContext *C, wmOperator *op, Object *ob, ListBase *list, int type, const bool setTarget)
2367{
2368 Main *bmain = CTX_data_main(C);
2369 bPoseChannel *pchan;
2370 bConstraint *con;
2371
2372 if (list == &ob->constraints) {
2373 pchan = nullptr;
2374 }
2375 else {
2377
2378 /* ensure not to confuse object/pose adding */
2379 if (pchan == nullptr) {
2380 BKE_report(op->reports, RPT_ERROR, "No active pose bone to add a constraint to");
2381 return OPERATOR_CANCELLED;
2382 }
2383 }
2384 /* check if constraint to be added is valid for the given constraints stack */
2385 if (type == CONSTRAINT_TYPE_NULL) {
2386 return OPERATOR_CANCELLED;
2387 }
2388
2389 /* Create a new constraint of the type required,
2390 * and add it to the active/given constraints list. */
2391 if (pchan) {
2392 con = BKE_constraint_add_for_pose(ob, pchan, nullptr, type);
2393 }
2394 else {
2395 con = BKE_constraint_add_for_object(ob, nullptr, type);
2396 }
2397
2398 /* get the first selected object/bone, and make that the target
2399 * - apart from the buttons-window add buttons, we shouldn't add in this way
2400 */
2401 if (setTarget) {
2402 Object *tar_ob = nullptr;
2403 bPoseChannel *tar_pchan = nullptr;
2404
2405 /* get the target objects, adding them as need be */
2406 if (get_new_constraint_target(C, type, &tar_ob, &tar_pchan, true)) {
2407 /* Method of setting target depends on the type of target we've got - by default,
2408 * just set the first target (distinction here is only for multiple-targeted constraints).
2409 */
2410 if (tar_pchan) {
2411 set_constraint_nth_target(con, tar_ob, tar_pchan->name, 0);
2412 }
2413 else {
2414 set_constraint_nth_target(con, tar_ob, "", 0);
2415 }
2416 }
2417 }
2418
2419 /* Do type-specific tweaking to the constraint settings. */
2420 switch (type) {
2421 case CONSTRAINT_TYPE_PYTHON: /* FIXME: this code is not really valid anymore */
2422 {
2423#ifdef WITH_PYTHON
2424 char *menustr;
2425 int scriptint = 0;
2426 /* popup a list of usable scripts */
2427 menustr = buildmenu_pyconstraints(bmain, nullptr, &scriptint);
2428 // scriptint = pupmenu(menustr); /* XXX */
2429 MEM_freeN(menustr);
2430
2431 /* only add constraint if a script was chosen */
2432 if (scriptint) {
2433 /* add constraint */
2434 validate_pyconstraint_cb(bmain, con->data, &scriptint);
2435
2436 /* make sure target allowance is set correctly */
2437 BPY_pyconstraint_update(ob, con);
2438 }
2439#endif
2440 break;
2441 }
2442
2443 default:
2444 break;
2445 }
2446
2447 /* make sure all settings are valid - similar to above checks, but sometimes can be wrong */
2448 object_test_constraints(bmain, ob);
2449
2450 if (pchan) {
2452 }
2453
2454 /* force depsgraph to get recalculated since new relationships added */
2456
2457 if ((ob->type == OB_ARMATURE) && (pchan)) {
2458 BKE_pose_tag_recalc(bmain, ob->pose); /* sort pose channels */
2460 }
2461 else {
2463 }
2464
2465 /* notifiers for updates */
2467
2468 return OPERATOR_FINISHED;
2469}
2470
2471/* ------------------ */
2472
2473/* dummy operator callback */
2475{
2477 int type = RNA_enum_get(op->ptr, "type");
2478 short with_targets = 0;
2479
2480 if (!ob) {
2481 BKE_report(op->reports, RPT_ERROR, "No active object to add constraint to");
2482 return OPERATOR_CANCELLED;
2483 }
2484
2485 /* hack: set constraint targets from selected objects in context is allowed when
2486 * operator name included 'with_targets', since the menu doesn't allow multiple properties
2487 */
2488 if (strstr(op->idname, "with_targets")) {
2489 with_targets = 1;
2490 }
2491
2492 return constraint_add_exec(C, op, ob, &ob->constraints, type, with_targets);
2493}
2494
2495/* dummy operator callback */
2497{
2499 int type = RNA_enum_get(op->ptr, "type");
2500 short with_targets = 0;
2501
2502 if (!ob) {
2503 BKE_report(op->reports, RPT_ERROR, "No active object to add constraint to");
2504 return OPERATOR_CANCELLED;
2505 }
2506
2507 /* hack: set constraint targets from selected objects in context is allowed when
2508 * operator name included 'with_targets', since the menu doesn't allow multiple properties
2509 */
2510 if (strstr(op->idname, "with_targets")) {
2511 with_targets = 1;
2512 }
2513
2514 return constraint_add_exec(C, op, ob, constraint_active_list(ob), type, with_targets);
2515}
2516
2517/* ------------------ */
2518
2519/* Filters constraints that are only compatible with bones */
2521 PointerRNA * /*ptr*/,
2522 PropertyRNA * /*prop*/,
2523 bool *r_free)
2524{
2526 EnumPropertyItem *object_constraint_items = nullptr;
2527 int totitem = 0;
2528
2529 while (item->identifier) {
2531 RNA_enum_item_add(&object_constraint_items, &totitem, item);
2532 }
2533 item++;
2534 }
2535
2536 RNA_enum_item_end(&object_constraint_items, &totitem);
2537 *r_free = true;
2538
2539 return object_constraint_items;
2540}
2541
2543{
2544 PropertyRNA *prop;
2545
2546 /* identifiers */
2547 ot->name = "Add Constraint";
2548 ot->description = "Add a constraint to the active object";
2549 ot->idname = "OBJECT_OT_constraint_add";
2550
2551 /* api callbacks */
2555
2556 /* flags */
2558
2559 /* properties */
2560 prop = RNA_def_enum(ot->srna, "type", rna_enum_dummy_NULL_items, 0, "Type", "");
2562 ot->prop = prop;
2563}
2564
2567/* ------------------------------------------------------------------- */
2572{
2573 PropertyRNA *prop;
2574
2575 /* identifiers */
2576 ot->name = "Add Constraint (with Targets)";
2577 ot->description =
2578 "Add a constraint to the active object, with target (where applicable) set to the "
2579 "selected objects/bones";
2580 ot->idname = "OBJECT_OT_constraint_add_with_targets";
2581
2582 /* api callbacks */
2586
2587 /* flags */
2589
2590 /* properties */
2591 prop = RNA_def_enum(ot->srna, "type", rna_enum_dummy_NULL_items, 0, "Type", "");
2593 ot->prop = prop;
2594}
2595
2597{
2598 /* identifiers */
2599 ot->name = "Add Constraint";
2600 ot->description = "Add a constraint to the active bone";
2601 ot->idname = "POSE_OT_constraint_add";
2602
2603 /* api callbacks */
2607
2608 /* flags */
2610
2611 /* properties */
2612 ot->prop = RNA_def_enum(ot->srna, "type", rna_enum_constraint_type_items, 0, "Type", "");
2613}
2614
2616{
2617 /* identifiers */
2618 ot->name = "Add Constraint (with Targets)";
2619 ot->description =
2620 "Add a constraint to the active bone, with target (where applicable) set to the selected "
2621 "Objects/Bones";
2622 ot->idname = "POSE_OT_constraint_add_with_targets";
2623
2624 /* api callbacks */
2628
2629 /* flags */
2631
2632 /* properties */
2633 ot->prop = RNA_def_enum(ot->srna, "type", rna_enum_constraint_type_items, 0, "Type", "");
2634}
2635
2638/* ------------------------------------------------------------------- */
2644/* TODO: should these be here, or back in `editors/armature/poseobject.c` again? */
2645
2646/* present menu with options + validation for targets to use */
2647static int pose_ik_add_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/)
2648{
2651 bConstraint *con = nullptr;
2652
2653 uiPopupMenu *pup;
2654 uiLayout *layout;
2655 Object *tar_ob = nullptr;
2656 bPoseChannel *tar_pchan = nullptr;
2657
2658 /* must have active bone */
2659 if (ELEM(nullptr, ob, pchan)) {
2660 BKE_report(op->reports, RPT_ERROR, "Must have an active bone to add IK constraint to");
2661 return OPERATOR_CANCELLED;
2662 }
2663
2664 /* bone must not have any constraints already */
2665 for (con = static_cast<bConstraint *>(pchan->constraints.first); con; con = con->next) {
2666 if (con->type == CONSTRAINT_TYPE_KINEMATIC) {
2667 break;
2668 }
2669 }
2670 if (con) {
2671 BKE_report(op->reports, RPT_ERROR, "Bone already has an IK constraint");
2672 return OPERATOR_CANCELLED;
2673 }
2674
2675 /* prepare popup menu to choose targeting options */
2676 pup = UI_popup_menu_begin(C, IFACE_("Add IK"), ICON_NONE);
2677 layout = UI_popup_menu_layout(pup);
2678
2679 /* the type of targets we'll set determines the menu entries to show... */
2680 if (get_new_constraint_target(C, CONSTRAINT_TYPE_KINEMATIC, &tar_ob, &tar_pchan, false)) {
2681 /* bone target, or object target?
2682 * - the only thing that matters is that we want a target...
2683 */
2684 if (tar_pchan) {
2686 layout, IFACE_("To Active Bone"), ICON_NONE, "POSE_OT_ik_add", "with_targets", 1);
2687 }
2688 else {
2690 layout, IFACE_("To Active Object"), ICON_NONE, "POSE_OT_ik_add", "with_targets", 1);
2691 }
2692 }
2693 else {
2694 /* we have a choice of adding to a new empty, or not setting any target (targetless IK) */
2696 layout, IFACE_("To New Empty Object"), ICON_NONE, "POSE_OT_ik_add", "with_targets", 1);
2698 layout, IFACE_("Without Targets"), ICON_NONE, "POSE_OT_ik_add", "with_targets", 0);
2699 }
2700
2701 /* finish building the menu, and process it (should result in calling self again) */
2702 UI_popup_menu_end(C, pup);
2703
2704 return OPERATOR_INTERFACE;
2705}
2706
2707/* call constraint_add_exec() to add the IK constraint */
2709{
2711 const bool with_targets = RNA_boolean_get(op->ptr, "with_targets");
2712
2713 /* add the constraint - all necessary checks should have
2714 * been done by the invoke() callback already... */
2715 return constraint_add_exec(
2716 C, op, ob, constraint_active_list(ob), CONSTRAINT_TYPE_KINEMATIC, with_targets);
2717}
2718
2720{
2721 /* identifiers */
2722 ot->name = "Add IK to Bone";
2723 ot->description = "Add IK Constraint to the active Bone";
2724 ot->idname = "POSE_OT_ik_add";
2725
2726 /* api callbacks */
2730
2731 /* flags */
2733
2734 /* properties */
2736 "with_targets",
2737 true,
2738 "With Targets",
2739 "Assign IK Constraint with targets derived from the select bones/objects");
2740}
2741
2744/* ------------------------------------------------------------------- */
2751{
2752 Object *prev_ob = nullptr;
2753
2754 /* only remove IK Constraints */
2755 CTX_DATA_BEGIN_WITH_ID (C, bPoseChannel *, pchan, selected_pose_bones, Object *, ob) {
2756 bConstraint *con, *next;
2757
2758 /* TODO: should we be checking if these constraints were local
2759 * before we try and remove them? */
2760 for (con = static_cast<bConstraint *>(pchan->constraints.first); con; con = next) {
2761 next = con->next;
2762 if (con->type == CONSTRAINT_TYPE_KINEMATIC) {
2763 BKE_constraint_remove(&pchan->constraints, con);
2764 }
2765 }
2766 pchan->constflag &= ~(PCHAN_HAS_IK | PCHAN_HAS_NO_TARGET);
2767
2768 if (prev_ob != ob) {
2769 prev_ob = ob;
2770
2771 /* Refresh depsgraph. */
2773
2774 /* NOTE: notifier might evolve. */
2776 }
2777 }
2779
2780 return OPERATOR_FINISHED;
2781}
2782
2784{
2785 /* identifiers */
2786 ot->name = "Remove IK";
2787 ot->description = "Remove all IK Constraints from selected bones";
2788 ot->idname = "POSE_OT_ik_clear";
2789
2790 /* api callbacks */
2793
2794 /* flags */
2796}
2797
2800} // namespace blender::ed::object
Functions and classes to work with Actions.
Functions for backward compatibility with the legacy Action API.
Functions to work with AnimData.
void BIK_test_constraint(struct Object *ob, struct bConstraint *cons)
Blender kernel action and pose functionality.
void BKE_pose_tag_recalc(Main *bmain, bPose *pose) ATTR_NONNULL(1
void BKE_pose_update_constraint_flags(bPose *pose) ATTR_NONNULL(1)
bPoseChannel * BKE_pose_channel_active_or_first_selected(Object *ob) ATTR_WARN_UNUSED_RESULT
bPoseChannel * BKE_pose_channel_active_if_bonecoll_visible(Object *ob) ATTR_WARN_UNUSED_RESULT
void BKE_pose_tag_update_constraint_flags(bPose *pose) ATTR_NONNULL(1)
bArmature * BKE_armature_from_object(Object *ob)
Definition armature.cc:522
Bone * BKE_armature_find_bone_name(bArmature *arm, const char *name)
Definition armature.cc:779
bool BKE_constraint_remove(ListBase *list, struct bConstraint *con)
struct bConstraint * BKE_constraints_find_name(struct ListBase *list, const char *name)
void BKE_constraints_free(struct ListBase *list)
void BKE_constraint_targets_flush(struct bConstraint *con, struct ListBase *targets, bool no_copy)
int BKE_constraint_targets_get(struct bConstraint *con, struct ListBase *r_targets)
void BKE_constraints_copy(struct ListBase *dst, const struct ListBase *src, bool do_extern)
bool BKE_constraint_remove_ex(ListBase *list, struct Object *ob, struct bConstraint *con)
struct bConstraint * BKE_constraint_copy_for_pose(struct Object *ob, struct bPoseChannel *pchan, struct bConstraint *src)
struct bConstraint * BKE_constraint_add_for_object(struct Object *ob, const char *name, short type)
bool BKE_constraint_apply_and_remove_for_pose(struct Depsgraph *depsgraph, struct Scene *scene, ListBase *constraints, struct Object *ob, struct bConstraint *con, struct bPoseChannel *pchan)
bool BKE_constraint_is_nonlocal_in_liboverride(const struct Object *ob, const struct bConstraint *con)
struct bConstraint * BKE_constraint_add_for_pose(struct Object *ob, struct bPoseChannel *pchan, const char *name, short type)
bool BKE_constraint_apply_and_remove_for_object(struct Depsgraph *depsgraph, struct Scene *scene, ListBase *constraints, struct Object *ob, struct bConstraint *con)
struct bConstraint * BKE_constraint_copy_for_object(struct Object *ob, struct bConstraint *src)
struct bConstraint * BKE_constraints_active_get(struct ListBase *list)
void BKE_constraints_active_set(ListBase *list, struct bConstraint *con)
#define CTX_DATA_BEGIN_WITH_ID(C, Type, instance, member, Type_id, instance_id)
PointerRNA CTX_data_pointer_get(const bContext *C, const char *member)
bPoseChannel * CTX_data_active_pose_bone(const bContext *C)
#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)
Object * CTX_data_active_object(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)
FModifier * add_fmodifier(ListBase *modifiers, int type, FCurve *owner_fcu)
@ G_DEBUG
void BKE_view_layer_synced_ensure(const Scene *scene, ViewLayer *view_layer)
Base * BKE_view_layer_active_base_get(ViewLayer *view_layer)
General operations, lookup, etc. for blender objects.
bool BKE_object_exists_check(Main *bmain, const Object *obtest)
Object * BKE_object_add(Main *bmain, Scene *scene, ViewLayer *view_layer, int type, const char *name) ATTR_NONNULL(1
void BKE_object_eval_constraints(Depsgraph *depsgraph, Scene *scene, Object *ob)
Object * BKE_object_pose_armature_get(Object *ob)
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
struct MovieTrackingObject * BKE_tracking_object_get_camera(const struct MovieTracking *tracking)
struct MovieTrackingObject * BKE_tracking_object_get_named(struct MovieTracking *tracking, const char *name)
Definition tracking.cc:1966
struct MovieTrackingTrack * BKE_tracking_object_find_track_with_name(struct MovieTrackingObject *tracking_object, const char *name)
Definition tracking.cc:1993
#define BLI_assert(a)
Definition BLI_assert.h:50
A dynamically sized string ADT.
char * BLI_dynstr_get_cstring(const DynStr *ds) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition BLI_dynstr.c:149
DynStr * BLI_dynstr_new(void) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
Definition BLI_dynstr.c:37
void BLI_dynstr_free(DynStr *ds) ATTR_NONNULL()
Definition BLI_dynstr.c:174
void BLI_dynstr_append(DynStr *__restrict ds, const char *cstr) ATTR_NONNULL()
Definition BLI_dynstr.c:62
#define LISTBASE_FOREACH(type, var, list)
void BLI_insertlinkafter(struct ListBase *listbase, void *vprevlink, void *vnewlink) ATTR_NONNULL(1)
Definition listbase.cc:331
void BLI_remlink(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:130
void void void bool BLI_listbase_link_move(ListBase *listbase, void *vlink, int step) ATTR_NONNULL()
Definition listbase.cc:435
int BLI_findindex(const struct ListBase *listbase, const void *vlink) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
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)
void unit_m4(float m[4][4])
Definition rct.c:1127
void mul_v3_m4v3(float r[3], const float mat[4][4], const float vec[3])
MINLINE void copy_v3_v3(float r[3], const float a[3])
#define STRNCPY(dst, src)
Definition BLI_string.h:593
#define SNPRINTF(dst, format,...)
Definition BLI_string.h:597
#define UNUSED_VARS(...)
#define ELEM(...)
#define STREQ(a, b)
#define IFACE_(msgid)
bool BPY_is_pyconstraint(Text *text)
Definition stubs.cc:22
void BPY_pyconstraint_update(Object *owner, bConstraint *con)
Definition stubs.cc:26
void DEG_id_tag_update(ID *id, unsigned int flags)
void DEG_relations_tag_update(Main *bmain)
Scene * DEG_get_evaluated_scene(const Depsgraph *graph)
@ ID_RECALC_TRANSFORM
Definition DNA_ID.h:1021
@ ID_RECALC_SYNC_TO_EVAL
Definition DNA_ID.h:1085
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:1041
#define ID_IS_EDITABLE(_id)
Definition DNA_ID.h:658
@ ID_OB
@ PCHAN_HAS_NO_TARGET
@ PCHAN_HAS_IK
@ FMODIFIER_TYPE_GENERATOR
@ CONSTRAINT_OFF
@ CONSTRAINT_OVERRIDE_LIBRARY_LOCAL
@ CONSTRAINT_ACTIVE
@ CONSTRAINT_DISABLE
@ CONSTRAINT_TYPE_TRACKTO
@ CONSTRAINT_TYPE_PIVOT
@ CONSTRAINT_TYPE_CHILDOF
@ CONSTRAINT_TYPE_FOLLOWTRACK
@ CONSTRAINT_TYPE_OBJECTSOLVER
@ CONSTRAINT_TYPE_ARMATURE
@ CONSTRAINT_TYPE_SHRINKWRAP
@ CONSTRAINT_TYPE_ROTLIMIT
@ CONSTRAINT_TYPE_CAMERASOLVER
@ CONSTRAINT_TYPE_SPLINEIK
@ CONSTRAINT_TYPE_PYTHON
@ CONSTRAINT_TYPE_KINEMATIC
@ CONSTRAINT_TYPE_NULL
@ CONSTRAINT_TYPE_DISTLIMIT
@ CONSTRAINT_TYPE_LOCLIMIT
@ CONSTRAINT_TYPE_CLAMPTO
@ CONSTRAINT_TYPE_LOCKTRACK
@ CONSTRAINT_TYPE_SIZELIMIT
@ CONSTRAINT_TYPE_ACTION
@ CONSTRAINT_TYPE_FOLLOWPATH
@ CONSTRAINT_TYPE_STRETCHTO
@ CONSTRAINT_TYPE_SAMEVOL
@ CONSTRAINT_TYPE_TRANSFORM_CACHE
@ CONSTRAINT_OBTYPE_OBJECT
@ CONSTRAINT_OBTYPE_BONE
@ CAMERASOLVER_ACTIVECLIP
@ ACTCON_USE_EVAL_TIME
@ FOLLOWPATH_STATIC
@ OBJECTSOLVER_SET_INVERSE
@ CHILDOF_SET_INVERSE
@ CU_PATH
#define MAX_NAME
Definition DNA_defs.h:50
@ OB_MODE_POSE
Object is a sort of wrapper for general info.
@ OB_EMPTY
@ OB_ARMATURE
@ OB_MESH
@ OB_CURVES_LEGACY
#define MINAFRAME
#define MAXFRAME
@ OPERATOR_PASS_THROUGH
bool ED_operator_object_active_local_editable(bContext *C)
bool ED_operator_object_active_editable(bContext *C)
bool ED_operator_posemode_exclusive(bContext *C)
bool ED_operator_object_active_local_editable_posemode_exclusive(bContext *C)
bool ED_operator_object_active_editable_ex(bContext *C, const Object *ob)
Read Guarded memory(de)allocation.
#define MEM_SAFE_FREE(v)
@ PROP_SKIP_SAVE
Definition RNA_types.hh:245
@ PROP_HIDDEN
Definition RNA_types.hh:239
void uiItemBooleanO(uiLayout *layout, const char *name, int icon, const char *opname, const char *propname, int value)
void UI_popup_menu_end(bContext *C, uiPopupMenu *pup)
uiPopupMenu * UI_popup_menu_begin(bContext *C, const char *title, int icon) ATTR_NONNULL()
uiLayout * UI_popup_menu_layout(uiPopupMenu *pup)
PointerRNA * UI_region_panel_custom_data_under_cursor(const bContext *C, const wmEvent *event)
@ OPTYPE_UNDO
Definition WM_types.hh:162
@ OPTYPE_REGISTER
Definition WM_types.hh:160
#define NA_ADDED
Definition WM_types.hh:552
#define ND_POSE
Definition WM_types.hh:425
#define ND_CONSTRAINT
Definition WM_types.hh:431
#define NA_REMOVED
Definition WM_types.hh:553
#define ND_TRANSFORM
Definition WM_types.hh:423
#define NC_OBJECT
Definition WM_types.hh:346
#define A
btSequentialImpulseConstraintSolverMt int btPersistentManifold int btTypedConstraint ** constraints
Slot * slot_for_handle(slot_handle_t handle)
#define printf
const Depsgraph * depsgraph
int len
draw_view in_light_buf[] float
#define str(s)
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
static ulong * next
#define B
#define G(x, y, z)
bool action_treat_as_legacy(const bAction &action)
bAction * id_action_ensure(Main *bmain, ID *id)
Definition animdata.cc:195
FCurve * action_fcurve_ensure(Main *bmain, bAction *act, const char group[], PointerRNA *ptr, FCurveDescriptor fcurve_descriptor)
FCurve * fcurve_find_in_assigned_slot(AnimData &adt, FCurveDescriptor fcurve_descriptor)
static int constraint_type_get(Object *owner, bPoseChannel *pchan)
void object_test_constraints(Main *bmain, Object *ob)
static int stretchto_reset_exec(bContext *C, wmOperator *op)
static bool edit_constraint_poll(bContext *C)
bConstraint * constraint_active_get(Object *ob)
static int followpath_path_animate_exec(bContext *C, wmOperator *op)
void constraint_link(Main *bmain, Object *ob_dst, ListBase *dst, ListBase *src)
static int constraint_delete_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static bool edit_constraint_poll_generic(bContext *C, StructRNA *rna_type, const bool is_liboverride_allowed)
void constraint_copy_for_object(Main *bmain, Object *ob_dst, bConstraint *con)
static int objectsolver_set_inverse_invoke(bContext *C, wmOperator *op, const wmEvent *)
static void object_pose_tag_update(Main *bmain, Object *ob)
static int constraint_apply_exec(bContext *C, wmOperator *op)
void CONSTRAINT_OT_move_down(wmOperatorType *ot)
static int stretchto_reset_invoke(bContext *C, wmOperator *op, const wmEvent *)
static void edit_constraint_report_property(wmOperatorType *ot)
static void object_test_constraint(Main *bmain, Object *ob, bConstraint *con)
void constraint_active_set(Object *ob, bConstraint *con)
void constraint_copy_for_pose(Main *bmain, Object *ob_dst, bPoseChannel *pchan, bConstraint *con)
static int object_constraints_clear_exec(bContext *C, wmOperator *)
static void test_constraints(Main *bmain, Object *ob, bPoseChannel *pchan)
void constraint_dependency_tag_update(Main *bmain, Object *ob, bConstraint *con)
static int pose_constraints_clear_exec(bContext *C, wmOperator *)
static bool edit_constraint_liboverride_allowed_poll(bContext *C)
static int constraint_copy_exec(bContext *C, wmOperator *op)
static int objectsolver_clear_inverse_invoke(bContext *C, wmOperator *op, const wmEvent *)
void constraint_update(Main *bmain, Object *ob)
static int constraint_add_exec(bContext *C, wmOperator *op, Object *ob, ListBase *list, int type, const bool setTarget)
static bConstraint * edit_constraint_property_get(bContext *C, wmOperator *op, Object *ob, int type)
static const EnumPropertyItem * object_constraint_add_itemf(bContext *, PointerRNA *, PropertyRNA *, bool *r_free)
static int limitdistance_reset_exec(bContext *C, wmOperator *op)
void OBJECT_OT_constraint_add_with_targets(wmOperatorType *ot)
void CONSTRAINT_OT_objectsolver_set_inverse(wmOperatorType *ot)
static bool constraint_copy_to_selected_poll(bContext *C)
static void force_evaluation_if_constraint_disabled(bContext *C, Object *ob, bConstraint *con)
static int limitdistance_reset_invoke(bContext *C, wmOperator *op, const wmEvent *)
void base_select(Base *base, eObjectSelect_Mode mode)
static int objectsolver_clear_inverse_exec(bContext *C, wmOperator *op)
ListBase * pose_constraint_list(const bContext *C)
void CONSTRAINT_OT_limitdistance_reset(wmOperatorType *ot)
static bool get_new_constraint_target(bContext *C, int con_type, Object **tar_ob, bPoseChannel **tar_pchan, bool add)
void CONSTRAINT_OT_copy(wmOperatorType *ot)
static int constraint_apply_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static int pose_constraint_add_exec(bContext *C, wmOperator *op)
static int constraint_copy_invoke(bContext *C, wmOperator *op, const wmEvent *event)
void CONSTRAINT_OT_apply(wmOperatorType *ot)
void constraint_tag_update(Main *bmain, Object *ob, bConstraint *con)
void CONSTRAINT_OT_move_up(wmOperatorType *ot)
ListBase * constraint_active_list(Object *ob)
void POSE_OT_constraints_copy(wmOperatorType *ot)
static int object_constraint_add_exec(bContext *C, wmOperator *op)
static int constraint_move_up_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static int constraint_move_up_exec(bContext *C, wmOperator *op)
static int constraint_move_down_exec(bContext *C, wmOperator *op)
static int pose_constraint_copy_exec(bContext *C, wmOperator *op)
Object * context_active_object(const bContext *C)
static int childof_set_inverse_exec(bContext *C, wmOperator *op)
void POSE_OT_ik_add(wmOperatorType *ot)
void POSE_OT_ik_clear(wmOperatorType *ot)
static int object_constraint_copy_exec(bContext *C, wmOperator *)
void POSE_OT_constraint_add(wmOperatorType *ot)
void constraint_dependency_update(Main *bmain, Object *ob)
void CONSTRAINT_OT_delete(wmOperatorType *ot)
static int objectsolver_set_inverse_exec(bContext *C, wmOperator *op)
bool constraint_move_to_index(Object *ob, bConstraint *con, int index)
static int constraint_delete_exec(bContext *C, wmOperator *op)
static int followpath_path_animate_invoke(bContext *C, wmOperator *op, const wmEvent *)
void CONSTRAINT_OT_stretchto_reset(wmOperatorType *ot)
static int childof_clear_inverse_exec(bContext *C, wmOperator *op)
static int pose_ik_add_invoke(bContext *C, wmOperator *op, const wmEvent *)
void OBJECT_OT_constraint_add(wmOperatorType *ot)
static int constraint_move_to_index_invoke(bContext *C, wmOperator *op, const wmEvent *event)
void CONSTRAINT_OT_move_to_index(wmOperatorType *ot)
void OBJECT_OT_constraints_copy(wmOperatorType *ot)
static void test_constraint(Main *bmain, Object *owner, bPoseChannel *pchan, bConstraint *con, int type)
static int childof_set_inverse_invoke(bContext *C, wmOperator *op, const wmEvent *)
static int childof_clear_inverse_invoke(bContext *C, wmOperator *op, const wmEvent *)
static int pose_ik_clear_exec(bContext *C, wmOperator *)
static int pose_ik_add_exec(bContext *C, wmOperator *op)
void OBJECT_OT_constraints_clear(wmOperatorType *ot)
static void edit_constraint_properties(wmOperatorType *ot)
static void set_constraint_nth_target(bConstraint *con, Object *target, const char subtarget[], int index)
static int constraint_copy_to_selected_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static int constraint_move_to_index_exec(bContext *C, wmOperator *op)
void CONSTRAINT_OT_objectsolver_clear_inverse(wmOperatorType *ot)
static int constraint_copy_to_selected_exec(bContext *C, wmOperator *op)
void CONSTRAINT_OT_childof_clear_inverse(wmOperatorType *ot)
void CONSTRAINT_OT_followpath_path_animate(wmOperatorType *ot)
static const EnumPropertyItem constraint_owner_items[]
void CONSTRAINT_OT_childof_set_inverse(wmOperatorType *ot)
static bool edit_constraint_invoke_properties(bContext *C, wmOperator *op, const wmEvent *event, int *r_retval)
void POSE_OT_constraint_add_with_targets(wmOperatorType *ot)
static int constraint_move_down_invoke(bContext *C, wmOperator *op, const wmEvent *event)
void CONSTRAINT_OT_copy_to_selected(wmOperatorType *ot)
void POSE_OT_constraints_clear(wmOperatorType *ot)
ListBase * constraint_list_from_constraint(Object *ob, bConstraint *con, bPoseChannel **r_pchan)
#define EDIT_CONSTRAINT_OWNER_BONE
#define EDIT_CONSTRAINT_OWNER_OBJECT
bool RNA_struct_is_a(const StructRNA *type, const StructRNA *srna)
void RNA_string_set(PointerRNA *ptr, const char *name, const char *value)
PropertyRNA * RNA_struct_find_property(PointerRNA *ptr, const char *identifier)
void RNA_string_get(PointerRNA *ptr, const char *name, char *value)
int RNA_int_get(PointerRNA *ptr, const char *name)
bool RNA_pointer_is_null(const PointerRNA *ptr)
bool RNA_struct_property_is_set(PointerRNA *ptr, const char *identifier)
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
void RNA_enum_set(PointerRNA *ptr, const char *name, int value)
PointerRNA RNA_pointer_create(ID *id, StructRNA *type, void *data)
int RNA_enum_get(PointerRNA *ptr, const char *name)
PointerRNA RNA_id_pointer_create(ID *id)
const EnumPropertyItem rna_enum_constraint_type_items[]
PropertyRNA * RNA_def_string(StructOrFunctionRNA *cont_, const char *identifier, const char *default_value, const int maxlen, const char *ui_name, const char *ui_description)
PropertyRNA * RNA_def_enum(StructOrFunctionRNA *cont_, const char *identifier, const EnumPropertyItem *items, const int default_value, const char *ui_name, const char *ui_description)
void RNA_enum_item_end(EnumPropertyItem **items, int *totitem)
void RNA_enum_item_add(EnumPropertyItem **items, int *totitem, const EnumPropertyItem *item)
PropertyRNA * RNA_def_boolean(StructOrFunctionRNA *cont_, const char *identifier, const bool default_value, const char *ui_name, const char *ui_description)
void RNA_def_property_flag(PropertyRNA *prop, PropertyFlag flag)
void RNA_def_enum_funcs(PropertyRNA *prop, EnumPropertyItemFunc itemfunc)
PropertyRNA * RNA_def_int(StructOrFunctionRNA *cont_, const char *identifier, const int default_value, const int hardmin, const int hardmax, const char *ui_name, const char *ui_description, const int softmin, const int softmax)
std::optional< std::string > RNA_path_from_ID_to_property(const PointerRNA *ptr, PropertyRNA *prop)
Definition rna_path.cc:1166
const EnumPropertyItem rna_enum_dummy_NULL_items[]
Definition rna_rna.cc:29
bAction * action
struct AnimData * adt
const char * identifier
Definition RNA_types.hh:506
FPoint * fpt
BezTriple * bezt
ListBase modifiers
Definition DNA_ID.h:413
char name[66]
Definition DNA_ID.h:425
void * first
ListBase texts
Definition BKE_main.hh:227
ListBase constraints
struct bPose * pose
float loc[3]
ID * owner_id
Definition RNA_types.hh:40
StructRNA * type
Definition RNA_types.hh:41
void * data
Definition RNA_types.hh:42
struct Base * basact
struct bConstraintTarget * next
struct bConstraint * prev
struct bConstraint * next
ListBase chanbase
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(* invoke)(bContext *C, wmOperator *op, const wmEvent *event) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1022
int(* exec)(bContext *C, wmOperator *op) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1006
const char * description
Definition WM_types.hh:996
PropertyRNA * prop
Definition WM_types.hh:1092
StructRNA * srna
Definition WM_types.hh:1080
struct ReportList * reports
struct PointerRNA * ptr
ccl_device_inline int abs(int x)
Definition util/math.h:120
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
int WM_menu_invoke(bContext *C, wmOperator *op, const wmEvent *)