GPencil: Primitive: Polyline Tool
[blender.git] / source / blender / editors / space_view3d / view3d_walk.c
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  */
16
17 /** \file
18  * \ingroup spview3d
19  */
20
21 /* defines VIEW3D_OT_navigate - walk modal operator */
22
23 #include "DNA_scene_types.h"
24 #include "DNA_object_types.h"
25
26 #include "MEM_guardedalloc.h"
27
28 #include "BLI_math.h"
29 #include "BLI_blenlib.h"
30 #include "BLI_kdopbvh.h"
31 #include "BLI_utildefines.h"
32
33 #include "BKE_context.h"
34 #include "BKE_main.h"
35 #include "BKE_report.h"
36
37 #include "BLT_translation.h"
38
39 #include "WM_api.h"
40 #include "WM_types.h"
41
42 #include "ED_screen.h"
43 #include "ED_space_api.h"
44 #include "ED_transform_snap_object_context.h"
45
46 #include "PIL_time.h" /* smoothview */
47
48 #include "UI_interface.h"
49 #include "UI_resources.h"
50
51 #include "GPU_immediate.h"
52
53 #include "DEG_depsgraph.h"
54
55 #include "view3d_intern.h" /* own include */
56
57 #ifdef WITH_INPUT_NDOF
58 //#  define NDOF_WALK_DEBUG
59 /* is this needed for ndof? - commented so redraw doesn't thrash - campbell */
60 //#  define NDOF_WALK_DRAW_TOOMUCH
61 #endif
62
63 #define USE_TABLET_SUPPORT
64
65 /* ensure the target position is one we can reach, see: T45771 */
66 #define USE_PIXELSIZE_NATIVE_SUPPORT
67
68 /* NOTE: these defines are saved in keymap files,
69  * do not change values but just add new ones */
70 enum {
71   WALK_MODAL_CANCEL = 1,
72   WALK_MODAL_CONFIRM,
73   WALK_MODAL_DIR_FORWARD,
74   WALK_MODAL_DIR_FORWARD_STOP,
75   WALK_MODAL_DIR_BACKWARD,
76   WALK_MODAL_DIR_BACKWARD_STOP,
77   WALK_MODAL_DIR_LEFT,
78   WALK_MODAL_DIR_LEFT_STOP,
79   WALK_MODAL_DIR_RIGHT,
80   WALK_MODAL_DIR_RIGHT_STOP,
81   WALK_MODAL_DIR_UP,
82   WALK_MODAL_DIR_UP_STOP,
83   WALK_MODAL_DIR_DOWN,
84   WALK_MODAL_DIR_DOWN_STOP,
85   WALK_MODAL_FAST_ENABLE,
86   WALK_MODAL_FAST_DISABLE,
87   WALK_MODAL_SLOW_ENABLE,
88   WALK_MODAL_SLOW_DISABLE,
89   WALK_MODAL_JUMP,
90   WALK_MODAL_JUMP_STOP,
91   WALK_MODAL_TELEPORT,
92   WALK_MODAL_TOGGLE,
93   WALK_MODAL_ACCELERATE,
94   WALK_MODAL_DECELERATE,
95 };
96
97 enum {
98   WALK_BIT_FORWARD = 1 << 0,
99   WALK_BIT_BACKWARD = 1 << 1,
100   WALK_BIT_LEFT = 1 << 2,
101   WALK_BIT_RIGHT = 1 << 3,
102   WALK_BIT_UP = 1 << 4,
103   WALK_BIT_DOWN = 1 << 5,
104 };
105
106 typedef enum eWalkTeleportState {
107   WALK_TELEPORT_STATE_OFF = 0,
108   WALK_TELEPORT_STATE_ON,
109 } eWalkTeleportState;
110
111 typedef enum eWalkMethod {
112   WALK_MODE_FREE = 0,
113   WALK_MODE_GRAVITY,
114 } eWalkMethod;
115
116 typedef enum eWalkGravityState {
117   WALK_GRAVITY_STATE_OFF = 0,
118   WALK_GRAVITY_STATE_JUMP,
119   WALK_GRAVITY_STATE_START,
120   WALK_GRAVITY_STATE_ON,
121 } eWalkGravityState;
122
123 /* called in transform_ops.c, on each regeneration of keymaps  */
124 void walk_modal_keymap(wmKeyConfig *keyconf)
125 {
126   static const EnumPropertyItem modal_items[] = {
127       {WALK_MODAL_CONFIRM, "CONFIRM", 0, "Confirm", ""},
128       {WALK_MODAL_CANCEL, "CANCEL", 0, "Cancel", ""},
129
130       {WALK_MODAL_DIR_FORWARD, "FORWARD", 0, "Forward", ""},
131       {WALK_MODAL_DIR_BACKWARD, "BACKWARD", 0, "Backward", ""},
132       {WALK_MODAL_DIR_LEFT, "LEFT", 0, "Left", ""},
133       {WALK_MODAL_DIR_RIGHT, "RIGHT", 0, "Right", ""},
134       {WALK_MODAL_DIR_UP, "UP", 0, "Up", ""},
135       {WALK_MODAL_DIR_DOWN, "DOWN", 0, "Down", ""},
136
137       {WALK_MODAL_DIR_FORWARD_STOP, "FORWARD_STOP", 0, "Stop Move Forward", ""},
138       {WALK_MODAL_DIR_BACKWARD_STOP, "BACKWARD_STOP", 0, "Stop Mode Backward", ""},
139       {WALK_MODAL_DIR_LEFT_STOP, "LEFT_STOP", 0, "Stop Move Left", ""},
140       {WALK_MODAL_DIR_RIGHT_STOP, "RIGHT_STOP", 0, "Stop Mode Right", ""},
141       {WALK_MODAL_DIR_UP_STOP, "UP_STOP", 0, "Stop Move Up", ""},
142       {WALK_MODAL_DIR_DOWN_STOP, "DOWN_STOP", 0, "Stop Mode Down", ""},
143
144       {WALK_MODAL_TELEPORT, "TELEPORT", 0, "Teleport", "Move forward a few units at once"},
145
146       {WALK_MODAL_ACCELERATE, "ACCELERATE", 0, "Accelerate", ""},
147       {WALK_MODAL_DECELERATE, "DECELERATE", 0, "Decelerate", ""},
148
149       {WALK_MODAL_FAST_ENABLE, "FAST_ENABLE", 0, "Fast", "Move faster (walk or fly)"},
150       {WALK_MODAL_FAST_DISABLE, "FAST_DISABLE", 0, "Fast (Off)", "Resume regular speed"},
151
152       {WALK_MODAL_SLOW_ENABLE, "SLOW_ENABLE", 0, "Slow", "Move slower (walk or fly)"},
153       {WALK_MODAL_SLOW_DISABLE, "SLOW_DISABLE", 0, "Slow (Off)", "Resume regular speed"},
154
155       {WALK_MODAL_JUMP, "JUMP", 0, "Jump", "Jump when in walk mode"},
156       {WALK_MODAL_JUMP_STOP, "JUMP_STOP", 0, "Jump (Off)", "Stop pushing jump"},
157
158       {WALK_MODAL_TOGGLE, "GRAVITY_TOGGLE", 0, "Toggle Gravity", "Toggle gravity effect"},
159
160       {0, NULL, 0, NULL, NULL},
161   };
162
163   wmKeyMap *keymap = WM_modalkeymap_get(keyconf, "View3D Walk Modal");
164
165   /* this function is called for each spacetype, only needs to add map once */
166   if (keymap && keymap->modal_items) {
167     return;
168   }
169
170   keymap = WM_modalkeymap_add(keyconf, "View3D Walk Modal", modal_items);
171
172   /* assign map to operators */
173   WM_modalkeymap_assign(keymap, "VIEW3D_OT_walk");
174 }
175
176 typedef struct WalkTeleport {
177   eWalkTeleportState state;
178   float duration; /* from user preferences */
179   float origin[3];
180   float direction[3];
181   double initial_time;
182   eWalkMethod navigation_mode; /* teleport always set FREE mode on */
183
184 } WalkTeleport;
185
186 typedef struct WalkInfo {
187   /* context stuff */
188   RegionView3D *rv3d;
189   View3D *v3d;
190   ARegion *ar;
191   struct Depsgraph *depsgraph;
192   Scene *scene;
193
194   /** Needed for for updating that isn't triggered by input. */
195   wmTimer *timer;
196
197   short state;
198   bool redraw;
199
200   /**
201    * Needed for auto-keyframing, when animation isn't playing, only keyframe on confirmation.
202    *
203    * Currently we can't cancel this operator usefully while recording on animation playback
204    * (this would need to un-key all previous frames).
205    */
206   bool anim_playing;
207   bool need_rotation_keyframe;
208   bool need_translation_keyframe;
209
210   /** Previous 2D mouse values. */
211   int prev_mval[2];
212   /** Center mouse values. */
213   int center_mval[2];
214
215   int moffset[2];
216
217 #ifdef WITH_INPUT_NDOF
218   /** Latest 3D mouse values. */
219   wmNDOFMotionData *ndof;
220 #endif
221
222   /* walk state state */
223   /** The base speed without run/slow down modifications. */
224   float base_speed;
225   /** The speed the view is moving per redraw. */
226   float speed;
227   /** World scale 1.0 default. */
228   float grid;
229
230   /* compare between last state */
231   /** Time between draws. */
232   double time_lastdraw;
233
234   void *draw_handle_pixel;
235
236   /* use for some lag */
237   /** Keep the previous value to smooth transitions (use lag). */
238   float dvec_prev[3];
239
240   /** Walk/free movement. */
241   eWalkMethod navigation_mode;
242
243   /* teleport */
244   WalkTeleport teleport;
245
246   /** Look speed factor - user preferences. */
247   float mouse_speed;
248
249   /** Speed adjustments. */
250   bool is_fast;
251   bool is_slow;
252
253   /** Mouse reverse. */
254   bool is_reversed;
255
256 #ifdef USE_TABLET_SUPPORT
257   /** Check if we had a cursor event before. */
258   bool is_cursor_first;
259
260   /** Tablet devices (we can't relocate the cursor). */
261   bool is_cursor_absolute;
262 #endif
263
264   /** Gravity system. */
265   eWalkGravityState gravity_state;
266   float gravity;
267
268   /** Height to use in walk mode. */
269   float view_height;
270
271   /** Counting system to allow movement to continue if a direction (WASD) key is still pressed. */
272   int active_directions;
273
274   float speed_jump;
275   /** Maximum jump height. */
276   float jump_height;
277   /** To use for fast/slow speeds. */
278   float speed_factor;
279
280   struct SnapObjectContext *snap_context;
281
282   struct View3DCameraControl *v3d_camera_control;
283
284 } WalkInfo;
285
286 /* prototypes */
287 #ifdef WITH_INPUT_NDOF
288 static void walkApply_ndof(bContext *C, WalkInfo *walk, bool is_confirm);
289 #endif /* WITH_INPUT_NDOF */
290 static int walkApply(bContext *C, struct WalkInfo *walk, bool force_autokey);
291 static float getVelocityZeroTime(const float gravity, const float velocity);
292
293 static void drawWalkPixel(const struct bContext *UNUSED(C), ARegion *ar, void *arg)
294 {
295   /* draws an aim/cross in the center */
296   WalkInfo *walk = arg;
297
298   const int outter_length = 24;
299   const int inner_length = 14;
300   int xoff, yoff;
301   rctf viewborder;
302
303   if (ED_view3d_cameracontrol_object_get(walk->v3d_camera_control)) {
304     ED_view3d_calc_camera_border(
305         walk->scene, walk->depsgraph, ar, walk->v3d, walk->rv3d, &viewborder, false);
306     xoff = viewborder.xmin + BLI_rctf_size_x(&viewborder) * 0.5f;
307     yoff = viewborder.ymin + BLI_rctf_size_y(&viewborder) * 0.5f;
308   }
309   else {
310     xoff = walk->ar->winx / 2;
311     yoff = walk->ar->winy / 2;
312   }
313
314   GPUVertFormat *format = immVertexFormat();
315   uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT);
316
317   immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
318
319   immUniformThemeColor(TH_VIEW_OVERLAY);
320
321   immBegin(GPU_PRIM_LINES, 8);
322
323   /* North */
324   immVertex2i(pos, xoff, yoff + inner_length);
325   immVertex2i(pos, xoff, yoff + outter_length);
326
327   /* East */
328   immVertex2i(pos, xoff + inner_length, yoff);
329   immVertex2i(pos, xoff + outter_length, yoff);
330
331   /* South */
332   immVertex2i(pos, xoff, yoff - inner_length);
333   immVertex2i(pos, xoff, yoff - outter_length);
334
335   /* West */
336   immVertex2i(pos, xoff - inner_length, yoff);
337   immVertex2i(pos, xoff - outter_length, yoff);
338
339   immEnd();
340   immUnbindProgram();
341 }
342
343 static void walk_navigation_mode_set(WalkInfo *walk, eWalkMethod mode)
344 {
345   if (mode == WALK_MODE_FREE) {
346     walk->navigation_mode = WALK_MODE_FREE;
347     walk->gravity_state = WALK_GRAVITY_STATE_OFF;
348   }
349   else { /* WALK_MODE_GRAVITY */
350     walk->navigation_mode = WALK_MODE_GRAVITY;
351     walk->gravity_state = WALK_GRAVITY_STATE_START;
352   }
353 }
354
355 /**
356  * \param r_distance: Distance to the hit point
357  */
358 static bool walk_floor_distance_get(RegionView3D *rv3d,
359                                     WalkInfo *walk,
360                                     const float dvec[3],
361                                     float *r_distance)
362 {
363   float ray_normal[3] = {0, 0, -1}; /* down */
364   float ray_start[3];
365   float r_location[3];
366   float r_normal_dummy[3];
367   float dvec_tmp[3];
368   bool ret;
369
370   *r_distance = BVH_RAYCAST_DIST_MAX;
371
372   copy_v3_v3(ray_start, rv3d->viewinv[3]);
373
374   mul_v3_v3fl(dvec_tmp, dvec, walk->grid);
375   add_v3_v3(ray_start, dvec_tmp);
376
377   ret = ED_transform_snap_object_project_ray(walk->snap_context,
378                                              &(const struct SnapObjectParams){
379                                                  .snap_select = SNAP_ALL,
380                                              },
381                                              ray_start,
382                                              ray_normal,
383                                              r_distance,
384                                              r_location,
385                                              r_normal_dummy);
386
387   /* artificially scale the distance to the scene size */
388   *r_distance /= walk->grid;
389   return ret;
390 }
391
392 /**
393  * \param ray_distance: Distance to the hit point
394  * \param r_location: Location of the hit point
395  * \param r_normal: Normal of the hit surface, transformed to always face the camera
396  */
397 static bool walk_ray_cast(RegionView3D *rv3d,
398                           WalkInfo *walk,
399                           float r_location[3],
400                           float r_normal[3],
401                           float *ray_distance)
402 {
403   float ray_normal[3] = {0, 0, -1}; /* forward */
404   float ray_start[3];
405   bool ret;
406
407   *ray_distance = BVH_RAYCAST_DIST_MAX;
408
409   copy_v3_v3(ray_start, rv3d->viewinv[3]);
410
411   mul_mat3_m4_v3(rv3d->viewinv, ray_normal);
412
413   normalize_v3(ray_normal);
414
415   ret = ED_transform_snap_object_project_ray(walk->snap_context,
416                                              &(const struct SnapObjectParams){
417                                                  .snap_select = SNAP_ALL,
418                                              },
419                                              ray_start,
420                                              ray_normal,
421                                              NULL,
422                                              r_location,
423                                              r_normal);
424
425   /* dot is positive if both rays are facing the same direction */
426   if (dot_v3v3(ray_normal, r_normal) > 0) {
427     negate_v3(r_normal);
428   }
429
430   /* artificially scale the distance to the scene size */
431   *ray_distance /= walk->grid;
432
433   return ret;
434 }
435
436 /* WalkInfo->state */
437 enum {
438   WALK_RUNNING = 0,
439   WALK_CANCEL = 1,
440   WALK_CONFIRM = 2,
441 };
442
443 /* keep the previous speed until user changes userpreferences */
444 static float base_speed = -1.f;
445 static float userdef_speed = -1.f;
446
447 static bool initWalkInfo(bContext *C, WalkInfo *walk, wmOperator *op)
448 {
449   wmWindowManager *wm = CTX_wm_manager(C);
450   Main *bmain = CTX_data_main(C);
451   wmWindow *win = CTX_wm_window(C);
452
453   walk->rv3d = CTX_wm_region_view3d(C);
454   walk->v3d = CTX_wm_view3d(C);
455   walk->ar = CTX_wm_region(C);
456   walk->depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
457   walk->scene = CTX_data_scene(C);
458
459 #ifdef NDOF_WALK_DEBUG
460   puts("\n-- walk begin --");
461 #endif
462
463   /* sanity check: for rare but possible case (if lib-linking the camera fails) */
464   if ((walk->rv3d->persp == RV3D_CAMOB) && (walk->v3d->camera == NULL)) {
465     walk->rv3d->persp = RV3D_PERSP;
466   }
467
468   if (walk->rv3d->persp == RV3D_CAMOB && ID_IS_LINKED(walk->v3d->camera)) {
469     BKE_report(op->reports, RPT_ERROR, "Cannot navigate a camera from an external library");
470     return false;
471   }
472
473   if (ED_view3d_offset_lock_check(walk->v3d, walk->rv3d)) {
474     BKE_report(op->reports, RPT_ERROR, "Cannot navigate when the view offset is locked");
475     return false;
476   }
477
478   if (walk->rv3d->persp == RV3D_CAMOB && walk->v3d->camera->constraints.first) {
479     BKE_report(op->reports, RPT_ERROR, "Cannot navigate an object with constraints");
480     return false;
481   }
482
483   walk->state = WALK_RUNNING;
484
485   if (fabsf(U.walk_navigation.walk_speed - userdef_speed) > 0.1f) {
486     base_speed = U.walk_navigation.walk_speed;
487     userdef_speed = U.walk_navigation.walk_speed;
488   }
489
490   walk->speed = 0.0f;
491   walk->is_fast = false;
492   walk->is_slow = false;
493   walk->grid = (walk->scene->unit.system == USER_UNIT_NONE) ? 1.f :
494                                                               1.f / walk->scene->unit.scale_length;
495
496   /* user preference settings */
497   walk->teleport.duration = U.walk_navigation.teleport_time;
498   walk->mouse_speed = U.walk_navigation.mouse_speed;
499
500   if ((U.walk_navigation.flag & USER_WALK_GRAVITY)) {
501     walk_navigation_mode_set(walk, WALK_MODE_GRAVITY);
502   }
503   else {
504     walk_navigation_mode_set(walk, WALK_MODE_FREE);
505   }
506
507   walk->view_height = U.walk_navigation.view_height;
508   walk->jump_height = U.walk_navigation.jump_height;
509   walk->speed = U.walk_navigation.walk_speed;
510   walk->speed_factor = U.walk_navigation.walk_speed_factor;
511
512   walk->gravity_state = WALK_GRAVITY_STATE_OFF;
513
514   if ((walk->scene->physics_settings.flag & PHYS_GLOBAL_GRAVITY)) {
515     walk->gravity = fabsf(walk->scene->physics_settings.gravity[2]);
516   }
517   else {
518     walk->gravity = 9.80668f; /* m/s2 */
519   }
520
521   walk->is_reversed = ((U.walk_navigation.flag & USER_WALK_MOUSE_REVERSE) != 0);
522
523 #ifdef USE_TABLET_SUPPORT
524   walk->is_cursor_first = true;
525
526   walk->is_cursor_absolute = false;
527 #endif
528
529   walk->active_directions = 0;
530
531 #ifdef NDOF_WALK_DRAW_TOOMUCH
532   walk->redraw = 1;
533 #endif
534   zero_v3(walk->dvec_prev);
535
536   walk->timer = WM_event_add_timer(CTX_wm_manager(C), win, TIMER, 0.01f);
537
538 #ifdef WITH_INPUT_NDOF
539   walk->ndof = NULL;
540 #endif
541
542   walk->anim_playing = ED_screen_animation_playing(wm);
543   walk->need_rotation_keyframe = false;
544   walk->need_translation_keyframe = false;
545
546   walk->time_lastdraw = PIL_check_seconds_timer();
547
548   walk->draw_handle_pixel = ED_region_draw_cb_activate(
549       walk->ar->type, drawWalkPixel, walk, REGION_DRAW_POST_PIXEL);
550
551   walk->rv3d->rflag |= RV3D_NAVIGATING;
552
553   walk->snap_context = ED_transform_snap_object_context_create_view3d(
554       bmain, walk->scene, CTX_data_ensure_evaluated_depsgraph(C), 0, walk->ar, walk->v3d);
555
556   walk->v3d_camera_control = ED_view3d_cameracontrol_acquire(
557       walk->depsgraph,
558       walk->scene,
559       walk->v3d,
560       walk->rv3d,
561       (U.uiflag & USER_CAM_LOCK_NO_PARENT) == 0);
562
563   /* center the mouse */
564   walk->center_mval[0] = walk->ar->winx * 0.5f;
565   walk->center_mval[1] = walk->ar->winy * 0.5f;
566
567 #ifdef USE_PIXELSIZE_NATIVE_SUPPORT
568   walk->center_mval[0] += walk->ar->winrct.xmin;
569   walk->center_mval[1] += walk->ar->winrct.ymin;
570
571   WM_cursor_compatible_xy(win, &walk->center_mval[0], &walk->center_mval[1]);
572
573   walk->center_mval[0] -= walk->ar->winrct.xmin;
574   walk->center_mval[1] -= walk->ar->winrct.ymin;
575 #endif
576
577   copy_v2_v2_int(walk->prev_mval, walk->center_mval);
578
579   WM_cursor_warp(win,
580                  walk->ar->winrct.xmin + walk->center_mval[0],
581                  walk->ar->winrct.ymin + walk->center_mval[1]);
582
583   /* remove the mouse cursor temporarily */
584   WM_cursor_modal_set(win, WM_CURSOR_NONE);
585
586   return 1;
587 }
588
589 static int walkEnd(bContext *C, WalkInfo *walk)
590 {
591   wmWindow *win;
592   RegionView3D *rv3d;
593
594   if (walk->state == WALK_RUNNING) {
595     return OPERATOR_RUNNING_MODAL;
596   }
597   else if (walk->state == WALK_CONFIRM) {
598     /* Needed for auto_keyframe. */
599 #ifdef WITH_INPUT_NDOF
600     if (walk->ndof) {
601       walkApply_ndof(C, walk, true);
602     }
603     else
604 #endif /* WITH_INPUT_NDOF */
605     {
606       walkApply(C, walk, true);
607     }
608   }
609
610 #ifdef NDOF_WALK_DEBUG
611   puts("\n-- walk end --");
612 #endif
613
614   win = CTX_wm_window(C);
615   rv3d = walk->rv3d;
616
617   WM_event_remove_timer(CTX_wm_manager(C), win, walk->timer);
618
619   ED_region_draw_cb_exit(walk->ar->type, walk->draw_handle_pixel);
620
621   ED_transform_snap_object_context_destroy(walk->snap_context);
622
623   ED_view3d_cameracontrol_release(walk->v3d_camera_control, walk->state == WALK_CANCEL);
624
625   rv3d->rflag &= ~RV3D_NAVIGATING;
626
627 #ifdef WITH_INPUT_NDOF
628   if (walk->ndof) {
629     MEM_freeN(walk->ndof);
630   }
631 #endif
632
633   /* restore the cursor */
634   WM_cursor_modal_restore(win);
635
636 #ifdef USE_TABLET_SUPPORT
637   if (walk->is_cursor_absolute == false)
638 #endif
639   {
640     /* center the mouse */
641     WM_cursor_warp(win,
642                    walk->ar->winrct.xmin + walk->center_mval[0],
643                    walk->ar->winrct.ymin + walk->center_mval[1]);
644   }
645
646   if (walk->state == WALK_CONFIRM) {
647     MEM_freeN(walk);
648     return OPERATOR_FINISHED;
649   }
650
651   MEM_freeN(walk);
652   return OPERATOR_CANCELLED;
653 }
654
655 static void walkEvent(bContext *C, WalkInfo *walk, const wmEvent *event)
656 {
657   if (event->type == TIMER && event->customdata == walk->timer) {
658     walk->redraw = true;
659   }
660   else if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
661
662 #ifdef USE_TABLET_SUPPORT
663     if (walk->is_cursor_first) {
664       /* wait until we get the 'warp' event */
665       if ((walk->center_mval[0] == event->mval[0]) && (walk->center_mval[1] == event->mval[1])) {
666         walk->is_cursor_first = false;
667       }
668       else {
669         /* note, its possible the system isn't giving us the warp event
670          * ideally we shouldn't have to worry about this, see: T45361 */
671         wmWindow *win = CTX_wm_window(C);
672         WM_cursor_warp(win,
673                        walk->ar->winrct.xmin + walk->center_mval[0],
674                        walk->ar->winrct.ymin + walk->center_mval[1]);
675       }
676       return;
677     }
678
679     if ((walk->is_cursor_absolute == false) && event->is_motion_absolute) {
680       walk->is_cursor_absolute = true;
681       copy_v2_v2_int(walk->prev_mval, event->mval);
682       copy_v2_v2_int(walk->center_mval, event->mval);
683       /* without this we can't turn 180d */
684       CLAMP_MIN(walk->mouse_speed, 4.0f);
685     }
686 #endif /* USE_TABLET_SUPPORT */
687
688     walk->moffset[0] += event->mval[0] - walk->prev_mval[0];
689     walk->moffset[1] += event->mval[1] - walk->prev_mval[1];
690
691     copy_v2_v2_int(walk->prev_mval, event->mval);
692
693     if ((walk->center_mval[0] != event->mval[0]) || (walk->center_mval[1] != event->mval[1])) {
694       walk->redraw = true;
695
696 #ifdef USE_TABLET_SUPPORT
697       if (walk->is_cursor_absolute) {
698         /* pass */
699       }
700       else
701 #endif
702           if (WM_event_is_last_mousemove(event)) {
703         wmWindow *win = CTX_wm_window(C);
704
705 #ifdef __APPLE__
706         if ((abs(walk->prev_mval[0] - walk->center_mval[0]) > walk->center_mval[0] / 2) ||
707             (abs(walk->prev_mval[1] - walk->center_mval[1]) > walk->center_mval[1] / 2))
708 #endif
709         {
710           WM_cursor_warp(win,
711                          walk->ar->winrct.xmin + walk->center_mval[0],
712                          walk->ar->winrct.ymin + walk->center_mval[1]);
713           copy_v2_v2_int(walk->prev_mval, walk->center_mval);
714         }
715       }
716     }
717   }
718 #ifdef WITH_INPUT_NDOF
719   else if (event->type == NDOF_MOTION) {
720     /* do these automagically get delivered? yes. */
721     // puts("ndof motion detected in walk mode!");
722     // static const char *tag_name = "3D mouse position";
723
724     const wmNDOFMotionData *incoming_ndof = event->customdata;
725     switch (incoming_ndof->progress) {
726       case P_STARTING:
727         /* start keeping track of 3D mouse position */
728 #  ifdef NDOF_WALK_DEBUG
729         puts("start keeping track of 3D mouse position");
730 #  endif
731         /* fall-through */
732       case P_IN_PROGRESS:
733         /* update 3D mouse position */
734 #  ifdef NDOF_WALK_DEBUG
735         putchar('.');
736         fflush(stdout);
737 #  endif
738         if (walk->ndof == NULL) {
739           // walk->ndof = MEM_mallocN(sizeof(wmNDOFMotionData), tag_name);
740           walk->ndof = MEM_dupallocN(incoming_ndof);
741           // walk->ndof = malloc(sizeof(wmNDOFMotionData));
742         }
743         else {
744           memcpy(walk->ndof, incoming_ndof, sizeof(wmNDOFMotionData));
745         }
746         break;
747       case P_FINISHING:
748         /* stop keeping track of 3D mouse position */
749 #  ifdef NDOF_WALK_DEBUG
750         puts("stop keeping track of 3D mouse position");
751 #  endif
752         if (walk->ndof) {
753           MEM_freeN(walk->ndof);
754           // free(walk->ndof);
755           walk->ndof = NULL;
756         }
757
758         /* update the time else the view will jump when 2D mouse/timer resume */
759         walk->time_lastdraw = PIL_check_seconds_timer();
760
761         break;
762       default:
763         break; /* should always be one of the above 3 */
764     }
765   }
766 #endif /* WITH_INPUT_NDOF */
767   /* handle modal keymap first */
768   else if (event->type == EVT_MODAL_MAP) {
769     switch (event->val) {
770       case WALK_MODAL_CANCEL:
771         walk->state = WALK_CANCEL;
772         break;
773       case WALK_MODAL_CONFIRM:
774         walk->state = WALK_CONFIRM;
775         break;
776
777       case WALK_MODAL_ACCELERATE:
778         base_speed *= 1.0f + (walk->is_slow ? 0.01f : 0.1f);
779         break;
780       case WALK_MODAL_DECELERATE:
781         base_speed /= 1.0f + (walk->is_slow ? 0.01f : 0.1f);
782         break;
783
784       /* implement WASD keys */
785       case WALK_MODAL_DIR_FORWARD:
786         walk->active_directions |= WALK_BIT_FORWARD;
787         break;
788       case WALK_MODAL_DIR_BACKWARD:
789         walk->active_directions |= WALK_BIT_BACKWARD;
790         break;
791       case WALK_MODAL_DIR_LEFT:
792         walk->active_directions |= WALK_BIT_LEFT;
793         break;
794       case WALK_MODAL_DIR_RIGHT:
795         walk->active_directions |= WALK_BIT_RIGHT;
796         break;
797       case WALK_MODAL_DIR_UP:
798         walk->active_directions |= WALK_BIT_UP;
799         break;
800       case WALK_MODAL_DIR_DOWN:
801         walk->active_directions |= WALK_BIT_DOWN;
802         break;
803
804       case WALK_MODAL_DIR_FORWARD_STOP:
805         walk->active_directions &= ~WALK_BIT_FORWARD;
806         break;
807       case WALK_MODAL_DIR_BACKWARD_STOP:
808         walk->active_directions &= ~WALK_BIT_BACKWARD;
809         break;
810       case WALK_MODAL_DIR_LEFT_STOP:
811         walk->active_directions &= ~WALK_BIT_LEFT;
812         break;
813       case WALK_MODAL_DIR_RIGHT_STOP:
814         walk->active_directions &= ~WALK_BIT_RIGHT;
815         break;
816       case WALK_MODAL_DIR_UP_STOP:
817         walk->active_directions &= ~WALK_BIT_UP;
818         break;
819       case WALK_MODAL_DIR_DOWN_STOP:
820         walk->active_directions &= ~WALK_BIT_DOWN;
821         break;
822
823       case WALK_MODAL_FAST_ENABLE:
824         walk->is_fast = true;
825         break;
826       case WALK_MODAL_FAST_DISABLE:
827         walk->is_fast = false;
828         break;
829       case WALK_MODAL_SLOW_ENABLE:
830         walk->is_slow = true;
831         break;
832       case WALK_MODAL_SLOW_DISABLE:
833         walk->is_slow = false;
834         break;
835
836 #define JUMP_SPEED_MIN 1.0f
837 #define JUMP_TIME_MAX 0.2f /* s */
838 #define JUMP_SPEED_MAX sqrtf(2.0f * walk->gravity * walk->jump_height)
839
840       case WALK_MODAL_JUMP_STOP:
841         if (walk->gravity_state == WALK_GRAVITY_STATE_JUMP) {
842           float t;
843
844           /* delta time */
845           t = (float)(PIL_check_seconds_timer() - walk->teleport.initial_time);
846
847           /* reduce the veolocity, if JUMP wasn't hold for long enough */
848           t = min_ff(t, JUMP_TIME_MAX);
849           walk->speed_jump = JUMP_SPEED_MIN +
850                              t * (JUMP_SPEED_MAX - JUMP_SPEED_MIN) / JUMP_TIME_MAX;
851
852           /* when jumping, duration is how long it takes before we start going down */
853           walk->teleport.duration = getVelocityZeroTime(walk->gravity, walk->speed_jump);
854
855           /* no more increase of jump speed */
856           walk->gravity_state = WALK_GRAVITY_STATE_ON;
857         }
858         break;
859       case WALK_MODAL_JUMP:
860         if ((walk->navigation_mode == WALK_MODE_GRAVITY) &&
861             (walk->gravity_state == WALK_GRAVITY_STATE_OFF) &&
862             (walk->teleport.state == WALK_TELEPORT_STATE_OFF)) {
863           /* no need to check for ground,
864            * walk->gravity wouldn't be off
865            * if we were over a hole */
866           walk->gravity_state = WALK_GRAVITY_STATE_JUMP;
867           walk->speed_jump = JUMP_SPEED_MAX;
868
869           walk->teleport.initial_time = PIL_check_seconds_timer();
870           copy_v3_v3(walk->teleport.origin, walk->rv3d->viewinv[3]);
871
872           /* using previous vec because WASD keys are not called when SPACE is */
873           copy_v2_v2(walk->teleport.direction, walk->dvec_prev);
874
875           /* when jumping, duration is how long it takes before we start going down */
876           walk->teleport.duration = getVelocityZeroTime(walk->gravity, walk->speed_jump);
877         }
878
879         break;
880
881       case WALK_MODAL_TELEPORT: {
882         float loc[3], nor[3];
883         float distance;
884         bool ret = walk_ray_cast(walk->rv3d, walk, loc, nor, &distance);
885
886         /* in case we are teleporting middle way from a jump */
887         walk->speed_jump = 0.0f;
888
889         if (ret) {
890           WalkTeleport *teleport = &walk->teleport;
891           teleport->state = WALK_TELEPORT_STATE_ON;
892           teleport->initial_time = PIL_check_seconds_timer();
893           teleport->duration = U.walk_navigation.teleport_time;
894
895           teleport->navigation_mode = walk->navigation_mode;
896           walk_navigation_mode_set(walk, WALK_MODE_FREE);
897
898           copy_v3_v3(teleport->origin, walk->rv3d->viewinv[3]);
899
900           /* stop the camera from a distance (camera height) */
901           normalize_v3_length(nor, walk->view_height);
902           add_v3_v3(loc, nor);
903
904           sub_v3_v3v3(teleport->direction, loc, teleport->origin);
905         }
906         else {
907           walk->teleport.state = WALK_TELEPORT_STATE_OFF;
908         }
909         break;
910       }
911
912 #undef JUMP_SPEED_MAX
913 #undef JUMP_TIME_MAX
914 #undef JUMP_SPEED_MIN
915
916       case WALK_MODAL_TOGGLE:
917         if (walk->navigation_mode == WALK_MODE_GRAVITY) {
918           walk_navigation_mode_set(walk, WALK_MODE_FREE);
919         }
920         else { /* WALK_MODE_FREE */
921           walk_navigation_mode_set(walk, WALK_MODE_GRAVITY);
922         }
923         break;
924     }
925   }
926 }
927
928 static void walkMoveCamera(bContext *C,
929                            WalkInfo *walk,
930                            const bool do_rotate,
931                            const bool do_translate,
932                            const bool is_confirm)
933 {
934   /* we only consider autokeying on playback or if user confirmed walk on the same frame
935    * otherwise we get a keyframe even if the user cancels. */
936   const bool use_autokey = is_confirm || walk->anim_playing;
937   ED_view3d_cameracontrol_update(
938       walk->v3d_camera_control, use_autokey, C, do_rotate, do_translate);
939   if (use_autokey) {
940     walk->need_rotation_keyframe = false;
941     walk->need_translation_keyframe = false;
942   }
943 }
944
945 static float getFreeFallDistance(const float gravity, const float time)
946 {
947   return gravity * (time * time) * 0.5f;
948 }
949
950 static float getVelocityZeroTime(const float gravity, const float velocity)
951 {
952   return velocity / gravity;
953 }
954
955 static int walkApply(bContext *C, WalkInfo *walk, bool is_confirm)
956 {
957 #define WALK_ROTATE_FAC 2.2f /* more is faster */
958 #define WALK_TOP_LIMIT DEG2RADF(85.0f)
959 #define WALK_BOTTOM_LIMIT DEG2RADF(-80.0f)
960 #define WALK_MOVE_SPEED base_speed
961 #define WALK_BOOST_FACTOR ((void)0, walk->speed_factor)
962
963   /* walk mode - Ctrl+Shift+F
964    * a walk loop where the user can move move the view as if they are in a walk game
965    */
966   RegionView3D *rv3d = walk->rv3d;
967   ARegion *ar = walk->ar;
968
969   /* 3x3 copy of the view matrix so we can move along the view axis */
970   float mat[3][3];
971   /* this is the direction that's added to the view offset per redraw */
972   float dvec[3] = {0.0f, 0.0f, 0.0f};
973
974   int moffset[2];    /* mouse offset from the views center */
975   float tmp_quat[4]; /* used for rotating the view */
976
977 #ifdef NDOF_WALK_DEBUG
978   {
979     static uint iteration = 1;
980     printf("walk timer %d\n", iteration++);
981   }
982 #endif
983
984   {
985     /* mouse offset from the center */
986     copy_v2_v2_int(moffset, walk->moffset);
987
988     /* apply moffset so we can re-accumulate */
989     walk->moffset[0] = 0;
990     walk->moffset[1] = 0;
991
992     /* revert mouse */
993     if (walk->is_reversed) {
994       moffset[1] = -moffset[1];
995     }
996
997     /* Should we redraw? */
998     if ((walk->active_directions) || moffset[0] || moffset[1] ||
999         walk->teleport.state == WALK_TELEPORT_STATE_ON ||
1000         walk->gravity_state != WALK_GRAVITY_STATE_OFF || is_confirm) {
1001       float dvec_tmp[3];
1002
1003       /* time how fast it takes for us to redraw,
1004        * this is so simple scenes don't walk too fast */
1005       double time_current;
1006       float time_redraw;
1007 #ifdef NDOF_WALK_DRAW_TOOMUCH
1008       walk->redraw = 1;
1009 #endif
1010       time_current = PIL_check_seconds_timer();
1011       time_redraw = (float)(time_current - walk->time_lastdraw);
1012
1013       walk->time_lastdraw = time_current;
1014
1015       /* base speed in m/s */
1016       walk->speed = WALK_MOVE_SPEED;
1017
1018       if (walk->is_fast) {
1019         walk->speed *= WALK_BOOST_FACTOR;
1020       }
1021       else if (walk->is_slow) {
1022         walk->speed *= 1.0f / WALK_BOOST_FACTOR;
1023       }
1024
1025       copy_m3_m4(mat, rv3d->viewinv);
1026
1027       {
1028         /* rotate about the X axis- look up/down */
1029         if (moffset[1]) {
1030           float upvec[3];
1031           float angle;
1032           float y;
1033
1034           /* relative offset */
1035           y = (float)moffset[1] / ar->winy;
1036
1037           /* speed factor */
1038           y *= WALK_ROTATE_FAC;
1039
1040           /* user adjustment factor */
1041           y *= walk->mouse_speed;
1042
1043           /* clamp the angle limits */
1044           /* it ranges from 90.0f to -90.0f */
1045           angle = -asinf(rv3d->viewmat[2][2]);
1046
1047           if (angle > WALK_TOP_LIMIT && y > 0.0f) {
1048             y = 0.0f;
1049           }
1050           else if (angle < WALK_BOTTOM_LIMIT && y < 0.0f) {
1051             y = 0.0f;
1052           }
1053
1054           copy_v3_fl3(upvec, 1.0f, 0.0f, 0.0f);
1055           mul_m3_v3(mat, upvec);
1056           /* Rotate about the relative up vec */
1057           axis_angle_to_quat(tmp_quat, upvec, -y);
1058           mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, tmp_quat);
1059         }
1060
1061         /* rotate about the Y axis- look left/right */
1062         if (moffset[0]) {
1063           float upvec[3];
1064           float x;
1065
1066           /* if we're upside down invert the moffset */
1067           copy_v3_fl3(upvec, 0.0f, 1.0f, 0.0f);
1068           mul_m3_v3(mat, upvec);
1069
1070           if (upvec[2] < 0.0f) {
1071             moffset[0] = -moffset[0];
1072           }
1073
1074           /* relative offset */
1075           x = (float)moffset[0] / ar->winx;
1076
1077           /* speed factor */
1078           x *= WALK_ROTATE_FAC;
1079
1080           /* user adjustment factor */
1081           x *= walk->mouse_speed;
1082
1083           /* Rotate about the relative up vec */
1084           axis_angle_to_quat_single(tmp_quat, 'Z', x);
1085           mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, tmp_quat);
1086         }
1087       }
1088
1089       /* WASD - 'move' translation code */
1090       if ((walk->active_directions) && (walk->gravity_state == WALK_GRAVITY_STATE_OFF)) {
1091
1092         short direction;
1093         zero_v3(dvec);
1094
1095         if ((walk->active_directions & WALK_BIT_FORWARD) ||
1096             (walk->active_directions & WALK_BIT_BACKWARD)) {
1097
1098           direction = 0;
1099
1100           if ((walk->active_directions & WALK_BIT_FORWARD)) {
1101             direction += 1;
1102           }
1103
1104           if ((walk->active_directions & WALK_BIT_BACKWARD)) {
1105             direction -= 1;
1106           }
1107
1108           copy_v3_fl3(dvec_tmp, 0.0f, 0.0f, direction);
1109           mul_m3_v3(mat, dvec_tmp);
1110
1111           if (walk->navigation_mode == WALK_MODE_GRAVITY) {
1112             dvec_tmp[2] = 0.0f;
1113           }
1114
1115           normalize_v3(dvec_tmp);
1116           add_v3_v3(dvec, dvec_tmp);
1117         }
1118
1119         if ((walk->active_directions & WALK_BIT_LEFT) ||
1120             (walk->active_directions & WALK_BIT_RIGHT)) {
1121
1122           direction = 0;
1123
1124           if ((walk->active_directions & WALK_BIT_LEFT)) {
1125             direction += 1;
1126           }
1127
1128           if ((walk->active_directions & WALK_BIT_RIGHT)) {
1129             direction -= 1;
1130           }
1131
1132           dvec_tmp[0] = direction * rv3d->viewinv[0][0];
1133           dvec_tmp[1] = direction * rv3d->viewinv[0][1];
1134           dvec_tmp[2] = 0.0f;
1135
1136           normalize_v3(dvec_tmp);
1137           add_v3_v3(dvec, dvec_tmp);
1138         }
1139
1140         if ((walk->active_directions & WALK_BIT_UP) || (walk->active_directions & WALK_BIT_DOWN)) {
1141
1142           if (walk->navigation_mode == WALK_MODE_FREE) {
1143
1144             direction = 0;
1145
1146             if ((walk->active_directions & WALK_BIT_UP)) {
1147               direction -= 1;
1148             }
1149
1150             if ((walk->active_directions & WALK_BIT_DOWN)) {
1151               direction = 1;
1152             }
1153
1154             copy_v3_fl3(dvec_tmp, 0.0f, 0.0f, direction);
1155             add_v3_v3(dvec, dvec_tmp);
1156           }
1157         }
1158
1159         /* apply movement */
1160         mul_v3_fl(dvec, walk->speed * time_redraw);
1161       }
1162
1163       /* stick to the floor */
1164       if (walk->navigation_mode == WALK_MODE_GRAVITY &&
1165           ELEM(walk->gravity_state, WALK_GRAVITY_STATE_OFF, WALK_GRAVITY_STATE_START)) {
1166
1167         bool ret;
1168         float ray_distance;
1169         float difference = -100.0f;
1170         float fall_distance;
1171
1172         ret = walk_floor_distance_get(rv3d, walk, dvec, &ray_distance);
1173
1174         if (ret) {
1175           difference = walk->view_height - ray_distance;
1176         }
1177
1178         /* the distance we would fall naturally smoothly enough that we
1179          * can manually drop the object without activating gravity */
1180         fall_distance = time_redraw * walk->speed * WALK_BOOST_FACTOR;
1181
1182         if (fabsf(difference) < fall_distance) {
1183           /* slope/stairs */
1184           dvec[2] -= difference;
1185
1186           /* in case we switched from FREE to GRAVITY too close to the ground */
1187           if (walk->gravity_state == WALK_GRAVITY_STATE_START) {
1188             walk->gravity_state = WALK_GRAVITY_STATE_OFF;
1189           }
1190         }
1191         else {
1192           /* hijack the teleport variables */
1193           walk->teleport.initial_time = PIL_check_seconds_timer();
1194           walk->gravity_state = WALK_GRAVITY_STATE_ON;
1195           walk->teleport.duration = 0.0f;
1196
1197           copy_v3_v3(walk->teleport.origin, walk->rv3d->viewinv[3]);
1198           copy_v2_v2(walk->teleport.direction, dvec);
1199         }
1200       }
1201
1202       /* Falling or jumping) */
1203       if (ELEM(walk->gravity_state, WALK_GRAVITY_STATE_ON, WALK_GRAVITY_STATE_JUMP)) {
1204         float t;
1205         float z_cur, z_new;
1206         bool ret;
1207         float ray_distance, difference = -100.0f;
1208
1209         /* delta time */
1210         t = (float)(PIL_check_seconds_timer() - walk->teleport.initial_time);
1211
1212         /* keep moving if we were moving */
1213         copy_v2_v2(dvec, walk->teleport.direction);
1214
1215         z_cur = walk->rv3d->viewinv[3][2];
1216         z_new = walk->teleport.origin[2] - getFreeFallDistance(walk->gravity, t) * walk->grid;
1217
1218         /* jump */
1219         z_new += t * walk->speed_jump * walk->grid;
1220
1221         /* duration is the jump duration */
1222         if (t > walk->teleport.duration) {
1223
1224           /* check to see if we are landing */
1225           ret = walk_floor_distance_get(rv3d, walk, dvec, &ray_distance);
1226
1227           if (ret) {
1228             difference = walk->view_height - ray_distance;
1229           }
1230
1231           if (difference > 0.0f) {
1232             /* quit falling, lands at "view_height" from the floor */
1233             dvec[2] -= difference;
1234             walk->gravity_state = WALK_GRAVITY_STATE_OFF;
1235             walk->speed_jump = 0.0f;
1236           }
1237           else {
1238             /* keep falling */
1239             dvec[2] = z_cur - z_new;
1240           }
1241         }
1242         else {
1243           /* keep going up (jump) */
1244           dvec[2] = z_cur - z_new;
1245         }
1246       }
1247
1248       /* Teleport */
1249       else if (walk->teleport.state == WALK_TELEPORT_STATE_ON) {
1250         float t; /* factor */
1251         float new_loc[3];
1252         float cur_loc[3];
1253
1254         /* linear interpolation */
1255         t = (float)(PIL_check_seconds_timer() - walk->teleport.initial_time);
1256         t /= walk->teleport.duration;
1257
1258         /* clamp so we don't go past our limit */
1259         if (t >= 1.0f) {
1260           t = 1.0f;
1261           walk->teleport.state = WALK_TELEPORT_STATE_OFF;
1262           walk_navigation_mode_set(walk, walk->teleport.navigation_mode);
1263         }
1264
1265         mul_v3_v3fl(new_loc, walk->teleport.direction, t);
1266         add_v3_v3(new_loc, walk->teleport.origin);
1267
1268         copy_v3_v3(cur_loc, walk->rv3d->viewinv[3]);
1269         sub_v3_v3v3(dvec, cur_loc, new_loc);
1270       }
1271
1272       if (rv3d->persp == RV3D_CAMOB) {
1273         Object *lock_ob = ED_view3d_cameracontrol_object_get(walk->v3d_camera_control);
1274         if (lock_ob->protectflag & OB_LOCK_LOCX) {
1275           dvec[0] = 0.0f;
1276         }
1277         if (lock_ob->protectflag & OB_LOCK_LOCY) {
1278           dvec[1] = 0.0f;
1279         }
1280         if (lock_ob->protectflag & OB_LOCK_LOCZ) {
1281           dvec[2] = 0.0f;
1282         }
1283       }
1284
1285       /* scale the movement to the scene size */
1286       mul_v3_v3fl(dvec_tmp, dvec, walk->grid);
1287       add_v3_v3(rv3d->ofs, dvec_tmp);
1288
1289       if (rv3d->persp == RV3D_CAMOB) {
1290         walk->need_rotation_keyframe |= (moffset[0] || moffset[1]);
1291         walk->need_translation_keyframe |= (len_squared_v3(dvec_tmp) > FLT_EPSILON);
1292         walkMoveCamera(
1293             C, walk, walk->need_rotation_keyframe, walk->need_translation_keyframe, is_confirm);
1294       }
1295     }
1296     else {
1297       /* we're not redrawing but we need to update the time else the view will jump */
1298       walk->time_lastdraw = PIL_check_seconds_timer();
1299     }
1300     /* end drawing */
1301     copy_v3_v3(walk->dvec_prev, dvec);
1302   }
1303
1304   return OPERATOR_FINISHED;
1305 #undef WALK_ROTATE_FAC
1306 #undef WALK_ZUP_CORRECT_FAC
1307 #undef WALK_ZUP_CORRECT_ACCEL
1308 #undef WALK_SMOOTH_FAC
1309 #undef WALK_TOP_LIMIT
1310 #undef WALK_BOTTOM_LIMIT
1311 #undef WALK_MOVE_SPEED
1312 #undef WALK_BOOST_FACTOR
1313 }
1314
1315 #ifdef WITH_INPUT_NDOF
1316 static void walkApply_ndof(bContext *C, WalkInfo *walk, bool is_confirm)
1317 {
1318   Object *lock_ob = ED_view3d_cameracontrol_object_get(walk->v3d_camera_control);
1319   bool has_translate, has_rotate;
1320
1321   view3d_ndof_fly(walk->ndof,
1322                   walk->v3d,
1323                   walk->rv3d,
1324                   walk->is_slow,
1325                   lock_ob ? lock_ob->protectflag : 0,
1326                   &has_translate,
1327                   &has_rotate);
1328
1329   if (has_translate || has_rotate) {
1330     walk->redraw = true;
1331
1332     if (walk->rv3d->persp == RV3D_CAMOB) {
1333       walk->need_rotation_keyframe |= has_rotate;
1334       walk->need_translation_keyframe |= has_translate;
1335       walkMoveCamera(
1336           C, walk, walk->need_rotation_keyframe, walk->need_translation_keyframe, is_confirm);
1337     }
1338   }
1339 }
1340 #endif /* WITH_INPUT_NDOF */
1341
1342 /****** walk operator ******/
1343 static int walk_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1344 {
1345   RegionView3D *rv3d = CTX_wm_region_view3d(C);
1346   WalkInfo *walk;
1347
1348   if (rv3d->viewlock & RV3D_LOCKED) {
1349     return OPERATOR_CANCELLED;
1350   }
1351
1352   walk = MEM_callocN(sizeof(WalkInfo), "NavigationWalkOperation");
1353
1354   op->customdata = walk;
1355
1356   if (initWalkInfo(C, walk, op) == false) {
1357     MEM_freeN(op->customdata);
1358     return OPERATOR_CANCELLED;
1359   }
1360
1361   walkEvent(C, walk, event);
1362
1363   WM_event_add_modal_handler(C, op);
1364
1365   return OPERATOR_RUNNING_MODAL;
1366 }
1367
1368 static void walk_cancel(bContext *C, wmOperator *op)
1369 {
1370   WalkInfo *walk = op->customdata;
1371
1372   walk->state = WALK_CANCEL;
1373   walkEnd(C, walk);
1374   op->customdata = NULL;
1375 }
1376
1377 static int walk_modal(bContext *C, wmOperator *op, const wmEvent *event)
1378 {
1379   int exit_code;
1380   bool do_draw = false;
1381   WalkInfo *walk = op->customdata;
1382   RegionView3D *rv3d = walk->rv3d;
1383   Object *walk_object = ED_view3d_cameracontrol_object_get(walk->v3d_camera_control);
1384
1385   walk->redraw = false;
1386
1387   walkEvent(C, walk, event);
1388
1389 #ifdef WITH_INPUT_NDOF
1390   if (walk->ndof) { /* 3D mouse overrules [2D mouse + timer] */
1391     if (event->type == NDOF_MOTION) {
1392       walkApply_ndof(C, walk, false);
1393     }
1394   }
1395   else
1396 #endif /* WITH_INPUT_NDOF */
1397       if (event->type == TIMER && event->customdata == walk->timer) {
1398     walkApply(C, walk, false);
1399   }
1400
1401   do_draw |= walk->redraw;
1402
1403   exit_code = walkEnd(C, walk);
1404
1405   if (exit_code != OPERATOR_RUNNING_MODAL) {
1406     do_draw = true;
1407   }
1408
1409   if (do_draw) {
1410     if (rv3d->persp == RV3D_CAMOB) {
1411       WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, walk_object);
1412     }
1413
1414     // too frequent, commented with NDOF_WALK_DRAW_TOOMUCH for now
1415     // puts("redraw!");
1416     ED_region_tag_redraw(CTX_wm_region(C));
1417   }
1418   return exit_code;
1419 }
1420
1421 void VIEW3D_OT_walk(wmOperatorType *ot)
1422 {
1423   /* identifiers */
1424   ot->name = "Walk Navigation";
1425   ot->description = "Interactively walk around the scene";
1426   ot->idname = "VIEW3D_OT_walk";
1427
1428   /* api callbacks */
1429   ot->invoke = walk_invoke;
1430   ot->cancel = walk_cancel;
1431   ot->modal = walk_modal;
1432   ot->poll = ED_operator_region_view3d_active;
1433
1434   /* flags */
1435   ot->flag = OPTYPE_BLOCKING;
1436 }