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