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