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