Blender V4.3
view3d_navigate_fly.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
15#ifdef WITH_INPUT_NDOF
16// # define NDOF_FLY_DEBUG
17/* NOTE(@ideasman42): is this needed for NDOF? commented so redraw doesn't thrash. */
18// # define NDOF_FLY_DRAW_TOOMUCH
19#endif /* WITH_INPUT_NDOF */
20
21#include "DNA_object_types.h"
22
23#include "MEM_guardedalloc.h"
24
25#include "BLI_math_matrix.h"
26#include "BLI_math_rotation.h"
27#include "BLI_rect.h"
28#include "BLI_time.h" /* Smooth-view. */
29
30#include "BKE_context.hh"
31#include "BKE_lib_id.hh"
32#include "BKE_report.hh"
33
34#include "WM_api.hh"
35#include "WM_types.hh"
36
37#include "ED_screen.hh"
38#include "ED_space_api.hh"
39#include "ED_undo.hh"
40
41#include "UI_resources.hh"
42
43#include "GPU_immediate.hh"
44
45#include "view3d_intern.hh" /* own include */
46#include "view3d_navigate.hh"
47
48#include "BLI_strict_flags.h" /* Keep last. */
49
50/* -------------------------------------------------------------------- */
54/* NOTE: these defines are saved in keymap files,
55 * do not change values but just add new ones */
56enum {
75 FLY_MODAL_SPEED, /* mouse-pan typically. */
76};
77
93
95{
96 static const EnumPropertyItem modal_items[] = {
97 {FLY_MODAL_CONFIRM, "CONFIRM", 0, "Confirm", ""},
98 {FLY_MODAL_CANCEL, "CANCEL", 0, "Cancel", ""},
99
100 {FLY_MODAL_DIR_FORWARD, "FORWARD", 0, "Forward", ""},
101 {FLY_MODAL_DIR_BACKWARD, "BACKWARD", 0, "Backward", ""},
102 {FLY_MODAL_DIR_LEFT, "LEFT", 0, "Left", ""},
103 {FLY_MODAL_DIR_RIGHT, "RIGHT", 0, "Right", ""},
104 {FLY_MODAL_DIR_UP, "UP", 0, "Up", ""},
105 {FLY_MODAL_DIR_DOWN, "DOWN", 0, "Down", ""},
106
107 {FLY_MODAL_PAN_ENABLE, "PAN_ENABLE", 0, "Pan", ""},
108 {FLY_MODAL_PAN_DISABLE, "PAN_DISABLE", 0, "Pan (Off)", ""},
109
110 {FLY_MODAL_ACCELERATE, "ACCELERATE", 0, "Accelerate", ""},
111 {FLY_MODAL_DECELERATE, "DECELERATE", 0, "Decelerate", ""},
112
113 {FLY_MODAL_AXIS_LOCK_X, "AXIS_LOCK_X", 0, "X Axis Correction", "X axis correction (toggle)"},
114 {FLY_MODAL_AXIS_LOCK_Z, "AXIS_LOCK_Z", 0, "Z Axis Correction", "Z axis correction (toggle)"},
115
116 {FLY_MODAL_PRECISION_ENABLE, "PRECISION_ENABLE", 0, "Precision", ""},
117 {FLY_MODAL_PRECISION_DISABLE, "PRECISION_DISABLE", 0, "Precision (Off)", ""},
118
119 {FLY_MODAL_FREELOOK_ENABLE, "FREELOOK_ENABLE", 0, "Rotation", ""},
120 {FLY_MODAL_FREELOOK_DISABLE, "FREELOOK_DISABLE", 0, "Rotation (Off)", ""},
121
122 {0, nullptr, 0, nullptr, nullptr},
123 };
124
125 wmKeyMap *keymap = WM_modalkeymap_find(keyconf, "View3D Fly Modal");
126
127 /* This function is called for each space-type, only needs to add map once. */
128 if (keymap && keymap->modal_items) {
129 return;
130 }
131
132 keymap = WM_modalkeymap_ensure(keyconf, "View3D Fly Modal", modal_items);
133
134 /* Assign map to operators. */
135 WM_modalkeymap_assign(keymap, "VIEW3D_OT_fly");
136}
137
140/* -------------------------------------------------------------------- */
144struct FlyInfo {
145 /* context stuff */
149 Depsgraph *depsgraph;
151
154
155 short state;
156 bool redraw;
160
168
170 int mval[2];
175
176#ifdef WITH_INPUT_NDOF
178 wmNDOFMotionData *ndof;
179#endif
180
181 /* Fly state. */
183 float speed;
185 short axis;
188
193 float grid;
194
195 /* Compare between last state. */
200
202
204 float dvec_prev[3];
205
207};
208
211/* -------------------------------------------------------------------- */
215/* Prototypes. */
216#ifdef WITH_INPUT_NDOF
217static void flyApply_ndof(bContext *C, FlyInfo *fly, bool is_confirm);
218#endif /* WITH_INPUT_NDOF */
219static int flyApply(bContext *C, FlyInfo *fly, bool is_confirm);
220
221static void drawFlyPixel(const bContext * /*C*/, ARegion * /*region*/, void *arg)
222{
223 FlyInfo *fly = static_cast<FlyInfo *>(arg);
224 rctf viewborder;
225 int xoff, yoff;
226
229 fly->scene, fly->depsgraph, fly->region, fly->v3d, fly->rv3d, false, &viewborder);
230 xoff = int(viewborder.xmin);
231 yoff = int(viewborder.ymin);
232 }
233 else {
234 xoff = 0;
235 yoff = 0;
236 }
237
238 /* Draws 4 edge brackets that frame the safe area where the
239 * mouse can move during fly mode without spinning the view. */
240
241 const float x1 = float(xoff) + 0.45f * fly->viewport_size[0];
242 const float y1 = float(yoff) + 0.45f * fly->viewport_size[1];
243 const float x2 = float(xoff) + 0.55f * fly->viewport_size[0];
244 const float y2 = float(yoff) + 0.55f * fly->viewport_size[1];
245
248
250
252
254
255 /* Bottom left. */
256 immVertex2f(pos, x1, y1);
257 immVertex2f(pos, x1, y1 + 5);
258
259 immVertex2f(pos, x1, y1);
260 immVertex2f(pos, x1 + 5, y1);
261
262 /* Top right. */
263 immVertex2f(pos, x2, y2);
264 immVertex2f(pos, x2, y2 - 5);
265
266 immVertex2f(pos, x2, y2);
267 immVertex2f(pos, x2 - 5, y2);
268
269 /* Top left. */
270 immVertex2f(pos, x1, y2);
271 immVertex2f(pos, x1, y2 - 5);
272
273 immVertex2f(pos, x1, y2);
274 immVertex2f(pos, x1 + 5, y2);
275
276 /* Bottom right. */
277 immVertex2f(pos, x2, y1);
278 immVertex2f(pos, x2, y1 + 5);
279
280 immVertex2f(pos, x2, y1);
281 immVertex2f(pos, x2 - 5, y1);
282
283 immEnd();
285}
286
289/* -------------------------------------------------------------------- */
294enum {
298};
299
300static bool initFlyInfo(bContext *C, FlyInfo *fly, wmOperator *op, const wmEvent *event)
301{
303 wmWindow *win = CTX_wm_window(C);
304 rctf viewborder;
305
306 float upvec[3];
307 float mat[3][3];
308
309 fly->rv3d = CTX_wm_region_view3d(C);
310 fly->v3d = CTX_wm_view3d(C);
311 fly->region = CTX_wm_region(C);
313 fly->scene = CTX_data_scene(C);
314
315#ifdef NDOF_FLY_DEBUG
316 puts("\n-- fly begin --");
317#endif
318
319 /* Sanity check: for rare but possible case (if lib-linking the camera fails). */
320 if ((fly->rv3d->persp == RV3D_CAMOB) && (fly->v3d->camera == nullptr)) {
321 fly->rv3d->persp = RV3D_PERSP;
322 }
323
324 if (fly->rv3d->persp == RV3D_CAMOB &&
326 {
328 RPT_ERROR,
329 "Cannot navigate a camera from an external library or non-editable override");
330
331 return false;
332 }
333
334 if (ED_view3d_offset_lock_check(fly->v3d, fly->rv3d)) {
335 BKE_report(op->reports, RPT_ERROR, "Cannot fly when the view offset is locked");
336 return false;
337 }
338
339 if (fly->rv3d->persp == RV3D_CAMOB && fly->v3d->camera->constraints.first) {
340 BKE_report(op->reports, RPT_ERROR, "Cannot fly an object with constraints");
341 return false;
342 }
343
344 fly->state = FLY_RUNNING;
345 fly->speed = 0.0f;
346 fly->axis = 2;
347 fly->pan_view = false;
350 fly->xlock_momentum = 0.0f;
351 fly->zlock_momentum = 0.0f;
352 fly->grid = 1.0f;
353 fly->use_precision = false;
354 fly->use_freelook = false;
356
357#ifdef NDOF_FLY_DRAW_TOOMUCH
358 fly->redraw = 1;
359#endif
360 zero_v3(fly->dvec_prev);
361
362 fly->timer = WM_event_timer_add(CTX_wm_manager(C), win, TIMER, 0.01f);
363
364 copy_v2_v2_int(fly->mval, event->mval);
365
366#ifdef WITH_INPUT_NDOF
367 fly->ndof = nullptr;
368#endif
369
371
374
375 fly->rv3d->rflag |= RV3D_NAVIGATING;
376
377 /* Detect whether to start with Z locking. */
378 copy_v3_fl3(upvec, 1.0f, 0.0f, 0.0f);
379 copy_m3_m4(mat, fly->rv3d->viewinv);
380 mul_m3_v3(mat, upvec);
381 if (fabsf(upvec[2]) < 0.1f) {
383 }
384
386 fly->depsgraph, fly->scene, fly->v3d, fly->rv3d);
387
388 /* Calculate center. */
391 fly->scene, fly->depsgraph, fly->region, fly->v3d, fly->rv3d, false, &viewborder);
392
393 fly->viewport_size[0] = BLI_rctf_size_x(&viewborder);
394 fly->viewport_size[1] = BLI_rctf_size_y(&viewborder);
395
396 fly->center_mval[0] = int(viewborder.xmin + (fly->viewport_size[0] / 2.0f));
397 fly->center_mval[1] = int(viewborder.ymin + (fly->viewport_size[1] / 2));
398 }
399 else {
400 fly->viewport_size[0] = fly->region->winx;
401 fly->viewport_size[1] = fly->region->winy;
402
403 fly->center_mval[0] = int(fly->viewport_size[0] / 2.0f);
404 fly->center_mval[1] = int(fly->viewport_size[1] / 2.0f);
405 }
406
407 /* Center the mouse, probably the UI mafia are against this but without its quite annoying. */
408 WM_cursor_warp(win,
409 fly->region->winrct.xmin + fly->center_mval[0],
410 fly->region->winrct.ymin + fly->center_mval[1]);
411
412 return true;
413}
414
415static int flyEnd(bContext *C, FlyInfo *fly)
416{
417 wmWindow *win;
418 RegionView3D *rv3d;
419
420 if (fly->state == FLY_RUNNING) {
422 }
423 if (fly->state == FLY_CONFIRM) {
424 /* Needed for auto-keyframe. */
425#ifdef WITH_INPUT_NDOF
426 if (fly->ndof) {
427 flyApply_ndof(C, fly, true);
428 }
429 else
430#endif /* WITH_INPUT_NDOF */
431 {
432 flyApply(C, fly, true);
433 }
434 }
435
436#ifdef NDOF_FLY_DEBUG
437 puts("\n-- fly end --");
438#endif
439
440 win = CTX_wm_window(C);
441 rv3d = fly->rv3d;
442
444
446
448
449 rv3d->rflag &= ~RV3D_NAVIGATING;
450
451#ifdef WITH_INPUT_NDOF
452 if (fly->ndof) {
453 MEM_freeN(fly->ndof);
454 }
455#endif
456
457 if (fly->state == FLY_CONFIRM) {
458 MEM_freeN(fly);
459 return OPERATOR_FINISHED;
460 }
461
462 MEM_freeN(fly);
463 return OPERATOR_CANCELLED;
464}
465
466static void flyEvent(FlyInfo *fly, const wmEvent *event)
467{
468 if (event->type == TIMER && event->customdata == fly->timer) {
469 fly->redraw = true;
470 }
471 else if (event->type == MOUSEMOVE) {
472 copy_v2_v2_int(fly->mval, event->mval);
473 }
474#ifdef WITH_INPUT_NDOF
475 else if (event->type == NDOF_MOTION) {
476 /* Do these auto-magically get delivered? yes. */
477 // puts("ndof motion detected in fly mode!");
478 // static const char *tag_name = "3D mouse position";
479
480 const wmNDOFMotionData *incoming_ndof = static_cast<const wmNDOFMotionData *>(
481 event->customdata);
482 switch (incoming_ndof->progress) {
483 case P_STARTING: {
484 /* Start keeping track of 3D mouse position. */
485# ifdef NDOF_FLY_DEBUG
486 puts("start keeping track of 3D mouse position");
487# endif
488 /* Fall-through. */
489 }
490 case P_IN_PROGRESS: {
491 /* Update 3D mouse position. */
492# ifdef NDOF_FLY_DEBUG
493 putchar('.');
494 fflush(stdout);
495# endif
496 if (fly->ndof == nullptr) {
497 // fly->ndof = MEM_mallocN(sizeof(wmNDOFMotionData), tag_name);
498 fly->ndof = static_cast<wmNDOFMotionData *>(MEM_dupallocN(incoming_ndof));
499 // fly->ndof = malloc(sizeof(wmNDOFMotionData));
500 }
501 else {
502 memcpy(fly->ndof, incoming_ndof, sizeof(wmNDOFMotionData));
503 }
504 break;
505 }
506 case P_FINISHING: {
507 /* Stop keeping track of 3D mouse position. */
508# ifdef NDOF_FLY_DEBUG
509 puts("stop keeping track of 3D mouse position");
510# endif
511 if (fly->ndof) {
512 MEM_freeN(fly->ndof);
513 // free(fly->ndof);
514 fly->ndof = nullptr;
515 }
516 /* Update the time else the view will jump when 2D mouse/timer resume. */
518 break;
519 }
520 default: {
521 /* Should always be one of the above 3. */
522 break;
523 }
524 }
525 }
526#endif /* WITH_INPUT_NDOF */
527 /* Handle modal key-map first. */
528 else if (event->type == EVT_MODAL_MAP) {
529 switch (event->val) {
530 case FLY_MODAL_CANCEL: {
531 fly->state = FLY_CANCEL;
532 break;
533 }
534 case FLY_MODAL_CONFIRM: {
535 fly->state = FLY_CONFIRM;
536 break;
537 }
538 /* Speed adjusting with mouse-pan (trackpad). */
539 case FLY_MODAL_SPEED: {
540 float fac = 0.02f * float(event->prev_xy[1] - event->xy[1]);
541
542 /* Allowing to brake immediate. */
543 if (fac > 0.0f && fly->speed < 0.0f) {
544 fly->speed = 0.0f;
545 }
546 else if (fac < 0.0f && fly->speed > 0.0f) {
547 fly->speed = 0.0f;
548 }
549 else {
550 fly->speed += fly->grid * fac;
551 }
552
553 break;
554 }
556 double time_currwheel;
557 float time_wheel;
558
559 /* Not quite correct but avoids confusion WASD/arrow keys 'locking up'. */
560 if (fly->axis == -1) {
561 fly->axis = 2;
562 fly->speed = fabsf(fly->speed);
563 }
564
565 time_currwheel = BLI_time_now_seconds();
566 time_wheel = float(time_currwheel - fly->time_lastwheel);
567 fly->time_lastwheel = time_currwheel;
568 /* Mouse wheel delays range from (0.5 == slow) to (0.01 == fast). */
569 /* 0-0.5 -> 0-5.0 */
570 time_wheel = 1.0f + (10.0f - (20.0f * min_ff(time_wheel, 0.5f)));
571
572 if (fly->speed < 0.0f) {
573 fly->speed = 0.0f;
574 }
575 else {
576 fly->speed += fly->grid * time_wheel * (fly->use_precision ? 0.1f : 1.0f);
577 }
578 break;
579 }
581 double time_currwheel;
582 float time_wheel;
583
584 /* Not quite correct but avoids confusion WASD/arrow keys 'locking up'. */
585 if (fly->axis == -1) {
586 fly->axis = 2;
587 fly->speed = -fabsf(fly->speed);
588 }
589
590 time_currwheel = BLI_time_now_seconds();
591 time_wheel = float(time_currwheel - fly->time_lastwheel);
592 fly->time_lastwheel = time_currwheel;
593 /* 0-0.5 -> 0-5.0 */
594 time_wheel = 1.0f + (10.0f - (20.0f * min_ff(time_wheel, 0.5f)));
595
596 if (fly->speed > 0.0f) {
597 fly->speed = 0;
598 }
599 else {
600 fly->speed -= fly->grid * time_wheel * (fly->use_precision ? 0.1f : 1.0f);
601 }
602 break;
603 }
605 fly->pan_view = true;
606 break;
607 }
609 fly->pan_view = false;
610 break;
611 }
612 /* Implement WASD keys, comments only for 'forward'. */
614 if (fly->axis == 2 && fly->speed < 0.0f) {
615 /* Reverse direction stops, tap again to continue. */
616 fly->axis = -1;
617 }
618 else {
619 /* Flip speed rather than stopping, game like motion,
620 * else increase like mouse-wheel if we're already moving in that direction. */
621 if (fly->speed < 0.0f) {
622 fly->speed = -fly->speed;
623 }
624 else if (fly->axis == 2) {
625 fly->speed += fly->grid;
626 }
627 fly->axis = 2;
628 }
629 break;
630 }
632 if (fly->axis == 2 && fly->speed > 0.0f) {
633 fly->axis = -1;
634 }
635 else {
636 if (fly->speed > 0.0f) {
637 fly->speed = -fly->speed;
638 }
639 else if (fly->axis == 2) {
640 fly->speed -= fly->grid;
641 }
642
643 fly->axis = 2;
644 }
645 break;
646 }
647 case FLY_MODAL_DIR_LEFT: {
648 if (fly->axis == 0 && fly->speed < 0.0f) {
649 fly->axis = -1;
650 }
651 else {
652 if (fly->speed < 0.0f) {
653 fly->speed = -fly->speed;
654 }
655 else if (fly->axis == 0) {
656 fly->speed += fly->grid;
657 }
658
659 fly->axis = 0;
660 }
661 break;
662 }
663 case FLY_MODAL_DIR_RIGHT: {
664 if (fly->axis == 0 && fly->speed > 0.0f) {
665 fly->axis = -1;
666 }
667 else {
668 if (fly->speed > 0.0f) {
669 fly->speed = -fly->speed;
670 }
671 else if (fly->axis == 0) {
672 fly->speed -= fly->grid;
673 }
674
675 fly->axis = 0;
676 }
677 break;
678 }
679 case FLY_MODAL_DIR_DOWN: {
680 if (fly->axis == 1 && fly->speed < 0.0f) {
681 fly->axis = -1;
682 }
683 else {
684 if (fly->speed < 0.0f) {
685 fly->speed = -fly->speed;
686 }
687 else if (fly->axis == 1) {
688 fly->speed += fly->grid;
689 }
690 fly->axis = 1;
691 }
692 break;
693 }
694 case FLY_MODAL_DIR_UP: {
695 if (fly->axis == 1 && fly->speed > 0.0f) {
696 fly->axis = -1;
697 }
698 else {
699 if (fly->speed > 0.0f) {
700 fly->speed = -fly->speed;
701 }
702 else if (fly->axis == 1) {
703 fly->speed -= fly->grid;
704 }
705 fly->axis = 1;
706 }
707 break;
708 }
710 if (fly->xlock != FLY_AXISLOCK_STATE_OFF) {
712 }
713 else {
715 fly->xlock_momentum = 0.0;
716 }
717 break;
718 }
720 if (fly->zlock != FLY_AXISLOCK_STATE_OFF) {
722 }
723 else {
725 fly->zlock_momentum = 0.0;
726 }
727 break;
728 }
730 fly->use_precision = true;
731 break;
732 }
734 fly->use_precision = false;
735 break;
736 }
738 fly->use_freelook = true;
739 break;
740 }
742 fly->use_freelook = false;
743 break;
744 }
745 }
746 }
747}
748
749static void flyMoveCamera(bContext *C,
750 FlyInfo *fly,
751 const bool do_rotate,
752 const bool do_translate,
753 const bool is_confirm)
754{
755 /* We only consider auto-keying on playback or if user confirmed fly on the same frame
756 * otherwise we get a keyframe even if the user cancels. */
757 const bool use_autokey = is_confirm || fly->anim_playing;
758 ED_view3d_cameracontrol_update(fly->v3d_camera_control, use_autokey, C, do_rotate, do_translate);
759}
760
761static int flyApply(bContext *C, FlyInfo *fly, bool is_confirm)
762{
763#define FLY_ROTATE_FAC 10.0f /* More is faster. */
764#define FLY_ZUP_CORRECT_FAC 0.1f /* Amount to correct per step. */
765#define FLY_ZUP_CORRECT_ACCEL 0.05f /* Increase upright momentum each step. */
766#define FLY_SMOOTH_FAC 20.0f /* Higher value less lag. */
767
768 RegionView3D *rv3d = fly->rv3d;
769
770 /* 3x3 copy of the view matrix so we can move along the view axis. */
771 float mat[3][3];
772 /* This is the direction that's added to the view offset per redraw. */
773 float dvec[3] = {0, 0, 0};
774
775 /* Camera Up-righting variables. */
776 float moffset[2]; /* Mouse offset from the views center. */
777 float tmp_quat[4]; /* Used for rotating the view. */
778
779#ifdef NDOF_FLY_DEBUG
780 {
781 static uint iteration = 1;
782 printf("fly timer %d\n", iteration++);
783 }
784#endif
785
786 /* X and Y margin defining the safe area where the mouse's movement won't rotate the view. */
787 const float xmargin = fly->viewport_size[0] / 20.0f;
788 const float ymargin = fly->viewport_size[1] / 20.0f;
789
790 {
791
792 /* Mouse offset from the center. */
793 moffset[0] = float(fly->mval[0] - fly->center_mval[0]);
794 moffset[1] = float(fly->mval[1] - fly->center_mval[1]);
795
796 /* Enforce a view margin. */
797 if (moffset[0] > xmargin) {
798 moffset[0] -= xmargin;
799 }
800 else if (moffset[0] < -xmargin) {
801 moffset[0] += xmargin;
802 }
803 else {
804 moffset[0] = 0;
805 }
806
807 if (moffset[1] > ymargin) {
808 moffset[1] -= ymargin;
809 }
810 else if (moffset[1] < -ymargin) {
811 moffset[1] += ymargin;
812 }
813 else {
814 moffset[1] = 0;
815 }
816
817 /* Scale the mouse movement by this value - scales mouse movement to the view size
818 * `moffset[0] / (region->winx-xmargin * 2)` - window size minus margin (same for Y).
819 *
820 * the mouse moves isn't linear. */
821
822 if (moffset[0]) {
823 moffset[0] /= fly->viewport_size[0] - (xmargin * 2);
824 moffset[0] *= fabsf(moffset[0]);
825 }
826
827 if (moffset[1]) {
828 moffset[1] /= fly->viewport_size[1] - (ymargin * 2);
829 moffset[1] *= fabsf(moffset[1]);
830 }
831
832 /* Should we redraw? */
833 if ((fly->speed != 0.0f) || moffset[0] || moffset[1] ||
835 dvec[0] || dvec[1] || dvec[2])
836 {
837 float dvec_tmp[3];
838
839 /* Time how fast it takes for us to redraw,
840 * this is so simple scenes don't fly too fast. */
841 double time_current;
842 float time_redraw;
843 float time_redraw_clamped;
844#ifdef NDOF_FLY_DRAW_TOOMUCH
845 fly->redraw = 1;
846#endif
847 time_current = BLI_time_now_seconds();
848 time_redraw = float(time_current - fly->time_lastdraw);
849
850 /* Clamp redraw time to avoid jitter in roll correction. */
851 time_redraw_clamped = min_ff(0.05f, time_redraw);
852
853 fly->time_lastdraw = time_current;
854
855 /* Scale the time to use shift to scale the speed down - just like
856 * shift slows many other areas of blender down. */
857 if (fly->use_precision) {
858 fly->speed = fly->speed * (1.0f - time_redraw_clamped);
859 }
860
861 copy_m3_m4(mat, rv3d->viewinv);
862
863 if (fly->pan_view == true) {
864 /* Pan only. */
865 copy_v3_fl3(dvec_tmp, -moffset[0], -moffset[1], 0.0f);
866
867 if (fly->use_precision) {
868 dvec_tmp[0] *= 0.1f;
869 dvec_tmp[1] *= 0.1f;
870 }
871
872 mul_m3_v3(mat, dvec_tmp);
873 mul_v3_fl(dvec_tmp, time_redraw * 200.0f * fly->grid);
874 }
875 else {
876 /* Similar to the angle between the camera's up and the Z-up,
877 * but its very rough so just roll. */
878 float roll;
879
880 /* Rotate about the X axis- look up/down. */
881 if (moffset[1]) {
882 float upvec[3];
883 copy_v3_fl3(upvec, 1.0f, 0.0f, 0.0f);
884 mul_m3_v3(mat, upvec);
885 /* Rotate about the relative up vector. */
886 axis_angle_to_quat(tmp_quat, upvec, moffset[1] * time_redraw * -FLY_ROTATE_FAC);
887 mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, tmp_quat);
888
889 if (fly->xlock != FLY_AXISLOCK_STATE_OFF) {
890 fly->xlock = FLY_AXISLOCK_STATE_ACTIVE; /* Check for rotation. */
891 }
892 if (fly->zlock != FLY_AXISLOCK_STATE_OFF) {
894 }
895 fly->xlock_momentum = 0.0f;
896 }
897
898 /* Rotate about the Y axis- look left/right. */
899 if (moffset[0]) {
900 float upvec[3];
901 /* If we're upside down invert the `moffset`. */
902 copy_v3_fl3(upvec, 0.0f, 1.0f, 0.0f);
903 mul_m3_v3(mat, upvec);
904
905 if (upvec[2] < 0.0f) {
906 moffset[0] = -moffset[0];
907 }
908
909 /* Make the lock vectors. */
910 if (fly->zlock) {
911 copy_v3_fl3(upvec, 0.0f, 0.0f, 1.0f);
912 }
913 else {
914 copy_v3_fl3(upvec, 0.0f, 1.0f, 0.0f);
915 mul_m3_v3(mat, upvec);
916 }
917
918 /* Rotate about the relative up vector. */
919 axis_angle_to_quat(tmp_quat, upvec, moffset[0] * time_redraw * FLY_ROTATE_FAC);
920 mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, tmp_quat);
921
922 if (fly->xlock != FLY_AXISLOCK_STATE_OFF) {
923 fly->xlock = FLY_AXISLOCK_STATE_ACTIVE; /* Check for rotation. */
924 }
925 if (fly->zlock != FLY_AXISLOCK_STATE_OFF) {
927 }
928 }
929
930 if (fly->zlock == FLY_AXISLOCK_STATE_ACTIVE) {
931 float upvec[3];
932 copy_v3_fl3(upvec, 1.0f, 0.0f, 0.0f);
933 mul_m3_v3(mat, upvec);
934
935 /* Make sure we have some Z rolling. */
936 if (fabsf(upvec[2]) > 0.00001f) {
937 roll = upvec[2] * 5.0f;
938 /* Rotate the view about this axis. */
939 copy_v3_fl3(upvec, 0.0f, 0.0f, 1.0f);
940 mul_m3_v3(mat, upvec);
941 /* Rotate about the relative up vector. */
942 axis_angle_to_quat(tmp_quat,
943 upvec,
944 roll * time_redraw_clamped * fly->zlock_momentum *
946 mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, tmp_quat);
947
949 }
950 else {
951 /* Don't check until the view rotates again. */
953 fly->zlock_momentum = 0.0f;
954 }
955 }
956
957 /* Only apply X-axis correction when mouse isn't applying X rotation. */
958 if (fly->xlock == FLY_AXISLOCK_STATE_ACTIVE && moffset[1] == 0) {
959 float upvec[3];
960 copy_v3_fl3(upvec, 0.0f, 0.0f, 1.0f);
961 mul_m3_v3(mat, upvec);
962 /* Make sure we have some Z rolling. */
963 if (fabsf(upvec[2]) > 0.00001f) {
964 roll = upvec[2] * -5.0f;
965 /* Rotate the view about this axis. */
966 copy_v3_fl3(upvec, 1.0f, 0.0f, 0.0f);
967 mul_m3_v3(mat, upvec);
968
969 /* Rotate about the relative up vector. */
971 tmp_quat, upvec, roll * time_redraw_clamped * fly->xlock_momentum * 0.1f);
972 mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, tmp_quat);
973
974 fly->xlock_momentum += 0.05f;
975 }
976 else {
977 fly->xlock = FLY_AXISLOCK_STATE_IDLE; /* See above. */
978 fly->xlock_momentum = 0.0f;
979 }
980 }
981
982 if (fly->axis == -1) {
983 /* Pause. */
984 zero_v3(dvec_tmp);
985 }
986 else if (!fly->use_freelook) {
987 /* Normal operation. */
988 /* Define `dvec`, view direction vector. */
989 zero_v3(dvec_tmp);
990 /* Move along the current axis. */
991 dvec_tmp[fly->axis] = 1.0f;
992
993 mul_m3_v3(mat, dvec_tmp);
994 }
995 else {
996 normalize_v3_v3(dvec_tmp, fly->dvec_prev);
997 if (fly->speed < 0.0f) {
998 negate_v3(dvec_tmp);
999 }
1000 }
1001
1002 mul_v3_fl(dvec_tmp, fly->speed * time_redraw * 0.25f);
1003 }
1004
1005 /* Impose a directional lag. */
1007 dvec, dvec_tmp, fly->dvec_prev, (1.0f / (1.0f + (time_redraw * FLY_SMOOTH_FAC))));
1008
1009 add_v3_v3(rv3d->ofs, dvec);
1010
1011 if (rv3d->persp == RV3D_CAMOB) {
1012 const bool do_rotate = ((fly->xlock != FLY_AXISLOCK_STATE_OFF) ||
1013 (fly->zlock != FLY_AXISLOCK_STATE_OFF) ||
1014 ((moffset[0] || moffset[1]) && !fly->pan_view));
1015 const bool do_translate = (fly->speed != 0.0f || fly->pan_view);
1016 flyMoveCamera(C, fly, do_rotate, do_translate, is_confirm);
1017 }
1018 }
1019 else {
1020 /* We're not redrawing but we need to update the time else the view will jump. */
1022 }
1023 /* End drawing. */
1024 copy_v3_v3(fly->dvec_prev, dvec);
1025 }
1026
1027 return OPERATOR_FINISHED;
1028}
1029
1030#ifdef WITH_INPUT_NDOF
1031static void flyApply_ndof(bContext *C, FlyInfo *fly, bool is_confirm)
1032{
1034 bool has_translate, has_rotate;
1035
1036 view3d_ndof_fly(fly->ndof,
1037 fly->v3d,
1038 fly->rv3d,
1039 fly->use_precision,
1040 lock_ob ? lock_ob->protectflag : 0,
1041 &has_translate,
1042 &has_rotate);
1043
1044 if (has_translate || has_rotate) {
1045 fly->redraw = true;
1046
1047 if (fly->rv3d->persp == RV3D_CAMOB) {
1048 flyMoveCamera(C, fly, has_rotate, has_translate, is_confirm);
1049 }
1050 }
1051}
1052#endif /* WITH_INPUT_NDOF */
1053
1056/* -------------------------------------------------------------------- */
1060static int fly_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1061{
1063
1065 return OPERATOR_CANCELLED;
1066 }
1067
1068 FlyInfo *fly = MEM_cnew<FlyInfo>("FlyOperation");
1069
1070 op->customdata = fly;
1071
1072 if (initFlyInfo(C, fly, op, event) == false) {
1073 MEM_freeN(op->customdata);
1074 return OPERATOR_CANCELLED;
1075 }
1076
1077 flyEvent(fly, event);
1078
1080
1082}
1083
1084static void fly_cancel(bContext *C, wmOperator *op)
1085{
1086 FlyInfo *fly = static_cast<FlyInfo *>(op->customdata);
1087
1088 fly->state = FLY_CANCEL;
1089 flyEnd(C, fly);
1090 op->customdata = nullptr;
1091}
1092
1093static int fly_modal(bContext *C, wmOperator *op, const wmEvent *event)
1094{
1095 int exit_code;
1096 bool do_draw = false;
1097 FlyInfo *fly = static_cast<FlyInfo *>(op->customdata);
1098 View3D *v3d = fly->v3d;
1099 RegionView3D *rv3d = fly->rv3d;
1101
1102 fly->redraw = false;
1103
1104 flyEvent(fly, event);
1105
1106#ifdef WITH_INPUT_NDOF
1107 if (fly->ndof) { /* 3D mouse overrules [2D mouse + timer]. */
1108 if (event->type == NDOF_MOTION) {
1109 flyApply_ndof(C, fly, false);
1110 }
1111 }
1112 else
1113#endif /* WITH_INPUT_NDOF */
1114 if (event->type == TIMER && event->customdata == fly->timer) {
1115 flyApply(C, fly, false);
1116 }
1117
1118 do_draw |= fly->redraw;
1119
1120 exit_code = flyEnd(C, fly);
1121
1122 if (exit_code == OPERATOR_FINISHED) {
1123 const bool is_undo_pushed = ED_view3d_camera_lock_undo_push(op->type->name, v3d, rv3d, C);
1124 /* If generic 'locked camera' code did not push an undo, but there is a valid 'flying
1125 * object', an undo push is still needed, since that object transform was modified. */
1126 if (!is_undo_pushed && fly_object && ED_undo_is_memfile_compatible(C)) {
1127 ED_undo_push(C, op->type->name);
1128 }
1129 }
1130 if (exit_code != OPERATOR_RUNNING_MODAL) {
1131 do_draw = true;
1132 }
1133
1134 if (do_draw) {
1135 if (rv3d->persp == RV3D_CAMOB) {
1137 }
1138
1139 // puts("redraw!"); // too frequent, commented with NDOF_FLY_DRAW_TOOMUCH for now
1141 }
1142
1143 return exit_code;
1144}
1145
1147{
1148 /* Identifiers. */
1149 ot->name = "Fly Navigation";
1150 ot->description = "Interactively fly around the scene";
1151 ot->idname = "VIEW3D_OT_fly";
1152
1153 /* API callbacks. */
1154 ot->invoke = fly_invoke;
1155 ot->cancel = fly_cancel;
1156 ot->modal = fly_modal;
1158
1159 /* Flags. */
1161}
1162
Depsgraph * CTX_data_expect_evaluated_depsgraph(const bContext *C)
wmWindow * CTX_wm_window(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
Main * CTX_data_main(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)
bool BKE_id_is_editable(const Main *bmain, const ID *id)
Definition lib_id.cc:2456
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:125
MINLINE float min_ff(float a, float b)
void mul_m3_v3(const float M[3][3], float r[3])
void copy_m3_m4(float m1[3][3], const float m2[4][4])
void axis_angle_to_quat(float r[4], const float axis[3], float angle)
void mul_qt_qtqt(float q[4], const float a[4], const float b[4])
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 copy_v3_fl3(float v[3], float x, float y, float z)
void interp_v3_v3v3(float r[3], const float a[3], const float b[3], float t)
Definition math_vector.c:36
MINLINE void negate_v3(float r[3])
MINLINE float normalize_v3_v3(float r[3], const float a[3])
MINLINE void zero_v3(float r[3])
MINLINE void add_v3_v3(float r[3], const float a[3])
BLI_INLINE float BLI_rctf_size_x(const struct rctf *rct)
Definition BLI_rect.h:197
BLI_INLINE float BLI_rctf_size_y(const struct rctf *rct)
Definition BLI_rect.h:201
unsigned int uint
Platform independent time functions.
double BLI_time_now_seconds(void)
Definition time.c:65
Object is a sort of wrapper for general info.
#define RV3D_LOCK_FLAGS(rv3d)
@ RV3D_NAVIGATING
@ RV3D_CAMOB
@ RV3D_PERSP
@ RV3D_LOCK_ANY_TRANSFORM
@ OPERATOR_RUNNING_MODAL
bool ED_operator_region_view3d_active(bContext *C)
bScreen * ED_screen_animation_playing(const wmWindowManager *wm)
void ED_region_tag_redraw(ARegion *region)
Definition area.cc:634
void * ED_region_draw_cb_activate(ARegionType *art, void(*draw)(const bContext *, ARegion *, void *), void *customdata, int type)
bool ED_region_draw_cb_exit(ARegionType *art, void *handle)
#define REGION_DRAW_POST_PIXEL
void ED_undo_push(bContext *C, const char *str)
Definition ed_undo.cc:104
bool ED_undo_is_memfile_compatible(const bContext *C)
Definition ed_undo.cc:409
bool ED_view3d_offset_lock_check(const View3D *v3d, const RegionView3D *rv3d)
bool ED_view3d_camera_lock_undo_push(const char *str, const View3D *v3d, const RegionView3D *rv3d, bContext *C)
void ED_view3d_calc_camera_border(const Scene *scene, const Depsgraph *depsgraph, const ARegion *region, const View3D *v3d, const RegionView3D *rv3d, bool no_shift, rctf *r_viewborder)
void immEnd()
void immUnbindProgram()
void immVertex2f(uint attr_id, float x, float y)
void immBindBuiltinProgram(eGPUBuiltinShader shader_id)
void immUniformThemeColor3(int color_id)
GPUVertFormat * immVertexFormat()
void immBegin(GPUPrimType, uint vertex_len)
@ GPU_PRIM_LINES
@ GPU_SHADER_3D_UNIFORM_COLOR
@ GPU_FETCH_FLOAT
uint GPU_vertformat_attr_add(GPUVertFormat *, const char *name, GPUVertCompType, uint comp_len, GPUVertFetchMode)
@ GPU_COMP_F32
Read Guarded memory(de)allocation.
@ TH_VIEW_OVERLAY
@ OPTYPE_BLOCKING
Definition WM_types.hh:164
#define ND_TRANSFORM
Definition WM_types.hh:423
#define NC_OBJECT
Definition WM_types.hh:346
@ P_IN_PROGRESS
Definition WM_types.hh:815
@ P_STARTING
Definition WM_types.hh:814
@ P_FINISHING
Definition WM_types.hh:816
#define printf
#define fabsf(x)
draw_view in_light_buf[] float
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
format
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
void *(* MEM_dupallocN)(const void *vmemh)
Definition mallocn.cc:39
struct ARegionType * type
float viewport_size[2]
eFlyPanState xlock
RegionView3D * rv3d
Depsgraph * depsgraph
View3DCameraControl * v3d_camera_control
eFlyPanState zlock
void * draw_handle_pixel
void * first
ListBase constraints
short protectflag
float viewinv[4][4]
struct Object * camera
float xmin
float ymin
int ymin
int xmin
short val
Definition WM_types.hh:724
int xy[2]
Definition WM_types.hh:726
int mval[2]
Definition WM_types.hh:728
int prev_xy[2]
Definition WM_types.hh:785
short type
Definition WM_types.hh:722
void * customdata
Definition WM_types.hh:772
const void * modal_items
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
const char * description
Definition WM_types.hh:996
void(* cancel)(bContext *C, wmOperator *op)
Definition WM_types.hh:1028
struct ReportList * reports
struct wmOperatorType * type
View3DCameraControl * ED_view3d_cameracontrol_acquire(Depsgraph *depsgraph, Scene *scene, View3D *v3d, RegionView3D *rv3d)
Object * ED_view3d_cameracontrol_object_get(View3DCameraControl *vctrl)
void ED_view3d_cameracontrol_update(View3DCameraControl *vctrl, const bool use_autokey, bContext *C, const bool do_rotate, const bool do_translate)
void ED_view3d_cameracontrol_release(View3DCameraControl *vctrl, const bool restore)
#define FLY_ZUP_CORRECT_ACCEL
#define FLY_SMOOTH_FAC
static void flyEvent(FlyInfo *fly, const wmEvent *event)
@ FLY_AXISLOCK_STATE_OFF
@ FLY_AXISLOCK_STATE_ACTIVE
@ FLY_AXISLOCK_STATE_IDLE
static int flyEnd(bContext *C, FlyInfo *fly)
static int flyApply(bContext *C, FlyInfo *fly, bool is_confirm)
void fly_modal_keymap(wmKeyConfig *keyconf)
static void drawFlyPixel(const bContext *, ARegion *, void *arg)
#define FLY_ZUP_CORRECT_FAC
static void fly_cancel(bContext *C, wmOperator *op)
#define FLY_ROTATE_FAC
static bool initFlyInfo(bContext *C, FlyInfo *fly, wmOperator *op, const wmEvent *event)
void VIEW3D_OT_fly(wmOperatorType *ot)
static int fly_modal(bContext *C, wmOperator *op, const wmEvent *event)
static int fly_invoke(bContext *C, wmOperator *op, const wmEvent *event)
@ FLY_MODAL_CANCEL
@ FLY_MODAL_ACCELERATE
@ FLY_MODAL_SPEED
@ FLY_MODAL_DIR_DOWN
@ FLY_MODAL_PRECISION_ENABLE
@ FLY_MODAL_AXIS_LOCK_Z
@ FLY_MODAL_PAN_DISABLE
@ FLY_MODAL_DIR_RIGHT
@ FLY_MODAL_DIR_UP
@ FLY_MODAL_FREELOOK_DISABLE
@ FLY_MODAL_FREELOOK_ENABLE
@ FLY_MODAL_DIR_FORWARD
@ FLY_MODAL_PRECISION_DISABLE
@ FLY_MODAL_AXIS_LOCK_X
@ FLY_MODAL_DIR_LEFT
@ FLY_MODAL_DIR_BACKWARD
@ FLY_MODAL_DECELERATE
@ FLY_MODAL_PAN_ENABLE
@ FLY_MODAL_CONFIRM
static void flyMoveCamera(bContext *C, FlyInfo *fly, const bool do_rotate, const bool do_translate, const bool is_confirm)
wmEventHandler_Op * WM_event_add_modal_handler(bContext *C, wmOperator *op)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
@ TIMER
@ EVT_MODAL_MAP
@ MOUSEMOVE
@ NDOF_MOTION
wmOperatorType * ot
Definition wm_files.cc:4125
wmKeyMap * WM_modalkeymap_ensure(wmKeyConfig *keyconf, const char *idname, const EnumPropertyItem *items)
Definition wm_keymap.cc:933
void WM_modalkeymap_assign(wmKeyMap *km, const char *opname)
wmKeyMap * WM_modalkeymap_find(wmKeyConfig *keyconf, const char *idname)
Definition wm_keymap.cc:960
void WM_cursor_warp(wmWindow *win, int x, int y)
void WM_event_timer_remove(wmWindowManager *wm, wmWindow *, wmTimer *timer)
wmTimer * WM_event_timer_add(wmWindowManager *wm, wmWindow *win, const int event_type, const double time_step)