Blender V5.0
object_transform.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
8
9#include <cstdlib>
10#include <cstring>
11#include <numeric>
12
13#include "DNA_anim_types.h"
14#include "DNA_armature_types.h"
17#include "DNA_lattice_types.h"
18#include "DNA_light_types.h"
19#include "DNA_mesh_types.h"
20#include "DNA_meta_types.h"
21#include "DNA_object_types.h"
23#include "DNA_scene_types.h"
24
25#include "BLI_array.hh"
26#include "BLI_listbase.h"
27#include "BLI_math_geom.h"
28#include "BLI_math_matrix.h"
29#include "BLI_math_matrix.hh"
30#include "BLI_math_rotation.h"
31#include "BLI_task.hh"
32#include "BLI_utildefines.h"
33#include "BLI_vector.hh"
34
35#include "BKE_armature.hh"
36#include "BKE_context.hh"
37#include "BKE_curve.hh"
38#include "BKE_curves.hh"
39#include "BKE_editmesh.hh"
40#include "BKE_grease_pencil.hh"
41#include "BKE_idtype.hh"
42#include "BKE_lattice.hh"
43#include "BKE_layer.hh"
44#include "BKE_lib_id.hh"
45#include "BKE_library.hh"
46#include "BKE_main.hh"
47#include "BKE_mball.hh"
48#include "BKE_mesh.hh"
49#include "BKE_multires.hh"
50#include "BKE_object.hh"
51#include "BKE_report.hh"
52#include "BKE_scene.hh"
53#include "BKE_tracking.h"
54
55#include "BLT_translation.hh"
56
57#include "DEG_depsgraph.hh"
59
60#include "RNA_access.hh"
61#include "RNA_define.hh"
62
63#include "UI_interface_icons.hh"
64
65#include "WM_api.hh"
66#include "WM_types.hh"
67
68#include "ANIM_action.hh"
69#include "ANIM_keyframing.hh"
70#include "ANIM_keyingsets.hh"
71
72#include "ED_anim_api.hh"
73#include "ED_armature.hh"
74#include "ED_keyframing.hh"
75#include "ED_mesh.hh"
76#include "ED_object.hh"
77#include "ED_screen.hh"
78#include "ED_view3d.hh"
79
80#include "MEM_guardedalloc.h"
81
82#include "RNA_access.hh"
83#include "RNA_prototypes.hh"
84
85#include "object_intern.hh"
86
87namespace blender::ed::object {
88
89/* -------------------------------------------------------------------- */
92
93/* clear location of object */
94static void object_clear_loc(Object *ob, const bool clear_delta)
95{
96 /* clear location if not locked */
97 if ((ob->protectflag & OB_LOCK_LOCX) == 0) {
98 ob->loc[0] = 0.0f;
99 if (clear_delta) {
100 ob->dloc[0] = 0.0f;
101 }
102 }
103 if ((ob->protectflag & OB_LOCK_LOCY) == 0) {
104 ob->loc[1] = 0.0f;
105 if (clear_delta) {
106 ob->dloc[1] = 0.0f;
107 }
108 }
109 if ((ob->protectflag & OB_LOCK_LOCZ) == 0) {
110 ob->loc[2] = 0.0f;
111 if (clear_delta) {
112 ob->dloc[2] = 0.0f;
113 }
114 }
115}
116
117/* clear rotation of object */
118static void object_clear_rot(Object *ob, const bool clear_delta)
119{
120 /* clear rotations that aren't locked */
122 if (ob->protectflag & OB_LOCK_ROT4D) {
123 /* perform clamping on a component by component basis */
124 if (ob->rotmode == ROT_MODE_AXISANGLE) {
125 if ((ob->protectflag & OB_LOCK_ROTW) == 0) {
126 ob->rotAngle = 0.0f;
127 if (clear_delta) {
128 ob->drotAngle = 0.0f;
129 }
130 }
131 if ((ob->protectflag & OB_LOCK_ROTX) == 0) {
132 ob->rotAxis[0] = 0.0f;
133 if (clear_delta) {
134 ob->drotAxis[0] = 0.0f;
135 }
136 }
137 if ((ob->protectflag & OB_LOCK_ROTY) == 0) {
138 ob->rotAxis[1] = 0.0f;
139 if (clear_delta) {
140 ob->drotAxis[1] = 0.0f;
141 }
142 }
143 if ((ob->protectflag & OB_LOCK_ROTZ) == 0) {
144 ob->rotAxis[2] = 0.0f;
145 if (clear_delta) {
146 ob->drotAxis[2] = 0.0f;
147 }
148 }
149
150 /* Check validity of axis - axis should never be 0,0,0
151 * (if so, then we make it rotate about y). */
152 if (IS_EQF(ob->rotAxis[0], ob->rotAxis[1]) && IS_EQF(ob->rotAxis[1], ob->rotAxis[2])) {
153 ob->rotAxis[1] = 1.0f;
154 }
155 if (IS_EQF(ob->drotAxis[0], ob->drotAxis[1]) && IS_EQF(ob->drotAxis[1], ob->drotAxis[2]) &&
156 clear_delta)
157 {
158 ob->drotAxis[1] = 1.0f;
159 }
160 }
161 else if (ob->rotmode == ROT_MODE_QUAT) {
162 if ((ob->protectflag & OB_LOCK_ROTW) == 0) {
163 ob->quat[0] = 1.0f;
164 if (clear_delta) {
165 ob->dquat[0] = 1.0f;
166 }
167 }
168 if ((ob->protectflag & OB_LOCK_ROTX) == 0) {
169 ob->quat[1] = 0.0f;
170 if (clear_delta) {
171 ob->dquat[1] = 0.0f;
172 }
173 }
174 if ((ob->protectflag & OB_LOCK_ROTY) == 0) {
175 ob->quat[2] = 0.0f;
176 if (clear_delta) {
177 ob->dquat[2] = 0.0f;
178 }
179 }
180 if ((ob->protectflag & OB_LOCK_ROTZ) == 0) {
181 ob->quat[3] = 0.0f;
182 if (clear_delta) {
183 ob->dquat[3] = 0.0f;
184 }
185 }
186 /* TODO: does this quat need normalizing now? */
187 }
188 else {
189 /* the flag may have been set for the other modes, so just ignore the extra flag... */
190 if ((ob->protectflag & OB_LOCK_ROTX) == 0) {
191 ob->rot[0] = 0.0f;
192 if (clear_delta) {
193 ob->drot[0] = 0.0f;
194 }
195 }
196 if ((ob->protectflag & OB_LOCK_ROTY) == 0) {
197 ob->rot[1] = 0.0f;
198 if (clear_delta) {
199 ob->drot[1] = 0.0f;
200 }
201 }
202 if ((ob->protectflag & OB_LOCK_ROTZ) == 0) {
203 ob->rot[2] = 0.0f;
204 if (clear_delta) {
205 ob->drot[2] = 0.0f;
206 }
207 }
208 }
209 }
210 else {
211 /* perform clamping using euler form (3-components) */
212 /* FIXME: deltas are not handled for these cases yet... */
213 float eul[3], oldeul[3], quat1[4] = {0};
214
215 if (ob->rotmode == ROT_MODE_QUAT) {
216 copy_qt_qt(quat1, ob->quat);
217 quat_to_eul(oldeul, ob->quat);
218 }
219 else if (ob->rotmode == ROT_MODE_AXISANGLE) {
221 }
222 else {
223 copy_v3_v3(oldeul, ob->rot);
224 }
225
226 eul[0] = eul[1] = eul[2] = 0.0f;
227
228 if (ob->protectflag & OB_LOCK_ROTX) {
229 eul[0] = oldeul[0];
230 }
231 if (ob->protectflag & OB_LOCK_ROTY) {
232 eul[1] = oldeul[1];
233 }
234 if (ob->protectflag & OB_LOCK_ROTZ) {
235 eul[2] = oldeul[2];
236 }
237
238 if (ob->rotmode == ROT_MODE_QUAT) {
239 eul_to_quat(ob->quat, eul);
240 /* quaternions flip w sign to accumulate rotations correctly */
241 if ((quat1[0] < 0.0f && ob->quat[0] > 0.0f) || (quat1[0] > 0.0f && ob->quat[0] < 0.0f)) {
242 mul_qt_fl(ob->quat, -1.0f);
243 }
244 }
245 else if (ob->rotmode == ROT_MODE_AXISANGLE) {
247 }
248 else {
249 copy_v3_v3(ob->rot, eul);
250 }
251 }
252 } /* Duplicated in source/blender/editors/armature/editarmature.c */
253 else {
254 if (ob->rotmode == ROT_MODE_QUAT) {
255 unit_qt(ob->quat);
256 if (clear_delta) {
257 unit_qt(ob->dquat);
258 }
259 }
260 else if (ob->rotmode == ROT_MODE_AXISANGLE) {
262 if (clear_delta) {
264 }
265 }
266 else {
267 zero_v3(ob->rot);
268 if (clear_delta) {
269 zero_v3(ob->drot);
270 }
271 }
272 }
273}
274
275/* clear scale of object */
276static void object_clear_scale(Object *ob, const bool clear_delta)
277{
278 /* clear scale factors which are not locked */
279 if ((ob->protectflag & OB_LOCK_SCALEX) == 0) {
280 ob->scale[0] = 1.0f;
281 if (clear_delta) {
282 ob->dscale[0] = 1.0f;
283 }
284 }
285 if ((ob->protectflag & OB_LOCK_SCALEY) == 0) {
286 ob->scale[1] = 1.0f;
287 if (clear_delta) {
288 ob->dscale[1] = 1.0f;
289 }
290 }
291 if ((ob->protectflag & OB_LOCK_SCALEZ) == 0) {
292 ob->scale[2] = 1.0f;
293 if (clear_delta) {
294 ob->dscale[2] = 1.0f;
295 }
296 }
297}
298
299/* generic exec for clear-transform operators */
301 wmOperator *op,
302 void (*clear_func)(Object *,
303 const bool),
304 const char default_ksName[])
305{
307 Main *bmain = CTX_data_main(C);
308 Scene *scene = CTX_data_scene(C);
309 ViewLayer *view_layer = CTX_data_view_layer(C);
310 /* May be null. */
311 View3D *v3d = CTX_wm_view3d(C);
312 KeyingSet *ks;
313 const bool clear_delta = RNA_boolean_get(op->ptr, "clear_delta");
314
315 BLI_assert(!ELEM(nullptr, clear_func, default_ksName));
316
317 Vector<Object *> objects;
318 FOREACH_SELECTED_EDITABLE_OBJECT_BEGIN (view_layer, v3d, ob) {
319 objects.append(ob);
320 }
322
323 if (objects.is_empty()) {
324 return OPERATOR_CANCELLED;
325 }
326
327 /* Support transforming the object data. */
328 const bool use_transform_skip_children = (scene->toolsettings->transform_flag &
330 const bool use_transform_data_origin = (scene->toolsettings->transform_flag &
332 XFormObjectSkipChild_Container *xcs = nullptr;
333 XFormObjectData_Container *xds = nullptr;
334
335 if (use_transform_skip_children) {
339 xcs, scene, view_layer, objects.data(), objects.size());
340 }
341 if (use_transform_data_origin) {
344 }
345
346 /* get KeyingSet to use */
347 ks = blender::animrig::get_keyingset_for_autokeying(scene, default_ksName);
348
351 }
352
353 for (Object *ob : objects) {
354 if (use_transform_data_origin) {
356 }
357
358 /* run provided clearing function */
359 clear_func(ob, clear_delta);
360
361 animrig::autokeyframe_object(C, scene, ob, ks);
362
363 /* tag for updates */
365 }
366
367 if (use_transform_skip_children) {
370 }
371
372 if (use_transform_data_origin) {
375 }
376
377 /* this is needed so children are also updated */
379
380 return OPERATOR_FINISHED;
381}
382
384
385/* -------------------------------------------------------------------- */
388
393
395{
396 /* identifiers */
397 ot->name = "Clear Location";
398 ot->description = "Clear the object's location";
399 ot->idname = "OBJECT_OT_location_clear";
400
401 /* API callbacks. */
404
405 /* flags */
407
408 /* properties */
409 ot->prop = RNA_def_boolean(
410 ot->srna,
411 "clear_delta",
412 false,
413 "Clear Delta",
414 "Clear delta location in addition to clearing the normal location transform");
415}
416
418
419/* -------------------------------------------------------------------- */
422
427
429{
430 /* identifiers */
431 ot->name = "Clear Rotation";
432 ot->description = "Clear the object's rotation";
433 ot->idname = "OBJECT_OT_rotation_clear";
434
435 /* API callbacks. */
438
439 /* flags */
441
442 /* properties */
443 ot->prop = RNA_def_boolean(
444 ot->srna,
445 "clear_delta",
446 false,
447 "Clear Delta",
448 "Clear delta rotation in addition to clearing the normal rotation transform");
449}
450
452
453/* -------------------------------------------------------------------- */
456
461
463{
464 /* identifiers */
465 ot->name = "Clear Scale";
466 ot->description = "Clear the object's scale";
467 ot->idname = "OBJECT_OT_scale_clear";
468
469 /* API callbacks. */
472
473 /* flags */
475
476 /* properties */
477 ot->prop = RNA_def_boolean(
478 ot->srna,
479 "clear_delta",
480 false,
481 "Clear Delta",
482 "Clear delta scale in addition to clearing the normal scale transform");
483}
484
486
487/* -------------------------------------------------------------------- */
490
492{
493 float *v1, *v3;
494 float mat[3][3];
495
496 CTX_DATA_BEGIN (C, Object *, ob, selected_editable_objects) {
497 if (ob->parent) {
498 /* vectors pointed to by v1 and v3 will get modified */
499 v1 = ob->loc;
500 v3 = ob->parentinv[3];
501
502 copy_m3_m4(mat, ob->parentinv);
503 negate_v3_v3(v3, v1);
504 mul_m3_v3(mat, v3);
505 }
506
508 }
510
512
513 return OPERATOR_FINISHED;
514}
515
517{
518 /* identifiers */
519 ot->name = "Clear Origin";
520 ot->description = "Clear the object's origin";
521 ot->idname = "OBJECT_OT_origin_clear";
522
523 /* API callbacks. */
526
527 /* flags */
529}
530
532
533/* -------------------------------------------------------------------- */
536
537/* use this when the loc/size/rot of the parent has changed but the children
538 * should stay in the same place, e.g. for apply-size-rot or object center */
539static void ignore_parent_tx(Main *bmain, Depsgraph *depsgraph, Scene *scene, Object *ob)
540{
542
543 /* a change was made, adjust the children to compensate */
544 LISTBASE_FOREACH (Object *, ob_child, &bmain->objects) {
545 if (ob_child->parent == ob) {
546 Object *ob_child_eval = DEG_get_evaluated(depsgraph, ob_child);
547 BKE_object_apply_mat4(ob_child_eval, ob_child_eval->object_to_world().ptr(), true, false);
548 invert_m4_m4(ob_child->parentinv,
549 BKE_object_calc_parent(depsgraph, scene, ob_child_eval).ptr());
550 /* Copy result of BKE_object_apply_mat4(). */
551 BKE_object_transform_copy(ob_child, ob_child_eval);
552 /* Make sure evaluated object is in a consistent state with the original one.
553 * It might be needed for applying transform on its children. */
554 copy_m4_m4(ob_child_eval->parentinv, ob_child->parentinv);
555 BKE_object_eval_transform_all(depsgraph, scene_eval, ob_child_eval);
556 /* Tag for update.
557 * This is because parent matrix did change, so in theory the child object might now be
558 * evaluated to a different location in another editing context. */
560 }
561 }
562}
563
565 Object *object,
566 Object **sorted_objects,
567 int *object_index)
568{
569 if (!ELEM(object->parent, nullptr, root_object)) {
571 root_object, object->parent, sorted_objects, object_index);
572 }
573 if (object->id.tag & ID_TAG_DOIT) {
574 sorted_objects[*object_index] = object;
575 (*object_index)++;
576 object->id.tag &= ~ID_TAG_DOIT;
577 }
578}
579
581{
582 Main *bmain = CTX_data_main(C);
583
584 /* Count all objects, but also tag all the selected ones. */
585 BKE_main_id_tag_all(bmain, ID_TAG_DOIT, false);
586 int objects_num = 0;
587 CTX_DATA_BEGIN (C, Object *, object, selected_editable_objects) {
588 object->id.tag |= ID_TAG_DOIT;
589 objects_num++;
590 }
592 if (objects_num == 0) {
593 return {};
594 }
595
596 /* Append all the objects. */
597 Array<Object *> sorted_objects(objects_num);
598 int object_index = 0;
599 CTX_DATA_BEGIN (C, Object *, object, selected_editable_objects) {
600 if ((object->id.tag & ID_TAG_DOIT) == 0) {
601 continue;
602 }
603 append_sorted_object_parent_hierarchy(object, object, sorted_objects.data(), &object_index);
604 }
606
607 return sorted_objects;
608}
609
614{
616
617 if (ELEM(nullptr, obact, obact->data)) {
618 return false;
619 }
620
621 if (ID_REAL_USERS(obact->data) == 1) {
622 return false;
623 }
624
625 bool all_objects_same_data = true;
626 bool obact_selected = false;
627
628 CTX_DATA_BEGIN (C, Object *, ob, selected_editable_objects) {
629 if (ob->data != obact->data) {
630 all_objects_same_data = false;
631 break;
632 }
633
634 if (ob == obact) {
635 obact_selected = true;
636 }
637 }
639
640 return all_objects_same_data && obact_selected;
641}
642
649{
652
653 /* Counting the number of objects is valid since it's known the
654 * selection is only made up of users of the active objects data. */
655 return (ID_REAL_USERS(ob->data) > CTX_DATA_COUNT(C, selected_editable_objects));
656}
657
659 ReportList *reports,
660 bool apply_loc,
661 bool apply_rot,
662 bool apply_scale,
663 bool do_props,
664 bool do_single_user)
665{
666 Main *bmain = CTX_data_main(C);
667 Scene *scene = CTX_data_scene(C);
669 float rsmat[3][3], obmat[3][3], iobmat[3][3], mat[4][4], scale;
670 bool changed = true;
671 bool const do_multi_user = apply_objects_internal_can_multiuser(C);
672 float obact_invmat[4][4], obact_parent[4][4], obact_parentinv[4][4];
673
674 /* Only used when do_multi_user is set. */
675 Object *obact = nullptr;
676 bool make_single_user = false;
677
678 if (do_multi_user) {
679 obact = CTX_data_active_object(C);
680 invert_m4_m4(obact_invmat, obact->object_to_world().ptr());
681
682 copy_m4_m4(obact_parent, BKE_object_calc_parent(depsgraph, scene, obact).ptr());
683 copy_m4_m4(obact_parentinv, obact->parentinv);
684
686 if (do_single_user) {
687 make_single_user = true;
688 }
689 else {
690 ID *obact_data = static_cast<ID *>(obact->data);
691 BKE_reportf(reports,
692 RPT_ERROR,
693 R"(Cannot apply to a multi user: Object "%s", %s "%s", aborting)",
694 obact->id.name + 2,
695 BKE_idtype_idcode_to_name(GS(obact_data->name)),
696 obact_data->name + 2);
697 return OPERATOR_CANCELLED;
698 }
699 }
700 }
701
702 /* first check if we can execute */
703 CTX_DATA_BEGIN (C, Object *, ob, selected_editable_objects) {
704 if (ELEM(ob->type,
705 OB_MESH,
708 OB_MBALL,
710 OB_SURF,
711 OB_FONT,
712 OB_CURVES,
715 {
716 ID *obdata = static_cast<ID *>(ob->data);
717 if (!do_multi_user && ID_REAL_USERS(obdata) > 1) {
718 BKE_reportf(reports,
719 RPT_ERROR,
720 R"(Cannot apply to a multi user: Object "%s", %s "%s", aborting)",
721 ob->id.name + 2,
723 obdata->name + 2);
724 changed = false;
725 }
726
727 if (!ID_IS_EDITABLE(obdata) || ID_IS_OVERRIDE_LIBRARY(obdata)) {
728 BKE_reportf(reports,
729 RPT_ERROR,
730 R"(Cannot apply to library or override data: Object "%s", %s "%s", aborting)",
731 ob->id.name + 2,
733 obdata->name + 2);
734 changed = false;
735 }
736 }
737
738 if (ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF)) {
739 ID *obdata = static_cast<ID *>(ob->data);
740 Curve *cu = static_cast<Curve *>(ob->data);
741
742 if (((ob->type == OB_CURVES_LEGACY) && !(cu->flag & CU_3D)) && (apply_rot || apply_loc)) {
744 reports,
745 RPT_ERROR,
746 R"(Rotation/Location cannot apply to a 2D curve: Object "%s", %s "%s", aborting)",
747 ob->id.name + 2,
749 obdata->name + 2);
750 changed = false;
751 }
752 if (cu->key) {
753 BKE_reportf(reports,
754 RPT_ERROR,
755 R"(Can't apply to a curve with shape-keys: Object "%s", %s "%s", aborting)",
756 ob->id.name + 2,
758 obdata->name + 2);
759 changed = false;
760 }
761 }
762
763 if (ob->type == OB_FONT) {
764 if (apply_rot || apply_loc) {
765 BKE_reportf(reports,
766 RPT_ERROR,
767 "Text objects can only have their scale applied: \"%s\"",
768 ob->id.name + 2);
769 changed = false;
770 }
771 }
772
773 if (ob->type == OB_LAMP) {
774 Light *la = static_cast<Light *>(ob->data);
775 if (la->type == LA_AREA) {
776 if (apply_rot || apply_loc) {
777 BKE_reportf(reports,
778 RPT_ERROR,
779 "Area Lights can only have scale applied: \"%s\"",
780 ob->id.name + 2);
781 changed = false;
782 }
783 }
784 }
785 }
787
788 if (!changed) {
789 return OPERATOR_CANCELLED;
790 }
791
792 changed = false;
793
794 /* now execute */
795
796 if (make_single_user) {
797 /* Make single user. */
798 single_obdata_user_make(bmain, scene, obact);
802 }
803
805 if (objects.is_empty()) {
806 return OPERATOR_CANCELLED;
807 }
808
809 bool has_non_invertable_matrix = false;
810
811 for (Object *ob : objects) {
812 /* calculate rotation/scale matrix */
813 if (apply_scale && apply_rot) {
814 BKE_object_to_mat3(ob, rsmat);
815 }
816 else if (apply_scale) {
817 BKE_object_scale_to_mat3(ob, rsmat);
818 }
819 else if (apply_rot) {
820 float tmat[3][3], timat[3][3];
821
822 /* simple rotation matrix */
823 BKE_object_rot_to_mat3(ob, rsmat, true);
824
825 /* correct for scale, note mul_m3_m3m3 has swapped args! */
826 BKE_object_scale_to_mat3(ob, tmat);
827 if (!invert_m3_m3(timat, tmat)) {
829 reports,
831 "Object \"%s\" has a non-invertible transformation matrix, not applying transform",
832 ob->id.name + 2);
833 has_non_invertable_matrix = true;
834 continue;
835 }
836 mul_m3_m3m3(rsmat, timat, rsmat);
837 mul_m3_m3m3(rsmat, rsmat, tmat);
838 }
839 else {
840 unit_m3(rsmat);
841 }
842
843 copy_m4_m3(mat, rsmat);
844
845 /* calculate translation */
846 if (apply_loc) {
847 add_v3_v3v3(mat[3], ob->loc, ob->dloc);
848
849 if (!(apply_scale && apply_rot)) {
850 float tmat[3][3];
851 /* correct for scale and rotation that is still applied */
852 BKE_object_to_mat3(ob, obmat);
853 invert_m3_m3(iobmat, obmat);
854 mul_m3_m3m3(tmat, rsmat, iobmat);
855 mul_m3_v3(tmat, mat[3]);
856 }
857 }
858
859 /* apply to object data */
860 if (do_multi_user && ob != obact) {
861 /* Don't apply, just set the new object data, the correct
862 * transformations will happen later. */
863 id_us_min((ID *)ob->data);
864 ob->data = obact->data;
865 id_us_plus((ID *)ob->data);
866 }
867 else if (ob->type == OB_MESH) {
868 Mesh *mesh = static_cast<Mesh *>(ob->data);
869
870 if (apply_scale) {
872 }
873
874 /* adjust data */
875 bke::mesh_transform(*mesh, float4x4(mat), true);
876 }
877 else if (ob->type == OB_ARMATURE) {
878 bArmature *arm = static_cast<bArmature *>(ob->data);
879 BKE_armature_transform(arm, mat, do_props);
880 }
881 else if (ob->type == OB_LATTICE) {
882 Lattice *lt = static_cast<Lattice *>(ob->data);
883
884 BKE_lattice_transform(lt, mat, true);
885 }
886 else if (ob->type == OB_MBALL) {
887 MetaBall *mb = static_cast<MetaBall *>(ob->data);
888 BKE_mball_transform(mb, mat, do_props);
889 }
890 else if (ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF)) {
891 Curve *cu = static_cast<Curve *>(ob->data);
892 scale = mat3_to_scale(rsmat);
893 BKE_curve_transform_ex(cu, mat, true, do_props, scale);
894 }
895 else if (ob->type == OB_FONT) {
896 Curve *cu = static_cast<Curve *>(ob->data);
897
898 scale = mat3_to_scale(rsmat);
899
900 for (int i = 0; i < cu->totbox; i++) {
901 TextBox *tb = &cu->tb[i];
902 tb->x *= scale;
903 tb->y *= scale;
904 tb->w *= scale;
905 tb->h *= scale;
906 }
907
908 if (do_props) {
909 cu->fsize *= scale;
910 }
911 }
912 else if (ob->type == OB_CURVES) {
913 Curves &curves = *static_cast<Curves *>(ob->data);
914 curves.geometry.wrap().transform(float4x4(mat));
915 curves.geometry.wrap().calculate_bezier_auto_handles();
916 }
917 else if (ob->type == OB_GREASE_PENCIL) {
918 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(ob->data);
919
920 const float scalef = mat4_to_scale(mat);
921
922 for (const int layer_i : grease_pencil.layers().index_range()) {
923 bke::greasepencil::Layer &layer = grease_pencil.layer(layer_i);
924 const float4x4 layer_to_object = layer.to_object_space(*ob);
925 const float4x4 object_to_layer = math::invert(layer_to_object);
927 frames.foreach_item(
929 GreasePencilDrawingBase *base = grease_pencil.drawing(frame.drawing_index);
930 if (base->type != GP_DRAWING) {
931 return;
932 }
934 reinterpret_cast<GreasePencilDrawing *>(base)->wrap();
936 MutableSpan<float> radii = drawing.radii_for_write();
937 threading::parallel_for(radii.index_range(), 8192, [&](const IndexRange range) {
938 for (const int i : range) {
939 radii[i] *= scalef;
940 }
941 });
942
943 curves.transform(object_to_layer * float4x4(mat) * layer_to_object);
944 curves.calculate_bezier_auto_handles();
945 });
946 }
947 }
948 else if (ob->type == OB_POINTCLOUD) {
949 PointCloud &pointcloud = *static_cast<PointCloud *>(ob->data);
950 math::transform_points(float4x4(mat), pointcloud.positions_for_write());
951 pointcloud.tag_positions_changed();
952 }
953 else if (ob->type == OB_CAMERA) {
954 MovieClip *clip = BKE_object_movieclip_get(scene, ob, false);
955
956 /* applying scale on camera actually scales clip's reconstruction.
957 * of there's clip assigned to camera nothing to do actually.
958 */
959 if (!clip) {
960 continue;
961 }
962
963 if (apply_scale) {
965 }
966 }
967 else if (ob->type == OB_EMPTY) {
968 /* It's possible for empties too, even though they don't
969 * really have obdata, since we can simply apply the maximum
970 * scaling to the empty's drawsize.
971 *
972 * Core Assumptions:
973 * 1) Most scaled empties have uniform scaling
974 * (i.e. for visibility reasons), AND/OR
975 * 2) Preserving non-uniform scaling is not that important,
976 * and is something that many users would be willing to
977 * sacrifice for having an easy way to do this.
978 */
979
980 if (apply_scale) {
981 float max_scale = max_fff(fabsf(ob->scale[0]), fabsf(ob->scale[1]), fabsf(ob->scale[2]));
982 ob->empty_drawsize *= max_scale;
983 }
984 }
985 else if (ob->type == OB_LAMP) {
986 Light *la = static_cast<Light *>(ob->data);
987 if (la->type != LA_AREA) {
988 continue;
989 }
990
991 bool keeps_aspect_ratio = compare_ff_relative(rsmat[0][0], rsmat[1][1], FLT_EPSILON, 64);
992 if ((la->area_shape == LA_AREA_SQUARE) && !keeps_aspect_ratio) {
994 la->area_sizey = la->area_size;
995 }
996 else if ((la->area_shape == LA_AREA_DISK) && !keeps_aspect_ratio) {
998 la->area_sizey = la->area_size;
999 }
1000
1001 la->area_size *= rsmat[0][0];
1002 la->area_sizey *= rsmat[1][1];
1003 la->area_sizez *= rsmat[2][2];
1004
1005 /* Explicit tagging is required for Lamp ID because, unlike Geometry IDs like Mesh,
1006 * it is not covered by the `ID_RECALC_GEOMETRY` flag applied to the object at the end
1007 * of this loop. */
1009 }
1010 else {
1011 continue;
1012 }
1013
1014 if (do_multi_user && ob != obact) {
1015 float _obmat[4][4], _iobmat[4][4];
1016 float _mat[4][4];
1017
1018 copy_m4_m4(_obmat, ob->object_to_world().ptr());
1019 invert_m4_m4(_iobmat, _obmat);
1020
1021 copy_m4_m4(_mat, _obmat);
1022 mul_m4_m4_post(_mat, obact_invmat);
1023 mul_m4_m4_post(_mat, obact_parent);
1024 mul_m4_m4_post(_mat, obact_parentinv);
1025
1026 if (apply_loc && apply_scale && apply_rot) {
1027 BKE_object_apply_mat4(ob, _mat, false, true);
1028 }
1029 else {
1030 Object ob_temp = dna::shallow_copy(*ob);
1031 BKE_object_apply_mat4(&ob_temp, _mat, false, true);
1032
1033 if (apply_loc) {
1034 copy_v3_v3(ob->loc, ob_temp.loc);
1035 }
1036
1037 if (apply_scale) {
1038 copy_v3_v3(ob->scale, ob_temp.scale);
1039 }
1040
1041 if (apply_rot) {
1042 copy_v4_v4(ob->quat, ob_temp.quat);
1043 copy_v3_v3(ob->rot, ob_temp.rot);
1044 copy_v3_v3(ob->rotAxis, ob_temp.rotAxis);
1045 ob->rotAngle = ob_temp.rotAngle;
1046 }
1047 }
1048 }
1049 else {
1050 if (apply_loc) {
1051 zero_v3(ob->loc);
1052 zero_v3(ob->dloc);
1053 }
1054 if (apply_scale) {
1055 copy_v3_fl(ob->scale, 1.0f);
1056 copy_v3_fl(ob->dscale, 1.0f);
1057 }
1058 if (apply_rot) {
1059 zero_v3(ob->rot);
1060 zero_v3(ob->drot);
1061 unit_qt(ob->quat);
1062 unit_qt(ob->dquat);
1063 unit_axis_angle(ob->rotAxis, &ob->rotAngle);
1064 unit_axis_angle(ob->drotAxis, &ob->drotAngle);
1065 }
1066 }
1067
1068 Object *ob_eval = DEG_get_evaluated(depsgraph, ob);
1069 BKE_object_transform_copy(ob_eval, ob);
1070
1071 BKE_object_where_is_calc(depsgraph, scene, ob_eval);
1072 if (ob->type == OB_ARMATURE) {
1073 /* needed for bone parents */
1074 BKE_armature_copy_bone_transforms(static_cast<bArmature *>(ob_eval->data),
1075 static_cast<bArmature *>(ob->data));
1076 BKE_pose_where_is(depsgraph, scene, ob_eval);
1077 }
1078
1079 ignore_parent_tx(bmain, depsgraph, scene, ob);
1080
1082
1083 changed = true;
1084 }
1085
1086 if (!changed) {
1087 BKE_report(reports, RPT_WARNING, "Objects have no data to transform");
1088 return OPERATOR_CANCELLED;
1089 }
1090 if (has_non_invertable_matrix) {
1091 BKE_report(reports, RPT_WARNING, "Failed to apply rotation to some of the objects");
1092 }
1093
1095 return OPERATOR_FINISHED;
1096}
1097
1099{
1100 Scene *scene = CTX_data_scene(C);
1102 bool changed = false;
1103
1104 CTX_DATA_BEGIN (C, Object *, ob, selected_editable_objects) {
1105 Object *ob_eval = DEG_get_evaluated(depsgraph, ob);
1106 BKE_object_where_is_calc(depsgraph, scene, ob_eval);
1107 BKE_object_apply_mat4(ob_eval, ob_eval->object_to_world().ptr(), true, true);
1108 BKE_object_transform_copy(ob, ob_eval);
1109
1110 /* update for any children that may get moved */
1112
1113 changed = true;
1114 }
1116
1117 if (!changed) {
1118 return OPERATOR_CANCELLED;
1119 }
1120
1122 return OPERATOR_FINISHED;
1123}
1124
1126{
1127 /* identifiers */
1128 ot->name = "Apply Visual Transform";
1129 ot->description = "Apply the object's visual transformation to its data";
1130 ot->idname = "OBJECT_OT_visual_transform_apply";
1131
1132 /* API callbacks. */
1135
1136 /* flags */
1137 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1138}
1139
1141{
1142 const bool loc = RNA_boolean_get(op->ptr, "location");
1143 const bool rot = RNA_boolean_get(op->ptr, "rotation");
1144 const bool sca = RNA_boolean_get(op->ptr, "scale");
1145 const bool do_props = RNA_boolean_get(op->ptr, "properties");
1146 const bool do_single_user = RNA_boolean_get(op->ptr, "isolate_users");
1147
1148 if (loc || rot || sca) {
1149 return apply_objects_internal(C, op->reports, loc, rot, sca, do_props, do_single_user);
1150 }
1151 /* allow for redo */
1152 return OPERATOR_FINISHED;
1153}
1154
1156 wmOperator *op,
1157 const wmEvent * /*event*/)
1158{
1160
1161 bool can_handle_multiuser = apply_objects_internal_can_multiuser(C);
1162 bool need_single_user = can_handle_multiuser && apply_objects_internal_need_single_user(C);
1163
1164 if ((ob != nullptr) && (ob->data != nullptr) && need_single_user) {
1165 PropertyRNA *prop = RNA_struct_find_property(op->ptr, "isolate_users");
1166 if (!RNA_property_is_set(op->ptr, prop)) {
1167 RNA_property_boolean_set(op->ptr, prop, true);
1168 }
1169 if (RNA_property_boolean_get(op->ptr, prop)) {
1171 op,
1172 IFACE_("Apply Object Transformations"),
1173 IFACE_("Warning: Multiple objects share the same data.\nMake "
1174 "single user and then apply transformations?"),
1175 IFACE_("Apply"),
1177 false);
1178 }
1179 }
1180 return object_transform_apply_exec(C, op);
1181}
1182
1184{
1185 /* identifiers */
1186 ot->name = "Apply Object Transform";
1187 ot->description = "Apply the object's transformation to its data";
1188 ot->idname = "OBJECT_OT_transform_apply";
1189
1190 /* API callbacks. */
1193 ot->poll = ED_operator_objectmode;
1194
1195 /* flags */
1196 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1197
1198 RNA_def_boolean(ot->srna, "location", true, "Location", "");
1199 RNA_def_boolean(ot->srna, "rotation", true, "Rotation", "");
1200 RNA_def_boolean(ot->srna, "scale", true, "Scale", "");
1201 RNA_def_boolean(ot->srna,
1202 "properties",
1203 true,
1204 "Apply Properties",
1205 "Modify properties such as curve vertex radius, font size and bone envelope");
1206 PropertyRNA *prop = RNA_def_boolean(ot->srna,
1207 "isolate_users",
1208 false,
1209 "Isolate Multi User Data",
1210 "Create new object-data users if needed");
1213}
1214
1216
1217/* -------------------------------------------------------------------- */
1220
1222{
1223 CTX_DATA_BEGIN (C, Object *, ob, selected_editable_objects) {
1224 if (ob->parent == nullptr) {
1225 continue;
1226 }
1227
1230 }
1232
1234
1235 return OPERATOR_FINISHED;
1236}
1237
1239{
1240 /* identifiers */
1241 ot->name = "Apply Parent Inverse";
1242 ot->description = "Apply the object's parent inverse to its data";
1243 ot->idname = "OBJECT_OT_parent_inverse_apply";
1244
1245 /* API callbacks. */
1247 ot->poll = ED_operator_objectmode;
1248
1249 /* flags */
1250 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1251}
1252
1254
1255/* -------------------------------------------------------------------- */
1258
1259enum {
1265};
1266
1268{
1269 if (values.is_empty()) {
1270 return float3(0);
1271 }
1272 /* TODO: Use a method that avoids overflow. */
1273 return std::accumulate(values.begin(), values.end(), float3(0)) / values.size();
1274}
1275
1276static void translate_positions(MutableSpan<float3> positions, const float3 &translation)
1277{
1278 threading::parallel_for(positions.index_range(), 2048, [&](const IndexRange range) {
1279 for (float3 &position : positions.slice(range)) {
1280 position += translation;
1281 }
1282 });
1283}
1284
1286{
1287 Main *bmain = CTX_data_main(C);
1288 Scene *scene = CTX_data_scene(C);
1290 Object *obedit = CTX_data_edit_object(C);
1292 float3 cent, cent_neg, centn;
1293 const float *cursor = scene->cursor.location;
1294 int centermode = RNA_enum_get(op->ptr, "type");
1295
1296 /* keep track of what is changed */
1297 int tot_change = 0, tot_lib_error = 0, tot_multiuser_arm_error = 0;
1298
1299 if (obedit && centermode != GEOMETRY_TO_ORIGIN) {
1300 BKE_report(op->reports, RPT_ERROR, "Operation cannot be performed in edit mode");
1301 return OPERATOR_CANCELLED;
1302 }
1303
1304 int around;
1305 {
1306 PropertyRNA *prop_center = RNA_struct_find_property(op->ptr, "center");
1307 if (RNA_property_is_set(op->ptr, prop_center)) {
1308 around = RNA_property_enum_get(op->ptr, prop_center);
1309 }
1310 else {
1312 around = V3D_AROUND_CENTER_BOUNDS;
1313 }
1314 else {
1315 around = V3D_AROUND_CENTER_MEDIAN;
1316 }
1317 RNA_property_enum_set(op->ptr, prop_center, around);
1318 }
1319 }
1320
1321 zero_v3(cent);
1322
1323 if (obedit) {
1324 if (obedit->type == OB_MESH) {
1325 Mesh *mesh = static_cast<Mesh *>(obedit->data);
1326 BMEditMesh *em = mesh->runtime->edit_mesh.get();
1327 BMVert *eve;
1328 BMIter iter;
1329
1330 if (centermode == ORIGIN_TO_CURSOR) {
1331 copy_v3_v3(cent, cursor);
1332 invert_m4_m4(obedit->runtime->world_to_object.ptr(), obedit->object_to_world().ptr());
1333 mul_m4_v3(obedit->world_to_object().ptr(), cent);
1334 }
1335 else {
1336 if (around == V3D_AROUND_CENTER_BOUNDS) {
1337 float min[3], max[3];
1339 BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) {
1340 minmax_v3v3_v3(min, max, eve->co);
1341 }
1342 mid_v3_v3v3(cent, min, max);
1343 }
1344 else { /* #V3D_AROUND_CENTER_MEDIAN. */
1345 if (em->bm->totvert) {
1346 const float total_div = 1.0f / float(em->bm->totvert);
1347 BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) {
1348 madd_v3_v3fl(cent, eve->co, total_div);
1349 }
1350 }
1351 }
1352 }
1353
1354 BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) {
1355 sub_v3_v3(eve->co, cent);
1356 }
1357
1359 tot_change++;
1361 }
1362 }
1363
1365 if (objects.is_empty()) {
1366 return OPERATOR_CANCELLED;
1367 }
1368
1369 /* reset flags */
1370 for (const int object_index : objects.index_range()) {
1371 Object *ob = objects[object_index];
1372 ob->flag &= ~OB_DONE;
1373
1374 /* move active first */
1375 if (ob == obact && objects.size() > 1) {
1376 memmove(&objects[1], objects.data(), object_index * sizeof(Object *));
1377 objects[0] = ob;
1378 }
1379 }
1380
1381 LISTBASE_FOREACH (Object *, tob, &bmain->objects) {
1382 if (tob->data) {
1383 ((ID *)tob->data)->tag &= ~ID_TAG_DOIT;
1384 }
1385 if (tob->instance_collection) {
1386 ((ID *)tob->instance_collection)->tag &= ~ID_TAG_DOIT;
1387 }
1388 }
1389
1390 for (Object *ob : objects) {
1391 if (ob->flag & OB_DONE) {
1392 continue;
1393 }
1394
1395 bool do_inverse_offset = false;
1396 ob->flag |= OB_DONE;
1397
1398 if (centermode == ORIGIN_TO_CURSOR) {
1399 copy_v3_v3(cent, cursor);
1400 invert_m4_m4(ob->runtime->world_to_object.ptr(), ob->object_to_world().ptr());
1401 mul_m4_v3(ob->world_to_object().ptr(), cent);
1402 }
1403
1404 if (ob->data == nullptr) {
1405 /* Special support for instanced collections. */
1406 if ((ob->transflag & OB_DUPLICOLLECTION) && ob->instance_collection &&
1407 (ob->instance_collection->id.tag & ID_TAG_DOIT) == 0)
1408 {
1409 if (!BKE_id_is_editable(bmain, &ob->instance_collection->id)) {
1410 tot_lib_error++;
1411 }
1412 else {
1413 if (centermode == ORIGIN_TO_CURSOR) {
1414 /* done */
1415 }
1416 else {
1417 float3 min, max;
1418 /* only bounds support */
1420 BKE_object_minmax_dupli(depsgraph, scene, ob, min, max, true);
1421 mid_v3_v3v3(cent, min, max);
1422 invert_m4_m4(ob->runtime->world_to_object.ptr(), ob->object_to_world().ptr());
1423 mul_m4_v3(ob->world_to_object().ptr(), cent);
1424 }
1425
1426 add_v3_v3(ob->instance_collection->instance_offset, cent);
1427
1428 tot_change++;
1429 ob->instance_collection->id.tag |= ID_TAG_DOIT;
1430 do_inverse_offset = true;
1431 }
1432 }
1433 }
1434 else if (!ID_IS_EDITABLE(ob->data) || ID_IS_OVERRIDE_LIBRARY(ob->data)) {
1435 tot_lib_error++;
1436 }
1437 else if (ob->type == OB_MESH) {
1438 if (obedit == nullptr) {
1439 Mesh *mesh = static_cast<Mesh *>(ob->data);
1440
1441 if (centermode == ORIGIN_TO_CURSOR) {
1442 /* done */
1443 }
1444 else if (centermode == ORIGIN_TO_CENTER_OF_MASS_SURFACE) {
1446 }
1447 else if (centermode == ORIGIN_TO_CENTER_OF_MASS_VOLUME) {
1449 }
1450 else if (around == V3D_AROUND_CENTER_BOUNDS) {
1451 if (const std::optional<Bounds<float3>> bounds = mesh->bounds_min_max()) {
1452 cent = math::midpoint(bounds->min, bounds->max);
1453 }
1454 }
1455 else { /* #V3D_AROUND_CENTER_MEDIAN. */
1457 }
1458
1459 negate_v3_v3(cent_neg, cent);
1460 bke::mesh_translate(*mesh, cent_neg, true);
1461
1462 tot_change++;
1463 mesh->id.tag |= ID_TAG_DOIT;
1464 do_inverse_offset = true;
1465 }
1466 }
1467 else if (ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF)) {
1468 Curve *cu = static_cast<Curve *>(ob->data);
1469
1470 if (centermode == ORIGIN_TO_CURSOR) {
1471 /* done */
1472 }
1473 else if (around == V3D_AROUND_CENTER_BOUNDS) {
1474 if (std::optional<Bounds<float3>> bounds = BKE_curve_minmax(cu, true)) {
1475 cent = math::midpoint(bounds->min, bounds->max);
1476 }
1477 }
1478 else { /* #V3D_AROUND_CENTER_MEDIAN. */
1479 BKE_curve_center_median(cu, cent);
1480 }
1481
1482 /* don't allow Z change if curve is 2D */
1483 if ((ob->type == OB_CURVES_LEGACY) && !(cu->flag & CU_3D)) {
1484 cent[2] = 0.0;
1485 }
1486
1487 negate_v3_v3(cent_neg, cent);
1488 BKE_curve_translate(cu, cent_neg, true);
1489
1490 tot_change++;
1491 cu->id.tag |= ID_TAG_DOIT;
1492 do_inverse_offset = true;
1493
1494 if (obedit) {
1495 if (centermode == GEOMETRY_TO_ORIGIN) {
1497 }
1498 break;
1499 }
1500 }
1501 else if (ob->type == OB_FONT) {
1502 /* Get from bounding-box. */
1503
1504 Curve *cu = static_cast<Curve *>(ob->data);
1505 std::optional<Bounds<float3>> bounds = BKE_curve_minmax(cu, true);
1506
1507 if (!bounds && (centermode != ORIGIN_TO_CURSOR)) {
1508 /* Do nothing. */
1509 }
1510 else {
1511 if (centermode == ORIGIN_TO_CURSOR) {
1512 /* Done. */
1513 }
1514 else {
1515 /* extra 0.5 is the height o above line */
1516 cent = math::midpoint(bounds->min, bounds->max);
1517 }
1518
1519 cent[2] = 0.0f;
1520
1521 cu->xof = cu->xof - cent[0];
1522 cu->yof = cu->yof - cent[1];
1523
1524 tot_change++;
1525 cu->id.tag |= ID_TAG_DOIT;
1526 do_inverse_offset = true;
1527 }
1528 }
1529 else if (ob->type == OB_ARMATURE) {
1530 bArmature *arm = static_cast<bArmature *>(ob->data);
1531
1532 if (ID_REAL_USERS(arm) > 1) {
1533#if 0
1534 BKE_report(op->reports, RPT_ERROR, "Cannot apply to a multi user armature");
1535 return;
1536#endif
1537 tot_multiuser_arm_error++;
1538 }
1539 else {
1540 /* Function to recenter armatures in editarmature.c
1541 * Bone + object locations are handled there.
1542 */
1543 ED_armature_origin_set(bmain, ob, cursor, centermode, around);
1544
1545 tot_change++;
1546 arm->id.tag |= ID_TAG_DOIT;
1547 // do_inverse_offset = true; /* docenter_armature() handles this. */
1548
1549 Object *ob_eval = DEG_get_evaluated(depsgraph, ob);
1550 BKE_object_transform_copy(ob_eval, ob);
1551 BKE_armature_copy_bone_transforms(static_cast<bArmature *>(ob_eval->data),
1552 static_cast<bArmature *>(ob->data));
1553 BKE_object_where_is_calc(depsgraph, scene, ob_eval);
1554 BKE_pose_where_is(depsgraph, scene, ob_eval); /* needed for bone parents */
1555
1556 ignore_parent_tx(bmain, depsgraph, scene, ob);
1557
1558 if (obedit) {
1559 break;
1560 }
1561 }
1562 }
1563 else if (ob->type == OB_MBALL) {
1564 MetaBall *mb = static_cast<MetaBall *>(ob->data);
1565
1566 if (centermode == ORIGIN_TO_CURSOR) {
1567 /* done */
1568 }
1569 else if (around == V3D_AROUND_CENTER_BOUNDS) {
1570 BKE_mball_center_bounds(mb, cent);
1571 }
1572 else { /* #V3D_AROUND_CENTER_MEDIAN. */
1573 BKE_mball_center_median(mb, cent);
1574 }
1575
1576 negate_v3_v3(cent_neg, cent);
1577 BKE_mball_translate(mb, cent_neg);
1578
1579 tot_change++;
1580 mb->id.tag |= ID_TAG_DOIT;
1581 do_inverse_offset = true;
1582
1583 if (obedit) {
1584 if (centermode == GEOMETRY_TO_ORIGIN) {
1586 }
1587 break;
1588 }
1589 }
1590 else if (ob->type == OB_LATTICE) {
1591 Lattice *lt = static_cast<Lattice *>(ob->data);
1592
1593 if (centermode == ORIGIN_TO_CURSOR) {
1594 /* done */
1595 }
1596 else if (around == V3D_AROUND_CENTER_BOUNDS) {
1597 if (std::optional<Bounds<float3>> bounds = BKE_lattice_minmax(lt)) {
1598 cent = math::midpoint(bounds->min, bounds->max);
1599 }
1600 }
1601 else { /* #V3D_AROUND_CENTER_MEDIAN. */
1602 BKE_lattice_center_median(lt, cent);
1603 }
1604
1605 negate_v3_v3(cent_neg, cent);
1606 BKE_lattice_translate(lt, cent_neg, true);
1607
1608 tot_change++;
1609 lt->id.tag |= ID_TAG_DOIT;
1610 do_inverse_offset = true;
1611 }
1612 else if (ob->type == OB_CURVES) {
1613 Curves &curves_id = *static_cast<Curves *>(ob->data);
1614 bke::CurvesGeometry &curves = curves_id.geometry.wrap();
1617 {
1618 BKE_report(
1619 op->reports, RPT_WARNING, "Curves Object does not support this set origin operation");
1620 continue;
1621 }
1622
1623 if (curves.is_empty()) {
1624 continue;
1625 }
1626
1627 if (centermode == ORIGIN_TO_CURSOR) {
1628 /* done */
1629 }
1630 else if (around == V3D_AROUND_CENTER_BOUNDS) {
1631 const Bounds<float3> bounds = *curves.bounds_min_max();
1632 cent = math::midpoint(bounds.min, bounds.max);
1633 }
1634 else if (around == V3D_AROUND_CENTER_MEDIAN) {
1635 cent = arithmetic_mean(curves.positions());
1636 }
1637
1638 tot_change++;
1639 curves.translate(-cent);
1640 curves_id.id.tag |= ID_TAG_DOIT;
1641 do_inverse_offset = true;
1642 }
1643 else if (ob->type == OB_GREASE_PENCIL) {
1644 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(ob->data);
1647 {
1648 BKE_report(op->reports,
1650 "Grease Pencil Object does not support this set origin operation");
1651 continue;
1652 }
1653
1654 if (centermode == ORIGIN_TO_CURSOR) {
1655 /* done */
1656 }
1657 else if (around == V3D_AROUND_CENTER_BOUNDS) {
1658 const int current_frame = scene->r.cfra;
1659 const Bounds<float3> bounds = *grease_pencil.bounds_min_max(current_frame);
1660 cent = math::midpoint(bounds.min, bounds.max);
1661 }
1662 else if (around == V3D_AROUND_CENTER_MEDIAN) {
1663 const int current_frame = scene->r.cfra;
1664 float3 center = float3(0.0f);
1665 int total_points = 0;
1666
1667 for (const int layer_i : grease_pencil.layers().index_range()) {
1668 const bke::greasepencil::Layer &layer = grease_pencil.layer(layer_i);
1669 const float4x4 layer_to_object = layer.local_transform();
1670 if (!layer.is_visible()) {
1671 continue;
1672 }
1673 if (const bke::greasepencil::Drawing *drawing = grease_pencil.get_drawing_at(
1674 layer, current_frame))
1675 {
1676 const bke::CurvesGeometry &curves = drawing->strokes();
1677 const Span<float3> positions = curves.positions();
1678
1679 for (const int i : positions.index_range()) {
1680 center += math::transform_point(layer_to_object, positions[i]);
1681 }
1682 total_points += positions.size();
1683 }
1684 }
1685
1686 if (total_points != 0) {
1687 cent = center / total_points;
1688 }
1689 }
1690
1691 tot_change++;
1692
1693 for (const int layer_i : grease_pencil.layers().index_range()) {
1694 bke::greasepencil::Layer &layer = grease_pencil.layer(layer_i);
1695 const float4x4 layer_to_object = layer.local_transform();
1696 const float4x4 object_to_layer = math::invert(layer_to_object);
1698 frames.foreach_item(
1700 GreasePencilDrawingBase *base = grease_pencil.drawing(frame.drawing_index);
1701 if (base->type != GP_DRAWING) {
1702 return;
1703 }
1705 reinterpret_cast<GreasePencilDrawing *>(base)->wrap();
1707
1708 curves.translate(math::transform_direction(object_to_layer, -cent));
1709 curves.calculate_bezier_auto_handles();
1710 });
1711 }
1712
1713 grease_pencil.id.tag |= ID_TAG_DOIT;
1714 do_inverse_offset = true;
1715 }
1716 else if (ob->type == OB_POINTCLOUD) {
1717 PointCloud &pointcloud = *static_cast<PointCloud *>(ob->data);
1718 MutableSpan<float3> positions = pointcloud.positions_for_write();
1721 {
1722 BKE_report(op->reports,
1724 "Point cloud object does not support this set origin operation");
1725 continue;
1726 }
1727
1728 if (centermode == ORIGIN_TO_CURSOR) {
1729 /* Done. */
1730 }
1731 else if (around == V3D_AROUND_CENTER_BOUNDS) {
1732 if (const std::optional<Bounds<float3>> bounds = pointcloud.bounds_min_max()) {
1733 cent = math::midpoint(bounds->min, bounds->max);
1734 }
1735 }
1736 else if (around == V3D_AROUND_CENTER_MEDIAN) {
1737 cent = arithmetic_mean(positions);
1738 }
1739
1740 tot_change++;
1741 translate_positions(positions, -cent);
1742 pointcloud.tag_positions_changed();
1743 pointcloud.id.tag |= ID_TAG_DOIT;
1744 do_inverse_offset = true;
1745 }
1746
1747 /* offset other selected objects */
1748 if (do_inverse_offset && (centermode != GEOMETRY_TO_ORIGIN)) {
1749 float obmat[4][4];
1750
1751 /* was the object data modified
1752 * NOTE: the functions above must set 'cent'. */
1753
1754 /* convert the offset to parent space */
1755 BKE_object_to_mat4(ob, obmat);
1756 mul_v3_mat3_m4v3(centn, obmat, cent); /* omit translation part */
1757
1758 add_v3_v3(ob->loc, centn);
1759
1760 Object *ob_eval = DEG_get_evaluated(depsgraph, ob);
1761 BKE_object_transform_copy(ob_eval, ob);
1762 BKE_object_where_is_calc(depsgraph, scene, ob_eval);
1763 if (ob->type == OB_ARMATURE) {
1764 /* needed for bone parents */
1765 BKE_armature_copy_bone_transforms(static_cast<bArmature *>(ob_eval->data),
1766 static_cast<bArmature *>(ob->data));
1767 BKE_pose_where_is(depsgraph, scene, ob_eval);
1768 }
1769
1770 ignore_parent_tx(bmain, depsgraph, scene, ob);
1771
1772 /* other users? */
1773 // CTX_DATA_BEGIN (C, Object *, ob_other, selected_editable_objects)
1774 //{
1775
1776 /* use existing context looper */
1777 for (Object *ob_other : objects) {
1778 if ((ob_other->flag & OB_DONE) == 0 &&
1779 ((ob->data && (ob->data == ob_other->data)) ||
1780 (ob->instance_collection == ob_other->instance_collection &&
1781 (ob->transflag | ob_other->transflag) & OB_DUPLICOLLECTION)))
1782 {
1783 ob_other->flag |= OB_DONE;
1785
1787 centn, ob_other->object_to_world().ptr(), cent); /* omit translation part */
1788 add_v3_v3(ob_other->loc, centn);
1789
1790 Object *ob_other_eval = DEG_get_evaluated(depsgraph, ob_other);
1791 BKE_object_transform_copy(ob_other_eval, ob_other);
1792 BKE_object_where_is_calc(depsgraph, scene, ob_other_eval);
1793 if (ob_other->type == OB_ARMATURE) {
1794 /* needed for bone parents */
1795 BKE_armature_copy_bone_transforms(static_cast<bArmature *>(ob_eval->data),
1796 static_cast<bArmature *>(ob->data));
1797 BKE_pose_where_is(depsgraph, scene, ob_other_eval);
1798 }
1799 ignore_parent_tx(bmain, depsgraph, scene, ob_other);
1800 }
1801 }
1802 // CTX_DATA_END;
1803 }
1804 }
1805
1806 LISTBASE_FOREACH (Object *, tob, &bmain->objects) {
1807 if (tob->data && (((ID *)tob->data)->tag & ID_TAG_DOIT)) {
1810 }
1811 /* Special support for dupli-groups. */
1812 else if (tob->instance_collection && tob->instance_collection->id.tag & ID_TAG_DOIT) {
1814 DEG_id_tag_update(&tob->instance_collection->id, ID_RECALC_SYNC_TO_EVAL);
1815 }
1816 }
1817
1818 if (tot_change) {
1820 }
1821
1822 /* Warn if any errors occurred */
1823 if (tot_lib_error + tot_multiuser_arm_error) {
1824 BKE_reportf(op->reports,
1826 "%i object(s) not centered, %i changed:",
1827 tot_lib_error + tot_multiuser_arm_error,
1828 tot_change);
1829 if (tot_lib_error) {
1830 BKE_reportf(op->reports, RPT_WARNING, "|%i linked library object(s)", tot_lib_error);
1831 }
1832 if (tot_multiuser_arm_error) {
1834 op->reports, RPT_WARNING, "|%i multiuser armature object(s)", tot_multiuser_arm_error);
1835 }
1836 }
1837
1838 return OPERATOR_FINISHED;
1839}
1840
1842{
1843 static const EnumPropertyItem prop_set_center_types[] = {
1845 "GEOMETRY_ORIGIN",
1846 0,
1847 "Geometry to Origin",
1848 "Move object geometry to object origin"},
1850 "ORIGIN_GEOMETRY",
1851 0,
1852 "Origin to Geometry",
1853 "Calculate the center of geometry based on the current pivot point (median, otherwise "
1854 "bounding box)"},
1856 "ORIGIN_CURSOR",
1857 0,
1858 "Origin to 3D Cursor",
1859 "Move object origin to position of the 3D cursor"},
1860 /* Intentional naming mismatch since some scripts refer to this. */
1862 "ORIGIN_CENTER_OF_MASS",
1863 0,
1864 "Origin to Center of Mass (Surface)",
1865 "Calculate the center of mass from the surface area"},
1867 "ORIGIN_CENTER_OF_VOLUME",
1868 0,
1869 "Origin to Center of Mass (Volume)",
1870 "Calculate the center of mass from the volume (must be manifold geometry with consistent "
1871 "normals)"},
1872 {0, nullptr, 0, nullptr, nullptr},
1873 };
1874
1875 static const EnumPropertyItem prop_set_bounds_types[] = {
1876 {V3D_AROUND_CENTER_MEDIAN, "MEDIAN", 0, "Median Center", ""},
1877 {V3D_AROUND_CENTER_BOUNDS, "BOUNDS", 0, "Bounds Center", ""},
1878 {0, nullptr, 0, nullptr, nullptr},
1879 };
1880
1881 /* identifiers */
1882 ot->name = "Set Origin";
1883 ot->description =
1884 "Set the object's origin, by either moving the data, or set to center of data, or use 3D "
1885 "cursor";
1886 ot->idname = "OBJECT_OT_origin_set";
1887
1888 /* API callbacks. */
1889 ot->invoke = WM_menu_invoke;
1890 ot->exec = object_origin_set_exec;
1891
1893
1894 /* flags */
1895 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1896
1897 ot->prop = RNA_def_enum(ot->srna, "type", prop_set_center_types, 0, "Type", "");
1898 RNA_def_enum(ot->srna, "center", prop_set_bounds_types, V3D_AROUND_CENTER_MEDIAN, "Center", "");
1899}
1900
1902
1903/* -------------------------------------------------------------------- */
1910
1912#define USE_RELATIVE_ROTATION
1914#define USE_RENDER_OVERRIDE
1919#define USE_FAKE_DEPTH_INIT
1920
1923 float rot_mat[3][3];
1924 void *obtfm;
1927
1928#ifdef USE_RELATIVE_ROTATION
1929 /* use when translating multiple */
1930 float xform_rot_offset[3][3];
1931#endif
1932};
1933
1949
1950#ifdef USE_FAKE_DEPTH_INIT
1952{
1953 float view_co_a[3], view_co_b[3];
1954 const float2 mval_fl = {float(mval[0]), float(mval[1])};
1955 ED_view3d_win_to_ray(xfd->vc.region, mval_fl, view_co_a, view_co_b);
1956 add_v3_v3(view_co_b, view_co_a);
1957 float center[3] = {0.0f};
1958 int center_tot = 0;
1959 for (XFormAxisItem &item : xfd->object_data) {
1960 const Object *ob = item.ob;
1961 const float *ob_co_a = ob->object_to_world().location();
1962 float ob_co_b[3];
1963 add_v3_v3v3(ob_co_b, ob->object_to_world().location(), ob->object_to_world().ptr()[2]);
1964 float view_isect[3], ob_isect[3];
1965 if (isect_line_line_v3(view_co_a, view_co_b, ob_co_a, ob_co_b, view_isect, ob_isect)) {
1966 add_v3_v3(center, view_isect);
1967 center_tot += 1;
1968 }
1969 }
1970 if (center_tot) {
1971 mul_v3_fl(center, 1.0f / center_tot);
1972 float center_proj[3];
1973 ED_view3d_project_v3(xfd->vc.region, center, center_proj);
1974 xfd->prev.depth = center_proj[2];
1975 xfd->prev.is_depth_valid = true;
1976 }
1977}
1978#endif /* USE_FAKE_DEPTH_INIT */
1979
1980static bool object_is_target_compat(const Object *ob)
1981{
1982 if (ob->type == OB_LAMP) {
1983 const Light *la = static_cast<Light *>(ob->data);
1984 if (ELEM(la->type, LA_SUN, LA_SPOT, LA_AREA)) {
1985 return true;
1986 }
1987 }
1988 /* We might want to enable this later, for now just lights. */
1989#if 0
1990 else if (ob->type == OB_CAMERA) {
1991 return true;
1992 }
1993#endif
1994 return false;
1995}
1996
1998{
1999 XFormAxisData *xfd = static_cast<XFormAxisData *>(op->customdata);
2000
2001#ifdef USE_RENDER_OVERRIDE
2002 if (xfd->depths) {
2004 }
2005#endif
2006
2007 for (XFormAxisItem &item : xfd->object_data) {
2008 MEM_freeN(item.obtfm);
2009 }
2010 MEM_delete(xfd);
2011 op->customdata = nullptr;
2012}
2013
2014/* We may want to expose as alternative to: BKE_object_apply_rotation */
2015static void object_apply_rotation(Object *ob, const float rmat[3][3])
2016{
2017 float size[3];
2018 float loc[3];
2019 float rmat4[4][4];
2020 copy_m4_m3(rmat4, rmat);
2021
2022 copy_v3_v3(size, ob->scale);
2023 copy_v3_v3(loc, ob->loc);
2024 BKE_object_apply_mat4(ob, rmat4, true, true);
2025 copy_v3_v3(ob->scale, size);
2026 copy_v3_v3(ob->loc, loc);
2027}
2028/* We may want to extract this to: BKE_object_apply_location */
2029static void object_apply_location(Object *ob, const float loc[3])
2030{
2031 /* quick but weak */
2032 Object ob_prev = dna::shallow_copy(*ob);
2033 float mat[4][4];
2034 copy_m4_m4(mat, ob->object_to_world().ptr());
2035 copy_v3_v3(mat[3], loc);
2036 BKE_object_apply_mat4(ob, mat, true, true);
2037 copy_v3_v3(mat[3], ob->loc);
2038 *ob = dna::shallow_copy(ob_prev);
2039 copy_v3_v3(ob->loc, mat[3]);
2040}
2041
2043 const float rot_orig[3][3],
2044 const float axis[3],
2045 const float location[3],
2046 const bool z_flip)
2047{
2048 float delta[3];
2049 sub_v3_v3v3(delta, ob->object_to_world().location(), location);
2050 if (normalize_v3(delta) != 0.0f) {
2051 if (z_flip) {
2052 negate_v3(delta);
2053 }
2054
2055 if (len_squared_v3v3(delta, axis) > FLT_EPSILON) {
2056 float delta_rot[3][3];
2057 float final_rot[3][3];
2058 rotation_between_vecs_to_mat3(delta_rot, axis, delta);
2059
2060 mul_m3_m3m3(final_rot, delta_rot, rot_orig);
2061
2062 object_apply_rotation(ob, final_rot);
2063
2064 return true;
2065 }
2066 }
2067 return false;
2068}
2069
2071{
2072 XFormAxisData *xfd = static_cast<XFormAxisData *>(op->customdata);
2073 for (XFormAxisItem &item : xfd->object_data) {
2074 BKE_object_tfm_restore(item.ob, item.obtfm);
2077 }
2078
2080}
2081
2083 wmOperator *op,
2084 const wmEvent *event)
2085{
2088
2089 if (vc.obact == nullptr || !object_is_target_compat(vc.obact)) {
2090 /* Falls back to texture space transform. */
2091 return OPERATOR_PASS_THROUGH;
2092 }
2093
2094#ifdef USE_RENDER_OVERRIDE
2095 int flag2_prev = vc.v3d->flag2;
2097#endif
2098
2099 ViewDepths *depths = nullptr;
2101 vc.depsgraph, vc.region, vc.v3d, nullptr, V3D_DEPTH_NO_GPENCIL, false, &depths);
2102
2103#ifdef USE_RENDER_OVERRIDE
2104 vc.v3d->flag2 = flag2_prev;
2105#endif
2106
2107 if (depths == nullptr) {
2108 BKE_report(op->reports, RPT_WARNING, "Unable to access depth buffer, using view plane");
2109 return OPERATOR_CANCELLED;
2110 }
2111
2113
2114 XFormAxisData *xfd = MEM_new<XFormAxisData>(__func__);
2115 op->customdata = xfd;
2116
2117 /* Don't change this at runtime. */
2118 xfd->vc = vc;
2119 xfd->depths = depths;
2120 xfd->vc.mval[0] = event->mval[0];
2121 xfd->vc.mval[1] = event->mval[1];
2122
2123 xfd->prev.depth = 1.0f;
2124 xfd->prev.is_depth_valid = false;
2125 xfd->prev.is_normal_valid = false;
2126 xfd->is_translate = false;
2127
2129
2130 xfd->object_data.append({});
2131 xfd->object_data.last().ob = xfd->vc.obact;
2132
2133 CTX_DATA_BEGIN (C, Object *, ob, selected_editable_objects) {
2134 if ((ob != xfd->vc.obact) && object_is_target_compat(ob)) {
2135 xfd->object_data.append({});
2136 xfd->object_data.last().ob = ob;
2137 }
2138 }
2140
2141 for (XFormAxisItem &item : xfd->object_data) {
2142 item.obtfm = BKE_object_tfm_backup(item.ob);
2143 BKE_object_rot_to_mat3(item.ob, item.rot_mat, true);
2144
2145 /* Detect negative scale matrix. */
2146 float full_mat3[3][3];
2147 BKE_object_to_mat3(item.ob, full_mat3);
2148 item.is_z_flip = dot_v3v3(item.rot_mat[2], full_mat3[2]) < 0.0f;
2149 }
2150
2152
2154}
2155
2157 wmOperator *op,
2158 const wmEvent *event)
2159{
2160 XFormAxisData *xfd = static_cast<XFormAxisData *>(op->customdata);
2161 ARegion *region = xfd->vc.region;
2162
2164
2165 const bool is_translate = event->modifier & KM_CTRL;
2166 const bool is_translate_init = is_translate && (xfd->is_translate != is_translate);
2167
2168 if (event->type == MOUSEMOVE || is_translate_init) {
2169 const ViewDepths *depths = xfd->depths;
2170 if (depths && (uint(event->mval[0]) < depths->w) && (uint(event->mval[1]) < depths->h)) {
2171 float depth_fl = 1.0f;
2172 ED_view3d_depth_read_cached(depths, event->mval, 0, &depth_fl);
2173 float location_world[3];
2174 if (depth_fl == 1.0f) {
2175 if (xfd->prev.is_depth_valid) {
2176 depth_fl = xfd->prev.depth;
2177 }
2178 }
2179
2180#ifdef USE_FAKE_DEPTH_INIT
2181 /* First time only. */
2182 if (depth_fl == 1.0f) {
2183 if (xfd->prev.is_depth_valid == false) {
2185 if (xfd->prev.is_depth_valid) {
2186 depth_fl = xfd->prev.depth;
2187 }
2188 }
2189 }
2190#endif
2191
2192 double depth = double(depth_fl);
2193 if ((depth > depths->depth_range[0]) && (depth < depths->depth_range[1])) {
2194 xfd->prev.depth = depth_fl;
2195 xfd->prev.is_depth_valid = true;
2196 if (ED_view3d_depth_unproject_v3(region, event->mval, depth, location_world)) {
2197 if (is_translate) {
2198
2199 float normal[3];
2200 bool normal_found = false;
2201 if (ED_view3d_depth_read_cached_normal(region, depths, event->mval, normal)) {
2202 normal_found = true;
2203
2204 /* cheap attempt to smooth normals out a bit! */
2205 const int ofs = 2;
2206 for (int x = -ofs; x <= ofs; x += ofs / 2) {
2207 for (int y = -ofs; y <= ofs; y += ofs / 2) {
2208 if (x != 0 && y != 0) {
2209 const int mval_ofs[2] = {event->mval[0] + x, event->mval[1] + y};
2210 float n[3];
2211 if (ED_view3d_depth_read_cached_normal(region, depths, mval_ofs, n)) {
2212 add_v3_v3(normal, n);
2213 }
2214 }
2215 }
2216 }
2217 normalize_v3(normal);
2218 }
2219 else if (xfd->prev.is_normal_valid) {
2220 copy_v3_v3(normal, xfd->prev.normal);
2221 normal_found = true;
2222 }
2223
2224 {
2225#ifdef USE_RELATIVE_ROTATION
2226 if (is_translate_init && xfd->object_data.size() > 1) {
2227 float xform_rot_offset_inv_first[3][3];
2228 for (const int i : xfd->object_data.index_range()) {
2229 XFormAxisItem &item = xfd->object_data[i];
2230 copy_m3_m4(item.xform_rot_offset, item.ob->object_to_world().ptr());
2232
2233 if (i == 0) {
2234 invert_m3_m3(xform_rot_offset_inv_first, xfd->object_data[0].xform_rot_offset);
2235 }
2236 else {
2238 item.xform_rot_offset, item.xform_rot_offset, xform_rot_offset_inv_first);
2239 }
2240 }
2241 }
2242
2243#endif
2244
2245 for (const int i : xfd->object_data.index_range()) {
2246 XFormAxisItem &item = xfd->object_data[i];
2247 if (is_translate_init) {
2248 float ob_axis[3];
2249 item.xform_dist = len_v3v3(item.ob->object_to_world().location(),
2250 location_world);
2251 normalize_v3_v3(ob_axis, item.ob->object_to_world().ptr()[2]);
2252 /* Scale to avoid adding distance when moving between surfaces. */
2253 if (normal_found) {
2254 float scale = fabsf(dot_v3v3(ob_axis, normal));
2255 item.xform_dist *= scale;
2256 }
2257 }
2258
2259 float target_normal[3];
2260
2261 if (normal_found) {
2262 copy_v3_v3(target_normal, normal);
2263 }
2264 else {
2265 normalize_v3_v3(target_normal, item.ob->object_to_world().ptr()[2]);
2266 }
2267
2268#ifdef USE_RELATIVE_ROTATION
2269 if (normal_found) {
2270 if (i != 0) {
2271 mul_m3_v3(item.xform_rot_offset, target_normal);
2272 }
2273 }
2274#endif
2275 {
2276 float loc[3];
2277
2278 copy_v3_v3(loc, location_world);
2279 madd_v3_v3fl(loc, target_normal, item.xform_dist);
2280 object_apply_location(item.ob, loc);
2281 /* so orient behaves as expected */
2282 copy_v3_v3(item.ob->runtime->object_to_world.location(), loc);
2283 }
2284
2286 item.ob, item.rot_mat, item.rot_mat[2], location_world, item.is_z_flip);
2287
2290 }
2291 if (normal_found) {
2292 copy_v3_v3(xfd->prev.normal, normal);
2293 xfd->prev.is_normal_valid = true;
2294 }
2295 }
2296 }
2297 else {
2298 for (XFormAxisItem &item : xfd->object_data) {
2300 item.ob, item.rot_mat, item.rot_mat[2], location_world, item.is_z_flip))
2301 {
2304 }
2305 }
2306 xfd->prev.is_normal_valid = false;
2307 }
2308 }
2309 }
2310 }
2311 xfd->is_translate = is_translate;
2312
2314 }
2315
2316 bool is_finished = false;
2317
2318 if (ISMOUSE_BUTTON(xfd->init_event)) {
2319 if ((event->type == xfd->init_event) && (event->val == KM_RELEASE)) {
2320 is_finished = true;
2321 }
2322 }
2323 else {
2324 if (ELEM(event->type, LEFTMOUSE, EVT_RETKEY, EVT_PADENTER)) {
2325 is_finished = true;
2326 }
2327 }
2328
2329 if (is_finished) {
2330 Scene *scene = CTX_data_scene(C);
2331 /* Perform auto-keying for rotational changes for all objects. */
2332 for (XFormAxisItem &item : xfd->object_data) {
2333 PointerRNA ptr = RNA_pointer_create_discrete(&item.ob->id, &RNA_Object, &item.ob->id);
2334 const char *rotation_property = "rotation_euler";
2335 switch (item.ob->rotmode) {
2336 case ROT_MODE_QUAT:
2337 rotation_property = "rotation_quaternion";
2338 break;
2339 case ROT_MODE_AXISANGLE:
2340 rotation_property = "rotation_axis_angle";
2341 break;
2342 default:
2343 break;
2344 }
2345 PropertyRNA *prop = RNA_struct_find_property(&ptr, rotation_property);
2346 animrig::autokeyframe_property(C, scene, &ptr, prop, -1, scene->r.cfra, true);
2347 }
2348
2350 return OPERATOR_FINISHED;
2351 }
2352 if (ELEM(event->type, EVT_ESCKEY, RIGHTMOUSE)) {
2354 return OPERATOR_CANCELLED;
2355 }
2356
2358}
2359
2361{
2362 /* identifiers */
2363 ot->name = "Interactive Light Track to Cursor";
2364 ot->description = "Interactively point cameras and lights to a location (Ctrl translates)";
2365 ot->idname = "OBJECT_OT_transform_axis_target";
2366
2367 /* API callbacks. */
2372
2373 /* flags */
2375}
2376
2377#undef USE_RELATIVE_ROTATION
2378
2380
2381} // namespace blender::ed::object
Functions and classes to work with Actions.
Functions to insert, delete or modify keyframes.
Functionality to interact with keying sets.
static constexpr const char * ANIM_KS_ROTATION_ID
static constexpr const char * ANIM_KS_SCALING_ID
static constexpr const char * ANIM_KS_LOCATION_ID
void BKE_armature_copy_bone_transforms(bArmature *armature_dst, const bArmature *armature_src)
void BKE_armature_transform(bArmature *arm, const float mat[4][4], bool do_props)
void BKE_pose_where_is(Depsgraph *depsgraph, Scene *scene, Object *ob)
#define CTX_DATA_BEGIN(C, Type, instance, member)
Depsgraph * CTX_data_ensure_evaluated_depsgraph(const bContext *C)
Depsgraph * CTX_data_depsgraph_pointer(const bContext *C)
Object * CTX_data_active_object(const bContext *C)
#define CTX_DATA_COUNT(C, member)
Scene * CTX_data_scene(const bContext *C)
Object * CTX_data_edit_object(const bContext *C)
Main * CTX_data_main(const bContext *C)
#define CTX_DATA_END
View3D * CTX_wm_view3d(const bContext *C)
ViewLayer * CTX_data_view_layer(const bContext *C)
void BKE_curve_translate(Curve *cu, const float offset[3], bool do_keys)
Definition curve.cc:5232
bool BKE_curve_center_median(Curve *cu, float cent[3])
Definition curve.cc:5117
std::optional< blender::Bounds< blender::float3 > > BKE_curve_minmax(const Curve *cu, bool use_radius)
Definition curve.cc:5097
void BKE_curve_transform_ex(Curve *cu, const float mat[4][4], bool do_keys, bool do_props, float unit_scale)
Definition curve.cc:5154
Low-level operations for curves.
Low-level operations for grease pencil.
const char * BKE_idtype_idcode_to_name(short idcode)
Definition idtype.cc:164
void BKE_lattice_transform(Lattice *lt, const float mat[4][4], bool do_keys)
Definition lattice.cc:631
std::optional< blender::Bounds< blender::float3 > > BKE_lattice_minmax(const Lattice *lt)
Definition lattice.cc:611
void BKE_lattice_center_median(Lattice *lt, float cent[3])
Definition lattice.cc:593
void BKE_lattice_translate(Lattice *lt, const float offset[3], bool do_keys)
Definition lattice.cc:651
#define FOREACH_SELECTED_EDITABLE_OBJECT_END
Definition BKE_layer.hh:327
#define FOREACH_SELECTED_EDITABLE_OBJECT_BEGIN(_view_layer, _v3d, _instance)
Definition BKE_layer.hh:315
bool BKE_id_is_editable(const Main *bmain, const ID *id)
Definition lib_id.cc:2523
void id_us_plus(ID *id)
Definition lib_id.cc:358
void BKE_main_id_newptr_and_tag_clear(Main *bmain)
Definition lib_id.cc:2001
void id_us_min(ID *id)
Definition lib_id.cc:366
void BKE_main_id_tag_all(Main *mainvar, int tag, bool value)
Definition lib_id.cc:1224
bool BKE_mball_center_bounds(const MetaBall *mb, float r_cent[3])
Definition mball.cc:513
void BKE_mball_translate(MetaBall *mb, const float offset[3])
Definition mball.cc:551
void BKE_mball_transform(MetaBall *mb, const float mat[4][4], bool do_props)
Definition mball.cc:525
bool BKE_mball_center_median(const MetaBall *mb, float r_cent[3])
Definition mball.cc:495
bool BKE_mesh_center_of_surface(const Mesh *mesh, float r_cent[3])
bool BKE_mesh_center_median(const Mesh *mesh, float r_cent[3])
bool BKE_mesh_center_of_volume(const Mesh *mesh, float r_cent[3])
void multiresModifier_scale_disp(Depsgraph *depsgraph, Scene *scene, Object *ob)
Definition multires.cc:708
General operations, lookup, etc. for blender objects.
void BKE_object_rot_to_mat3(const Object *ob, float r_mat[3][3], bool use_drot)
blender::float4x4 BKE_object_calc_parent(Depsgraph *depsgraph, Scene *scene, Object *ob)
MovieClip * BKE_object_movieclip_get(Scene *scene, const Object *ob, bool use_default)
void BKE_object_tfm_restore(Object *ob, void *obtfm_pt)
void BKE_object_apply_mat4(Object *ob, const float mat[4][4], bool use_compat, bool use_parent)
void BKE_object_batch_cache_dirty_tag(Object *ob)
void BKE_object_eval_transform_all(Depsgraph *depsgraph, Scene *scene, Object *object)
void BKE_object_scale_to_mat3(const Object *ob, float r_mat[3][3])
bool BKE_object_minmax_dupli(Depsgraph *depsgraph, Scene *scene, Object *ob, blender::float3 &r_min, blender::float3 &r_max, bool use_hidden)
void BKE_object_apply_parent_inverse(Object *ob)
void BKE_object_to_mat4(const Object *ob, float r_mat[4][4])
void * BKE_object_tfm_backup(Object *ob)
void BKE_object_to_mat3(const Object *ob, float r_mat[3][3])
void BKE_object_where_is_calc(Depsgraph *depsgraph, Scene *scene, Object *ob)
void BKE_object_transform_copy(Object *ob_tar, const Object *ob_src)
void BKE_reportf(ReportList *reports, eReportType type, const char *format,...) ATTR_PRINTF_FORMAT(3
@ RPT_ERROR
Definition BKE_report.hh:39
@ RPT_WARNING
Definition BKE_report.hh:38
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:153
void BKE_scene_graph_evaluated_ensure(Depsgraph *depsgraph, Main *bmain)
Definition scene.cc:2626
void BKE_tracking_reconstruction_scale(struct MovieTracking *tracking, float scale[3])
#define BLI_assert(a)
Definition BLI_assert.h:46
#define LISTBASE_FOREACH(type, var, list)
MINLINE float max_fff(float a, float b, float c)
MINLINE int compare_ff_relative(float a, float b, float max_diff, int max_ulps)
int isect_line_line_v3(const float v1[3], const float v2[3], const float v3[3], const float v4[3], float r_i1[3], float r_i2[3])
float mat3_to_scale(const float mat[3][3])
float mat4_to_scale(const float mat[4][4])
void mul_m3_v3(const float M[3][3], float r[3])
void unit_m3(float m[3][3])
void copy_m3_m4(float m1[3][3], const float m2[4][4])
bool invert_m3_m3(float inverse[3][3], const float mat[3][3])
void copy_m4_m3(float m1[4][4], const float m2[3][3])
void normalize_m3(float R[3][3]) ATTR_NONNULL()
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])
bool invert_m4_m4(float inverse[4][4], const float mat[4][4])
void mul_m3_m3m3(float R[3][3], const float A[3][3], const float B[3][3])
void mul_m4_m4_post(float R[4][4], const float B[4][4])
void mul_v3_mat3_m4v3(float r[3], const float mat[4][4], const float vec[3])
@ EULER_ORDER_DEFAULT
void eulO_to_axis_angle(float axis[3], float *angle, const float eul[3], short order)
void mul_qt_fl(float q[4], float f)
void unit_qt(float q[4])
void eul_to_quat(float quat[4], const float eul[3])
void quat_to_eul(float eul[3], const float quat[4])
void axis_angle_to_eulO(float eul[3], short order, const float axis[3], float angle)
void rotation_between_vecs_to_mat3(float m[3][3], const float v1[3], const float v2[3])
void unit_axis_angle(float axis[3], float *angle)
void copy_qt_qt(float q[4], const float a[4])
MINLINE void copy_v4_v4(float r[4], const float a[4])
MINLINE float len_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
void minmax_v3v3_v3(float min[3], float max[3], const float vec[3])
MINLINE void madd_v3_v3fl(float r[3], const float a[3], float f)
MINLINE void sub_v3_v3(float r[3], const float a[3])
MINLINE float len_squared_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void sub_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void mul_v3_fl(float r[3], float f)
MINLINE void copy_v3_v3(float r[3], const float a[3])
MINLINE void negate_v3_v3(float r[3], const float a[3])
MINLINE float dot_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void add_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void negate_v3(float r[3])
MINLINE float normalize_v3_v3(float r[3], const float a[3])
void mid_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void copy_v3_fl(float r[3], float f)
MINLINE void zero_v3(float r[3])
MINLINE void add_v3_v3(float r[3], const float a[3])
MINLINE float normalize_v3(float n[3])
unsigned int uint
#define INIT_MINMAX(min, max)
#define ELEM(...)
#define IS_EQF(a, b)
#define IFACE_(msgid)
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)
T * DEG_get_evaluated(const Depsgraph *depsgraph, T *id)
@ ID_TAG_DOIT
Definition DNA_ID.h:1036
@ ID_RECALC_PARAMETERS
Definition DNA_ID.h:1138
@ ID_RECALC_TRANSFORM
Definition DNA_ID.h:1054
@ ID_RECALC_SYNC_TO_EVAL
Definition DNA_ID.h:1118
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:1074
#define ID_IS_EDITABLE(_id)
Definition DNA_ID.h:705
#define ID_REAL_USERS(id)
Definition DNA_ID.h:676
#define ID_IS_OVERRIDE_LIBRARY(_id)
Definition DNA_ID.h:730
@ ROT_MODE_QUAT
@ ROT_MODE_AXISANGLE
Object groups, one object can be in many groups at once.
@ CU_3D
@ LA_AREA
@ LA_SPOT
@ LA_SUN
@ LA_AREA_ELLIPSE
@ LA_AREA_SQUARE
@ LA_AREA_RECT
@ LA_AREA_DISK
Object is a sort of wrapper for general info.
@ OB_LATTICE
@ OB_MBALL
@ OB_EMPTY
@ OB_SURF
@ OB_CAMERA
@ OB_FONT
@ OB_GREASE_PENCIL
@ OB_ARMATURE
@ OB_LAMP
@ OB_MESH
@ OB_POINTCLOUD
@ OB_CURVES_LEGACY
@ OB_CURVES
@ OB_DONE
@ OB_DUPLICOLLECTION
@ OB_LOCK_ROTZ
@ OB_LOCK_ROT4D
@ OB_LOCK_SCALEZ
@ OB_LOCK_ROTX
@ OB_LOCK_SCALEX
@ OB_LOCK_ROTW
@ OB_LOCK_LOCY
@ OB_LOCK_LOCZ
@ OB_LOCK_ROTY
@ OB_LOCK_SCALEY
@ OB_LOCK_LOCX
@ SCE_XFORM_SKIP_CHILDREN
@ SCE_XFORM_DATA_ORIGIN
@ V3D_HIDE_OVERLAYS
@ V3D_AROUND_CENTER_BOUNDS
@ V3D_AROUND_CENTER_MEDIAN
@ OPERATOR_CANCELLED
@ OPERATOR_FINISHED
@ OPERATOR_RUNNING_MODAL
@ OPERATOR_PASS_THROUGH
void EDBM_mesh_normals_update(BMEditMesh *em)
bool ED_operator_objectmode(bContext *C)
bool ED_operator_scene_editable(bContext *C)
bool ED_operator_region_view3d_active(bContext *C)
void ED_region_tag_redraw(ARegion *region)
Definition area.cc:618
bool ED_view3d_depth_read_cached(const ViewDepths *vd, const int mval[2], int margin, float *r_depth)
void ED_view3d_win_to_ray(const ARegion *region, const float mval[2], float r_ray_start[3], float r_ray_normal[3])
void ED_view3d_depth_override(Depsgraph *depsgraph, ARegion *region, View3D *v3d, Object *obact, eV3DDepthOverrideMode mode, bool use_overlay, ViewDepths **r_depths)
ViewContext ED_view3d_viewcontext_init(bContext *C, Depsgraph *depsgraph)
bool ED_view3d_depth_read_cached_normal(const ARegion *region, const ViewDepths *depths, const int mval[2], float r_normal[3])
void ED_view3d_depths_free(ViewDepths *depths)
@ V3D_DEPTH_NO_GPENCIL
Definition ED_view3d.hh:192
void ED_view3d_project_v3(const ARegion *region, const float world[3], float r_region_co[3])
void view3d_operator_needs_gpu(const bContext *C)
bool ED_view3d_depth_unproject_v3(const ARegion *region, const int mval[2], double depth, float r_location_world[3])
Read Guarded memory(de)allocation.
@ PROP_SKIP_SAVE
Definition RNA_types.hh:344
@ PROP_HIDDEN
Definition RNA_types.hh:338
#define C
Definition RandGen.cpp:29
@ ALERT_ICON_WARNING
#define NC_WINDOW
Definition WM_types.hh:375
@ KM_CTRL
Definition WM_types.hh:279
@ KM_RELEASE
Definition WM_types.hh:312
@ OPTYPE_BLOCKING
Definition WM_types.hh:184
@ OPTYPE_UNDO
Definition WM_types.hh:182
@ OPTYPE_REGISTER
Definition WM_types.hh:180
#define ND_TRANSFORM
Definition WM_types.hh:456
#define NC_OBJECT
Definition WM_types.hh:379
void ANIM_deselect_keys_in_animation_editors(bContext *C)
Definition anim_deps.cc:478
void ED_armature_origin_set(Main *bmain, Object *ob, const float cursor[3], int centermode, int around)
#define BM_ITER_MESH(ele, iter, bm, itype)
@ BM_VERTS_OF_MESH
BPy_StructRNA * depsgraph
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
int64_t size() const
Definition BLI_array.hh:256
const T * data() const
Definition BLI_array.hh:312
IndexRange index_range() const
Definition BLI_array.hh:360
bool is_empty() const
Definition BLI_array.hh:264
void foreach_item(const FuncT &func) const
Definition BLI_map.hh:687
constexpr IndexRange index_range() const
Definition BLI_span.hh:670
constexpr int64_t size() const
Definition BLI_span.hh:252
constexpr const T * end() const
Definition BLI_span.hh:224
constexpr IndexRange index_range() const
Definition BLI_span.hh:401
constexpr const T * begin() const
Definition BLI_span.hh:220
constexpr bool is_empty() const
Definition BLI_span.hh:260
int64_t size() const
void append(const T &value)
bool is_empty() const
MutableSpan< float > radii_for_write()
bke::CurvesGeometry & strokes_for_write()
const Map< FramesMapKeyT, GreasePencilFrame > & frames() const
float4x4 to_object_space(const Object &object) const
nullptr float
#define rot(x, k)
#define GS(x)
VecBase< float, 3 > float3
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
bool is_autokey_on(const Scene *scene)
KeyingSet * get_keyingset_for_autokeying(const Scene *scene, const char *transformKSName)
bool autokeyframe_property(bContext *C, Scene *scene, PointerRNA *ptr, PropertyRNA *prop, int rnaindex, float cfra, bool only_if_property_keyed)
void autokeyframe_object(bContext *C, const Scene *scene, Object *ob, Span< RNAPath > rna_paths)
void mesh_transform(Mesh &mesh, const float4x4 &transform, bool do_shape_keys)
void mesh_translate(Mesh &mesh, const float3 &translation, bool do_shape_keys)
static wmOperatorStatus visual_transform_apply_exec(bContext *C, wmOperator *)
void xform_skip_child_container_item_ensure_from_array(XFormObjectSkipChild_Container *xcs, const Scene *scene, ViewLayer *view_layer, Object **objects, uint objects_len)
static bool apply_objects_internal_need_single_user(bContext *C)
static wmOperatorStatus apply_objects_internal(bContext *C, ReportList *reports, bool apply_loc, bool apply_rot, bool apply_scale, bool do_props, bool do_single_user)
void single_obdata_user_make(Main *bmain, Scene *scene, Object *ob)
static Array< Object * > sorted_selected_editable_objects(bContext *C)
static void translate_positions(MutableSpan< float3 > positions, const float3 &translation)
static void object_transform_axis_target_calc_depth_init(XFormAxisData *xfd, const int mval[2])
static void object_clear_scale(Object *ob, const bool clear_delta)
void OBJECT_OT_origin_set(wmOperatorType *ot)
static bool object_orient_to_location(Object *ob, const float rot_orig[3][3], const float axis[3], const float location[3], const bool z_flip)
void data_xform_container_destroy(XFormObjectData_Container *xds)
static void object_apply_location(Object *ob, const float loc[3])
static wmOperatorStatus object_transform_axis_target_modal(bContext *C, wmOperator *op, const wmEvent *event)
void OBJECT_OT_transform_axis_target(wmOperatorType *ot)
static void object_clear_loc(Object *ob, const bool clear_delta)
static wmOperatorStatus object_clear_transform_generic_exec(bContext *C, wmOperator *op, void(*clear_func)(Object *, const bool), const char default_ksName[])
void OBJECT_OT_origin_clear(wmOperatorType *ot)
static wmOperatorStatus object_origin_set_exec(bContext *C, wmOperator *op)
static void object_transform_axis_target_free_data(wmOperator *op)
XFormObjectSkipChild_Container * xform_skip_child_container_create()
static void append_sorted_object_parent_hierarchy(Object *root_object, Object *object, Object **sorted_objects, int *object_index)
void OBJECT_OT_rotation_clear(wmOperatorType *ot)
static bool apply_objects_internal_can_multiuser(bContext *C)
void data_xform_container_update_all(XFormObjectData_Container *xds, Main *bmain, Depsgraph *depsgraph)
static wmOperatorStatus object_scale_clear_exec(bContext *C, wmOperator *op)
void object_xform_skip_child_container_destroy(XFormObjectSkipChild_Container *xcs)
static void object_clear_rot(Object *ob, const bool clear_delta)
Object * context_active_object(const bContext *C)
static bool object_is_target_compat(const Object *ob)
static void object_transform_axis_target_cancel(bContext *C, wmOperator *op)
static void ignore_parent_tx(Main *bmain, Depsgraph *depsgraph, Scene *scene, Object *ob)
void OBJECT_OT_scale_clear(wmOperatorType *ot)
static wmOperatorStatus object_transform_apply_invoke(bContext *C, wmOperator *op, const wmEvent *)
void OBJECT_OT_visual_transform_apply(wmOperatorType *ot)
static wmOperatorStatus object_parent_inverse_apply_exec(bContext *C, wmOperator *)
static wmOperatorStatus object_transform_apply_exec(bContext *C, wmOperator *op)
static wmOperatorStatus object_origin_clear_exec(bContext *C, wmOperator *)
void OBJECT_OT_location_clear(wmOperatorType *ot)
static wmOperatorStatus object_transform_axis_target_invoke(bContext *C, wmOperator *op, const wmEvent *event)
void OBJECT_OT_parent_inverse_apply(wmOperatorType *ot)
static float3 arithmetic_mean(const Span< float3 > values)
void data_xform_container_item_ensure(XFormObjectData_Container *xds, Object *ob)
void object_xform_skip_child_container_update_all(XFormObjectSkipChild_Container *xcs, Main *bmain, Depsgraph *depsgraph)
XFormObjectData_Container * data_xform_container_create()
static void object_apply_rotation(Object *ob, const float rmat[3][3])
static wmOperatorStatus object_rotation_clear_exec(bContext *C, wmOperator *op)
void OBJECT_OT_transform_apply(wmOperatorType *ot)
static wmOperatorStatus object_location_clear_exec(bContext *C, wmOperator *op)
CartesianBasis invert(const CartesianBasis &basis)
T midpoint(const T &a, const T &b)
VecBase< T, 3 > transform_direction(const MatBase< T, 3, 3 > &mat, const VecBase< T, 3 > &direction)
void transform_points(const float4x4 &transform, MutableSpan< float3 > points, bool use_threading=true)
VecBase< T, 3 > transform_point(const CartesianBasis &basis, const VecBase< T, 3 > &v)
void parallel_for(const IndexRange range, const int64_t grain_size, const Function &function, const TaskSizeHints &size_hints=detail::TaskSizeHints_Static(1))
Definition BLI_task.hh:93
MatBase< float, 4, 4 > float4x4
VecBase< float, 2 > float2
VecBase< float, 3 > float3
float wrap(float value, float max, float min)
Definition node_math.h:103
#define fabsf
PropertyRNA * RNA_struct_find_property(PointerRNA *ptr, const char *identifier)
bool RNA_property_is_set(PointerRNA *ptr, PropertyRNA *prop)
void RNA_property_enum_set(PointerRNA *ptr, PropertyRNA *prop, int value)
bool RNA_property_boolean_get(PointerRNA *ptr, PropertyRNA *prop)
void RNA_property_boolean_set(PointerRNA *ptr, PropertyRNA *prop, bool value)
int RNA_property_enum_get(PointerRNA *ptr, PropertyRNA *prop)
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
PointerRNA RNA_pointer_create_discrete(ID *id, StructRNA *type, void *data)
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)
void RNA_def_property_flag(PropertyRNA *prop, PropertyFlag flag)
#define min(a, b)
Definition sort.cc:36
float co[3]
int totvert
struct TextBox * tb
struct Key * key
float fsize
CurvesGeometry geometry
Definition DNA_ID.h:414
int tag
Definition DNA_ID.h:442
char name[258]
Definition DNA_ID.h:432
float area_sizez
float area_sizey
short area_shape
float area_size
short type
ListBase objects
Definition BKE_main.hh:280
struct MovieTracking tracking
ObjectRuntimeHandle * runtime
float drot[3]
float dquat[4]
float loc[3]
float dloc[3]
float scale[3]
float rot[3]
float parentinv[4][4]
float drotAxis[3]
float quat[4]
float rotAxis[3]
float drotAngle
float dscale[3]
short protectflag
struct ToolSettings * toolsettings
struct RenderData r
View3DCursor cursor
ARegion * region
Definition ED_view3d.hh:77
int mval[2]
Definition ED_view3d.hh:82
View3D * v3d
Definition ED_view3d.hh:78
Object * obact
Definition ED_view3d.hh:75
Depsgraph * depsgraph
Definition ED_view3d.hh:72
unsigned short w
Definition ED_view3d.hh:86
double depth_range[2]
Definition ED_view3d.hh:90
unsigned short h
Definition ED_view3d.hh:86
struct blender::ed::object::XFormAxisData::@376123072344272263004275065344300367214160356160 prev
wmEventType type
Definition WM_types.hh:757
short val
Definition WM_types.hh:759
int mval[2]
Definition WM_types.hh:763
struct ReportList * reports
struct PointerRNA * ptr
i
Definition text_draw.cc:230
max
Definition text_draw.cc:251
int WM_userdef_event_type_from_keymap_type(int kmitype)
wmEventHandler_Op * WM_event_add_modal_handler(bContext *C, wmOperator *op)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
#define ISMOUSE_BUTTON(event_type)
@ RIGHTMOUSE
@ EVT_PADENTER
@ MOUSEMOVE
@ LEFTMOUSE
@ EVT_ESCKEY
@ EVT_RETKEY
PointerRNA * ptr
Definition wm_files.cc:4238
wmOperatorType * ot
Definition wm_files.cc:4237
wmOperatorStatus WM_menu_invoke(bContext *C, wmOperator *op, const wmEvent *)
wmOperatorStatus WM_operator_confirm_ex(bContext *C, wmOperator *op, const char *title, const char *message, const char *confirm_text, int icon, bool cancel_default)