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