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