Blender V4.3
view3d_snap.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 "MEM_guardedalloc.h"
10
11#include "DNA_armature_types.h"
12#include "DNA_object_types.h"
13
14#include "BLI_bounds.hh"
15#include "BLI_math_matrix.h"
16#include "BLI_math_matrix.hh"
17#include "BLI_math_vector.h"
18#include "BLI_math_vector.hh"
19#include "BLI_utildefines.h"
20#include "BLI_vector.hh"
21
22#include "BKE_action.hh"
23#include "BKE_armature.hh"
24#include "BKE_context.hh"
25#include "BKE_crazyspace.hh"
26#include "BKE_editmesh.hh"
27#include "BKE_layer.hh"
28#include "BKE_main.hh"
29#include "BKE_mball.hh"
30#include "BKE_object.hh"
31#include "BKE_report.hh"
32#include "BKE_scene.hh"
33#include "BKE_tracking.h"
34
35#include "DEG_depsgraph.hh"
37
38#include "WM_api.hh"
39#include "WM_types.hh"
40
41#include "RNA_access.hh"
42#include "RNA_define.hh"
43
44#include "ED_anim_api.hh"
45#include "ED_curves.hh"
46#include "ED_grease_pencil.hh"
47#include "ED_keyframing.hh"
48#include "ED_object.hh"
49#include "ED_screen.hh"
50#include "ED_transverts.hh"
51
52#include "ANIM_action.hh"
54#include "ANIM_keyframing.hh"
55
56#include "view3d_intern.hh"
57
58using blender::Vector;
59
60static bool snap_curs_to_sel_ex(bContext *C, const int pivot_point, float r_cursor[3]);
61static bool snap_calc_active_center(bContext *C, const bool select_only, float r_center[3]);
62
63/* -------------------------------------------------------------------- */
69{
70 using namespace blender::ed;
74 Scene *scene = CTX_data_scene(C);
75 ARegion *region = CTX_wm_region(C);
76 View3D *v3d = CTX_wm_view3d(C);
77 TransVertStore tvs = {nullptr};
78 TransVert *tv;
79 float gridf, imat[3][3], bmat[3][3], vec[3];
80 int a;
81
82 gridf = ED_view3d_grid_view_scale(scene, v3d, region, nullptr);
83
84 if (OBEDIT_FROM_OBACT(obact)) {
85 ViewLayer *view_layer = CTX_data_view_layer(C);
87 scene, view_layer, CTX_wm_view3d(C));
88 for (Object *obedit : objects) {
89 if (obedit->type == OB_MESH) {
91
92 if (em->bm->totvertsel == 0) {
93 continue;
94 }
95 }
96
98 continue;
99 }
100
101 if (ED_transverts_check_obedit(obedit)) {
102 ED_transverts_create_from_obedit(&tvs, obedit, 0);
103 }
104
105 if (tvs.transverts_tot != 0) {
106 copy_m3_m4(bmat, obedit->object_to_world().ptr());
107 invert_m3_m3(imat, bmat);
108
109 tv = tvs.transverts;
110 for (a = 0; a < tvs.transverts_tot; a++, tv++) {
111 copy_v3_v3(vec, tv->loc);
112 mul_m3_v3(bmat, vec);
113 add_v3_v3(vec, obedit->object_to_world().location());
114 vec[0] = gridf * floorf(0.5f + vec[0] / gridf);
115 vec[1] = gridf * floorf(0.5f + vec[1] / gridf);
116 vec[2] = gridf * floorf(0.5f + vec[2] / gridf);
117 sub_v3_v3(vec, obedit->object_to_world().location());
118
119 mul_m3_v3(imat, vec);
120 copy_v3_v3(tv->loc, vec);
121 }
122 ED_transverts_update_obedit(&tvs, obedit);
123 }
124 ED_transverts_free(&tvs);
125 }
126 }
127 else if (OBPOSE_FROM_OBACT(obact)) {
129 Vector<Object *> objects_eval = BKE_object_pose_array_get(scene, view_layer_eval, v3d);
130 for (Object *ob_eval : objects_eval) {
131 Object *ob = DEG_get_original_object(ob_eval);
132 bArmature *arm_eval = static_cast<bArmature *>(ob_eval->data);
133
134 invert_m4_m4(ob_eval->runtime->world_to_object.ptr(), ob_eval->object_to_world().ptr());
135
136 LISTBASE_FOREACH (bPoseChannel *, pchan_eval, &ob_eval->pose->chanbase) {
137 if (pchan_eval->bone->flag & BONE_SELECTED) {
138 if (ANIM_bonecoll_is_visible_pchan(arm_eval, pchan_eval)) {
139 if ((pchan_eval->bone->flag & BONE_CONNECTED) == 0) {
140 float nLoc[3];
141
142 /* get nearest grid point to snap to */
143 copy_v3_v3(nLoc, pchan_eval->pose_mat[3]);
144 /* We must operate in world space! */
145 mul_m4_v3(ob_eval->object_to_world().ptr(), nLoc);
146 vec[0] = gridf * floorf(0.5f + nLoc[0] / gridf);
147 vec[1] = gridf * floorf(0.5f + nLoc[1] / gridf);
148 vec[2] = gridf * floorf(0.5f + nLoc[2] / gridf);
149 /* Back in object space... */
150 mul_m4_v3(ob_eval->world_to_object().ptr(), vec);
151
152 /* Get location of grid point in pose space. */
153 BKE_armature_loc_pose_to_bone(pchan_eval, vec, vec);
154
155 /* Adjust location on the original pchan. */
156 bPoseChannel *pchan = BKE_pose_channel_find_name(ob->pose, pchan_eval->name);
157 if ((pchan->protectflag & OB_LOCK_LOCX) == 0) {
158 pchan->loc[0] = vec[0];
159 }
160 if ((pchan->protectflag & OB_LOCK_LOCY) == 0) {
161 pchan->loc[1] = vec[1];
162 }
163 if ((pchan->protectflag & OB_LOCK_LOCZ) == 0) {
164 pchan->loc[2] = vec[2];
165 }
166
167 /* auto-keyframing */
168 blender::animrig::autokeyframe_pchan(C, scene, ob, pchan, ks);
169 }
170 /* if the bone has a parent and is connected to the parent,
171 * don't do anything - will break chain unless we do auto-ik.
172 */
173 }
174 }
175 }
177
179 }
180 }
181 else {
182 /* Object mode. */
183 Main *bmain = CTX_data_main(C);
184
186
187 const bool use_transform_skip_children = (scene->toolsettings->transform_flag &
189 const bool use_transform_data_origin = (scene->toolsettings->transform_flag &
193
194 /* Build object array. */
195 Vector<Object *> objects_eval;
196 Vector<Object *> objects_orig;
197 {
198 FOREACH_SELECTED_EDITABLE_OBJECT_BEGIN (view_layer_eval, v3d, ob_eval) {
199 objects_eval.append(ob_eval);
200 objects_orig.append(DEG_get_original_object(ob_eval));
201 }
203 }
204
205 if (use_transform_skip_children) {
206 ViewLayer *view_layer = CTX_data_view_layer(C);
207
208 Vector<Object *> objects(objects_eval.size());
209 for (Object *ob_eval : objects_eval) {
210 objects.append_unchecked(DEG_get_original_object(ob_eval));
211 }
213 xcs = object::xform_skip_child_container_create();
214 object::xform_skip_child_container_item_ensure_from_array(
215 xcs, scene, view_layer, objects.data(), objects.size());
216 }
217 if (use_transform_data_origin) {
219 xds = object::data_xform_container_create();
220 }
221
224 }
225
226 for (Object *ob_eval : objects_eval) {
227 Object *ob = DEG_get_original_object(ob_eval);
228 vec[0] = -ob_eval->object_to_world().location()[0] +
229 gridf * floorf(0.5f + ob_eval->object_to_world().location()[0] / gridf);
230 vec[1] = -ob_eval->object_to_world().location()[1] +
231 gridf * floorf(0.5f + ob_eval->object_to_world().location()[1] / gridf);
232 vec[2] = -ob_eval->object_to_world().location()[2] +
233 gridf * floorf(0.5f + ob_eval->object_to_world().location()[2] / gridf);
234
235 if (ob->parent) {
236 float originmat[3][3];
237 BKE_object_where_is_calc_ex(depsgraph, scene, nullptr, ob, originmat);
238
239 invert_m3_m3(imat, originmat);
240 mul_m3_v3(imat, vec);
241 }
242 if ((ob->protectflag & OB_LOCK_LOCX) == 0) {
243 ob->loc[0] = ob_eval->loc[0] + vec[0];
244 }
245 if ((ob->protectflag & OB_LOCK_LOCY) == 0) {
246 ob->loc[1] = ob_eval->loc[1] + vec[1];
247 }
248 if ((ob->protectflag & OB_LOCK_LOCZ) == 0) {
249 ob->loc[2] = ob_eval->loc[2] + vec[2];
250 }
251
252 /* auto-keyframing */
254
255 if (use_transform_data_origin) {
256 object::data_xform_container_item_ensure(xds, ob);
257 }
258
260 }
261
262 if (use_transform_skip_children) {
263 object::object_xform_skip_child_container_update_all(xcs, bmain, depsgraph);
264 object::object_xform_skip_child_container_destroy(xcs);
265 }
266 if (use_transform_data_origin) {
267 object::data_xform_container_update_all(xds, bmain, depsgraph);
268 object::data_xform_container_destroy(xds);
269 }
270 }
271
273
274 return OPERATOR_FINISHED;
275}
276
278{
279 /* identifiers */
280 ot->name = "Snap Selection to Grid";
281 ot->description = "Snap selected item(s) to their nearest grid division";
282 ot->idname = "VIEW3D_OT_snap_selected_to_grid";
283
284 /* api callbacks */
287
288 /* flags */
290}
291
294/* -------------------------------------------------------------------- */
308 wmOperator *op,
309 const float snap_target_global[3],
310 const bool use_offset,
311 const int pivot_point,
312 const bool use_toolsettings)
313{
314 using namespace blender::ed;
315 Scene *scene = CTX_data_scene(C);
316 Object *obedit = CTX_data_edit_object(C);
317 Object *obact = CTX_data_active_object(C);
318 View3D *v3d = CTX_wm_view3d(C);
319 TransVertStore tvs = {nullptr};
320 TransVert *tv;
321 float imat[3][3], bmat[3][3];
322 float center_global[3];
323 float offset_global[3];
324 int a;
325
326 if (use_offset) {
327 if ((pivot_point == V3D_AROUND_ACTIVE) && snap_calc_active_center(C, true, center_global)) {
328 /* pass */
329 }
330 else {
331 snap_curs_to_sel_ex(C, pivot_point, center_global);
332 }
333 sub_v3_v3v3(offset_global, snap_target_global, center_global);
334 }
335
336 if (obedit) {
337 float snap_target_local[3];
338 ViewLayer *view_layer = CTX_data_view_layer(C);
340 scene, view_layer, v3d);
341 for (const int ob_index : objects.index_range()) {
342 obedit = objects[ob_index];
343
344 if (obedit->type == OB_MESH) {
346
347 if (em->bm->totvertsel == 0) {
348 continue;
349 }
350 }
351
353 continue;
354 }
355
356 if (ED_transverts_check_obedit(obedit)) {
357 ED_transverts_create_from_obedit(&tvs, obedit, 0);
358 }
359
360 if (tvs.transverts_tot != 0) {
361 copy_m3_m4(bmat, obedit->object_to_world().ptr());
362 invert_m3_m3(imat, bmat);
363
364 /* get the cursor in object space */
365 sub_v3_v3v3(snap_target_local, snap_target_global, obedit->object_to_world().location());
366 mul_m3_v3(imat, snap_target_local);
367
368 if (use_offset) {
369 float offset_local[3];
370
371 mul_v3_m3v3(offset_local, imat, offset_global);
372
373 tv = tvs.transverts;
374 for (a = 0; a < tvs.transverts_tot; a++, tv++) {
375 add_v3_v3(tv->loc, offset_local);
376 }
377 }
378 else {
379 tv = tvs.transverts;
380 for (a = 0; a < tvs.transverts_tot; a++, tv++) {
381 copy_v3_v3(tv->loc, snap_target_local);
382 }
383 }
384 ED_transverts_update_obedit(&tvs, obedit);
385 }
386 ED_transverts_free(&tvs);
387 }
388 }
389 else if (OBPOSE_FROM_OBACT(obact)) {
391 ViewLayer *view_layer = CTX_data_view_layer(C);
392 Vector<Object *> objects = BKE_object_pose_array_get(scene, view_layer, v3d);
393
394 for (Object *ob : objects) {
395 bArmature *arm = static_cast<bArmature *>(ob->data);
396 float snap_target_local[3];
397
398 invert_m4_m4(ob->runtime->world_to_object.ptr(), ob->object_to_world().ptr());
399 mul_v3_m4v3(snap_target_local, ob->world_to_object().ptr(), snap_target_global);
400
401 LISTBASE_FOREACH (bPoseChannel *, pchan, &ob->pose->chanbase) {
402 if ((pchan->bone->flag & BONE_SELECTED) && PBONE_VISIBLE(arm, pchan->bone) &&
403 /* if the bone has a parent and is connected to the parent,
404 * don't do anything - will break chain unless we do auto-ik.
405 */
406 (pchan->bone->flag & BONE_CONNECTED) == 0)
407 {
408 pchan->bone->flag |= BONE_TRANSFORM;
409 }
410 else {
411 pchan->bone->flag &= ~BONE_TRANSFORM;
412 }
413 }
414
415 LISTBASE_FOREACH (bPoseChannel *, pchan, &ob->pose->chanbase) {
416 if ((pchan->bone->flag & BONE_TRANSFORM) &&
417 /* check that our parents not transformed (if we have one) */
418 ((pchan->bone->parent &&
419 BKE_armature_bone_flag_test_recursive(pchan->bone->parent, BONE_TRANSFORM)) == 0))
420 {
421 /* Get position in pchan (pose) space. */
422 float cursor_pose[3];
423
424 if (use_offset) {
425 mul_v3_m4v3(cursor_pose, ob->object_to_world().ptr(), pchan->pose_mat[3]);
426 add_v3_v3(cursor_pose, offset_global);
427
428 mul_m4_v3(ob->world_to_object().ptr(), cursor_pose);
429 BKE_armature_loc_pose_to_bone(pchan, cursor_pose, cursor_pose);
430 }
431 else {
432 BKE_armature_loc_pose_to_bone(pchan, snap_target_local, cursor_pose);
433 }
434
435 /* copy new position */
436 if (use_toolsettings) {
437 if ((pchan->protectflag & OB_LOCK_LOCX) == 0) {
438 pchan->loc[0] = cursor_pose[0];
439 }
440 if ((pchan->protectflag & OB_LOCK_LOCY) == 0) {
441 pchan->loc[1] = cursor_pose[1];
442 }
443 if ((pchan->protectflag & OB_LOCK_LOCZ) == 0) {
444 pchan->loc[2] = cursor_pose[2];
445 }
446
447 /* auto-keyframing */
448 blender::animrig::autokeyframe_pchan(C, scene, ob, pchan, ks);
449 }
450 else {
451 copy_v3_v3(pchan->loc, cursor_pose);
452 }
453 }
454 }
455
456 LISTBASE_FOREACH (bPoseChannel *, pchan, &ob->pose->chanbase) {
457 pchan->bone->flag &= ~BONE_TRANSFORM;
458 }
459
460 ob->pose->flag |= (POSE_LOCKED | POSE_DO_UNLOCK);
461
463 }
464 }
465 else {
467 Main *bmain = CTX_data_main(C);
469
470 /* Reset flags. */
471 for (Object *ob = static_cast<Object *>(bmain->objects.first); ob;
472 ob = static_cast<Object *>(ob->id.next))
473 {
474 ob->flag &= ~OB_DONE;
475 }
476
477 /* Build object array, tag objects we're transforming. */
478 ViewLayer *view_layer = CTX_data_view_layer(C);
479 Vector<Object *> objects;
480 {
481 FOREACH_SELECTED_EDITABLE_OBJECT_BEGIN (view_layer, v3d, ob) {
482 objects.append(ob);
483 ob->flag |= OB_DONE;
484 }
486 }
487
488 const bool use_transform_skip_children = use_toolsettings &&
489 (scene->toolsettings->transform_flag &
491 const bool use_transform_data_origin = use_toolsettings &&
492 (scene->toolsettings->transform_flag &
496
497 if (use_transform_skip_children) {
499 xcs = object::xform_skip_child_container_create();
500 object::xform_skip_child_container_item_ensure_from_array(
501 xcs, scene, view_layer, objects.data(), objects.size());
502 }
503 if (use_transform_data_origin) {
505 xds = object::data_xform_container_create();
506
507 /* Initialize the transform data in a separate loop because the depsgraph
508 * may be evaluated while setting the locations. */
509 for (Object *ob : objects) {
510 object::data_xform_container_item_ensure(xds, ob);
511 }
512 }
513
516 }
517
518 for (Object *ob : objects) {
519 if (ob->parent && BKE_object_flag_test_recursive(ob->parent, OB_DONE)) {
520 continue;
521 }
522
523 float cursor_parent[3]; /* parent-relative */
524
525 if (use_offset) {
526 add_v3_v3v3(cursor_parent, ob->object_to_world().location(), offset_global);
527 }
528 else {
529 copy_v3_v3(cursor_parent, snap_target_global);
530 }
531
532 sub_v3_v3(cursor_parent, ob->object_to_world().location());
533
534 if (ob->parent) {
535 float originmat[3][3], parentmat[4][4];
536 /* Use the evaluated object here because sometimes
537 * `ob->parent->runtime->curve_cache` is required. */
540
541 BKE_object_get_parent_matrix(ob_eval, ob_eval->parent, parentmat);
542 mul_m3_m4m4(originmat, parentmat, ob->parentinv);
543 invert_m3_m3(imat, originmat);
544 mul_m3_v3(imat, cursor_parent);
545 }
546 if (use_toolsettings) {
547 if ((ob->protectflag & OB_LOCK_LOCX) == 0) {
548 ob->loc[0] += cursor_parent[0];
549 }
550 if ((ob->protectflag & OB_LOCK_LOCY) == 0) {
551 ob->loc[1] += cursor_parent[1];
552 }
553 if ((ob->protectflag & OB_LOCK_LOCZ) == 0) {
554 ob->loc[2] += cursor_parent[2];
555 }
556
557 /* auto-keyframing */
559 }
560 else {
561 add_v3_v3(ob->loc, cursor_parent);
562 }
563
565 }
566
567 if (use_transform_skip_children) {
568 object::object_xform_skip_child_container_update_all(xcs, bmain, depsgraph);
569 object::object_xform_skip_child_container_destroy(xcs);
570 }
571 if (use_transform_data_origin) {
572 object::data_xform_container_update_all(xds, bmain, depsgraph);
573 object::data_xform_container_destroy(xds);
574 }
575 }
576
578
579 return true;
580}
581
583 wmOperator *op,
584 const float snap_target_global[3],
585 const int pivot_point)
586{
587 /* These could be passed as arguments if needed. */
588 /* Always use pivot point. */
589 const bool use_offset = true;
590 /* Disable object protected flags & auto-keyframing,
591 * so this can be used as a low level function. */
592 const bool use_toolsettings = false;
594 C, op, snap_target_global, use_offset, pivot_point, use_toolsettings);
595}
596
599/* -------------------------------------------------------------------- */
604{
605 const bool use_offset = RNA_boolean_get(op->ptr, "use_offset");
606
607 Scene *scene = CTX_data_scene(C);
608
609 const float *snap_target_global = scene->cursor.location;
610 const int pivot_point = scene->toolsettings->transform_pivot_point;
611
612 if (snap_selected_to_location(C, op, snap_target_global, use_offset, pivot_point, true)) {
613 return OPERATOR_FINISHED;
614 }
615 return OPERATOR_CANCELLED;
616}
617
619{
620 /* identifiers */
621 ot->name = "Snap Selection to Cursor";
622 ot->description = "Snap selected item(s) to the 3D cursor";
623 ot->idname = "VIEW3D_OT_snap_selected_to_cursor";
624
625 /* api callbacks */
628
629 /* flags */
631
632 /* rna */
634 "use_offset",
635 true,
636 "Offset",
637 "If the selection should be snapped as a whole or by each object center");
638}
639
642/* -------------------------------------------------------------------- */
648{
649 float snap_target_global[3];
650
651 if (snap_calc_active_center(C, false, snap_target_global) == false) {
652 BKE_report(op->reports, RPT_ERROR, "No active element found!");
653 return OPERATOR_CANCELLED;
654 }
655
656 if (!snap_selected_to_location(C, op, snap_target_global, false, -1, true)) {
657 return OPERATOR_CANCELLED;
658 }
659 return OPERATOR_FINISHED;
660}
661
663{
664 /* identifiers */
665 ot->name = "Snap Selection to Active";
666 ot->description = "Snap selected item(s) to the active item";
667 ot->idname = "VIEW3D_OT_snap_selected_to_active";
668
669 /* api callbacks */
672
673 /* flags */
675}
676
679/* -------------------------------------------------------------------- */
685{
686 Scene *scene = CTX_data_scene(C);
687 ARegion *region = CTX_wm_region(C);
688 View3D *v3d = CTX_wm_view3d(C);
689 float gridf, *curs;
690
691 gridf = ED_view3d_grid_view_scale(scene, v3d, region, nullptr);
692 curs = scene->cursor.location;
693
694 curs[0] = gridf * floorf(0.5f + curs[0] / gridf);
695 curs[1] = gridf * floorf(0.5f + curs[1] / gridf);
696 curs[2] = gridf * floorf(0.5f + curs[2] / gridf);
697
698 WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, nullptr); /* hrm */
700
701 return OPERATOR_FINISHED;
702}
703
705{
706 /* identifiers */
707 ot->name = "Snap Cursor to Grid";
708 ot->description = "Snap 3D cursor to the nearest grid division";
709 ot->idname = "VIEW3D_OT_snap_cursor_to_grid";
710
711 /* api callbacks */
714
715 /* flags */
717}
718
721/* -------------------------------------------------------------------- */
729static void bundle_midpoint(Scene *scene, Object *ob, float r_vec[3])
730{
731 MovieClip *clip = BKE_object_movieclip_get(scene, ob, false);
732 bool ok = false;
733 float min[3], max[3], mat[4][4], pos[3], cammat[4][4];
734
735 if (!clip) {
736 return;
737 }
738
739 MovieTracking *tracking = &clip->tracking;
740
741 copy_m4_m4(cammat, ob->object_to_world().ptr());
742
744
745 INIT_MINMAX(min, max);
746
747 LISTBASE_FOREACH (MovieTrackingObject *, tracking_object, &tracking->objects) {
748 float obmat[4][4];
749
750 if (tracking_object->flag & TRACKING_OBJECT_CAMERA) {
751 copy_m4_m4(obmat, mat);
752 }
753 else {
754 float imat[4][4];
755
757 tracking, tracking_object, scene->r.cfra, imat);
758 invert_m4(imat);
759
760 mul_m4_m4m4(obmat, cammat, imat);
761 }
762
763 LISTBASE_FOREACH (const MovieTrackingTrack *, track, &tracking_object->tracks) {
764 if ((track->flag & TRACK_HAS_BUNDLE) && TRACK_SELECTED(track)) {
765 ok = true;
766 mul_v3_m4v3(pos, obmat, track->bundle_pos);
767 minmax_v3v3_v3(min, max, pos);
768 }
769 }
770 }
771
772 if (ok) {
773 mid_v3_v3v3(r_vec, min, max);
774 }
775}
776
778static bool snap_curs_to_sel_ex(bContext *C, const int pivot_point, float r_cursor[3])
779{
782 Object *obedit = CTX_data_edit_object(C);
783 Scene *scene = CTX_data_scene(C);
784 View3D *v3d = CTX_wm_view3d(C);
785 TransVertStore tvs = {nullptr};
786 TransVert *tv;
787 float bmat[3][3], vec[3], min[3], max[3], centroid[3];
788 int count = 0;
789
790 INIT_MINMAX(min, max);
791 zero_v3(centroid);
792
793 if (obedit) {
794 ViewLayer *view_layer = CTX_data_view_layer(C);
796 scene, view_layer, CTX_wm_view3d(C));
797 for (const int ob_index : objects.index_range()) {
798 obedit = objects[ob_index];
799
800 /* We can do that quick check for meshes only... */
801 if (obedit->type == OB_MESH) {
803
804 if (em->bm->totvertsel == 0) {
805 continue;
806 }
807 }
808
809 if (ED_transverts_check_obedit(obedit)) {
811 }
812
813 count += tvs.transverts_tot;
814 if (tvs.transverts_tot != 0) {
815 Object *obedit_eval = DEG_get_evaluated_object(depsgraph, obedit);
816 copy_m3_m4(bmat, obedit_eval->object_to_world().ptr());
817
818 tv = tvs.transverts;
819 for (int i = 0; i < tvs.transverts_tot; i++, tv++) {
820 copy_v3_v3(vec, tv->loc);
821 mul_m3_v3(bmat, vec);
822 add_v3_v3(vec, obedit_eval->object_to_world().location());
823 add_v3_v3(centroid, vec);
824 minmax_v3v3_v3(min, max, vec);
825 }
826 }
827 ED_transverts_free(&tvs);
828 }
829 }
830 else {
831 Object *obact = CTX_data_active_object(C);
832
833 if (obact && (obact->mode & OB_MODE_POSE)) {
834 Object *obact_eval = DEG_get_evaluated_object(depsgraph, obact);
835 bArmature *arm = static_cast<bArmature *>(obact_eval->data);
836 LISTBASE_FOREACH (bPoseChannel *, pchan, &obact_eval->pose->chanbase) {
837 if (ANIM_bonecoll_is_visible_pchan(arm, pchan)) {
838 if (pchan->bone->flag & BONE_SELECTED) {
839 copy_v3_v3(vec, pchan->pose_head);
840 mul_m4_v3(obact_eval->object_to_world().ptr(), vec);
841 add_v3_v3(centroid, vec);
842 minmax_v3v3_v3(min, max, vec);
843 count++;
844 }
845 }
846 }
847 }
848 else {
849 FOREACH_SELECTED_OBJECT_BEGIN (view_layer_eval, v3d, ob_eval) {
850 copy_v3_v3(vec, ob_eval->object_to_world().location());
851
852 /* special case for camera -- snap to bundles */
853 if (ob_eval->type == OB_CAMERA) {
854 /* snap to bundles should happen only when bundles are visible */
855 if (v3d->flag2 & V3D_SHOW_RECONSTRUCTION) {
856 bundle_midpoint(scene, DEG_get_original_object(ob_eval), vec);
857 }
858 }
859
860 add_v3_v3(centroid, vec);
861 minmax_v3v3_v3(min, max, vec);
862 count++;
863 }
865 }
866 }
867
868 if (count == 0) {
869 return false;
870 }
871
872 if (pivot_point == V3D_AROUND_CENTER_BOUNDS) {
873 mid_v3_v3v3(r_cursor, min, max);
874 }
875 else {
876 mul_v3_fl(centroid, 1.0f / float(count));
877 copy_v3_v3(r_cursor, centroid);
878 }
879 return true;
880}
881
883{
884 Scene *scene = CTX_data_scene(C);
885 const int pivot_point = scene->toolsettings->transform_pivot_point;
886 if (snap_curs_to_sel_ex(C, pivot_point, scene->cursor.location)) {
889
890 return OPERATOR_FINISHED;
891 }
892 return OPERATOR_CANCELLED;
893}
894
896{
897 /* identifiers */
898 ot->name = "Snap Cursor to Selected";
899 ot->description = "Snap 3D cursor to the middle of the selected item(s)";
900 ot->idname = "VIEW3D_OT_snap_cursor_to_selected";
901
902 /* api callbacks */
905
906 /* flags */
908}
909
912/* -------------------------------------------------------------------- */
922static bool snap_calc_active_center(bContext *C, const bool select_only, float r_center[3])
923{
925 if (ob == nullptr) {
926 return false;
927 }
928 return blender::ed::object::calc_active_center(ob, select_only, r_center);
929}
930
932{
933 Scene *scene = CTX_data_scene(C);
934
935 if (snap_calc_active_center(C, false, scene->cursor.location)) {
938
939 return OPERATOR_FINISHED;
940 }
941 return OPERATOR_CANCELLED;
942}
943
945{
946 /* identifiers */
947 ot->name = "Snap Cursor to Active";
948 ot->description = "Snap 3D cursor to the active item";
949 ot->idname = "VIEW3D_OT_snap_cursor_to_active";
950
951 /* api callbacks */
954
955 /* flags */
957}
958
961/* -------------------------------------------------------------------- */
967{
968 Scene *scene = CTX_data_scene(C);
969
970 scene->cursor.set_matrix(blender::float4x4::identity(), false);
971
973
975 return OPERATOR_FINISHED;
976}
977
979{
980 /* identifiers */
981 ot->name = "Snap Cursor to World Origin";
982 ot->description = "Snap 3D cursor to the world origin";
983 ot->idname = "VIEW3D_OT_snap_cursor_to_center";
984
985 /* api callbacks */
988
989 /* flags */
991}
992
995/* -------------------------------------------------------------------- */
999static std::optional<blender::Bounds<blender::float3>> bounds_min_max_with_transform(
1000 const blender::float4x4 &transform,
1001 const blender::Span<blender::float3> positions,
1002 const blender::IndexMask &mask)
1003{
1004 using namespace blender;
1005 if (mask.is_empty()) {
1006 return std::nullopt;
1007 }
1008 return threading::parallel_reduce(
1009 mask.index_range(),
1010 1024,
1011 Bounds<float3>(math::transform_point(transform, positions[mask.first()])),
1012 [&](const IndexRange range, Bounds<float3> init) {
1013 mask.slice(range).foreach_index([&](const int i) {
1014 math::min_max(math::transform_point(transform, positions[i]), init.min, init.max);
1015 });
1016 return init;
1017 },
1018 [](const Bounds<float3> &a, const Bounds<float3> &b) { return bounds::merge(a, b); });
1019}
1020
1021bool ED_view3d_minmax_verts(const Scene *scene, Object *obedit, float r_min[3], float r_max[3])
1022{
1023 using namespace blender;
1024 using namespace blender::ed;
1025 TransVertStore tvs = {nullptr};
1026 TransVert *tv;
1027 float centroid[3], vec[3], bmat[3][3];
1028
1029 /* Metaballs are an exception. */
1030 if (obedit->type == OB_MBALL) {
1031 float ob_min[3], ob_max[3];
1032 bool changed;
1033
1034 changed = BKE_mball_minmax_ex(static_cast<const MetaBall *>(obedit->data),
1035 ob_min,
1036 ob_max,
1037 obedit->object_to_world().ptr(),
1038 SELECT);
1039 if (changed) {
1040 minmax_v3v3_v3(r_min, r_max, ob_min);
1041 minmax_v3v3_v3(r_min, r_max, ob_max);
1042 }
1043 return changed;
1044 }
1045 if (obedit->type == OB_CURVES) {
1046 const Object &ob_orig = *DEG_get_original_object(obedit);
1047 const Curves &curves_id = *static_cast<const Curves *>(ob_orig.data);
1048 const bke::CurvesGeometry &curves = curves_id.geometry.wrap();
1049
1050 IndexMaskMemory memory;
1051 const IndexMask mask = curves::retrieve_selected_points(curves, memory);
1052
1053 const bke::crazyspace::GeometryDeformation deformation =
1054 bke::crazyspace::get_evaluated_curves_deformation(obedit, ob_orig);
1055
1056 const std::optional<Bounds<float3>> curves_bounds = bounds_min_max_with_transform(
1057 obedit->object_to_world(), deformation.positions, mask);
1058
1059 if (curves_bounds) {
1060 minmax_v3v3_v3(r_min, r_max, curves_bounds->min);
1061 minmax_v3v3_v3(r_min, r_max, curves_bounds->max);
1062 return true;
1063 }
1064 return false;
1065 }
1066 if (obedit->type == OB_GREASE_PENCIL) {
1067 Object &ob_orig = *DEG_get_original_object(obedit);
1068 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(ob_orig.data);
1069
1070 std::optional<Bounds<float3>> grease_pencil_bounds = std::nullopt;
1071
1073 greasepencil::retrieve_editable_drawings(*scene, grease_pencil);
1074 for (const greasepencil::MutableDrawingInfo info : drawings) {
1075 const bke::CurvesGeometry &curves = info.drawing.strokes();
1076 if (curves.points_num() == 0) {
1077 continue;
1078 }
1079
1080 IndexMaskMemory memory;
1081 const IndexMask points = greasepencil::retrieve_editable_and_selected_points(
1082 ob_orig, info.drawing, info.layer_index, memory);
1083 if (points.is_empty()) {
1084 continue;
1085 }
1086
1087 const bke::crazyspace::GeometryDeformation deformation =
1088 bke::crazyspace::get_evaluated_grease_pencil_drawing_deformation(
1089 obedit, ob_orig, info.layer_index, info.frame_number);
1090
1091 const bke::greasepencil::Layer &layer = grease_pencil.layer(info.layer_index);
1092 const float4x4 layer_to_world = layer.to_world_space(*obedit);
1093
1094 grease_pencil_bounds = bounds::merge(
1095 grease_pencil_bounds,
1096 bounds_min_max_with_transform(layer_to_world, deformation.positions, points));
1097 }
1098
1099 if (grease_pencil_bounds) {
1100 minmax_v3v3_v3(r_min, r_max, grease_pencil_bounds->min);
1101 minmax_v3v3_v3(r_min, r_max, grease_pencil_bounds->max);
1102 return true;
1103 }
1104 return false;
1105 }
1106
1107 if (ED_transverts_check_obedit(obedit)) {
1109 }
1110
1111 if (tvs.transverts_tot == 0) {
1112 return false;
1113 }
1114
1115 copy_m3_m4(bmat, obedit->object_to_world().ptr());
1116
1117 tv = tvs.transverts;
1118 for (int a = 0; a < tvs.transverts_tot; a++, tv++) {
1119 copy_v3_v3(vec, (tv->flag & TX_VERT_USE_MAPLOC) ? tv->maploc : tv->loc);
1120 mul_m3_v3(bmat, vec);
1121 add_v3_v3(vec, obedit->object_to_world().location());
1122 add_v3_v3(centroid, vec);
1123 minmax_v3v3_v3(r_min, r_max, vec);
1124 }
1125
1126 ED_transverts_free(&tvs);
1127
1128 return true;
1129}
1130
Functions and classes to work with Actions.
C++ functions to deal with Armature collections (i.e. the successor of bone layers).
bool ANIM_bonecoll_is_visible_pchan(const bArmature *armature, const bPoseChannel *pchan)
Functions to insert, delete or modify keyframes.
Blender kernel action and pose functionality.
bPoseChannel * BKE_pose_channel_find_name(const bPose *pose, const char *name)
void BKE_armature_loc_pose_to_bone(bPoseChannel *pchan, const float inloc[3], float outloc[3])
Definition armature.cc:2270
bool BKE_armature_bone_flag_test_recursive(const Bone *bone, int flag)
Definition armature.cc:835
#define PBONE_VISIBLE(arm, bone)
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)
Scene * CTX_data_scene(const bContext *C)
Object * CTX_data_edit_object(const bContext *C)
Main * CTX_data_main(const bContext *C)
ARegion * CTX_wm_region(const bContext *C)
View3D * CTX_wm_view3d(const bContext *C)
ViewLayer * CTX_data_view_layer(const bContext *C)
BMEditMesh * BKE_editmesh_from_object(Object *ob)
Return the BMEditMesh for a given object.
Definition editmesh.cc:63
#define FOREACH_SELECTED_EDITABLE_OBJECT_END
Definition BKE_layer.hh:327
#define FOREACH_SELECTED_OBJECT_BEGIN(_view_layer, _v3d, _instance)
Definition BKE_layer.hh:298
#define FOREACH_SELECTED_EDITABLE_OBJECT_BEGIN(_view_layer, _v3d, _instance)
Definition BKE_layer.hh:315
#define FOREACH_SELECTED_OBJECT_END
Definition BKE_layer.hh:310
blender::Vector< Object * > BKE_view_layer_array_from_objects_in_edit_mode_unique_data(const Scene *scene, ViewLayer *view_layer, const View3D *v3d)
bool BKE_mball_minmax_ex(const MetaBall *mb, float min[3], float max[3], const float obmat[4][4], short flag)
Definition mball.cc:426
General operations, lookup, etc. for blender objects.
bool BKE_object_flag_test_recursive(const Object *ob, short flag)
MovieClip * BKE_object_movieclip_get(Scene *scene, const Object *ob, bool use_default)
void BKE_object_get_parent_matrix(const Object *ob, Object *par, float r_parentmat[4][4])
void BKE_object_where_is_calc_ex(Depsgraph *depsgraph, Scene *scene, RigidBodyWorld *rbw, Object *ob, float r_originmat[3][3])
blender::Vector< Object * > BKE_object_pose_array_get(const Scene *scene, ViewLayer *view_layer, View3D *v3d)
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_get_camera_object_matrix(const struct Object *camera_object, float mat[4][4])
#define TRACK_SELECTED(track)
void BKE_tracking_camera_get_reconstructed_interpolate(struct MovieTracking *tracking, struct MovieTrackingObject *tracking_object, float framenr, float mat[4][4])
Definition tracking.cc:2146
#define LISTBASE_FOREACH(type, var, list)
void mul_m3_v3(const float M[3][3], float r[3])
void mul_m4_m4m4(float R[4][4], const float A[4][4], const float B[4][4])
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 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])
bool invert_m4(float mat[4][4])
void mul_m3_m4m4(float R[3][3], const float A[4][4], const float B[4][4])
void minmax_v3v3_v3(float min[3], float max[3], const float vec[3])
MINLINE void sub_v3_v3(float r[3], const float a[3])
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 add_v3_v3v3(float r[3], const float a[3], const float b[3])
void mid_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void zero_v3(float r[3])
MINLINE void add_v3_v3(float r[3], const float a[3])
#define INIT_MINMAX(min, max)
void DEG_id_tag_update(ID *id, unsigned int flags)
ViewLayer * DEG_get_evaluated_view_layer(const Depsgraph *graph)
Object * DEG_get_original_object(Object *object)
Object * DEG_get_evaluated_object(const Depsgraph *depsgraph, Object *object)
@ 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
@ POSE_LOCKED
@ POSE_DO_UNLOCK
@ BONE_SELECTED
@ BONE_TRANSFORM
@ BONE_CONNECTED
@ OB_MODE_POSE
Object is a sort of wrapper for general info.
@ OB_DONE
@ OB_LOCK_LOCY
@ OB_LOCK_LOCZ
@ OB_LOCK_LOCX
@ OB_MBALL
@ OB_CAMERA
@ OB_GREASE_PENCIL
@ OB_MESH
@ OB_CURVES
#define OBPOSE_FROM_OBACT(ob)
@ SCE_XFORM_SKIP_CHILDREN
@ SCE_XFORM_DATA_ORIGIN
#define OBEDIT_FROM_OBACT(ob)
@ TRACK_HAS_BUNDLE
@ TRACKING_OBJECT_CAMERA
@ V3D_AROUND_ACTIVE
@ V3D_AROUND_CENTER_BOUNDS
@ V3D_SHOW_RECONSTRUCTION
#define ANIM_KS_LOCATION_ID
bool ED_operator_view3d_active(bContext *C)
bool ED_operator_region_view3d_active(bContext *C)
bool ED_transverts_check_obedit(const Object *obedit)
void ED_transverts_create_from_obedit(TransVertStore *tvs, const Object *obedit, int mode)
void ED_transverts_free(TransVertStore *tvs)
void ED_transverts_update_obedit(TransVertStore *tvs, Object *obedit)
@ TX_VERT_USE_MAPLOC
@ TM_SKIP_HANDLES
@ TM_CALC_MAPLOC
@ TM_ALL_JOINTS
float ED_view3d_grid_view_scale(const Scene *scene, const View3D *v3d, const ARegion *region, const char **r_grid_unit)
Read Guarded memory(de)allocation.
@ OPTYPE_UNDO
Definition WM_types.hh:162
@ OPTYPE_REGISTER
Definition WM_types.hh:160
#define ND_TRANSFORM
Definition WM_types.hh:423
#define ND_SPACE_VIEW3D
Definition WM_types.hh:494
#define NC_OBJECT
Definition WM_types.hh:346
#define NC_SPACE
Definition WM_types.hh:359
void ANIM_deselect_keys_in_animation_editors(bContext *C)
Definition anim_deps.cc:472
void init()
int64_t size() const
void append(const T &value)
local_group_size(16, 16) .push_constant(Type b
#define SELECT
const Depsgraph * depsgraph
#define floorf(x)
int count
KeyingSet * ANIM_get_keyingset_for_autokeying(const Scene *scene, const char *transformKSName)
void autokeyframe_object(bContext *C, Scene *scene, Object *ob, Span< RNAPath > rna_paths)
bool is_autokey_on(const Scene *scene)
bool autokeyframe_pchan(bContext *C, Scene *scene, Object *ob, bPoseChannel *pchan, KeyingSet *ks)
bool calc_active_center(Object *ob, bool select_only, float r_center[3])
bool shape_key_report_if_locked(const Object *obedit, ReportList *reports)
VecBase< T, 3 > transform_point(const CartesianBasis &basis, const VecBase< T, 3 > &v)
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
PropertyRNA * RNA_def_boolean(StructOrFunctionRNA *cont_, const char *identifier, const bool default_value, const char *ui_name, const char *ui_description)
#define min(a, b)
Definition sort.c:32
int totvertsel
CurvesGeometry geometry
void * first
ListBase objects
Definition BKE_main.hh:212
struct bPose * pose
float loc[3]
short protectflag
struct Object * parent
TransVert * transverts
float * loc
float maploc[3]
ListBase chanbase
const char * name
Definition WM_types.hh:990
bool(* poll)(bContext *C) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1042
const char * idname
Definition WM_types.hh:992
int(* exec)(bContext *C, wmOperator *op) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1006
const char * description
Definition WM_types.hh:996
StructRNA * srna
Definition WM_types.hh:1080
struct ReportList * reports
struct PointerRNA * ptr
static int snap_curs_to_center_exec(bContext *C, wmOperator *)
static int snap_curs_to_grid_exec(bContext *C, wmOperator *)
void VIEW3D_OT_snap_cursor_to_grid(wmOperatorType *ot)
bool ED_view3d_snap_selected_to_location(bContext *C, wmOperator *op, const float snap_target_global[3], const int pivot_point)
static void bundle_midpoint(Scene *scene, Object *ob, float r_vec[3])
static bool snap_selected_to_location(bContext *C, wmOperator *op, const float snap_target_global[3], const bool use_offset, const int pivot_point, const bool use_toolsettings)
static bool snap_curs_to_sel_ex(bContext *C, const int pivot_point, float r_cursor[3])
void VIEW3D_OT_snap_selected_to_grid(wmOperatorType *ot)
void VIEW3D_OT_snap_cursor_to_selected(wmOperatorType *ot)
static int snap_selected_to_active_exec(bContext *C, wmOperator *op)
void VIEW3D_OT_snap_cursor_to_center(wmOperatorType *ot)
static int snap_curs_to_sel_exec(bContext *C, wmOperator *)
static int snap_sel_to_grid_exec(bContext *C, wmOperator *op)
bool ED_view3d_minmax_verts(const Scene *scene, Object *obedit, float r_min[3], float r_max[3])
static bool snap_calc_active_center(bContext *C, const bool select_only, float r_center[3])
static int snap_selected_to_cursor_exec(bContext *C, wmOperator *op)
static std::optional< blender::Bounds< blender::float3 > > bounds_min_max_with_transform(const blender::float4x4 &transform, const blender::Span< blender::float3 > positions, const blender::IndexMask &mask)
void VIEW3D_OT_snap_selected_to_active(wmOperatorType *ot)
void VIEW3D_OT_snap_cursor_to_active(wmOperatorType *ot)
void VIEW3D_OT_snap_selected_to_cursor(wmOperatorType *ot)
static int snap_curs_to_active_exec(bContext *C, wmOperator *)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
wmOperatorType * ot
Definition wm_files.cc:4125