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