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