Blender V4.3
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
9#include <cstdlib>
10#include <cstring>
11#include <limits>
12#include <numeric>
13
14#include "DNA_anim_types.h"
15#include "DNA_armature_types.h"
19#include "DNA_lattice_types.h"
20#include "DNA_light_types.h"
21#include "DNA_mesh_types.h"
22#include "DNA_meta_types.h"
23#include "DNA_object_types.h"
25#include "DNA_scene_types.h"
26
27#include "BLI_array.hh"
28#include "BLI_listbase.h"
29#include "BLI_math_geom.h"
30#include "BLI_math_matrix.h"
31#include "BLI_math_matrix.hh"
32#include "BLI_math_rotation.h"
33#include "BLI_task.hh"
34#include "BLI_utildefines.h"
35#include "BLI_vector.hh"
36
37#include "BKE_armature.hh"
38#include "BKE_context.hh"
39#include "BKE_curve.hh"
40#include "BKE_curves.hh"
41#include "BKE_editmesh.hh"
43#include "BKE_gpencil_legacy.h"
44#include "BKE_grease_pencil.hh"
45#include "BKE_idtype.hh"
46#include "BKE_lattice.hh"
47#include "BKE_layer.hh"
48#include "BKE_lib_id.hh"
49#include "BKE_main.hh"
50#include "BKE_mball.hh"
51#include "BKE_mesh.hh"
52#include "BKE_multires.hh"
53#include "BKE_object.hh"
54#include "BKE_object_types.hh"
55#include "BKE_report.hh"
56#include "BKE_scene.hh"
57#include "BKE_tracking.h"
58
59#include "BLT_translation.hh"
60
61#include "DEG_depsgraph.hh"
63
64#include "RNA_access.hh"
65#include "RNA_define.hh"
66
67#include "UI_interface.hh"
68
69#include "WM_api.hh"
70#include "WM_types.hh"
71
72#include "ANIM_action.hh"
73#include "ANIM_keyframing.hh"
74
75#include "ED_anim_api.hh"
76#include "ED_armature.hh"
77#include "ED_keyframing.hh"
78#include "ED_mesh.hh"
79#include "ED_object.hh"
80#include "ED_screen.hh"
81#include "ED_view3d.hh"
82
83#include "MEM_guardedalloc.h"
84
85#include "object_intern.hh"
86
87namespace blender::ed::object {
88
89/* -------------------------------------------------------------------- */
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 *, const bool),
303 const char default_ksName[])
304{
306 Main *bmain = CTX_data_main(C);
307 Scene *scene = CTX_data_scene(C);
308 ViewLayer *view_layer = CTX_data_view_layer(C);
309 /* May be null. */
310 View3D *v3d = CTX_wm_view3d(C);
311 KeyingSet *ks;
312 const bool clear_delta = RNA_boolean_get(op->ptr, "clear_delta");
313
314 BLI_assert(!ELEM(nullptr, clear_func, default_ksName));
315
316 Vector<Object *> objects;
317 FOREACH_SELECTED_EDITABLE_OBJECT_BEGIN (view_layer, v3d, ob) {
318 objects.append(ob);
319 }
321
322 if (objects.is_empty()) {
323 return OPERATOR_CANCELLED;
324 }
325
326 /* Support transforming the object data. */
327 const bool use_transform_skip_children = (scene->toolsettings->transform_flag &
329 const bool use_transform_data_origin = (scene->toolsettings->transform_flag &
331 XFormObjectSkipChild_Container *xcs = nullptr;
332 XFormObjectData_Container *xds = nullptr;
333
334 if (use_transform_skip_children) {
338 xcs, scene, view_layer, objects.data(), objects.size());
339 }
340 if (use_transform_data_origin) {
343 }
344
345 /* get KeyingSet to use */
346 ks = ANIM_get_keyingset_for_autokeying(scene, default_ksName);
347
350 }
351
352 for (Object *ob : objects) {
353 if (use_transform_data_origin) {
355 }
356
357 /* run provided clearing function */
358 clear_func(ob, clear_delta);
359
360 animrig::autokeyframe_object(C, scene, ob, ks);
361
362 /* tag for updates */
364 }
365
366 if (use_transform_skip_children) {
369 }
370
371 if (use_transform_data_origin) {
374 }
375
376 /* this is needed so children are also updated */
378
379 return OPERATOR_FINISHED;
380}
381
384/* -------------------------------------------------------------------- */
392
394{
395 /* identifiers */
396 ot->name = "Clear Location";
397 ot->description = "Clear the object's location";
398 ot->idname = "OBJECT_OT_location_clear";
399
400 /* api callbacks */
403
404 /* flags */
406
407 /* properties */
409 ot->srna,
410 "clear_delta",
411 false,
412 "Clear Delta",
413 "Clear delta location in addition to clearing the normal location transform");
414}
415
418/* -------------------------------------------------------------------- */
426
428{
429 /* identifiers */
430 ot->name = "Clear Rotation";
431 ot->description = "Clear the object's rotation";
432 ot->idname = "OBJECT_OT_rotation_clear";
433
434 /* api callbacks */
437
438 /* flags */
440
441 /* properties */
443 ot->srna,
444 "clear_delta",
445 false,
446 "Clear Delta",
447 "Clear delta rotation in addition to clearing the normal rotation transform");
448}
449
452/* -------------------------------------------------------------------- */
460
462{
463 /* identifiers */
464 ot->name = "Clear Scale";
465 ot->description = "Clear the object's scale";
466 ot->idname = "OBJECT_OT_scale_clear";
467
468 /* api callbacks */
471
472 /* flags */
474
475 /* properties */
477 ot->srna,
478 "clear_delta",
479 false,
480 "Clear Delta",
481 "Clear delta scale in addition to clearing the normal scale transform");
482}
483
486/* -------------------------------------------------------------------- */
491{
492 float *v1, *v3;
493 float mat[3][3];
494
495 CTX_DATA_BEGIN (C, Object *, ob, selected_editable_objects) {
496 if (ob->parent) {
497 /* vectors pointed to by v1 and v3 will get modified */
498 v1 = ob->loc;
499 v3 = ob->parentinv[3];
500
501 copy_m3_m4(mat, ob->parentinv);
502 negate_v3_v3(v3, v1);
503 mul_m3_v3(mat, v3);
504 }
505
507 }
509
511
512 return OPERATOR_FINISHED;
513}
514
516{
517 /* identifiers */
518 ot->name = "Clear Origin";
519 ot->description = "Clear the object's origin";
520 ot->idname = "OBJECT_OT_origin_clear";
521
522 /* api callbacks */
525
526 /* flags */
528}
529
532/* -------------------------------------------------------------------- */
536/* use this when the loc/size/rot of the parent has changed but the children
537 * should stay in the same place, e.g. for apply-size-rot or object center */
538static void ignore_parent_tx(Main *bmain, Depsgraph *depsgraph, Scene *scene, Object *ob)
539{
541
542 /* a change was made, adjust the children to compensate */
543 LISTBASE_FOREACH (Object *, ob_child, &bmain->objects) {
544 if (ob_child->parent == ob) {
545 Object *ob_child_eval = DEG_get_evaluated_object(depsgraph, ob_child);
546 BKE_object_apply_mat4(ob_child_eval, ob_child_eval->object_to_world().ptr(), true, false);
547 invert_m4_m4(ob_child->parentinv,
548 BKE_object_calc_parent(depsgraph, scene, ob_child_eval).ptr());
549 /* Copy result of BKE_object_apply_mat4(). */
550 BKE_object_transform_copy(ob_child, ob_child_eval);
551 /* Make sure evaluated object is in a consistent state with the original one.
552 * It might be needed for applying transform on its children. */
553 copy_m4_m4(ob_child_eval->parentinv, ob_child->parentinv);
554 BKE_object_eval_transform_all(depsgraph, scene_eval, ob_child_eval);
555 /* Tag for update.
556 * This is because parent matrix did change, so in theory the child object might now be
557 * evaluated to a different location in another editing context. */
559 }
560 }
561}
562
564 Object *object,
565 Object **sorted_objects,
566 int *object_index)
567{
568 if (!ELEM(object->parent, nullptr, root_object)) {
570 root_object, object->parent, sorted_objects, object_index);
571 }
572 if (object->id.tag & ID_TAG_DOIT) {
573 sorted_objects[*object_index] = object;
574 (*object_index)++;
575 object->id.tag &= ~ID_TAG_DOIT;
576 }
577}
578
580{
581 Main *bmain = CTX_data_main(C);
582
583 /* Count all objects, but also tag all the selected ones. */
584 BKE_main_id_tag_all(bmain, ID_TAG_DOIT, false);
585 int objects_num = 0;
586 CTX_DATA_BEGIN (C, Object *, object, selected_editable_objects) {
587 object->id.tag |= ID_TAG_DOIT;
588 objects_num++;
589 }
591 if (objects_num == 0) {
592 return {};
593 }
594
595 /* Append all the objects. */
596 Array<Object *> sorted_objects(objects_num);
597 int object_index = 0;
598 CTX_DATA_BEGIN (C, Object *, object, selected_editable_objects) {
599 if ((object->id.tag & ID_TAG_DOIT) == 0) {
600 continue;
601 }
602 append_sorted_object_parent_hierarchy(object, object, sorted_objects.data(), &object_index);
603 }
605
606 return sorted_objects;
607}
608
613{
614 Object *obact = CTX_data_active_object(C);
615
616 if (ELEM(nullptr, obact, obact->data)) {
617 return false;
618 }
619
620 if (ID_REAL_USERS(obact->data) == 1) {
621 return false;
622 }
623
624 bool all_objects_same_data = true;
625 bool obact_selected = false;
626
627 CTX_DATA_BEGIN (C, Object *, ob, selected_editable_objects) {
628 if (ob->data != obact->data) {
629 all_objects_same_data = false;
630 break;
631 }
632
633 if (ob == obact) {
634 obact_selected = true;
635 }
636 }
638
639 return all_objects_same_data && obact_selected;
640}
641
648{
651
652 /* Counting the number of objects is valid since it's known the
653 * selection is only made up of users of the active objects data. */
654 return (ID_REAL_USERS(ob->data) > CTX_DATA_COUNT(C, selected_editable_objects));
655}
656
657static void transform_positions(MutableSpan<float3> positions, const float4x4 &matrix)
658{
659 threading::parallel_for(positions.index_range(), 1024, [&](const IndexRange range) {
660 for (float3 &position : positions.slice(range)) {
661 position = math::transform_point(matrix, position);
662 }
663 });
664}
665
667 ReportList *reports,
668 bool apply_loc,
669 bool apply_rot,
670 bool apply_scale,
671 bool do_props,
672 bool do_single_user)
673{
674 Main *bmain = CTX_data_main(C);
675 Scene *scene = CTX_data_scene(C);
677 float rsmat[3][3], obmat[3][3], iobmat[3][3], mat[4][4], scale;
678 bool changed = true;
679 bool const do_multi_user = apply_objects_internal_can_multiuser(C);
680 float obact_invmat[4][4], obact_parent[4][4], obact_parentinv[4][4];
681
682 /* Only used when do_multi_user is set. */
683 Object *obact = nullptr;
684 bool make_single_user = false;
685
686 if (do_multi_user) {
687 obact = CTX_data_active_object(C);
688 invert_m4_m4(obact_invmat, obact->object_to_world().ptr());
689
690 copy_m4_m4(obact_parent, BKE_object_calc_parent(depsgraph, scene, obact).ptr());
691 copy_m4_m4(obact_parentinv, obact->parentinv);
692
694 if (do_single_user) {
695 make_single_user = true;
696 }
697 else {
698 ID *obact_data = static_cast<ID *>(obact->data);
699 BKE_reportf(reports,
700 RPT_ERROR,
701 R"(Cannot apply to a multi user: Object "%s", %s "%s", aborting)",
702 obact->id.name + 2,
703 BKE_idtype_idcode_to_name(GS(obact_data->name)),
704 obact_data->name + 2);
705 return OPERATOR_CANCELLED;
706 }
707 }
708 }
709
710 /* first check if we can execute */
711 CTX_DATA_BEGIN (C, Object *, ob, selected_editable_objects) {
712 if (ELEM(ob->type,
713 OB_MESH,
716 OB_MBALL,
718 OB_SURF,
719 OB_FONT,
721 OB_CURVES,
724 {
725 ID *obdata = static_cast<ID *>(ob->data);
726 if (!do_multi_user && ID_REAL_USERS(obdata) > 1) {
727 BKE_reportf(reports,
728 RPT_ERROR,
729 R"(Cannot apply to a multi user: Object "%s", %s "%s", aborting)",
730 ob->id.name + 2,
732 obdata->name + 2);
733 changed = false;
734 }
735
736 if (!ID_IS_EDITABLE(obdata) || ID_IS_OVERRIDE_LIBRARY(obdata)) {
737 BKE_reportf(reports,
738 RPT_ERROR,
739 R"(Cannot apply to library or override data: Object "%s", %s "%s", aborting)",
740 ob->id.name + 2,
742 obdata->name + 2);
743 changed = false;
744 }
745 }
746
747 if (ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF)) {
748 ID *obdata = static_cast<ID *>(ob->data);
749 Curve *cu = static_cast<Curve *>(ob->data);
750
751 if (((ob->type == OB_CURVES_LEGACY) && !(cu->flag & CU_3D)) && (apply_rot || apply_loc)) {
753 reports,
754 RPT_ERROR,
755 R"(Rotation/Location can't apply to a 2D curve: Object "%s", %s "%s", aborting)",
756 ob->id.name + 2,
758 obdata->name + 2);
759 changed = false;
760 }
761 if (cu->key) {
762 BKE_reportf(reports,
763 RPT_ERROR,
764 R"(Can't apply to a curve with shape-keys: Object "%s", %s "%s", aborting)",
765 ob->id.name + 2,
767 obdata->name + 2);
768 changed = false;
769 }
770 }
771
772 if (ob->type == OB_FONT) {
773 if (apply_rot || apply_loc) {
774 BKE_reportf(reports,
775 RPT_ERROR,
776 "Text objects can only have their scale applied: \"%s\"",
777 ob->id.name + 2);
778 changed = false;
779 }
780 }
781
782 if (ob->type == OB_GPENCIL_LEGACY) {
783 bGPdata *gpd = static_cast<bGPdata *>(ob->data);
784 if (gpd) {
785 if (gpd->layers.first) {
786 /* Unsupported configuration */
787 bool has_unparented_layers = false;
788
789 LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
790 /* Parented layers aren't supported as we can't easily re-evaluate
791 * the scene to sample parent movement */
792 if (gpl->parent == nullptr) {
793 has_unparented_layers = true;
794 break;
795 }
796 }
797
798 if (has_unparented_layers == false) {
799 BKE_reportf(reports,
800 RPT_ERROR,
801 "Can't apply to a GP data-block where all layers are parented: Object "
802 "\"%s\", %s \"%s\", aborting",
803 ob->id.name + 2,
805 gpd->id.name + 2);
806 changed = false;
807 }
808 }
809 else {
810 /* No layers/data */
812 reports,
813 RPT_ERROR,
814 R"(Can't apply to GP data-block with no layers: Object "%s", %s "%s", aborting)",
815 ob->id.name + 2,
817 gpd->id.name + 2);
818 }
819 }
820 }
821
822 if (ob->type == OB_LAMP) {
823 Light *la = static_cast<Light *>(ob->data);
824 if (la->type == LA_AREA) {
825 if (apply_rot || apply_loc) {
826 BKE_reportf(reports,
827 RPT_ERROR,
828 "Area Lights can only have scale applied: \"%s\"",
829 ob->id.name + 2);
830 changed = false;
831 }
832 }
833 }
834 }
836
837 if (!changed) {
838 return OPERATOR_CANCELLED;
839 }
840
841 changed = false;
842
843 /* now execute */
844
845 if (make_single_user) {
846 /* Make single user. */
847 single_obdata_user_make(bmain, scene, obact);
849 WM_event_add_notifier(C, NC_WINDOW, nullptr);
851 }
852
854 if (objects.is_empty()) {
855 return OPERATOR_CANCELLED;
856 }
857
858 for (Object *ob : objects) {
859 /* calculate rotation/scale matrix */
860 if (apply_scale && apply_rot) {
861 BKE_object_to_mat3(ob, rsmat);
862 }
863 else if (apply_scale) {
864 BKE_object_scale_to_mat3(ob, rsmat);
865 }
866 else if (apply_rot) {
867 float tmat[3][3], timat[3][3];
868
869 /* simple rotation matrix */
870 BKE_object_rot_to_mat3(ob, rsmat, true);
871
872 /* correct for scale, note mul_m3_m3m3 has swapped args! */
873 BKE_object_scale_to_mat3(ob, tmat);
874 invert_m3_m3(timat, tmat);
875 mul_m3_m3m3(rsmat, timat, rsmat);
876 mul_m3_m3m3(rsmat, rsmat, tmat);
877 }
878 else {
879 unit_m3(rsmat);
880 }
881
882 copy_m4_m3(mat, rsmat);
883
884 /* calculate translation */
885 if (apply_loc) {
886 add_v3_v3v3(mat[3], ob->loc, ob->dloc);
887
888 if (!(apply_scale && apply_rot)) {
889 float tmat[3][3];
890 /* correct for scale and rotation that is still applied */
891 BKE_object_to_mat3(ob, obmat);
892 invert_m3_m3(iobmat, obmat);
893 mul_m3_m3m3(tmat, rsmat, iobmat);
894 mul_m3_v3(tmat, mat[3]);
895 }
896 }
897
898 /* apply to object data */
899 if (do_multi_user && ob != obact) {
900 /* Don't apply, just set the new object data, the correct
901 * transformations will happen later. */
902 id_us_min((ID *)ob->data);
903 ob->data = obact->data;
904 id_us_plus((ID *)ob->data);
905 }
906 else if (ob->type == OB_MESH) {
907 Mesh *mesh = static_cast<Mesh *>(ob->data);
908
909 if (apply_scale) {
911 }
912
913 /* adjust data */
914 BKE_mesh_transform(mesh, mat, true);
915 }
916 else if (ob->type == OB_ARMATURE) {
917 bArmature *arm = static_cast<bArmature *>(ob->data);
918 BKE_armature_transform(arm, mat, do_props);
919 }
920 else if (ob->type == OB_LATTICE) {
921 Lattice *lt = static_cast<Lattice *>(ob->data);
922
923 BKE_lattice_transform(lt, mat, true);
924 }
925 else if (ob->type == OB_MBALL) {
926 MetaBall *mb = static_cast<MetaBall *>(ob->data);
927 BKE_mball_transform(mb, mat, do_props);
928 }
929 else if (ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF)) {
930 Curve *cu = static_cast<Curve *>(ob->data);
931 scale = mat3_to_scale(rsmat);
932 BKE_curve_transform_ex(cu, mat, true, do_props, scale);
933 }
934 else if (ob->type == OB_FONT) {
935 Curve *cu = static_cast<Curve *>(ob->data);
936
937 scale = mat3_to_scale(rsmat);
938
939 for (int i = 0; i < cu->totbox; i++) {
940 TextBox *tb = &cu->tb[i];
941 tb->x *= scale;
942 tb->y *= scale;
943 tb->w *= scale;
944 tb->h *= scale;
945 }
946
947 if (do_props) {
948 cu->fsize *= scale;
949 }
950 }
951 else if (ob->type == OB_GPENCIL_LEGACY) {
952 bGPdata *gpd = static_cast<bGPdata *>(ob->data);
953 BKE_gpencil_transform(gpd, mat);
954 }
955 else if (ob->type == OB_CURVES) {
956 Curves &curves = *static_cast<Curves *>(ob->data);
957 curves.geometry.wrap().transform(float4x4(mat));
958 curves.geometry.wrap().calculate_bezier_auto_handles();
959 }
960 else if (ob->type == OB_GREASE_PENCIL) {
961 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(ob->data);
962
963 const float scalef = mat4_to_scale(mat);
964
965 for (const int layer_i : grease_pencil.layers().index_range()) {
966 bke::greasepencil::Layer &layer = grease_pencil.layer(layer_i);
967 const float4x4 layer_to_object = layer.to_object_space(*ob);
968 const float4x4 object_to_layer = math::invert(layer_to_object);
970 frames.foreach_item(
972 GreasePencilDrawingBase *base = grease_pencil.drawing(frame.drawing_index);
973 if (base->type != GP_DRAWING) {
974 return;
975 }
977 reinterpret_cast<GreasePencilDrawing *>(base)->wrap();
978 bke::CurvesGeometry &curves = drawing.strokes_for_write();
979 MutableSpan<float> radii = drawing.radii_for_write();
980 threading::parallel_for(radii.index_range(), 8192, [&](const IndexRange range) {
981 for (const int i : range) {
982 radii[i] *= scalef;
983 }
984 });
985
986 curves.transform(object_to_layer * float4x4(mat) * layer_to_object);
987 curves.calculate_bezier_auto_handles();
988 });
989 }
990 }
991 else if (ob->type == OB_POINTCLOUD) {
992 PointCloud &pointcloud = *static_cast<PointCloud *>(ob->data);
993 transform_positions(pointcloud.positions_for_write(), float4x4(mat));
994 pointcloud.tag_positions_changed();
995 }
996 else if (ob->type == OB_CAMERA) {
997 MovieClip *clip = BKE_object_movieclip_get(scene, ob, false);
998
999 /* applying scale on camera actually scales clip's reconstruction.
1000 * of there's clip assigned to camera nothing to do actually.
1001 */
1002 if (!clip) {
1003 continue;
1004 }
1005
1006 if (apply_scale) {
1007 BKE_tracking_reconstruction_scale(&clip->tracking, ob->scale);
1008 }
1009 }
1010 else if (ob->type == OB_EMPTY) {
1011 /* It's possible for empties too, even though they don't
1012 * really have obdata, since we can simply apply the maximum
1013 * scaling to the empty's drawsize.
1014 *
1015 * Core Assumptions:
1016 * 1) Most scaled empties have uniform scaling
1017 * (i.e. for visibility reasons), AND/OR
1018 * 2) Preserving non-uniform scaling is not that important,
1019 * and is something that many users would be willing to
1020 * sacrifice for having an easy way to do this.
1021 */
1022
1023 if ((apply_loc == false) && (apply_rot == false) && (apply_scale == true)) {
1024 float max_scale = max_fff(fabsf(ob->scale[0]), fabsf(ob->scale[1]), fabsf(ob->scale[2]));
1025 ob->empty_drawsize *= max_scale;
1026 }
1027 }
1028 else if (ob->type == OB_LAMP) {
1029 Light *la = static_cast<Light *>(ob->data);
1030 if (la->type != LA_AREA) {
1031 continue;
1032 }
1033
1034 bool keeps_aspect_ratio = compare_ff_relative(rsmat[0][0], rsmat[1][1], FLT_EPSILON, 64);
1035 if ((la->area_shape == LA_AREA_SQUARE) && !keeps_aspect_ratio) {
1037 la->area_sizey = la->area_size;
1038 }
1039 else if ((la->area_shape == LA_AREA_DISK) && !keeps_aspect_ratio) {
1041 la->area_sizey = la->area_size;
1042 }
1043
1044 la->area_size *= rsmat[0][0];
1045 la->area_sizey *= rsmat[1][1];
1046 la->area_sizez *= rsmat[2][2];
1047
1048 /* Explicit tagging is required for Lamp ID because, unlike Geometry IDs like Mesh,
1049 * it is not covered by the `ID_RECALC_GEOMETRY` flag applied to the object at the end
1050 * of this loop. */
1052 }
1053 else {
1054 continue;
1055 }
1056
1057 if (do_multi_user && ob != obact) {
1058 float _obmat[4][4], _iobmat[4][4];
1059 float _mat[4][4];
1060
1061 copy_m4_m4(_obmat, ob->object_to_world().ptr());
1062 invert_m4_m4(_iobmat, _obmat);
1063
1064 copy_m4_m4(_mat, _obmat);
1065 mul_m4_m4_post(_mat, obact_invmat);
1066 mul_m4_m4_post(_mat, obact_parent);
1067 mul_m4_m4_post(_mat, obact_parentinv);
1068
1069 if (apply_loc && apply_scale && apply_rot) {
1070 BKE_object_apply_mat4(ob, _mat, false, true);
1071 }
1072 else {
1073 Object ob_temp = dna::shallow_copy(*ob);
1074 BKE_object_apply_mat4(&ob_temp, _mat, false, true);
1075
1076 if (apply_loc) {
1077 copy_v3_v3(ob->loc, ob_temp.loc);
1078 }
1079
1080 if (apply_scale) {
1081 copy_v3_v3(ob->scale, ob_temp.scale);
1082 }
1083
1084 if (apply_rot) {
1085 copy_v4_v4(ob->quat, ob_temp.quat);
1086 copy_v3_v3(ob->rot, ob_temp.rot);
1087 copy_v3_v3(ob->rotAxis, ob_temp.rotAxis);
1088 ob->rotAngle = ob_temp.rotAngle;
1089 }
1090 }
1091 }
1092 else {
1093 if (apply_loc) {
1094 zero_v3(ob->loc);
1095 zero_v3(ob->dloc);
1096 }
1097 if (apply_scale) {
1098 copy_v3_fl(ob->scale, 1.0f);
1099 copy_v3_fl(ob->dscale, 1.0f);
1100 }
1101 if (apply_rot) {
1102 zero_v3(ob->rot);
1103 zero_v3(ob->drot);
1104 unit_qt(ob->quat);
1105 unit_qt(ob->dquat);
1106 unit_axis_angle(ob->rotAxis, &ob->rotAngle);
1107 unit_axis_angle(ob->drotAxis, &ob->drotAngle);
1108 }
1109 }
1110
1112 BKE_object_transform_copy(ob_eval, ob);
1113
1114 BKE_object_where_is_calc(depsgraph, scene, ob_eval);
1115 if (ob->type == OB_ARMATURE) {
1116 /* needed for bone parents */
1117 BKE_armature_copy_bone_transforms(static_cast<bArmature *>(ob_eval->data),
1118 static_cast<bArmature *>(ob->data));
1119 BKE_pose_where_is(depsgraph, scene, ob_eval);
1120 }
1121
1122 ignore_parent_tx(bmain, depsgraph, scene, ob);
1123
1125
1126 changed = true;
1127 }
1128
1129 if (!changed) {
1130 BKE_report(reports, RPT_WARNING, "Objects have no data to transform");
1131 return OPERATOR_CANCELLED;
1132 }
1133
1135 return OPERATOR_FINISHED;
1136}
1137
1139{
1140 Scene *scene = CTX_data_scene(C);
1142 bool changed = false;
1143
1144 CTX_DATA_BEGIN (C, Object *, ob, selected_editable_objects) {
1146 BKE_object_where_is_calc(depsgraph, scene, ob_eval);
1147 BKE_object_apply_mat4(ob_eval, ob_eval->object_to_world().ptr(), true, true);
1148 BKE_object_transform_copy(ob, ob_eval);
1149
1150 /* update for any children that may get moved */
1152
1153 changed = true;
1154 }
1156
1157 if (!changed) {
1158 return OPERATOR_CANCELLED;
1159 }
1160
1162 return OPERATOR_FINISHED;
1163}
1164
1166{
1167 /* identifiers */
1168 ot->name = "Apply Visual Transform";
1169 ot->description = "Apply the object's visual transformation to its data";
1170 ot->idname = "OBJECT_OT_visual_transform_apply";
1171
1172 /* api callbacks */
1175
1176 /* flags */
1178}
1179
1181{
1182 const bool loc = RNA_boolean_get(op->ptr, "location");
1183 const bool rot = RNA_boolean_get(op->ptr, "rotation");
1184 const bool sca = RNA_boolean_get(op->ptr, "scale");
1185 const bool do_props = RNA_boolean_get(op->ptr, "properties");
1186 const bool do_single_user = RNA_boolean_get(op->ptr, "isolate_users");
1187
1188 if (loc || rot || sca) {
1189 return apply_objects_internal(C, op->reports, loc, rot, sca, do_props, do_single_user);
1190 }
1191 /* allow for redo */
1192 return OPERATOR_FINISHED;
1193}
1194
1195static int object_transform_apply_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/)
1196{
1198
1199 bool can_handle_multiuser = apply_objects_internal_can_multiuser(C);
1200 bool need_single_user = can_handle_multiuser && apply_objects_internal_need_single_user(C);
1201
1202 if ((ob != nullptr) && (ob->data != nullptr) && need_single_user) {
1203 PropertyRNA *prop = RNA_struct_find_property(op->ptr, "isolate_users");
1204 if (!RNA_property_is_set(op->ptr, prop)) {
1205 RNA_property_boolean_set(op->ptr, prop, true);
1206 }
1207 if (RNA_property_boolean_get(op->ptr, prop)) {
1208 return WM_operator_confirm_ex(C,
1209 op,
1210 IFACE_("Apply Object Transformations"),
1211 IFACE_("Warning: Multiple objects share the same data.\nMake "
1212 "single user and then apply transformations?"),
1213 IFACE_("Apply"),
1215 false);
1216 }
1217 }
1218 return object_transform_apply_exec(C, op);
1219}
1220
1222{
1223 /* identifiers */
1224 ot->name = "Apply Object Transform";
1225 ot->description = "Apply the object's transformation to its data";
1226 ot->idname = "OBJECT_OT_transform_apply";
1227
1228 /* api callbacks */
1232
1233 /* flags */
1235
1236 RNA_def_boolean(ot->srna, "location", true, "Location", "");
1237 RNA_def_boolean(ot->srna, "rotation", true, "Rotation", "");
1238 RNA_def_boolean(ot->srna, "scale", true, "Scale", "");
1240 "properties",
1241 true,
1242 "Apply Properties",
1243 "Modify properties such as curve vertex radius, font size and bone envelope");
1245 "isolate_users",
1246 false,
1247 "Isolate Multi User Data",
1248 "Create new object-data users if needed");
1251}
1252
1255/* -------------------------------------------------------------------- */
1260{
1261 CTX_DATA_BEGIN (C, Object *, ob, selected_editable_objects) {
1262 if (ob->parent == nullptr) {
1263 continue;
1264 }
1265
1268 }
1270
1272
1273 return OPERATOR_FINISHED;
1274}
1275
1277{
1278 /* identifiers */
1279 ot->name = "Apply Parent Inverse";
1280 ot->description = "Apply the object's parent inverse to its data";
1281 ot->idname = "OBJECT_OT_parent_inverse_apply";
1282
1283 /* api callbacks */
1286
1287 /* flags */
1289}
1290
1293/* -------------------------------------------------------------------- */
1297enum {
1303};
1304
1306{
1307 if (values.is_empty()) {
1308 return float3(0);
1309 }
1310 /* TODO: Use a method that avoids overflow. */
1311 return std::accumulate(values.begin(), values.end(), float3(0)) / values.size();
1312}
1313
1314static void translate_positions(MutableSpan<float3> positions, const float3 &translation)
1315{
1316 threading::parallel_for(positions.index_range(), 2048, [&](const IndexRange range) {
1317 for (float3 &position : positions.slice(range)) {
1318 position += translation;
1319 }
1320 });
1321}
1322
1324{
1325 Main *bmain = CTX_data_main(C);
1326 Scene *scene = CTX_data_scene(C);
1327 Object *obact = CTX_data_active_object(C);
1328 Object *obedit = CTX_data_edit_object(C);
1330 float3 cent, cent_neg, centn;
1331 const float *cursor = scene->cursor.location;
1332 int centermode = RNA_enum_get(op->ptr, "type");
1333
1334 /* keep track of what is changed */
1335 int tot_change = 0, tot_lib_error = 0, tot_multiuser_arm_error = 0;
1336
1337 if (obedit && centermode != GEOMETRY_TO_ORIGIN) {
1338 BKE_report(op->reports, RPT_ERROR, "Operation cannot be performed in edit mode");
1339 return OPERATOR_CANCELLED;
1340 }
1341
1342 int around;
1343 {
1344 PropertyRNA *prop_center = RNA_struct_find_property(op->ptr, "center");
1345 if (RNA_property_is_set(op->ptr, prop_center)) {
1346 around = RNA_property_enum_get(op->ptr, prop_center);
1347 }
1348 else {
1349 if (scene->toolsettings->transform_pivot_point == V3D_AROUND_CENTER_BOUNDS) {
1350 around = V3D_AROUND_CENTER_BOUNDS;
1351 }
1352 else {
1353 around = V3D_AROUND_CENTER_MEDIAN;
1354 }
1355 RNA_property_enum_set(op->ptr, prop_center, around);
1356 }
1357 }
1358
1359 zero_v3(cent);
1360
1361 if (obedit) {
1362 if (obedit->type == OB_MESH) {
1363 Mesh *mesh = static_cast<Mesh *>(obedit->data);
1364 BMEditMesh *em = mesh->runtime->edit_mesh.get();
1365 BMVert *eve;
1366 BMIter iter;
1367
1368 if (centermode == ORIGIN_TO_CURSOR) {
1369 copy_v3_v3(cent, cursor);
1370 invert_m4_m4(obedit->runtime->world_to_object.ptr(), obedit->object_to_world().ptr());
1371 mul_m4_v3(obedit->world_to_object().ptr(), cent);
1372 }
1373 else {
1374 if (around == V3D_AROUND_CENTER_BOUNDS) {
1375 float min[3], max[3];
1376 INIT_MINMAX(min, max);
1377 BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) {
1378 minmax_v3v3_v3(min, max, eve->co);
1379 }
1380 mid_v3_v3v3(cent, min, max);
1381 }
1382 else { /* #V3D_AROUND_CENTER_MEDIAN. */
1383 if (em->bm->totvert) {
1384 const float total_div = 1.0f / float(em->bm->totvert);
1385 BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) {
1386 madd_v3_v3fl(cent, eve->co, total_div);
1387 }
1388 }
1389 }
1390 }
1391
1392 BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) {
1393 sub_v3_v3(eve->co, cent);
1394 }
1395
1397 tot_change++;
1399 }
1400 }
1401
1403 if (objects.is_empty()) {
1404 return OPERATOR_CANCELLED;
1405 }
1406
1407 /* reset flags */
1408 for (const int object_index : objects.index_range()) {
1409 Object *ob = objects[object_index];
1410 ob->flag &= ~OB_DONE;
1411
1412 /* move active first */
1413 if (ob == obact && objects.size() > 1) {
1414 memmove(&objects[1], objects.data(), object_index * sizeof(Object *));
1415 objects[0] = ob;
1416 }
1417 }
1418
1419 LISTBASE_FOREACH (Object *, tob, &bmain->objects) {
1420 if (tob->data) {
1421 ((ID *)tob->data)->tag &= ~ID_TAG_DOIT;
1422 }
1423 if (tob->instance_collection) {
1424 ((ID *)tob->instance_collection)->tag &= ~ID_TAG_DOIT;
1425 }
1426 }
1427
1428 for (Object *ob : objects) {
1429 if (ob->flag & OB_DONE) {
1430 continue;
1431 }
1432
1433 bool do_inverse_offset = false;
1434 ob->flag |= OB_DONE;
1435
1436 if (centermode == ORIGIN_TO_CURSOR) {
1437 copy_v3_v3(cent, cursor);
1438 invert_m4_m4(ob->runtime->world_to_object.ptr(), ob->object_to_world().ptr());
1439 mul_m4_v3(ob->world_to_object().ptr(), cent);
1440 }
1441
1442 if (ob->data == nullptr) {
1443 /* Special support for instanced collections. */
1444 if ((ob->transflag & OB_DUPLICOLLECTION) && ob->instance_collection &&
1445 (ob->instance_collection->id.tag & ID_TAG_DOIT) == 0)
1446 {
1447 if (!BKE_id_is_editable(bmain, &ob->instance_collection->id)) {
1448 tot_lib_error++;
1449 }
1450 else {
1451 if (centermode == ORIGIN_TO_CURSOR) {
1452 /* done */
1453 }
1454 else {
1455 float min[3], max[3];
1456 /* only bounds support */
1457 INIT_MINMAX(min, max);
1458 BKE_object_minmax_dupli(depsgraph, scene, ob, min, max, true);
1459 mid_v3_v3v3(cent, min, max);
1460 invert_m4_m4(ob->runtime->world_to_object.ptr(), ob->object_to_world().ptr());
1461 mul_m4_v3(ob->world_to_object().ptr(), cent);
1462 }
1463
1464 add_v3_v3(ob->instance_collection->instance_offset, cent);
1465
1466 tot_change++;
1467 ob->instance_collection->id.tag |= ID_TAG_DOIT;
1468 do_inverse_offset = true;
1469 }
1470 }
1471 }
1472 else if (!ID_IS_EDITABLE(ob->data) || ID_IS_OVERRIDE_LIBRARY(ob->data)) {
1473 tot_lib_error++;
1474 }
1475 else if (ob->type == OB_MESH) {
1476 if (obedit == nullptr) {
1477 Mesh *mesh = static_cast<Mesh *>(ob->data);
1478
1479 if (centermode == ORIGIN_TO_CURSOR) {
1480 /* done */
1481 }
1482 else if (centermode == ORIGIN_TO_CENTER_OF_MASS_SURFACE) {
1483 BKE_mesh_center_of_surface(mesh, cent);
1484 }
1485 else if (centermode == ORIGIN_TO_CENTER_OF_MASS_VOLUME) {
1486 BKE_mesh_center_of_volume(mesh, cent);
1487 }
1488 else if (around == V3D_AROUND_CENTER_BOUNDS) {
1489 if (const std::optional<Bounds<float3>> bounds = mesh->bounds_min_max()) {
1490 cent = math::midpoint(bounds->min, bounds->max);
1491 }
1492 }
1493 else { /* #V3D_AROUND_CENTER_MEDIAN. */
1494 BKE_mesh_center_median(mesh, cent);
1495 }
1496
1497 negate_v3_v3(cent_neg, cent);
1498 BKE_mesh_translate(mesh, cent_neg, true);
1499
1500 tot_change++;
1501 mesh->id.tag |= ID_TAG_DOIT;
1502 do_inverse_offset = true;
1503 }
1504 }
1505 else if (ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF)) {
1506 Curve *cu = static_cast<Curve *>(ob->data);
1507
1508 if (centermode == ORIGIN_TO_CURSOR) {
1509 /* done */
1510 }
1511 else if (around == V3D_AROUND_CENTER_BOUNDS) {
1512 if (std::optional<Bounds<float3>> bounds = BKE_curve_minmax(cu, true)) {
1513 cent = math::midpoint(bounds->min, bounds->max);
1514 }
1515 }
1516 else { /* #V3D_AROUND_CENTER_MEDIAN. */
1517 BKE_curve_center_median(cu, cent);
1518 }
1519
1520 /* don't allow Z change if curve is 2D */
1521 if ((ob->type == OB_CURVES_LEGACY) && !(cu->flag & CU_3D)) {
1522 cent[2] = 0.0;
1523 }
1524
1525 negate_v3_v3(cent_neg, cent);
1526 BKE_curve_translate(cu, cent_neg, true);
1527
1528 tot_change++;
1529 cu->id.tag |= ID_TAG_DOIT;
1530 do_inverse_offset = true;
1531
1532 if (obedit) {
1533 if (centermode == GEOMETRY_TO_ORIGIN) {
1535 }
1536 break;
1537 }
1538 }
1539 else if (ob->type == OB_FONT) {
1540 /* Get from bounding-box. */
1541
1542 Curve *cu = static_cast<Curve *>(ob->data);
1543 std::optional<Bounds<float3>> bounds = BKE_curve_minmax(cu, true);
1544
1545 if (!bounds && (centermode != ORIGIN_TO_CURSOR)) {
1546 /* Do nothing. */
1547 }
1548 else {
1549 if (centermode == ORIGIN_TO_CURSOR) {
1550 /* Done. */
1551 }
1552 else {
1553 /* extra 0.5 is the height o above line */
1554 cent = math::midpoint(bounds->min, bounds->max);
1555 }
1556
1557 cent[2] = 0.0f;
1558
1559 cu->xof = cu->xof - cent[0];
1560 cu->yof = cu->yof - cent[1];
1561
1562 tot_change++;
1563 cu->id.tag |= ID_TAG_DOIT;
1564 do_inverse_offset = true;
1565 }
1566 }
1567 else if (ob->type == OB_ARMATURE) {
1568 bArmature *arm = static_cast<bArmature *>(ob->data);
1569
1570 if (ID_REAL_USERS(arm) > 1) {
1571#if 0
1572 BKE_report(op->reports, RPT_ERROR, "Cannot apply to a multi user armature");
1573 return;
1574#endif
1575 tot_multiuser_arm_error++;
1576 }
1577 else {
1578 /* Function to recenter armatures in editarmature.c
1579 * Bone + object locations are handled there.
1580 */
1581 ED_armature_origin_set(bmain, ob, cursor, centermode, around);
1582
1583 tot_change++;
1584 arm->id.tag |= ID_TAG_DOIT;
1585 // do_inverse_offset = true; /* docenter_armature() handles this. */
1586
1588 BKE_object_transform_copy(ob_eval, ob);
1589 BKE_armature_copy_bone_transforms(static_cast<bArmature *>(ob_eval->data),
1590 static_cast<bArmature *>(ob->data));
1591 BKE_object_where_is_calc(depsgraph, scene, ob_eval);
1592 BKE_pose_where_is(depsgraph, scene, ob_eval); /* needed for bone parents */
1593
1594 ignore_parent_tx(bmain, depsgraph, scene, ob);
1595
1596 if (obedit) {
1597 break;
1598 }
1599 }
1600 }
1601 else if (ob->type == OB_MBALL) {
1602 MetaBall *mb = static_cast<MetaBall *>(ob->data);
1603
1604 if (centermode == ORIGIN_TO_CURSOR) {
1605 /* done */
1606 }
1607 else if (around == V3D_AROUND_CENTER_BOUNDS) {
1608 BKE_mball_center_bounds(mb, cent);
1609 }
1610 else { /* #V3D_AROUND_CENTER_MEDIAN. */
1611 BKE_mball_center_median(mb, cent);
1612 }
1613
1614 negate_v3_v3(cent_neg, cent);
1615 BKE_mball_translate(mb, cent_neg);
1616
1617 tot_change++;
1618 mb->id.tag |= ID_TAG_DOIT;
1619 do_inverse_offset = true;
1620
1621 if (obedit) {
1622 if (centermode == GEOMETRY_TO_ORIGIN) {
1624 }
1625 break;
1626 }
1627 }
1628 else if (ob->type == OB_LATTICE) {
1629 Lattice *lt = static_cast<Lattice *>(ob->data);
1630
1631 if (centermode == ORIGIN_TO_CURSOR) {
1632 /* done */
1633 }
1634 else if (around == V3D_AROUND_CENTER_BOUNDS) {
1635 if (std::optional<Bounds<float3>> bounds = BKE_lattice_minmax(lt)) {
1636 cent = math::midpoint(bounds->min, bounds->max);
1637 }
1638 }
1639 else { /* #V3D_AROUND_CENTER_MEDIAN. */
1640 BKE_lattice_center_median(lt, cent);
1641 }
1642
1643 negate_v3_v3(cent_neg, cent);
1644 BKE_lattice_translate(lt, cent_neg, true);
1645
1646 tot_change++;
1647 lt->id.tag |= ID_TAG_DOIT;
1648 do_inverse_offset = true;
1649 }
1650 else if (ob->type == OB_GPENCIL_LEGACY) {
1651 bGPdata *gpd = static_cast<bGPdata *>(ob->data);
1652 float gpcenter[3];
1653 if (gpd) {
1654 if (centermode == ORIGIN_TO_GEOMETRY) {
1655 zero_v3(gpcenter);
1656 BKE_gpencil_centroid_3d(gpd, gpcenter);
1657 add_v3_v3(gpcenter, ob->object_to_world().location());
1658 }
1659 if (centermode == ORIGIN_TO_CURSOR) {
1660 copy_v3_v3(gpcenter, cursor);
1661 }
1662 if (ELEM(centermode, ORIGIN_TO_GEOMETRY, ORIGIN_TO_CURSOR)) {
1663 bGPDspoint *pt;
1664 float imat[3][3], bmat[3][3];
1665 float offset_global[3];
1666 float offset_local[3];
1667 int i;
1668
1669 sub_v3_v3v3(offset_global, gpcenter, ob->object_to_world().location());
1670 copy_m3_m4(bmat, obact->object_to_world().ptr());
1671 invert_m3_m3(imat, bmat);
1672 mul_m3_v3(imat, offset_global);
1673 mul_v3_m3v3(offset_local, imat, offset_global);
1674
1675 float diff_mat[4][4];
1676 float inverse_diff_mat[4][4];
1677
1678 /* recalculate all strokes
1679 * (all layers are considered without evaluating lock attributes) */
1680 LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
1681 /* calculate difference matrix */
1683 /* undo matrix */
1684 invert_m4_m4(inverse_diff_mat, diff_mat);
1685 LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
1686 LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
1687 for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
1688 float mpt[3];
1689 mul_v3_m4v3(mpt, inverse_diff_mat, &pt->x);
1690 sub_v3_v3(mpt, offset_local);
1691 mul_v3_m4v3(&pt->x, diff_mat, mpt);
1692 }
1693
1694 /* Apply transform to edit-curve. */
1695 if (gps->editcurve != nullptr) {
1696 for (i = 0; i < gps->editcurve->tot_curve_points; i++) {
1697 BezTriple *bezt = &gps->editcurve->curve_points[i].bezt;
1698 for (int j = 0; j < 3; j++) {
1699 float mpt[3];
1700 mul_v3_m4v3(mpt, inverse_diff_mat, bezt->vec[j]);
1701 sub_v3_v3(mpt, offset_local);
1702 mul_v3_m4v3(bezt->vec[j], diff_mat, mpt);
1703 }
1704 }
1705 }
1707 }
1708 }
1709 }
1710 tot_change++;
1711 if (centermode == ORIGIN_TO_GEOMETRY) {
1712 copy_v3_v3(ob->loc, gpcenter);
1713 }
1716
1717 ob->id.tag |= ID_TAG_DOIT;
1718 do_inverse_offset = true;
1719 }
1720 else {
1721 BKE_report(op->reports,
1723 "Grease Pencil Object does not support this set origin option");
1724 }
1725 }
1726 }
1727 else if (ob->type == OB_CURVES) {
1728 Curves &curves_id = *static_cast<Curves *>(ob->data);
1729 bke::CurvesGeometry &curves = curves_id.geometry.wrap();
1732 {
1733 BKE_report(
1734 op->reports, RPT_WARNING, "Curves Object does not support this set origin operation");
1735 continue;
1736 }
1737
1738 if (curves.points_num() == 0) {
1739 continue;
1740 }
1741
1742 if (centermode == ORIGIN_TO_CURSOR) {
1743 /* done */
1744 }
1745 else if (around == V3D_AROUND_CENTER_BOUNDS) {
1746 const Bounds<float3> bounds = *curves.bounds_min_max();
1747 cent = math::midpoint(bounds.min, bounds.max);
1748 }
1749 else if (around == V3D_AROUND_CENTER_MEDIAN) {
1750 cent = arithmetic_mean(curves.positions());
1751 }
1752
1753 tot_change++;
1754 curves.translate(-cent);
1755 curves_id.id.tag |= ID_TAG_DOIT;
1756 do_inverse_offset = true;
1757 }
1758 else if (ob->type == OB_GREASE_PENCIL) {
1759 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(ob->data);
1762 {
1763 BKE_report(op->reports,
1765 "Grease Pencil Object does not support this set origin operation");
1766 continue;
1767 }
1768
1769 if (centermode == ORIGIN_TO_CURSOR) {
1770 /* done */
1771 }
1772 else if (around == V3D_AROUND_CENTER_BOUNDS) {
1773 const int current_frame = scene->r.cfra;
1774 const Bounds<float3> bounds = *grease_pencil.bounds_min_max(current_frame);
1775 cent = math::midpoint(bounds.min, bounds.max);
1776 }
1777 else if (around == V3D_AROUND_CENTER_MEDIAN) {
1778 const int current_frame = scene->r.cfra;
1779 float3 center = float3(0.0f);
1780 int total_points = 0;
1781
1782 for (const int layer_i : grease_pencil.layers().index_range()) {
1783 const bke::greasepencil::Layer &layer = grease_pencil.layer(layer_i);
1784 const float4x4 layer_to_object = layer.local_transform();
1785 if (!layer.is_visible()) {
1786 continue;
1787 }
1788 if (const bke::greasepencil::Drawing *drawing = grease_pencil.get_drawing_at(
1789 layer, current_frame))
1790 {
1791 const bke::CurvesGeometry &curves = drawing->strokes();
1792
1793 for (const int i : curves.points_range()) {
1794 center += math::transform_point(layer_to_object, curves.positions()[i]);
1795 }
1796 total_points += curves.points_num();
1797 }
1798 }
1799
1800 if (total_points != 0) {
1801 cent = center / total_points;
1802 }
1803 }
1804
1805 tot_change++;
1806
1807 for (const int layer_i : grease_pencil.layers().index_range()) {
1808 bke::greasepencil::Layer &layer = grease_pencil.layer(layer_i);
1809 const float4x4 layer_to_object = layer.local_transform();
1810 const float4x4 object_to_layer = math::invert(layer_to_object);
1812 frames.foreach_item(
1814 GreasePencilDrawingBase *base = grease_pencil.drawing(frame.drawing_index);
1815 if (base->type != GP_DRAWING) {
1816 return;
1817 }
1819 reinterpret_cast<GreasePencilDrawing *>(base)->wrap();
1820 bke::CurvesGeometry &curves = drawing.strokes_for_write();
1821
1822 curves.translate(math::transform_direction(object_to_layer, -cent));
1823 curves.calculate_bezier_auto_handles();
1824 });
1825 }
1826
1827 grease_pencil.id.tag |= ID_TAG_DOIT;
1828 do_inverse_offset = true;
1829 }
1830 else if (ob->type == OB_POINTCLOUD) {
1831 PointCloud &pointcloud = *static_cast<PointCloud *>(ob->data);
1832 MutableSpan<float3> positions = pointcloud.positions_for_write();
1835 {
1836 BKE_report(op->reports,
1838 "Point cloud object does not support this set origin operation");
1839 continue;
1840 }
1841
1842 if (centermode == ORIGIN_TO_CURSOR) {
1843 /* Done. */
1844 }
1845 else if (around == V3D_AROUND_CENTER_BOUNDS) {
1846 if (const std::optional<Bounds<float3>> bounds = pointcloud.bounds_min_max()) {
1847 cent = math::midpoint(bounds->min, bounds->max);
1848 }
1849 }
1850 else if (around == V3D_AROUND_CENTER_MEDIAN) {
1851 cent = arithmetic_mean(positions);
1852 }
1853
1854 tot_change++;
1855 translate_positions(positions, -cent);
1856 pointcloud.tag_positions_changed();
1857 pointcloud.id.tag |= ID_TAG_DOIT;
1858 do_inverse_offset = true;
1859 }
1860
1861 /* offset other selected objects */
1862 if (do_inverse_offset && (centermode != GEOMETRY_TO_ORIGIN)) {
1863 float obmat[4][4];
1864
1865 /* was the object data modified
1866 * NOTE: the functions above must set 'cent'. */
1867
1868 /* convert the offset to parent space */
1869 BKE_object_to_mat4(ob, obmat);
1870 mul_v3_mat3_m4v3(centn, obmat, cent); /* omit translation part */
1871
1872 add_v3_v3(ob->loc, centn);
1873
1875 BKE_object_transform_copy(ob_eval, ob);
1876 BKE_object_where_is_calc(depsgraph, scene, ob_eval);
1877 if (ob->type == OB_ARMATURE) {
1878 /* needed for bone parents */
1879 BKE_armature_copy_bone_transforms(static_cast<bArmature *>(ob_eval->data),
1880 static_cast<bArmature *>(ob->data));
1881 BKE_pose_where_is(depsgraph, scene, ob_eval);
1882 }
1883
1884 ignore_parent_tx(bmain, depsgraph, scene, ob);
1885
1886 /* other users? */
1887 // CTX_DATA_BEGIN (C, Object *, ob_other, selected_editable_objects)
1888 //{
1889
1890 /* use existing context looper */
1891 for (Object *ob_other : objects) {
1892 if ((ob_other->flag & OB_DONE) == 0 &&
1893 ((ob->data && (ob->data == ob_other->data)) ||
1894 (ob->instance_collection == ob_other->instance_collection &&
1895 (ob->transflag | ob_other->transflag) & OB_DUPLICOLLECTION)))
1896 {
1897 ob_other->flag |= OB_DONE;
1899
1901 centn, ob_other->object_to_world().ptr(), cent); /* omit translation part */
1902 add_v3_v3(ob_other->loc, centn);
1903
1904 Object *ob_other_eval = DEG_get_evaluated_object(depsgraph, ob_other);
1905 BKE_object_transform_copy(ob_other_eval, ob_other);
1906 BKE_object_where_is_calc(depsgraph, scene, ob_other_eval);
1907 if (ob_other->type == OB_ARMATURE) {
1908 /* needed for bone parents */
1909 BKE_armature_copy_bone_transforms(static_cast<bArmature *>(ob_eval->data),
1910 static_cast<bArmature *>(ob->data));
1911 BKE_pose_where_is(depsgraph, scene, ob_other_eval);
1912 }
1913 ignore_parent_tx(bmain, depsgraph, scene, ob_other);
1914 }
1915 }
1916 // CTX_DATA_END;
1917 }
1918 }
1919
1920 LISTBASE_FOREACH (Object *, tob, &bmain->objects) {
1921 if (tob->data && (((ID *)tob->data)->tag & ID_TAG_DOIT)) {
1924 }
1925 /* Special support for dupli-groups. */
1926 else if (tob->instance_collection && tob->instance_collection->id.tag & ID_TAG_DOIT) {
1928 DEG_id_tag_update(&tob->instance_collection->id, ID_RECALC_SYNC_TO_EVAL);
1929 }
1930 }
1931
1932 if (tot_change) {
1934 }
1935
1936 /* Warn if any errors occurred */
1937 if (tot_lib_error + tot_multiuser_arm_error) {
1938 BKE_reportf(op->reports,
1940 "%i object(s) not centered, %i changed:",
1941 tot_lib_error + tot_multiuser_arm_error,
1942 tot_change);
1943 if (tot_lib_error) {
1944 BKE_reportf(op->reports, RPT_WARNING, "|%i linked library object(s)", tot_lib_error);
1945 }
1946 if (tot_multiuser_arm_error) {
1948 op->reports, RPT_WARNING, "|%i multiuser armature object(s)", tot_multiuser_arm_error);
1949 }
1950 }
1951
1952 return OPERATOR_FINISHED;
1953}
1954
1956{
1957 static const EnumPropertyItem prop_set_center_types[] = {
1959 "GEOMETRY_ORIGIN",
1960 0,
1961 "Geometry to Origin",
1962 "Move object geometry to object origin"},
1964 "ORIGIN_GEOMETRY",
1965 0,
1966 "Origin to Geometry",
1967 "Calculate the center of geometry based on the current pivot point (median, otherwise "
1968 "bounding box)"},
1970 "ORIGIN_CURSOR",
1971 0,
1972 "Origin to 3D Cursor",
1973 "Move object origin to position of the 3D cursor"},
1974 /* Intentional naming mismatch since some scripts refer to this. */
1976 "ORIGIN_CENTER_OF_MASS",
1977 0,
1978 "Origin to Center of Mass (Surface)",
1979 "Calculate the center of mass from the surface area"},
1981 "ORIGIN_CENTER_OF_VOLUME",
1982 0,
1983 "Origin to Center of Mass (Volume)",
1984 "Calculate the center of mass from the volume (must be manifold geometry with consistent "
1985 "normals)"},
1986 {0, nullptr, 0, nullptr, nullptr},
1987 };
1988
1989 static const EnumPropertyItem prop_set_bounds_types[] = {
1990 {V3D_AROUND_CENTER_MEDIAN, "MEDIAN", 0, "Median Center", ""},
1991 {V3D_AROUND_CENTER_BOUNDS, "BOUNDS", 0, "Bounds Center", ""},
1992 {0, nullptr, 0, nullptr, nullptr},
1993 };
1994
1995 /* identifiers */
1996 ot->name = "Set Origin";
1997 ot->description =
1998 "Set the object's origin, by either moving the data, or set to center of data, or use 3D "
1999 "cursor";
2000 ot->idname = "OBJECT_OT_origin_set";
2001
2002 /* api callbacks */
2005
2007
2008 /* flags */
2010
2011 ot->prop = RNA_def_enum(ot->srna, "type", prop_set_center_types, 0, "Type", "");
2012 RNA_def_enum(ot->srna, "center", prop_set_bounds_types, V3D_AROUND_CENTER_MEDIAN, "Center", "");
2013}
2014
2017/* -------------------------------------------------------------------- */
2026#define USE_RELATIVE_ROTATION
2028#define USE_RENDER_OVERRIDE
2033#define USE_FAKE_DEPTH_INIT
2034
2037 float rot_mat[3][3];
2038 void *obtfm;
2041
2042#ifdef USE_RELATIVE_ROTATION
2043 /* use when translating multiple */
2044 float xform_rot_offset[3][3];
2045#endif
2046};
2047
2063
2064#ifdef USE_FAKE_DEPTH_INIT
2066{
2067 float view_co_a[3], view_co_b[3];
2068 const float2 mval_fl = {float(mval[0]), float(mval[1])};
2069 ED_view3d_win_to_ray(xfd->vc.region, mval_fl, view_co_a, view_co_b);
2070 add_v3_v3(view_co_b, view_co_a);
2071 float center[3] = {0.0f};
2072 int center_tot = 0;
2073 for (XFormAxisItem &item : xfd->object_data) {
2074 const Object *ob = item.ob;
2075 const float *ob_co_a = ob->object_to_world().location();
2076 float ob_co_b[3];
2077 add_v3_v3v3(ob_co_b, ob->object_to_world().location(), ob->object_to_world().ptr()[2]);
2078 float view_isect[3], ob_isect[3];
2079 if (isect_line_line_v3(view_co_a, view_co_b, ob_co_a, ob_co_b, view_isect, ob_isect)) {
2080 add_v3_v3(center, view_isect);
2081 center_tot += 1;
2082 }
2083 }
2084 if (center_tot) {
2085 mul_v3_fl(center, 1.0f / center_tot);
2086 float center_proj[3];
2087 ED_view3d_project_v3(xfd->vc.region, center, center_proj);
2088 xfd->prev.depth = center_proj[2];
2089 xfd->prev.is_depth_valid = true;
2090 }
2091}
2092#endif /* USE_FAKE_DEPTH_INIT */
2093
2094static bool object_is_target_compat(const Object *ob)
2095{
2096 if (ob->type == OB_LAMP) {
2097 const Light *la = static_cast<Light *>(ob->data);
2098 if (ELEM(la->type, LA_SUN, LA_SPOT, LA_AREA)) {
2099 return true;
2100 }
2101 }
2102 /* We might want to enable this later, for now just lights. */
2103#if 0
2104 else if (ob->type == OB_CAMERA) {
2105 return true;
2106 }
2107#endif
2108 return false;
2109}
2110
2112{
2113 XFormAxisData *xfd = static_cast<XFormAxisData *>(op->customdata);
2114
2115#ifdef USE_RENDER_OVERRIDE
2116 if (xfd->depths) {
2118 }
2119#endif
2120
2121 for (XFormAxisItem &item : xfd->object_data) {
2122 MEM_freeN(item.obtfm);
2123 }
2124 MEM_delete(xfd);
2125 op->customdata = nullptr;
2126}
2127
2128/* We may want to expose as alternative to: BKE_object_apply_rotation */
2129static void object_apply_rotation(Object *ob, const float rmat[3][3])
2130{
2131 float size[3];
2132 float loc[3];
2133 float rmat4[4][4];
2134 copy_m4_m3(rmat4, rmat);
2135
2136 copy_v3_v3(size, ob->scale);
2137 copy_v3_v3(loc, ob->loc);
2138 BKE_object_apply_mat4(ob, rmat4, true, true);
2139 copy_v3_v3(ob->scale, size);
2140 copy_v3_v3(ob->loc, loc);
2141}
2142/* We may want to extract this to: BKE_object_apply_location */
2143static void object_apply_location(Object *ob, const float loc[3])
2144{
2145 /* quick but weak */
2146 Object ob_prev = dna::shallow_copy(*ob);
2147 float mat[4][4];
2148 copy_m4_m4(mat, ob->object_to_world().ptr());
2149 copy_v3_v3(mat[3], loc);
2150 BKE_object_apply_mat4(ob, mat, true, true);
2151 copy_v3_v3(mat[3], ob->loc);
2152 *ob = dna::shallow_copy(ob_prev);
2153 copy_v3_v3(ob->loc, mat[3]);
2154}
2155
2157 const float rot_orig[3][3],
2158 const float axis[3],
2159 const float location[3],
2160 const bool z_flip)
2161{
2162 float delta[3];
2163 sub_v3_v3v3(delta, ob->object_to_world().location(), location);
2164 if (normalize_v3(delta) != 0.0f) {
2165 if (z_flip) {
2166 negate_v3(delta);
2167 }
2168
2169 if (len_squared_v3v3(delta, axis) > FLT_EPSILON) {
2170 float delta_rot[3][3];
2171 float final_rot[3][3];
2172 rotation_between_vecs_to_mat3(delta_rot, axis, delta);
2173
2174 mul_m3_m3m3(final_rot, delta_rot, rot_orig);
2175
2176 object_apply_rotation(ob, final_rot);
2177
2178 return true;
2179 }
2180 }
2181 return false;
2182}
2183
2185{
2186 XFormAxisData *xfd = static_cast<XFormAxisData *>(op->customdata);
2187 for (XFormAxisItem &item : xfd->object_data) {
2188 BKE_object_tfm_restore(item.ob, item.obtfm);
2191 }
2192
2194}
2195
2197{
2200
2201 if (vc.obact == nullptr || !object_is_target_compat(vc.obact)) {
2202 /* Falls back to texture space transform. */
2203 return OPERATOR_PASS_THROUGH;
2204 }
2205
2206#ifdef USE_RENDER_OVERRIDE
2207 int flag2_prev = vc.v3d->flag2;
2209#endif
2210
2211 ViewDepths *depths = nullptr;
2213 vc.depsgraph, vc.region, vc.v3d, nullptr, V3D_DEPTH_NO_GPENCIL, &depths);
2214
2215#ifdef USE_RENDER_OVERRIDE
2216 vc.v3d->flag2 = flag2_prev;
2217#endif
2218
2219 if (depths == nullptr) {
2220 BKE_report(op->reports, RPT_WARNING, "Unable to access depth buffer, using view plane");
2221 return OPERATOR_CANCELLED;
2222 }
2223
2225
2226 XFormAxisData *xfd = MEM_new<XFormAxisData>(__func__);
2227 op->customdata = xfd;
2228
2229 /* Don't change this at runtime. */
2230 xfd->vc = vc;
2231 xfd->depths = depths;
2232 xfd->vc.mval[0] = event->mval[0];
2233 xfd->vc.mval[1] = event->mval[1];
2234
2235 xfd->prev.depth = 1.0f;
2236 xfd->prev.is_depth_valid = false;
2237 xfd->prev.is_normal_valid = false;
2238 xfd->is_translate = false;
2239
2241
2242 xfd->object_data.append({});
2243 xfd->object_data.last().ob = xfd->vc.obact;
2244
2245 CTX_DATA_BEGIN (C, Object *, ob, selected_editable_objects) {
2246 if ((ob != xfd->vc.obact) && object_is_target_compat(ob)) {
2247 xfd->object_data.append({});
2248 xfd->object_data.last().ob = ob;
2249 }
2250 }
2252
2253 for (XFormAxisItem &item : xfd->object_data) {
2254 item.obtfm = BKE_object_tfm_backup(item.ob);
2255 BKE_object_rot_to_mat3(item.ob, item.rot_mat, true);
2256
2257 /* Detect negative scale matrix. */
2258 float full_mat3[3][3];
2259 BKE_object_to_mat3(item.ob, full_mat3);
2260 item.is_z_flip = dot_v3v3(item.rot_mat[2], full_mat3[2]) < 0.0f;
2261 }
2262
2264
2266}
2267
2269{
2270 XFormAxisData *xfd = static_cast<XFormAxisData *>(op->customdata);
2271 ARegion *region = xfd->vc.region;
2272
2274
2275 const bool is_translate = event->modifier & KM_CTRL;
2276 const bool is_translate_init = is_translate && (xfd->is_translate != is_translate);
2277
2278 if (event->type == MOUSEMOVE || is_translate_init) {
2279 const ViewDepths *depths = xfd->depths;
2280 if (depths && (uint(event->mval[0]) < depths->w) && (uint(event->mval[1]) < depths->h)) {
2281 float depth_fl = 1.0f;
2282 ED_view3d_depth_read_cached(depths, event->mval, 0, &depth_fl);
2283 float location_world[3];
2284 if (depth_fl == 1.0f) {
2285 if (xfd->prev.is_depth_valid) {
2286 depth_fl = xfd->prev.depth;
2287 }
2288 }
2289
2290#ifdef USE_FAKE_DEPTH_INIT
2291 /* First time only. */
2292 if (depth_fl == 1.0f) {
2293 if (xfd->prev.is_depth_valid == false) {
2295 if (xfd->prev.is_depth_valid) {
2296 depth_fl = xfd->prev.depth;
2297 }
2298 }
2299 }
2300#endif
2301
2302 double depth = double(depth_fl);
2303 if ((depth > depths->depth_range[0]) && (depth < depths->depth_range[1])) {
2304 xfd->prev.depth = depth_fl;
2305 xfd->prev.is_depth_valid = true;
2306 if (ED_view3d_depth_unproject_v3(region, event->mval, depth, location_world)) {
2307 if (is_translate) {
2308
2309 float normal[3];
2310 bool normal_found = false;
2311 if (ED_view3d_depth_read_cached_normal(region, depths, event->mval, normal)) {
2312 normal_found = true;
2313
2314 /* cheap attempt to smooth normals out a bit! */
2315 const int ofs = 2;
2316 for (int x = -ofs; x <= ofs; x += ofs / 2) {
2317 for (int y = -ofs; y <= ofs; y += ofs / 2) {
2318 if (x != 0 && y != 0) {
2319 const int mval_ofs[2] = {event->mval[0] + x, event->mval[1] + y};
2320 float n[3];
2321 if (ED_view3d_depth_read_cached_normal(region, depths, mval_ofs, n)) {
2322 add_v3_v3(normal, n);
2323 }
2324 }
2325 }
2326 }
2327 normalize_v3(normal);
2328 }
2329 else if (xfd->prev.is_normal_valid) {
2330 copy_v3_v3(normal, xfd->prev.normal);
2331 normal_found = true;
2332 }
2333
2334 {
2335#ifdef USE_RELATIVE_ROTATION
2336 if (is_translate_init && xfd->object_data.size() > 1) {
2337 float xform_rot_offset_inv_first[3][3];
2338 for (const int i : xfd->object_data.index_range()) {
2339 XFormAxisItem &item = xfd->object_data[i];
2340 copy_m3_m4(item.xform_rot_offset, item.ob->object_to_world().ptr());
2342
2343 if (i == 0) {
2344 invert_m3_m3(xform_rot_offset_inv_first, xfd->object_data[0].xform_rot_offset);
2345 }
2346 else {
2348 item.xform_rot_offset, item.xform_rot_offset, xform_rot_offset_inv_first);
2349 }
2350 }
2351 }
2352
2353#endif
2354
2355 for (const int i : xfd->object_data.index_range()) {
2356 XFormAxisItem &item = xfd->object_data[i];
2357 if (is_translate_init) {
2358 float ob_axis[3];
2359 item.xform_dist = len_v3v3(item.ob->object_to_world().location(),
2360 location_world);
2361 normalize_v3_v3(ob_axis, item.ob->object_to_world().ptr()[2]);
2362 /* Scale to avoid adding distance when moving between surfaces. */
2363 if (normal_found) {
2364 float scale = fabsf(dot_v3v3(ob_axis, normal));
2365 item.xform_dist *= scale;
2366 }
2367 }
2368
2369 float target_normal[3];
2370
2371 if (normal_found) {
2372 copy_v3_v3(target_normal, normal);
2373 }
2374 else {
2375 normalize_v3_v3(target_normal, item.ob->object_to_world().ptr()[2]);
2376 }
2377
2378#ifdef USE_RELATIVE_ROTATION
2379 if (normal_found) {
2380 if (i != 0) {
2381 mul_m3_v3(item.xform_rot_offset, target_normal);
2382 }
2383 }
2384#endif
2385 {
2386 float loc[3];
2387
2388 copy_v3_v3(loc, location_world);
2389 madd_v3_v3fl(loc, target_normal, item.xform_dist);
2390 object_apply_location(item.ob, loc);
2391 /* so orient behaves as expected */
2392 copy_v3_v3(item.ob->runtime->object_to_world.location(), loc);
2393 }
2394
2396 item.ob, item.rot_mat, item.rot_mat[2], location_world, item.is_z_flip);
2397
2400 }
2401 if (normal_found) {
2402 copy_v3_v3(xfd->prev.normal, normal);
2403 xfd->prev.is_normal_valid = true;
2404 }
2405 }
2406 }
2407 else {
2408 for (XFormAxisItem &item : xfd->object_data) {
2410 item.ob, item.rot_mat, item.rot_mat[2], location_world, item.is_z_flip))
2411 {
2414 }
2415 }
2416 xfd->prev.is_normal_valid = false;
2417 }
2418 }
2419 }
2420 }
2421 xfd->is_translate = is_translate;
2422
2424 }
2425
2426 bool is_finished = false;
2427
2428 if (ISMOUSE_BUTTON(xfd->init_event)) {
2429 if ((event->type == xfd->init_event) && (event->val == KM_RELEASE)) {
2430 is_finished = true;
2431 }
2432 }
2433 else {
2434 if (ELEM(event->type, LEFTMOUSE, EVT_RETKEY, EVT_PADENTER)) {
2435 is_finished = true;
2436 }
2437 }
2438
2439 if (is_finished) {
2441 return OPERATOR_FINISHED;
2442 }
2443 if (ELEM(event->type, EVT_ESCKEY, RIGHTMOUSE)) {
2445 return OPERATOR_CANCELLED;
2446 }
2447
2449}
2450
2452{
2453 /* identifiers */
2454 ot->name = "Interactive Light Track to Cursor";
2455 ot->description = "Interactively point cameras and lights to a location (Ctrl translates)";
2456 ot->idname = "OBJECT_OT_transform_axis_target";
2457
2458 /* api callbacks */
2463
2464 /* flags */
2466}
2467
2468#undef USE_RELATIVE_ROTATION
2469
2472} // namespace blender::ed::object
Functions and classes to work with Actions.
Functions to insert, delete or modify keyframes.
void BKE_armature_copy_bone_transforms(bArmature *armature_dst, const bArmature *armature_src)
Definition armature.cc:640
void BKE_armature_transform(bArmature *arm, const float mat[4][4], bool do_props)
Definition armature.cc:743
void BKE_pose_where_is(Depsgraph *depsgraph, Scene *scene, Object *ob)
Definition armature.cc:2941
#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:5245
bool BKE_curve_center_median(Curve *cu, float cent[3])
Definition curve.cc:5130
std::optional< blender::Bounds< blender::float3 > > BKE_curve_minmax(const Curve *cu, bool use_radius)
Definition curve.cc:5104
void BKE_curve_transform_ex(Curve *cu, const float mat[4][4], bool do_keys, bool do_props, float unit_scale)
Definition curve.cc:5167
Low-level operations for curves.
void BKE_gpencil_centroid_3d(struct bGPdata *gpd, float r_centroid[3])
void BKE_gpencil_stroke_geometry_update(struct bGPdata *gpd, struct bGPDstroke *gps)
void BKE_gpencil_transform(struct bGPdata *gpd, const float mat[4][4])
void BKE_gpencil_layer_transform_matrix_get(const struct Depsgraph *depsgraph, struct Object *obact, struct bGPDlayer *gpl, float diff_mat[4][4])
Low-level operations for grease pencil.
const char * BKE_idtype_idcode_to_name(short idcode)
Definition idtype.cc:168
void BKE_lattice_transform(Lattice *lt, const float mat[4][4], bool do_keys)
Definition lattice.cc:637
std::optional< blender::Bounds< blender::float3 > > BKE_lattice_minmax(const Lattice *lt)
Definition lattice.cc:617
void BKE_lattice_center_median(Lattice *lt, float cent[3])
Definition lattice.cc:599
void BKE_lattice_translate(Lattice *lt, const float offset[3], bool do_keys)
Definition lattice.cc:657
#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:2456
void id_us_plus(ID *id)
Definition lib_id.cc:351
void BKE_main_id_newptr_and_tag_clear(Main *bmain)
Definition lib_id.cc:1947
void id_us_min(ID *id)
Definition lib_id.cc:359
void BKE_main_id_tag_all(Main *mainvar, int tag, bool value)
Definition lib_id.cc:1198
bool BKE_mball_center_bounds(const MetaBall *mb, float r_cent[3])
Definition mball.cc:488
void BKE_mball_translate(MetaBall *mb, const float offset[3])
Definition mball.cc:526
void BKE_mball_transform(MetaBall *mb, const float mat[4][4], bool do_props)
Definition mball.cc:500
bool BKE_mball_center_median(const MetaBall *mb, float r_cent[3])
Definition mball.cc:470
bool BKE_mesh_center_of_surface(const Mesh *mesh, float r_cent[3])
void BKE_mesh_translate(Mesh *mesh, const float offset[3], bool do_keys)
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 BKE_mesh_transform(Mesh *mesh, const float mat[4][4], bool do_keys)
void multiresModifier_scale_disp(Depsgraph *depsgraph, Scene *scene, Object *ob)
Definition multires.cc:1432
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])
void BKE_object_apply_parent_inverse(Object *ob)
bool BKE_object_minmax_dupli(Depsgraph *depsgraph, Scene *scene, Object *ob, float r_min[3], float r_max[3], bool use_hidden)
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:125
void BKE_scene_graph_evaluated_ensure(Depsgraph *depsgraph, Main *bmain)
Definition scene.cc:2573
void BKE_tracking_reconstruction_scale(struct MovieTracking *tracking, float scale[3])
#define BLI_assert(a)
Definition BLI_assert.h:50
#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])
void mul_v3_m4v3(float r[3], const float mat[4][4], const float vec[3])
bool invert_m4_m4(float inverse[4][4], const float mat[4][4])
void mul_v3_m3v3(float r[3], const float M[3][3], const float a[3])
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)
typedef double(DMatrix)[4][4]
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)
Object * DEG_get_evaluated_object(const Depsgraph *depsgraph, Object *object)
@ ID_RECALC_PARAMETERS
Definition DNA_ID.h:1105
@ ID_RECALC_TRANSFORM
Definition DNA_ID.h:1021
@ ID_RECALC_SYNC_TO_EVAL
Definition DNA_ID.h:1085
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:1041
#define ID_IS_EDITABLE(_id)
Definition DNA_ID.h:658
@ ID_TAG_DOIT
Definition DNA_ID.h:1003
#define ID_REAL_USERS(id)
Definition DNA_ID.h:637
#define ID_IS_OVERRIDE_LIBRARY(_id)
Definition DNA_ID.h:683
@ ID_GD_LEGACY
@ 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_DONE
@ 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_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_GPENCIL_LEGACY
@ OB_CURVES
@ OB_DUPLICOLLECTION
@ SCE_XFORM_SKIP_CHILDREN
@ SCE_XFORM_DATA_ORIGIN
@ V3D_AROUND_CENTER_BOUNDS
@ V3D_AROUND_CENTER_MEDIAN
@ V3D_HIDE_OVERLAYS
@ OPERATOR_RUNNING_MODAL
@ OPERATOR_PASS_THROUGH
#define ANIM_KS_LOCATION_ID
#define ANIM_KS_SCALING_ID
#define ANIM_KS_ROTATION_ID
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:634
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])
ViewContext ED_view3d_viewcontext_init(bContext *C, Depsgraph *depsgraph)
void ED_view3d_depth_override(Depsgraph *depsgraph, ARegion *region, View3D *v3d, Object *obact, eV3DDepthOverrideMode mode, ViewDepths **r_depths)
bool ED_view3d_depth_read_cached_normal(const ARegion *region, const ViewDepths *depths, const int mval[2], float r_normal[3])
void view3d_operator_needs_opengl(const bContext *C)
void ED_view3d_depths_free(ViewDepths *depths)
@ V3D_DEPTH_NO_GPENCIL
Definition ED_view3d.hh:188
void ED_view3d_project_v3(const ARegion *region, const float world[3], float r_region_co[3])
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:245
@ PROP_HIDDEN
Definition RNA_types.hh:239
@ ALERT_ICON_WARNING
@ OPTYPE_BLOCKING
Definition WM_types.hh:164
@ OPTYPE_UNDO
Definition WM_types.hh:162
@ OPTYPE_REGISTER
Definition WM_types.hh:160
#define NC_WINDOW
Definition WM_types.hh:342
@ KM_RELEASE
Definition WM_types.hh:285
#define ND_TRANSFORM
Definition WM_types.hh:423
@ KM_CTRL
Definition WM_types.hh:256
#define NC_OBJECT
Definition WM_types.hh:346
void ANIM_deselect_keys_in_animation_editors(bContext *C)
Definition anim_deps.cc:472
void ED_armature_origin_set(Main *bmain, Object *ob, const float cursor[3], int centermode, int around)
static void translate_positions(MutableSpan< float3 > positions, const float3 &translation)
#define BM_ITER_MESH(ele, iter, bm, itype)
@ BM_VERTS_OF_MESH
static btDbvtVolume bounds(btDbvtNode **leaves, int count)
Definition btDbvt.cpp:299
const T * data() const
Definition BLI_array.hh:301
void foreach_item(const FuncT &func) const
Definition BLI_map.hh:649
void append(const T &value)
void translate(const float3 &translation)
MutableSpan< float > radii_for_write()
bke::CurvesGeometry & strokes_for_write()
const Depsgraph * depsgraph
#define fabsf(x)
draw_view in_light_buf[] float
#define rot(x, k)
static void transform_positions(const Span< blender::float3 > src, const blender::float4x4 &transform, blender::MutableSpan< blender::float3 > dst)
#define GS(x)
Definition iris.cc:202
KeyingSet * ANIM_get_keyingset_for_autokeying(const Scene *scene, const char *transformKSName)
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
void autokeyframe_object(bContext *C, Scene *scene, Object *ob, Span< RNAPath > rna_paths)
bool is_autokey_on(const Scene *scene)
static int object_origin_set_exec(bContext *C, wmOperator *op)
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)
void single_obdata_user_make(Main *bmain, Scene *scene, Object *ob)
static Array< Object * > sorted_selected_editable_objects(bContext *C)
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 int visual_transform_apply_exec(bContext *C, wmOperator *)
void OBJECT_OT_transform_axis_target(wmOperatorType *ot)
static void object_clear_loc(Object *ob, const bool clear_delta)
void OBJECT_OT_origin_clear(wmOperatorType *ot)
static int object_rotation_clear_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)
static int object_transform_axis_target_invoke(bContext *C, wmOperator *op, const wmEvent *event)
void data_xform_container_update_all(XFormObjectData_Container *xds, Main *bmain, Depsgraph *depsgraph)
static int object_origin_clear_exec(bContext *C, wmOperator *)
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)
void OBJECT_OT_visual_transform_apply(wmOperatorType *ot)
static int object_parent_inverse_apply_exec(bContext *C, wmOperator *)
static int object_transform_axis_target_modal(bContext *C, wmOperator *op, const wmEvent *event)
static int object_clear_transform_generic_exec(bContext *C, wmOperator *op, void(*clear_func)(Object *, const bool), const char default_ksName[])
static int apply_objects_internal(bContext *C, ReportList *reports, bool apply_loc, bool apply_rot, bool apply_scale, bool do_props, bool do_single_user)
void OBJECT_OT_location_clear(wmOperatorType *ot)
static int object_transform_apply_invoke(bContext *C, wmOperator *op, const wmEvent *)
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 int object_scale_clear_exec(bContext *C, wmOperator *op)
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 int object_location_clear_exec(bContext *C, wmOperator *op)
static int object_transform_apply_exec(bContext *C, wmOperator *op)
void OBJECT_OT_transform_apply(wmOperatorType *ot)
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:95
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)
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.c:32
float co[3]
int totvert
float vec[3][3]
struct TextBox * tb
struct Key * key
float fsize
CurvesGeometry geometry
Definition DNA_ID.h:413
int tag
Definition DNA_ID.h:434
char name[66]
Definition DNA_ID.h:425
float area_sizez
float area_sizey
short area_shape
float area_size
short type
void * first
ListBase objects
Definition BKE_main.hh:212
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 Object * parent
ARegion * region
Definition ED_view3d.hh:73
int mval[2]
Definition ED_view3d.hh:78
View3D * v3d
Definition ED_view3d.hh:74
Object * obact
Definition ED_view3d.hh:71
Depsgraph * depsgraph
Definition ED_view3d.hh:68
unsigned short w
Definition ED_view3d.hh:82
double depth_range[2]
Definition ED_view3d.hh:86
unsigned short h
Definition ED_view3d.hh:82
struct blender::ed::object::XFormAxisData::@460 prev
short val
Definition WM_types.hh:724
int mval[2]
Definition WM_types.hh:728
short type
Definition WM_types.hh:722
const char * name
Definition WM_types.hh:990
bool(* poll)(bContext *C) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1042
const char * idname
Definition WM_types.hh:992
int(* modal)(bContext *C, wmOperator *op, const wmEvent *event) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1036
int(* invoke)(bContext *C, wmOperator *op, const wmEvent *event) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1022
int(* exec)(bContext *C, wmOperator *op) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1006
const char * description
Definition WM_types.hh:996
PropertyRNA * prop
Definition WM_types.hh:1092
StructRNA * srna
Definition WM_types.hh:1080
void(* cancel)(bContext *C, wmOperator *op)
Definition WM_types.hh:1028
struct ReportList * reports
struct PointerRNA * ptr
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:4126
wmOperatorType * ot
Definition wm_files.cc:4125
int WM_menu_invoke(bContext *C, wmOperator *op, const wmEvent *)
int WM_operator_confirm_ex(bContext *C, wmOperator *op, const char *title, const char *message, const char *confirm_text, int icon, bool cancel_default)