Blender V5.0
view3d_navigate.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include "DNA_curve_types.h"
10
11#include "BLI_dial_2d.h"
12#include "BLI_listbase.h"
13#include "BLI_math_geom.h"
14#include "BLI_math_matrix.hh"
15#include "BLI_math_rotation.h"
16#include "BLI_math_vector.hh"
17#include "BLI_rect.h"
18
19#include "BKE_context.hh"
20#include "BKE_layer.hh"
21#include "BKE_object.hh"
22#include "BKE_paint.hh"
23#include "BKE_vfont.hh"
24
26
27#include "ED_screen.hh"
28#include "ED_transform.hh"
29
30#include "WM_api.hh"
31
32#include "RNA_access.hh"
33#include "RNA_define.hh"
34
35#include "view3d_intern.hh"
36
37#include "view3d_navigate.hh" /* own include */
38
39/* Prototypes. */
40static const ViewOpsType *view3d_navigation_type_from_idname(const char *idname);
41
43{
44 const bool use_select = (U.uiflag & USER_ORBIT_SELECTION) != 0;
45 const bool use_depth = (U.uiflag & USER_DEPTH_NAVIGATE) != 0;
46 const bool use_zoom_to_mouse = (U.uiflag & USER_ZOOM_TO_MOUSEPOS) != 0;
47
62
63 if (use_select) {
65 }
66 if (use_depth) {
68 }
69 if (use_zoom_to_mouse) {
71 }
72
73 return flag;
74}
75
76/* -------------------------------------------------------------------- */
79
81{
82 /* Store data. */
84 this->scene = CTX_data_scene(C);
85 this->area = CTX_wm_area(C);
86 this->region = CTX_wm_region(C);
87 this->v3d = static_cast<View3D *>(this->area->spacedata.first);
88 this->rv3d = static_cast<RegionView3D *>(this->region->regiondata);
89}
90
92{
93 copy_v3_v3(this->init.ofs, rv3d->ofs);
94 copy_v2_v2(this->init.ofs_lock, rv3d->ofs_lock);
95 this->init.camdx = rv3d->camdx;
96 this->init.camdy = rv3d->camdy;
97 this->init.camzoom = rv3d->camzoom;
98 this->init.dist = rv3d->dist;
99 copy_qt_qt(this->init.quat, rv3d->viewquat);
100
101 this->init.persp = rv3d->persp;
102 this->init.view = rv3d->view;
103 this->init.view_axis_roll = rv3d->view_axis_roll;
104}
105
107{
108 /* DOLLY, MOVE, ROTATE and ZOOM. */
109 {
110 /* For Move this only changes when offset is not locked. */
111 /* For Rotate this only changes when rotating around objects or last-brush. */
112 /* For Zoom this only changes when zooming to mouse position. */
113 /* Note this does not remove auto-keys on locked cameras. */
114 copy_v3_v3(this->rv3d->ofs, this->init.ofs);
115 }
116
117 /* MOVE and ZOOM. */
118 {
119 /* For Move this only changes when offset is not locked. */
120 /* For Zoom this only changes when zooming to mouse position in camera view. */
121 this->rv3d->camdx = this->init.camdx;
122 this->rv3d->camdy = this->init.camdy;
123 }
124
125 /* MOVE. */
126 {
127 if ((this->rv3d->persp == RV3D_CAMOB) && !ED_view3d_camera_lock_check(this->v3d, this->rv3d)) {
128 // this->rv3d->camdx = this->init.camdx;
129 // this->rv3d->camdy = this->init.camdy;
130 }
131 else if (ED_view3d_offset_lock_check(this->v3d, this->rv3d)) {
132 copy_v2_v2(this->rv3d->ofs_lock, this->init.ofs_lock);
133 }
134 else {
135 // copy_v3_v3(vod->rv3d->ofs, vod->init.ofs);
136 if (RV3D_LOCK_FLAGS(this->rv3d) & RV3D_BOXVIEW) {
137 view3d_boxview_sync(this->area, this->region);
138 }
139 }
140 }
141
142 /* ZOOM. */
143 {
144 this->rv3d->camzoom = this->init.camzoom;
145 }
146
147 /* ROTATE and ZOOM. */
148 {
149 /* For Rotate this only changes when orbiting from a camera view.
150 * In this case the `dist` is calculated based on the camera relative to the `ofs`. */
151
152 /* Note this does not remove auto-keys on locked cameras. */
153 this->rv3d->dist = this->init.dist;
154 }
155
156 /* ROLL and ROTATE. */
157 {
158 /* Note this does not remove auto-keys on locked cameras. */
159 copy_qt_qt(this->rv3d->viewquat, this->init.quat);
160 }
161
162 /* ROTATE. */
163 {
164 this->rv3d->persp = this->init.persp;
165 this->rv3d->view = this->init.view;
166 this->rv3d->view_axis_roll = this->init.view_axis_roll;
167 }
168
169 /* NOTE: there is no need to restore "last" values (as set by #ED_view3d_lastview_store). */
170
171 ED_view3d_camera_lock_sync(this->depsgraph, this->v3d, this->rv3d);
172}
173
175 Depsgraph *depsgraph,
176 ARegion *region,
177 View3D *v3d,
178 const wmEvent *event,
179 eViewOpsFlag viewops_flag,
180 const float dyn_ofs_override[3],
181 float r_pivot[3])
182{
183 if ((viewops_flag & VIEWOPS_FLAG_ORBIT_SELECT) && view3d_orbit_calc_center(C, r_pivot)) {
185 }
186
187 wmWindow *win = CTX_wm_window(C);
188
189 if (!(viewops_flag & VIEWOPS_FLAG_DEPTH_NAVIGATE)) {
191
192 /* Uses the `lastofs` in #view3d_orbit_calc_center. */
196 }
197 /* No valid pivot, don't use any dynamic offset. */
198 return VIEWOPS_FLAG_NONE;
199 }
200
201 if (dyn_ofs_override) {
202 ED_view3d_win_to_3d_int(v3d, region, dyn_ofs_override, event->mval, r_pivot);
204 }
205
206 const bool use_depth_last = ED_view3d_autodist_last_check(win, event);
207
208 if (use_depth_last) {
209 ED_view3d_autodist_last_get(win, r_pivot);
210 }
211 else {
212 float fallback_depth_pt[3];
213 negate_v3_v3(fallback_depth_pt, static_cast<RegionView3D *>(region->regiondata)->ofs);
214
217 depsgraph, region, v3d, nullptr, V3D_DEPTH_NO_GPENCIL, true, nullptr);
218 }
219
220 const bool is_set = ED_view3d_autodist(region, v3d, event->mval, r_pivot, fallback_depth_pt);
221
222 ED_view3d_autodist_last_set(win, event, r_pivot, is_set);
223 }
224
226}
227
229 const wmEvent *event,
230 const ViewOpsType *nav_type,
231 const float dyn_ofs_override[3],
232 const bool use_cursor_init)
233{
234 using namespace blender;
235 this->nav_type = nav_type;
237 constexpr eViewOpsFlag viewops_flag_dynamic_ofs = VIEWOPS_FLAG_DEPTH_NAVIGATE |
239
240 if (!use_cursor_init) {
242 }
243
244 bool calc_rv3d_dist = true;
245#ifdef WITH_INPUT_NDOF
246 if (ELEM(nav_type,
247 &ViewOpsType_ndof_orbit,
248 &ViewOpsType_ndof_orbit_zoom,
249 &ViewOpsType_ndof_pan,
250 &ViewOpsType_ndof_all))
251 {
252 calc_rv3d_dist = false;
253
254 /* When using "Free" NDOF navigation, ignore "Orbit Around Selected" preference.
255 * Logically it doesn't make sense to use the selection as a pivot when the first-person
256 * navigation pivots from the view-point. This also interferes with zoom-speed,
257 * causing zoom-speed scale based on the distance to the selection center, see: #115253. */
258 if (U.ndof_navigation_mode == NDOF_NAVIGATION_MODE_FLY) {
260 }
261 }
262#endif
263
264 /* Set the view from the camera, if view locking is enabled.
265 * we may want to make this optional but for now its needed always. */
267
268 this->state_backup();
269
270 if (viewops_flag & VIEWOPS_FLAG_PERSP_ENSURE) {
271 if (ED_view3d_persp_ensure(depsgraph, this->v3d, this->region)) {
272 /* If we're switching from camera view to the perspective one,
273 * need to tag viewport update, so camera view and borders are properly updated. */
275 }
276 }
277
278 if (viewops_flag & viewops_flag_dynamic_ofs) {
279 float pivot_new[3];
280 eViewOpsFlag pivot_type = navigate_pivot_get(
281 C, depsgraph, region, v3d, event, viewops_flag, dyn_ofs_override, pivot_new);
282
283 viewops_flag &= ~viewops_flag_dynamic_ofs;
284 viewops_flag |= pivot_type;
285
286 /* It's possible no offset can be found, see: #111098. */
287 if (viewops_flag & viewops_flag_dynamic_ofs) {
288 negate_v3_v3(this->dyn_ofs, pivot_new);
289 this->use_dyn_ofs = true;
290
291 if (pivot_type == VIEWOPS_FLAG_DEPTH_NAVIGATE) {
292 /* Ensure we'll always be able to zoom into the new pivot point and panning won't go bad
293 * when dist is zero. Therefore, set a new #RegionView3D::ofs and #RegionView3D::dist so
294 * that the dist value becomes the distance from the new pivot point. */
295
296 if (rv3d->is_persp) {
297 float my_origin[3]; /* Original #RegionView3D.ofs. */
298 float my_pivot[3]; /* View pivot. */
299 float dvec[3];
300
301 negate_v3_v3(my_origin, rv3d->ofs); /* ofs is flipped */
302
303 /* remove dist value */
304 float3 upvec;
305 upvec[0] = upvec[1] = 0;
306 upvec[2] = rv3d->dist;
307 float3x3 mat = float3x3(float4x4(rv3d->viewinv));
308
309 upvec = math::transform_point(mat, upvec);
310 add_v3_v3v3(my_pivot, my_origin, upvec);
311
312 /* find a new ofs value that is along the view axis
313 * (rather than the mouse location) */
314 float lambda = closest_to_line_v3(dvec, pivot_new, my_pivot, my_origin);
315
316 negate_v3_v3(rv3d->ofs, dvec);
317 rv3d->dist = len_v3v3(my_pivot, dvec);
318
319 if (lambda < 0.0f) {
320 /* The distance is actually negative. */
321 rv3d->dist *= -1;
322 }
323 }
324 else {
325 const float mval_region_mid[2] = {float(region->winx) / 2.0f,
326 float(region->winy) / 2.0f};
327 ED_view3d_win_to_3d(v3d, region, pivot_new, mval_region_mid, rv3d->ofs);
328 negate_v3(rv3d->ofs);
329 }
330 }
331
332 /* Reinitialize `this->init.dist` and `this->init.ofs` as these values may have changed
333 * when #ED_view3d_persp_ensure was called or when the operator uses `Auto Depth`.
334 *
335 * XXX: The initial state captured by #ViewOpsData::state_backup is being modified here.
336 * This causes the state not to be fully restored when canceling a navigation operation. */
337 this->init.dist = rv3d->dist;
338 copy_v3_v3(this->init.ofs, rv3d->ofs);
339 }
340 }
341
343 float tvec[3];
344 negate_v3_v3(tvec, rv3d->ofs);
345 this->init.zfac = ED_view3d_calc_zfac(rv3d, tvec);
346 }
347
348 this->init.persp_with_auto_persp_applied = rv3d->persp;
349
350 if (event) {
351 this->init.event_type = event->type;
352 copy_v2_v2_int(this->init.event_xy, event->xy);
353 copy_v2_v2_int(this->prev.event_xy, event->xy);
354
355 if (use_cursor_init) {
356 zero_v2_int(this->init.event_xy_offset);
357 }
358 else {
359 /* Simulate the event starting in the middle of the region. */
360 this->init.event_xy_offset[0] = BLI_rcti_cent_x(&this->region->winrct) - event->xy[0];
361 this->init.event_xy_offset[1] = BLI_rcti_cent_y(&this->region->winrct) - event->xy[1];
362 }
363
364 /* For dolly */
365 const float mval[2] = {float(event->mval[0]), float(event->mval[1])};
366 ED_view3d_win_to_vector(region, mval, this->init.mousevec);
367
368 {
369 int2 event_xy_offset = int2(event->xy) + this->init.event_xy_offset;
370
371 /* For rotation with trackball rotation. */
372 calctrackballvec(&region->winrct, event_xy_offset, this->init.trackvec);
373 }
374 }
375
376 copy_qt_qt(this->curr.viewquat, rv3d->viewquat);
377
378 this->reverse = 1.0f;
379 if (rv3d->persmat[2][1] < 0.0f) {
380 this->reverse = -1.0f;
381 }
382
383 this->viewops_flag = viewops_flag;
384
385 /* Default. */
386 this->use_dyn_ofs_ortho_correction = false;
387
388 rv3d->rflag |= RV3D_NAVIGATING;
389}
390
392{
393 this->rv3d->rflag &= ~RV3D_NAVIGATING;
394
395 if (this->timer) {
396 WM_event_timer_remove(CTX_wm_manager(C), this->timer->win, this->timer);
397 }
398
399 if (this->init.dial) {
400 BLI_dial_free(this->init.dial);
401 this->init.dial = nullptr;
402 }
403
404 /* Need to redraw because drawing code uses RV3D_NAVIGATING to draw
405 * faster while navigation operator runs. */
407}
408
410
411/* -------------------------------------------------------------------- */
414
415/* Used for navigation utility in operators. */
417 /* To track only the navigation #wmKeyMapItem items and allow changes to them, an internal
418 * #wmKeyMap is created with their copy. */
420
421 /* Used by #ED_view3d_navigation_do. */
422 bool is_modal_event = false;
423
424 ViewOpsData_Utility(bContext *C, const wmKeyMapItem *kmi_merge = nullptr)
426 {
427 this->init_context(C);
428
431
433
434 wmKeyMap keymap_tmp = {};
435
436 LISTBASE_FOREACH (wmKeyMapItem *, kmi, &keymap->items) {
437 if (!STRPREFIX(kmi->idname, "VIEW3D")) {
438 continue;
439 }
440 if (kmi->flag & KMI_INACTIVE) {
441 continue;
442 }
443 if (view3d_navigation_type_from_idname(kmi->idname) == nullptr) {
444 continue;
445 }
446
447 wmKeyMapItem *kmi_cpy = WM_keymap_add_item_copy(&keymap_tmp, kmi);
448 if (kmi_merge) {
449 if (kmi_merge->shift == KM_MOD_HELD ||
450 ELEM(kmi_merge->type, EVT_RIGHTSHIFTKEY, EVT_LEFTSHIFTKEY))
451 {
452 kmi_cpy->shift = KM_MOD_HELD;
453 }
454 if (kmi_merge->ctrl == KM_MOD_HELD ||
455 ELEM(kmi_merge->type, EVT_LEFTCTRLKEY, EVT_RIGHTCTRLKEY))
456 {
457 kmi_cpy->ctrl = KM_MOD_HELD;
458 }
459 if (kmi_merge->alt == KM_MOD_HELD ||
460 ELEM(kmi_merge->type, EVT_LEFTALTKEY, EVT_RIGHTALTKEY))
461 {
462 kmi_cpy->alt = KM_MOD_HELD;
463 }
464 if (kmi_merge->oskey == KM_MOD_HELD || ELEM(kmi_merge->type, EVT_OSKEY)) {
465 kmi_cpy->oskey = KM_MOD_HELD;
466 }
467 if (kmi_merge->hyper == KM_MOD_HELD || ELEM(kmi_merge->type, EVT_HYPER)) {
468 kmi_cpy->hyper = KM_MOD_HELD;
469 }
470 if (!ISKEYMODIFIER(kmi_merge->type)) {
471 kmi_cpy->keymodifier = kmi_merge->type;
472 }
473 }
474 }
475
476 /* Weak, but only the keymap items from the #wmKeyMap struct are needed here. */
477 this->keymap_items = keymap_tmp.items;
478
480 }
481
483 {
484 /* Weak, but rebuild the struct #wmKeyMap to clear the keymap items. */
486
487 wmKeyMap keymap_tmp = {};
488 keymap_tmp.items = this->keymap_items;
489 WM_keymap_clear(&keymap_tmp);
490
492 }
493
494 MEM_CXX_CLASS_ALLOC_FUNCS("ViewOpsData_Utility")
495};
496
497static bool view3d_navigation_poll_impl(bContext *C, const char viewlock)
498{
500 return false;
501 }
502
503 const RegionView3D *rv3d = CTX_wm_region_view3d(C);
504 return !(RV3D_LOCK_FLAGS(rv3d) & viewlock);
505}
506
508{
509 if (event->type == EVT_MODAL_MAP) {
510 switch (event->val) {
512 return VIEW_CANCEL;
514 return VIEW_CONFIRM;
516 vod->axis_snap = true;
517 return VIEW_APPLY;
520 vod->axis_snap = false;
521 return VIEW_APPLY;
525 const ViewOpsType *nav_type_new = (event->val == VIEWROT_MODAL_SWITCH_ZOOM) ?
527 (event->val == VIEWROT_MODAL_SWITCH_MOVE) ?
530 if (nav_type_new == vod->nav_type) {
531 break;
532 }
533 vod->nav_type = nav_type_new;
534 return VIEW_APPLY;
535 }
536 }
537 }
538 else {
539 if (event->type == TIMER && event->customdata == vod->timer) {
540 /* Zoom uses timer for continuous zoom. */
541 return VIEW_APPLY;
542 }
543 if (event->type == MOUSEMOVE) {
544 return VIEW_APPLY;
545 }
546 if (event->type == vod->init.event_type && event->val == KM_RELEASE) {
547 return VIEW_CONFIRM;
548 }
549 if (event->type == EVT_ESCKEY && event->val == KM_PRESS) {
550 return VIEW_CANCEL;
551 }
552 }
553
554 return VIEW_PASS;
555}
556
558 ViewOpsData *vod,
559 const wmEvent *event,
561 const ViewOpsType *nav_type,
562 const float dyn_ofs_override[3])
563{
564 if (!nav_type->init_fn) {
565 return OPERATOR_CANCELLED;
566 }
567
568 bool use_cursor_init = false;
569 if (PropertyRNA *prop = RNA_struct_find_property(ptr, "use_cursor_init")) {
570 use_cursor_init = RNA_property_boolean_get(ptr, prop);
571 }
572
573 vod->init_navigation(C, event, nav_type, dyn_ofs_override, use_cursor_init);
575
576 return nav_type->init_fn(C, vod, event, ptr);
577}
578
580 wmOperator *op,
581 const wmEvent *event,
582 const ViewOpsType *nav_type)
583{
584 ViewOpsData *vod = new ViewOpsData();
585 vod->init_context(C);
587 C, vod, event, op->ptr, nav_type, nullptr);
588 op->customdata = (void *)vod;
589
593 }
594
595 viewops_data_free(C, vod);
596 op->customdata = nullptr;
597 return ret;
598}
599
601
602/* -------------------------------------------------------------------- */
605
610
615
620
622{
623 /* This combination of flags is needed for the dolly operator,
624 * see code-comments there for details. */
626}
627
629{
630 ViewOpsData *vod = static_cast<ViewOpsData *>(op->customdata);
631
632 const ViewOpsType *nav_type_prev = vod->nav_type;
633 const eV3D_OpEvent event_code = view3d_navigate_event(vod, event);
634 if (nav_type_prev != vod->nav_type) {
635 wmOperatorType *ot_new = WM_operatortype_find(vod->nav_type->idname, false);
636 WM_operator_type_set(op, ot_new);
637 vod->end_navigation(C);
638 return view3d_navigation_invoke_generic(C, vod, event, op->ptr, vod->nav_type, nullptr);
639 }
640
641 wmOperatorStatus ret = vod->nav_type->apply_fn(C, vod, event_code, event->xy);
642
643 if ((ret & OPERATOR_RUNNING_MODAL) == 0) {
644 if (ret & OPERATOR_FINISHED) {
646 }
647 viewops_data_free(C, vod);
648 op->customdata = nullptr;
649 }
650
651 return ret;
652}
653
655{
656 viewops_data_free(C, static_cast<ViewOpsData *>(op->customdata));
657 op->customdata = nullptr;
658}
659
661
662/* -------------------------------------------------------------------- */
665
667{
669 PropertyRNA *prop;
670 prop = RNA_def_int(ot->srna, "mx", 0, 0, INT_MAX, "Region Position X", "", 0, INT_MAX);
672 prop = RNA_def_int(ot->srna, "my", 0, 0, INT_MAX, "Region Position Y", "", 0, INT_MAX);
674 }
675 if (flag & V3D_OP_PROP_DELTA) {
676 RNA_def_int(ot->srna, "delta", 0, INT_MIN, INT_MAX, "Delta", "", INT_MIN, INT_MAX);
677 }
679 PropertyRNA *prop;
680 prop = RNA_def_boolean(
681 ot->srna, "use_all_regions", false, "All Regions", "View selected for all regions");
683 }
686 }
687}
688
690
691/* -------------------------------------------------------------------- */
694
695void calctrackballvec(const rcti *rect, const int event_xy[2], float r_dir[3])
696{
697 const float radius = V3D_OP_TRACKBALLSIZE;
698 const float t = radius / float(M_SQRT2);
699 const float size[2] = {float(BLI_rcti_size_x(rect)), float(BLI_rcti_size_y(rect))};
700 /* Aspect correct so dragging in a non-square view doesn't squash the direction.
701 * So diagonal motion rotates the same direction the cursor is moving. */
702 const float size_min = min_ff(size[0], size[1]);
703 const float aspect[2] = {size_min / size[0], size_min / size[1]};
704
705 /* Normalize x and y. */
706 r_dir[0] = (event_xy[0] - BLI_rcti_cent_x(rect)) / ((size[0] * aspect[0]) / 2.0);
707 r_dir[1] = (event_xy[1] - BLI_rcti_cent_y(rect)) / ((size[1] * aspect[1]) / 2.0);
708 const float d = len_v2(r_dir);
709 if (d < t) {
710 /* Inside sphere. */
711 r_dir[2] = sqrtf(square_f(radius) - square_f(d));
712 }
713 else {
714 /* On hyperbola. */
715 r_dir[2] = square_f(t) / d;
716 }
717}
718
719void view3d_orbit_apply_dyn_ofs(float r_ofs[3],
720 const float ofs_old[3],
721 const float viewquat_old[4],
722 const float viewquat_new[4],
723 const float dyn_ofs[3])
724{
725 float q[4];
726 invert_qt_qt_normalized(q, viewquat_old);
727 mul_qt_qtqt(q, q, viewquat_new);
728
730
731 sub_v3_v3v3(r_ofs, ofs_old, dyn_ofs);
732 mul_qt_v3(q, r_ofs);
733 add_v3_v3(r_ofs, dyn_ofs);
734}
735
737 const float viewquat_old[4],
738 const float viewquat_new[4],
739 const float dyn_ofs[3])
740{
741 /* NOTE(@ideasman42): While orbiting in orthographic mode the "depth" of the offset
742 * (position along the views Z-axis) is only noticeable when the view contents is clipped.
743 * The likelihood of clipping depends on the clipping range & size of the scene.
744 * In practice some users might not run into this, however using dynamic-offset in
745 * orthographic views can cause the depth of the offset to drift while navigating the view,
746 * causing unexpected clipping that seems like a bug from the user perspective, see: #104385.
747 *
748 * Imagine a camera is focused on a distant object. Now imagine a closer object in front of
749 * the camera is used as a pivot, the camera is rotated to view it from the side (~90d rotation).
750 * The outcome is the camera is now focused on a distant region to the left/right.
751 * The new focal point is unlikely to point to anything useful (unless by accident).
752 * Instead of a focal point - the `rv3d->ofs` is being manipulated in this case.
753 *
754 * Resolve by moving #RegionView3D::ofs so it is depth-aligned to `dyn_ofs`,
755 * this is interpolated by the amount of rotation so minor rotations don't cause
756 * the view-clipping to suddenly jump.
757 *
758 * Perspective Views
759 * =================
760 *
761 * This logic could also be applied to perspective views because the issue of the `ofs`
762 * being a location which isn't useful exists there too, however the problem where this location
763 * impacts the clipping does *not* exist, as the clipping range starts from the view-point
764 * (`ofs` + `dist` along the view Z-axis) unlike orthographic views which center around `ofs`.
765 * Nevertheless there will be cases when having `ofs` and a large `dist` pointing nowhere doesn't
766 * give ideal behavior (zooming may jump in larger than expected steps and panning the view may
767 * move too much in relation to nearby objects - for example). So it's worth investigating but
768 * should be done with extra care as changing `ofs` in perspective view also requires changing
769 * the `dist` which could cause unexpected results if the calculated `dist` happens to be small.
770 * So disable this workaround in perspective view unless there are clear benefits to enabling. */
771
772 float q_inv[4];
773
774 float view_z_init[3] = {0.0f, 0.0f, 1.0f};
775 invert_qt_qt_normalized(q_inv, viewquat_old);
776 mul_qt_v3(q_inv, view_z_init);
777
778 float view_z_curr[3] = {0.0f, 0.0f, 1.0f};
779 invert_qt_qt_normalized(q_inv, viewquat_new);
780 mul_qt_v3(q_inv, view_z_curr);
781
782 const float angle_cos = max_ff(0.0f, dot_v3v3(view_z_init, view_z_curr));
783 /* 1.0 or more means no rotation, there is nothing to do in that case. */
784 if (LIKELY(angle_cos < 1.0f)) {
785 const float dot_ofs_curr = dot_v3v3(view_z_curr, ofs);
786 const float dot_ofs_next = dot_v3v3(view_z_curr, dyn_ofs);
787 const float ofs_delta = dot_ofs_next - dot_ofs_curr;
788 if (LIKELY(ofs_delta != 0.0f)) {
789 /* Calculate a factor where 0.0 represents no rotation and 1.0 represents 90d or more.
790 * NOTE: Without applying the factor, the distances immediately changes
791 * (useful for testing), but not good for the users experience as minor rotations
792 * should not immediately adjust the depth. */
793 const float factor = acosf(angle_cos) / M_PI_2;
794 madd_v3_v3fl(ofs, view_z_curr, ofs_delta * factor);
795 }
796 }
797}
798
799void viewrotate_apply_dyn_ofs(ViewOpsData *vod, const float viewquat_new[4])
800{
801 if (vod->use_dyn_ofs) {
802 RegionView3D *rv3d = vod->rv3d;
804 rv3d->ofs, vod->init.ofs, vod->init.quat, viewquat_new, vod->dyn_ofs);
805
808 rv3d->ofs, vod->init.quat, viewquat_new, vod->dyn_ofs);
809 }
810 }
811}
812
813bool view3d_orbit_calc_center(bContext *C, float r_dyn_ofs[3])
814{
815 using namespace blender;
816 float3 ofs = float3(0);
817 bool is_set = false;
818
823 View3D *v3d = CTX_wm_view3d(C);
824 BKE_view_layer_synced_ensure(scene_eval, view_layer_eval);
825 Object *ob_act_eval = BKE_view_layer_active_object_get(view_layer_eval);
826 Object *ob_act = DEG_get_original(ob_act_eval);
827
829 ofs = -float3(v3d->runtime.ofs_last_center);
830 }
831
832 if (ob_act && (ob_act->mode & OB_MODE_ALL_PAINT) &&
833 /* with weight-paint + pose-mode, fall through to using calculateTransformCenter */
834 ((ob_act->mode & OB_MODE_WEIGHT_PAINT) && BKE_object_pose_armature_get(ob_act)) == 0)
835 {
836 BKE_paint_stroke_get_average(paint, ob_act_eval, ofs);
837 is_set = true;
838 }
839 else if (ob_act && ELEM(ob_act->mode,
845 {
846 BKE_paint_stroke_get_average(paint, ob_act_eval, ofs);
847 is_set = true;
848 }
849 else if (ob_act && (ob_act->mode & OB_MODE_EDIT) && (ob_act->type == OB_FONT)) {
850 Curve *cu = static_cast<Curve *>(ob_act_eval->data);
851 EditFont *ef = cu->editfont;
852
853 ofs = float3(0);
854 for (int i = 0; i < 4; i++) {
855 add_v2_v2(ofs, ef->textcurs[i]);
856 }
857 ofs *= 0.25f;
858
859 ofs = math::transform_point(ob_act_eval->object_to_world(), ofs);
860
861 is_set = true;
862 }
863 else if (ob_act == nullptr || ob_act->mode == OB_MODE_OBJECT) {
864 /* Object mode uses bounding-box centers. */
865 int total = 0;
866 float3 select_center(0);
867
868 zero_v3(select_center);
869 LISTBASE_FOREACH (const Base *, base_eval, BKE_view_layer_object_bases_get(view_layer_eval)) {
870 if (BASE_SELECTED(v3d, base_eval)) {
871 /* Use the bounding-box if we can. */
872 const Object *ob_eval = base_eval->object;
873
874 if (const std::optional<Bounds<float3>> bounds = BKE_object_boundbox_get(ob_eval)) {
875 const float3 center = math::midpoint(bounds->min, bounds->max);
876 select_center += math::transform_point(ob_eval->object_to_world(), center);
877 }
878 else {
879 add_v3_v3(select_center, ob_eval->object_to_world().location());
880 }
881 total++;
882 }
883 }
884 if (total) {
885 mul_v3_fl(select_center, 1.0f / float(total));
886 copy_v3_v3(ofs, select_center);
887 is_set = true;
888 }
889 }
890 else {
891 /* If there's no selection, `ofs` is unmodified, the last offset will be used if set.
892 * Otherwise the value of `ofs` is zero and should not be used. */
894 }
895
896 if (is_set) {
899 }
900
901 copy_v3_v3(r_dyn_ofs, ofs);
902
903 return is_set;
904}
905
907 const wmEvent *event,
908 const ViewOpsType *nav_type,
909 const bool use_cursor_init)
910{
911 ViewOpsData *vod = new ViewOpsData();
912 vod->init_context(C);
913 vod->init_navigation(C, event, nav_type, nullptr, use_cursor_init);
914 return vod;
915}
916
918{
919 if (!vod) {
920 return;
921 }
922 vod->end_navigation(C);
923 delete vod;
924}
925
927
928/* -------------------------------------------------------------------- */
931
933 View3D *v3d,
934 ARegion *region,
935 const float quat_[4],
936 char view,
937 char view_axis_roll,
938 int perspo,
939 const float *align_to_quat,
940 const int smooth_viewtx)
941{
942 /* no nullptr check is needed, poll checks */
943 RegionView3D *rv3d = static_cast<RegionView3D *>(region->regiondata);
944
945 float quat[4];
946 const short orig_persp = rv3d->persp;
947 const char orig_view = rv3d->view;
948 const char orig_view_axis_roll = rv3d->view_axis_roll;
949
950 normalize_qt_qt(quat, quat_);
951
952 if (align_to_quat) {
953 mul_qt_qtqt(quat, quat, align_to_quat);
954 rv3d->view = view = RV3D_VIEW_USER;
956 }
957 else {
958 rv3d->view = view;
959 rv3d->view_axis_roll = view_axis_roll;
960 }
961
962 /* Redrawing when changes are detected is needed because the current view
963 * orientation may be a "User" view that matches the axis exactly.
964 * In this case smooth-view exits early as no view transition is needed.
965 * However, changing the view must redraw the region as it changes the
966 * viewport name & grid drawing. */
967 if ((rv3d->view != orig_view) || (rv3d->view_axis_roll != orig_view_axis_roll)) {
968 ED_region_tag_redraw(region);
969 }
970
972 return;
973 }
974
975 if (U.uiflag & USER_AUTOPERSP) {
976 rv3d->persp = RV3D_VIEW_IS_AXIS(view) ? RV3D_ORTHO : perspo;
977 }
978 else if (rv3d->persp == RV3D_CAMOB) {
979 rv3d->persp = perspo;
980 }
981 if (rv3d->persp != orig_persp) {
982 ED_region_tag_redraw(region);
983 }
984
985 if (rv3d->persp == RV3D_CAMOB && v3d->camera) {
986 /* to camera */
987 V3D_SmoothParams sview = {nullptr};
988 sview.camera_old = v3d->camera;
989 sview.ofs = rv3d->ofs;
990 sview.quat = quat;
991 /* No undo because this switches to/from camera. */
992 sview.undo_str = nullptr;
993
994 ED_view3d_smooth_view(C, v3d, region, smooth_viewtx, &sview);
995 }
996 else if (orig_persp == RV3D_CAMOB && v3d->camera) {
997 /* from camera */
998 float ofs[3], dist;
999
1000 copy_v3_v3(ofs, rv3d->ofs);
1001 dist = rv3d->dist;
1002
1003 /* so we animate _from_ the camera location */
1005 ED_view3d_from_object(camera_eval, rv3d->ofs, nullptr, &rv3d->dist, nullptr);
1006
1007 V3D_SmoothParams sview = {nullptr};
1008 sview.camera_old = camera_eval;
1009 sview.ofs = ofs;
1010 sview.quat = quat;
1011 sview.dist = &dist;
1012 /* No undo because this switches to/from camera. */
1013 sview.undo_str = nullptr;
1014
1015 ED_view3d_smooth_view(C, v3d, region, smooth_viewtx, &sview);
1016 }
1017 else {
1018 /* rotate around selection */
1019 const float *dyn_ofs_pt = nullptr;
1020 float dyn_ofs[3];
1021
1022 if (U.uiflag & USER_ORBIT_SELECTION) {
1023 if (view3d_orbit_calc_center(C, dyn_ofs)) {
1024 negate_v3(dyn_ofs);
1025 dyn_ofs_pt = dyn_ofs;
1026 }
1027 }
1028
1029 /* no camera involved */
1030 V3D_SmoothParams sview = {nullptr};
1031 sview.quat = quat;
1032 sview.dyn_ofs = dyn_ofs_pt;
1033 /* No undo because this switches to/from camera. */
1034 sview.undo_str = nullptr;
1035
1036 ED_view3d_smooth_view(C, v3d, region, smooth_viewtx, &sview);
1037 }
1038}
1039
1040void viewmove_apply(ViewOpsData *vod, int x, int y)
1041{
1042 const float event_ofs[2] = {
1043 float(vod->prev.event_xy[0] - x),
1044 float(vod->prev.event_xy[1] - y),
1045 };
1046
1047 if ((vod->rv3d->persp == RV3D_CAMOB) && !ED_view3d_camera_lock_check(vod->v3d, vod->rv3d)) {
1048 ED_view3d_camera_view_pan(vod->region, event_ofs);
1049 }
1050 else if (ED_view3d_offset_lock_check(vod->v3d, vod->rv3d)) {
1051 vod->rv3d->ofs_lock[0] -= (event_ofs[0] * 2.0f) / float(vod->region->winx);
1052 vod->rv3d->ofs_lock[1] -= (event_ofs[1] * 2.0f) / float(vod->region->winy);
1053 }
1054 else {
1055 float dvec[3];
1056
1057 ED_view3d_win_to_delta(vod->region, event_ofs, vod->init.zfac, dvec, true);
1058
1059 sub_v3_v3(vod->rv3d->ofs, dvec);
1060
1061 if (RV3D_LOCK_FLAGS(vod->rv3d) & RV3D_BOXVIEW) {
1062 view3d_boxview_sync(vod->area, vod->region);
1063 }
1064 }
1065
1066 vod->prev.event_xy[0] = x;
1067 vod->prev.event_xy[1] = y;
1068
1069 ED_view3d_camera_lock_sync(vod->depsgraph, vod->v3d, vod->rv3d);
1070
1072}
1073
1075
1076/* -------------------------------------------------------------------- */
1079
1080/* Detect the navigation operation, by the name of the navigation operator (obtained by
1081 * `wmKeyMapItem::idname`) */
1082static const ViewOpsType *view3d_navigation_type_from_idname(const char *idname)
1083{
1084 const blender::Array<const ViewOpsType *> nav_types = {
1089// &ViewOpsType_orbit,
1090// &ViewOpsType_roll,
1091// &ViewOpsType_dolly,
1092#ifdef WITH_INPUT_NDOF
1093 &ViewOpsType_ndof_orbit,
1094 &ViewOpsType_ndof_orbit_zoom,
1095 &ViewOpsType_ndof_pan,
1096 &ViewOpsType_ndof_all,
1097#endif
1098 };
1099
1100 const char *op_name = idname + sizeof("VIEW3D_OT_");
1101 for (const ViewOpsType *nav_type : nav_types) {
1102 if (STREQ(op_name, nav_type->idname + sizeof("VIEW3D_OT_"))) {
1103 return nav_type;
1104 }
1105 }
1106 return nullptr;
1107}
1108
1110{
1111 /* Unlike #viewops_data_create, #ED_view3d_navigation_init creates a navigation context along
1112 * with an array of `wmKeyMapItem`s used for navigation. */
1113 if (!CTX_wm_region_view3d(C)) {
1114 return nullptr;
1115 }
1116
1117 return new ViewOpsData_Utility(C, kmi_merge);
1118}
1119
1121 ViewOpsData *vod,
1122 const wmEvent *event,
1123 const float depth_loc_override[3])
1124{
1125 if (!vod) {
1126 return false;
1127 }
1128
1129 wmEvent event_tmp;
1130 if (event->type == EVT_MODAL_MAP) {
1131 /* Workaround to use the original event values. */
1132 event_tmp = *event;
1133 event_tmp.type = event->prev_type;
1134 event_tmp.val = event->prev_val;
1135 event = &event_tmp;
1136 }
1137
1139
1140 ViewOpsData_Utility *vod_intern = static_cast<ViewOpsData_Utility *>(vod);
1141 if (vod_intern->is_modal_event) {
1142 const eV3D_OpEvent event_code = view3d_navigate_event(vod, event);
1143 op_return = vod->nav_type->apply_fn(C, vod, event_code, event->xy);
1144 if (op_return != OPERATOR_RUNNING_MODAL) {
1145 vod->end_navigation(C);
1146 vod_intern->is_modal_event = false;
1147 }
1148 }
1149 else {
1150 LISTBASE_FOREACH (wmKeyMapItem *, kmi, &vod_intern->keymap_items) {
1151 if (!WM_event_match(event, kmi)) {
1152 continue;
1153 }
1154
1155 const ViewOpsType *nav_type = view3d_navigation_type_from_idname(kmi->idname);
1156 if (nav_type->poll_fn && !nav_type->poll_fn(C)) {
1157 break;
1158 }
1159
1161 C, vod, event, kmi->ptr, nav_type, depth_loc_override);
1162
1163 if (op_return == OPERATOR_RUNNING_MODAL) {
1164 vod_intern->is_modal_event = true;
1165 }
1166 else {
1167 vod->end_navigation(C);
1168 /* Postpone the navigation confirmation to the next call.
1169 * This avoids constant updating of the transform operation for example. */
1170 vod->rv3d->rflag |= RV3D_NAVIGATING;
1171 }
1172 break;
1173 }
1174 }
1175
1176 if (op_return != OPERATOR_CANCELLED) {
1177 /* Although #ED_view3d_update_viewmat is already called when redrawing the 3D View, do it here
1178 * as well, so the updated matrix values can be accessed by the operator. */
1180 vod->depsgraph, vod->scene, vod->v3d, vod->region, nullptr, nullptr, nullptr, false);
1181
1182 return true;
1183 }
1184 if (vod->rv3d->rflag & RV3D_NAVIGATING) {
1185 /* Add a fake confirmation. */
1186 vod->rv3d->rflag &= ~RV3D_NAVIGATING;
1187 return true;
1188 }
1189
1190 return false;
1191}
1192
1194{
1195 ViewOpsData_Utility *vod_intern = static_cast<ViewOpsData_Utility *>(vod);
1196 vod_intern->end_navigation(C);
1197 delete vod_intern;
1198}
1199
Depsgraph * CTX_data_ensure_evaluated_depsgraph(const bContext *C)
ScrArea * CTX_wm_area(const bContext *C)
wmWindow * CTX_wm_window(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
RegionView3D * CTX_wm_region_view3d(const bContext *C)
ARegion * CTX_wm_region(const bContext *C)
wmWindowManager * CTX_wm_manager(const bContext *C)
View3D * CTX_wm_view3d(const bContext *C)
void BKE_view_layer_synced_ensure(const Scene *scene, ViewLayer *view_layer)
Object * BKE_view_layer_active_object_get(const ViewLayer *view_layer)
ListBase * BKE_view_layer_object_bases_get(ViewLayer *view_layer)
General operations, lookup, etc. for blender objects.
Object * BKE_object_pose_armature_get(Object *ob)
std::optional< blender::Bounds< blender::float3 > > BKE_object_boundbox_get(const Object *ob)
void BKE_paint_stroke_get_average(const Paint *paint, const Object *ob, float stroke[3])
Definition paint.cc:1952
Paint * BKE_paint_get_active_from_context(const bContext *C)
Definition paint.cc:476
#define BLI_assert(a)
Definition BLI_assert.h:46
void BLI_dial_free(Dial *dial)
#define LISTBASE_FOREACH(type, var, list)
MINLINE float max_ff(float a, float b)
MINLINE float min_ff(float a, float b)
MINLINE float square_f(float a)
#define M_SQRT2
#define M_PI_2
float closest_to_line_v3(float r_close[3], const float p[3], const float l1[3], const float l2[3])
void invert_qt_normalized(float q[4])
void mul_qt_v3(const float q[4], float r[3])
float normalize_qt_qt(float r[4], const float q[4])
void invert_qt_qt_normalized(float q1[4], const float q2[4])
void mul_qt_qtqt(float q[4], const float a[4], const float b[4])
void copy_qt_qt(float q[4], const float a[4])
MINLINE float len_v2(const float v[2]) ATTR_WARN_UNUSED_RESULT
MINLINE float len_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
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 void sub_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void copy_v2_v2(float r[2], const float a[2])
MINLINE void mul_v3_fl(float r[3], float f)
MINLINE void copy_v2_v2_int(int r[2], const int a[2])
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 void add_v2_v2(float r[2], const float a[2])
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 void zero_v3(float r[3])
MINLINE void add_v3_v3(float r[3], const float a[3])
BLI_INLINE int BLI_rcti_size_y(const struct rcti *rct)
Definition BLI_rect.h:198
BLI_INLINE int BLI_rcti_size_x(const struct rcti *rct)
Definition BLI_rect.h:194
BLI_INLINE int BLI_rcti_cent_y(const struct rcti *rct)
Definition BLI_rect.h:181
BLI_INLINE int BLI_rcti_cent_x(const struct rcti *rct)
Definition BLI_rect.h:177
#define STRPREFIX(a, b)
#define ELEM(...)
#define STREQ(a, b)
#define LIKELY(x)
Scene * DEG_get_evaluated_scene(const Depsgraph *graph)
ViewLayer * DEG_get_evaluated_view_layer(const Depsgraph *graph)
T * DEG_get_original(T *id)
T * DEG_get_evaluated(const Depsgraph *depsgraph, T *id)
#define OB_MODE_ALL_PAINT
@ OB_MODE_VERTEX_GREASE_PENCIL
@ OB_MODE_EDIT
@ OB_MODE_WEIGHT_PAINT
@ OB_MODE_SCULPT_CURVES
@ OB_MODE_PAINT_GREASE_PENCIL
@ OB_MODE_SCULPT_GREASE_PENCIL
@ OB_MODE_OBJECT
@ OB_MODE_WEIGHT_GREASE_PENCIL
@ OB_FONT
#define BASE_SELECTED(v3d, base)
@ RGN_TYPE_WINDOW
@ SPACE_VIEW3D
@ USER_ORBIT_SELECTION
@ USER_AUTOPERSP
@ USER_ZOOM_TO_MOUSEPOS
@ USER_DEPTH_NAVIGATE
@ NDOF_NAVIGATION_MODE_FLY
#define RV3D_VIEW_IS_AXIS(view)
@ RV3D_VIEW_AXIS_ROLL_0
@ RV3D_VIEW_USER
#define RV3D_LOCK_FLAGS(rv3d)
@ RV3D_NAVIGATING
@ V3D_AROUND_CENTER_MEDIAN
@ V3D_RUNTIME_OFS_LAST_CENTER_IS_VALID
@ RV3D_CAMOB
@ RV3D_ORTHO
@ RV3D_LOCK_ROTATION
@ RV3D_LOCK_LOCATION
@ RV3D_LOCK_ZOOM_AND_DOLLY
@ RV3D_BOXVIEW
@ OPERATOR_CANCELLED
@ OPERATOR_FINISHED
@ OPERATOR_RUNNING_MODAL
bool ED_operator_region_view3d_active(bContext *C)
void ED_region_tag_redraw(ARegion *region)
Definition area.cc:618
void ED_view3d_win_to_delta(const ARegion *region, const float xy_delta[2], float zfac, float r_out[3], bool precise=false)
bool ED_view3d_camera_lock_sync(const Depsgraph *depsgraph, View3D *v3d, RegionView3D *rv3d)
void ED_view3d_depth_override(Depsgraph *depsgraph, ARegion *region, View3D *v3d, Object *obact, eV3DDepthOverrideMode mode, bool use_overlay, ViewDepths **r_depths)
void ED_view3d_win_to_3d_int(const View3D *v3d, const ARegion *region, const float depth_pt[3], const int mval[2], float r_out[3])
void ED_view3d_autodist_last_clear(wmWindow *win)
void ED_view3d_win_to_3d(const View3D *v3d, const ARegion *region, const float depth_pt[3], const float mval[2], float r_out[3])
bool ED_view3d_persp_ensure(const Depsgraph *depsgraph, View3D *v3d, ARegion *region)
bool ED_view3d_offset_lock_check(const View3D *v3d, const RegionView3D *rv3d)
bool ED_view3d_autodist_last_check(wmWindow *win, const wmEvent *event)
void ED_view3d_autodist_last_set(wmWindow *win, const wmEvent *event, const float ofs[3], const bool has_depth)
void ED_view3d_from_object(const Object *ob, float ofs[3], float quat[4], const float *dist, float *lens)
bool ED_view3d_autodist(ARegion *region, View3D *v3d, const int mval[2], float mouse_worldloc[3], const float fallback_depth_pt[3])
bool ED_view3d_has_depth_buffer_updated(const Depsgraph *depsgraph, const View3D *v3d)
bool ED_view3d_autodist_last_get(wmWindow *win, float r_ofs[3])
void ED_view3d_camera_lock_init_ex(const Depsgraph *depsgraph, View3D *v3d, RegionView3D *rv3d, bool calc_dist)
bool ED_view3d_camera_view_pan(ARegion *region, const float event_ofs[2])
bool ED_view3d_camera_lock_undo_push(const char *str, const View3D *v3d, const RegionView3D *rv3d, bContext *C)
float ED_view3d_calc_zfac(const RegionView3D *rv3d, const float co[3])
@ V3D_DEPTH_NO_GPENCIL
Definition ED_view3d.hh:192
bool ED_view3d_camera_lock_check(const View3D *v3d, const RegionView3D *rv3d)
void ED_view3d_win_to_vector(const ARegion *region, const float mval[2], float r_out[3])
void ED_view3d_update_viewmat(const Depsgraph *depsgraph, const Scene *scene, View3D *v3d, ARegion *region, const float viewmat[4][4], const float winmat[4][4], const rcti *rect, bool offscreen)
static AppView * view
@ PROP_SKIP_SAVE
Definition RNA_types.hh:344
@ PROP_HIDDEN
Definition RNA_types.hh:338
#define C
Definition RandGen.cpp:29
@ KM_PRESS
Definition WM_types.hh:311
@ KM_RELEASE
Definition WM_types.hh:312
#define KM_MOD_HELD
Definition WM_types.hh:326
#define U
BPy_StructRNA * depsgraph
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
static btDbvtVolume bounds(btDbvtNode **leaves, int count)
Definition btDbvt.cpp:299
nullptr float
#define acosf(x)
MatBase< 4, 4 > float4x4
MatBase< 3, 3 > float3x3
VecBase< int, 2 > int2
VecBase< float, 3 > float3
@ VIEW_CONFIRM
Definition image_ops.cc:601
@ VIEW_PASS
Definition image_ops.cc:599
@ VIEW_APPLY
Definition image_ops.cc:600
MINLINE void zero_v2_int(int r[2])
bool calc_pivot_pos(const bContext *C, const short pivot_type, float r_pivot_pos[3])
T midpoint(const T &a, const T &b)
VecBase< T, 3 > transform_point(const CartesianBasis &basis, const VecBase< T, 3 > &v)
return ret
#define sqrtf
PropertyRNA * RNA_struct_find_property(PointerRNA *ptr, const char *identifier)
bool RNA_property_boolean_get(PointerRNA *ptr, PropertyRNA *prop)
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)
PropertyRNA * RNA_def_int(StructOrFunctionRNA *cont_, const char *identifier, const int default_value, const int hardmin, const int hardmax, const char *ui_name, const char *ui_description, const int softmin, const int softmax)
void * regiondata
struct EditFont * editfont
blender::float2 textcurs[4]
Definition BKE_vfont.hh:49
const char * undo_str
const float * dyn_ofs
View3D_Runtime runtime
struct Object * camera
ViewOpsData_Utility(bContext *C, const wmKeyMapItem *kmi_merge=nullptr)
const ViewOpsType * nav_type
char persp_with_auto_persp_applied
void end_navigation(bContext *C)
eViewOpsFlag viewops_flag
bool use_dyn_ofs_ortho_correction
Depsgraph * depsgraph
blender::int2 event_xy
blender::int2 event_xy_offset
struct ViewOpsData::@041211063176270354313167244105136234141004235041 curr
struct ViewOpsData::@244345216304223004112237167211144027240265370072 prev
RegionView3D * rv3d
void init_navigation(bContext *C, const wmEvent *event, const ViewOpsType *nav_type, const float dyn_ofs_override[3]=nullptr, const bool use_cursor_init=false)
void init_context(bContext *C)
struct ViewOpsData::@010113264246165170144271002355152266226235365232 init
const char * idname
wmOperatorStatus(* apply_fn)(bContext *C, ViewOpsData *vod, const eV3D_OpEvent event_code, const int xy[2])
wmOperatorStatus(* init_fn)(bContext *C, ViewOpsData *vod, const wmEvent *event, PointerRNA *ptr)
bool(* poll_fn)(bContext *C)
wmEventType type
Definition WM_types.hh:757
short val
Definition WM_types.hh:759
int xy[2]
Definition WM_types.hh:761
int mval[2]
Definition WM_types.hh:763
void * customdata
Definition WM_types.hh:807
const char * name
Definition WM_types.hh:1033
struct wmOperatorType * type
struct PointerRNA * ptr
i
Definition text_draw.cc:230
void view3d_boxview_sync(ScrArea *area, ARegion *region)
void view3d_navigate_cancel_fn(bContext *C, wmOperator *op)
static const ViewOpsType * view3d_navigation_type_from_idname(const char *idname)
wmOperatorStatus view3d_navigate_invoke_impl(bContext *C, wmOperator *op, const wmEvent *event, const ViewOpsType *nav_type)
void view3d_operator_properties_common(wmOperatorType *ot, const enum eV3D_OpPropFlag flag)
void viewops_data_free(bContext *C, ViewOpsData *vod)
ViewOpsData * viewops_data_create(bContext *C, const wmEvent *event, const ViewOpsType *nav_type, const bool use_cursor_init)
static void view3d_orbit_apply_dyn_ofs_ortho_correction(float ofs[3], const float viewquat_old[4], const float viewquat_new[4], const float dyn_ofs[3])
wmOperatorStatus view3d_navigate_modal_fn(bContext *C, wmOperator *op, const wmEvent *event)
void view3d_orbit_apply_dyn_ofs(float r_ofs[3], const float ofs_old[3], const float viewquat_old[4], const float viewquat_new[4], const float dyn_ofs[3])
bool view3d_rotation_poll(bContext *C)
bool view3d_location_poll(bContext *C)
static eViewOpsFlag navigate_pivot_get(bContext *C, Depsgraph *depsgraph, ARegion *region, View3D *v3d, const wmEvent *event, eViewOpsFlag viewops_flag, const float dyn_ofs_override[3], float r_pivot[3])
static eViewOpsFlag viewops_flag_from_prefs()
static wmOperatorStatus view3d_navigation_invoke_generic(bContext *C, ViewOpsData *vod, const wmEvent *event, PointerRNA *ptr, const ViewOpsType *nav_type, const float dyn_ofs_override[3])
bool view3d_orbit_calc_center(bContext *C, float r_dyn_ofs[3])
void viewmove_apply(ViewOpsData *vod, int x, int y)
void ED_view3d_navigation_free(bContext *C, ViewOpsData *vod)
void calctrackballvec(const rcti *rect, const int event_xy[2], float r_dir[3])
static eV3D_OpEvent view3d_navigate_event(ViewOpsData *vod, const wmEvent *event)
void axis_set_view(bContext *C, View3D *v3d, ARegion *region, const float quat_[4], char view, char view_axis_roll, int perspo, const float *align_to_quat, const int smooth_viewtx)
static bool view3d_navigation_poll_impl(bContext *C, const char viewlock)
bool ED_view3d_navigation_do(bContext *C, ViewOpsData *vod, const wmEvent *event, const float depth_loc_override[3])
ViewOpsData * ED_view3d_navigation_init(bContext *C, const wmKeyMapItem *kmi_merge)
bool view3d_zoom_or_dolly_or_rotation_poll(bContext *C)
void viewrotate_apply_dyn_ofs(ViewOpsData *vod, const float viewquat_new[4])
bool view3d_zoom_or_dolly_poll(bContext *C)
const ViewOpsType ViewOpsType_zoom
void ED_view3d_smooth_view(bContext *C, View3D *v3d, ARegion *region, int smooth_viewtx, const V3D_SmoothParams *sview)
const ViewOpsType ViewOpsType_pan
eViewOpsFlag
@ VIEWOPS_FLAG_NONE
@ VIEWOPS_FLAG_ORBIT_SELECT
@ VIEWOPS_FLAG_DEPTH_NAVIGATE
@ VIEWOPS_FLAG_INIT_ZFAC
@ VIEWOPS_FLAG_ZOOM_TO_MOUSE
@ VIEWOPS_FLAG_PERSP_ENSURE
const ViewOpsType ViewOpsType_rotate
eV3D_OpEvent
@ VIEW_CANCEL
void calctrackballvec(const rcti *rect, const int event_xy[2], float r_dir[3])
eV3D_OpPropFlag
@ V3D_OP_PROP_USE_MOUSE_INIT
@ V3D_OP_PROP_DELTA
@ V3D_OP_PROP_USE_ALL_REGIONS
@ V3D_OP_PROP_MOUSE_CO
const ViewOpsType ViewOpsType_move
void ED_view3d_smooth_view_force_finish(bContext *C, View3D *v3d, ARegion *region)
@ VIEWROT_MODAL_SWITCH_ROTATE
@ VIEWROT_MODAL_AXIS_SNAP_ENABLE
@ VIEW_MODAL_CANCEL
@ VIEWROT_MODAL_SWITCH_MOVE
@ VIEW_MODAL_CONFIRM
@ VIEWROT_MODAL_SWITCH_ZOOM
@ VIEWROT_MODAL_AXIS_SNAP_DISABLE
#define V3D_OP_TRACKBALLSIZE
void WM_operator_type_set(wmOperator *op, wmOperatorType *ot)
Definition wm.cc:305
wmEventHandler_Op * WM_event_add_modal_handler(bContext *C, wmOperator *op)
bool WM_event_match(const wmEvent *winevent, const wmKeyMapItem *kmi)
#define ISKEYMODIFIER(event_type)
@ TIMER
@ EVT_MODAL_MAP
@ EVT_RIGHTCTRLKEY
@ EVT_OSKEY
@ EVT_LEFTCTRLKEY
@ MOUSEMOVE
@ EVT_RIGHTALTKEY
@ EVT_LEFTALTKEY
@ EVT_ESCKEY
@ EVT_RIGHTSHIFTKEY
@ EVT_LEFTSHIFTKEY
@ EVT_HYPER
PointerRNA * ptr
Definition wm_files.cc:4238
wmOperatorType * ot
Definition wm_files.cc:4237
wmKeyMapItem * WM_keymap_add_item_copy(wmKeyMap *keymap, wmKeyMapItem *kmi_src)
Definition wm_keymap.cc:567
void WM_keymap_clear(wmKeyMap *keymap)
Definition wm_keymap.cc:446
wmKeyMap * WM_keymap_find_all(wmWindowManager *wm, const char *idname, int spaceid, int regionid)
Definition wm_keymap.cc:909
void WM_keyconfig_update_suppress_begin()
void WM_keyconfig_update_suppress_end()
void WM_operator_properties_use_cursor_init(wmOperatorType *ot)
wmOperatorType * WM_operatortype_find(const char *idname, bool quiet)
void WM_event_timer_remove(wmWindowManager *wm, wmWindow *, wmTimer *timer)
uint8_t flag
Definition wm_window.cc:145