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