2 * ***** BEGIN GPL LICENSE BLOCK *****
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.
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.
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.
18 * Contributor(s): Dalai Felinto, Campbell Barton
20 * ***** END GPL LICENSE BLOCK *****
23 /** \file blender/editors/space_view3d/view3d_walk.c
27 /* defines VIEW3D_OT_navigate - walk modal operator */
29 #include "DNA_scene_types.h"
30 #include "DNA_object_types.h"
32 #include "MEM_guardedalloc.h"
35 #include "BLI_blenlib.h"
36 #include "BLI_kdopbvh.h"
37 #include "BLI_utildefines.h"
39 #include "BKE_context.h"
40 #include "BKE_report.h"
42 #include "BLT_translation.h"
50 #include "ED_screen.h"
51 #include "ED_space_api.h"
52 #include "ED_transform.h"
53 #include "ED_transform_snap_object_context.h"
55 #include "PIL_time.h" /* smoothview */
57 #include "UI_interface.h"
58 #include "UI_resources.h"
60 #include "view3d_intern.h" /* own include */
62 //#define NDOF_WALK_DEBUG
63 //#define NDOF_WALK_DRAW_TOOMUCH /* is this needed for ndof? - commented so redraw doesnt thrash - campbell */
65 #define USE_TABLET_SUPPORT
67 /* ensure the target position is one we can reach, see: T45771 */
68 #define USE_PIXELSIZE_NATIVE_SUPPORT
71 static float getVelocityZeroTime(const float gravity, const float velocity);
73 /* NOTE: these defines are saved in keymap files, do not change values but just add new ones */
75 WALK_MODAL_CANCEL = 1,
77 WALK_MODAL_DIR_FORWARD,
78 WALK_MODAL_DIR_FORWARD_STOP,
79 WALK_MODAL_DIR_BACKWARD,
80 WALK_MODAL_DIR_BACKWARD_STOP,
82 WALK_MODAL_DIR_LEFT_STOP,
84 WALK_MODAL_DIR_RIGHT_STOP,
86 WALK_MODAL_DIR_UP_STOP,
88 WALK_MODAL_DIR_DOWN_STOP,
89 WALK_MODAL_FAST_ENABLE,
90 WALK_MODAL_FAST_DISABLE,
91 WALK_MODAL_SLOW_ENABLE,
92 WALK_MODAL_SLOW_DISABLE,
97 WALK_MODAL_ACCELERATE,
98 WALK_MODAL_DECELERATE,
102 WALK_BIT_FORWARD = 1 << 0,
103 WALK_BIT_BACKWARD = 1 << 1,
104 WALK_BIT_LEFT = 1 << 2,
105 WALK_BIT_RIGHT = 1 << 3,
106 WALK_BIT_UP = 1 << 4,
107 WALK_BIT_DOWN = 1 << 5,
110 typedef enum eWalkTeleportState {
111 WALK_TELEPORT_STATE_OFF = 0,
112 WALK_TELEPORT_STATE_ON,
113 } eWalkTeleportState;
115 typedef enum eWalkMethod {
120 typedef enum eWalkGravityState {
121 WALK_GRAVITY_STATE_OFF = 0,
122 WALK_GRAVITY_STATE_JUMP,
123 WALK_GRAVITY_STATE_START,
124 WALK_GRAVITY_STATE_ON,
127 /* called in transform_ops.c, on each regeneration of keymaps */
128 void walk_modal_keymap(wmKeyConfig *keyconf)
130 static EnumPropertyItem modal_items[] = {
131 {WALK_MODAL_CANCEL, "CANCEL", 0, "Cancel", ""},
132 {WALK_MODAL_CONFIRM, "CONFIRM", 0, "Confirm", ""},
134 {WALK_MODAL_ACCELERATE, "ACCELERATE", 0, "Accelerate", ""},
135 {WALK_MODAL_DECELERATE, "DECELERATE", 0, "Decelerate", ""},
137 {WALK_MODAL_DIR_FORWARD, "FORWARD", 0, "Move Forward", ""},
138 {WALK_MODAL_DIR_BACKWARD, "BACKWARD", 0, "Move Backward", ""},
139 {WALK_MODAL_DIR_LEFT, "LEFT", 0, "Move Left (Strafe)", ""},
140 {WALK_MODAL_DIR_RIGHT, "RIGHT", 0, "Move Right (Strafe)", ""},
141 {WALK_MODAL_DIR_UP, "UP", 0, "Move Up", ""},
142 {WALK_MODAL_DIR_DOWN, "DOWN", 0, "Move Down", ""},
144 {WALK_MODAL_DIR_FORWARD_STOP, "FORWARD_STOP", 0, "Stop Move Forward", ""},
145 {WALK_MODAL_DIR_BACKWARD_STOP, "BACKWARD_STOP", 0, "Stop Mode Backward", ""},
146 {WALK_MODAL_DIR_LEFT_STOP, "LEFT_STOP", 0, "Stop Move Left", ""},
147 {WALK_MODAL_DIR_RIGHT_STOP, "RIGHT_STOP", 0, "Stop Mode Right", ""},
148 {WALK_MODAL_DIR_UP_STOP, "UP_STOP", 0, "Stop Move Up", ""},
149 {WALK_MODAL_DIR_DOWN_STOP, "DOWN_STOP", 0, "Stop Mode Down", ""},
151 {WALK_MODAL_TELEPORT, "TELEPORT", 0, "Teleport", "Move forward a few units at once"},
153 {WALK_MODAL_FAST_ENABLE, "FAST_ENABLE", 0, "Fast Enable", "Move faster (walk or fly)"},
154 {WALK_MODAL_FAST_DISABLE, "FAST_DISABLE", 0, "Fast Disable", "Resume regular speed"},
156 {WALK_MODAL_SLOW_ENABLE, "SLOW_ENABLE", 0, "Slow Enable", "Move slower (walk or fly)"},
157 {WALK_MODAL_SLOW_DISABLE, "SLOW_DISABLE", 0, "Slow Disable", "Resume regular speed"},
159 {WALK_MODAL_JUMP, "JUMP", 0, "Jump", "Jump when in walk mode"},
160 {WALK_MODAL_JUMP_STOP, "JUMP_STOP", 0, "Jump Stop", "Stop pushing jump"},
162 {WALK_MODAL_TOGGLE, "GRAVITY_TOGGLE", 0, "Toggle Gravity", "Toggle gravity effect"},
164 {0, NULL, 0, NULL, NULL}};
166 wmKeyMap *keymap = WM_modalkeymap_get(keyconf, "View3D Walk Modal");
168 /* this function is called for each spacetype, only needs to add map once */
169 if (keymap && keymap->modal_items)
172 keymap = WM_modalkeymap_add(keyconf, "View3D Walk Modal", modal_items);
174 /* items for modal map */
175 WM_modalkeymap_add_item(keymap, ESCKEY, KM_PRESS, KM_ANY, 0, WALK_MODAL_CANCEL);
176 WM_modalkeymap_add_item(keymap, RIGHTMOUSE, KM_ANY, KM_ANY, 0, WALK_MODAL_CANCEL);
178 WM_modalkeymap_add_item(keymap, LEFTMOUSE, KM_ANY, KM_ANY, 0, WALK_MODAL_CONFIRM);
179 WM_modalkeymap_add_item(keymap, RETKEY, KM_PRESS, KM_ANY, 0, WALK_MODAL_CONFIRM);
180 WM_modalkeymap_add_item(keymap, PADENTER, KM_PRESS, KM_ANY, 0, WALK_MODAL_CONFIRM);
182 WM_modalkeymap_add_item(keymap, LEFTSHIFTKEY, KM_PRESS, KM_ANY, 0, WALK_MODAL_FAST_ENABLE);
183 WM_modalkeymap_add_item(keymap, LEFTSHIFTKEY, KM_RELEASE, KM_ANY, 0, WALK_MODAL_FAST_DISABLE);
185 WM_modalkeymap_add_item(keymap, LEFTALTKEY, KM_PRESS, KM_ANY, 0, WALK_MODAL_SLOW_ENABLE);
186 WM_modalkeymap_add_item(keymap, LEFTALTKEY, KM_RELEASE, KM_ANY, 0, WALK_MODAL_SLOW_DISABLE);
189 WM_modalkeymap_add_item(keymap, WKEY, KM_PRESS, KM_ANY, 0, WALK_MODAL_DIR_FORWARD);
190 WM_modalkeymap_add_item(keymap, SKEY, KM_PRESS, KM_ANY, 0, WALK_MODAL_DIR_BACKWARD);
191 WM_modalkeymap_add_item(keymap, AKEY, KM_PRESS, KM_ANY, 0, WALK_MODAL_DIR_LEFT);
192 WM_modalkeymap_add_item(keymap, DKEY, KM_PRESS, KM_ANY, 0, WALK_MODAL_DIR_RIGHT);
193 WM_modalkeymap_add_item(keymap, EKEY, KM_PRESS, KM_ANY, 0, WALK_MODAL_DIR_UP);
194 WM_modalkeymap_add_item(keymap, QKEY, KM_PRESS, KM_ANY, 0, WALK_MODAL_DIR_DOWN);
196 WM_modalkeymap_add_item(keymap, WKEY, KM_RELEASE, KM_ANY, 0, WALK_MODAL_DIR_FORWARD_STOP);
197 WM_modalkeymap_add_item(keymap, SKEY, KM_RELEASE, KM_ANY, 0, WALK_MODAL_DIR_BACKWARD_STOP);
198 WM_modalkeymap_add_item(keymap, AKEY, KM_RELEASE, KM_ANY, 0, WALK_MODAL_DIR_LEFT_STOP);
199 WM_modalkeymap_add_item(keymap, DKEY, KM_RELEASE, KM_ANY, 0, WALK_MODAL_DIR_RIGHT_STOP);
200 WM_modalkeymap_add_item(keymap, EKEY, KM_RELEASE, KM_ANY, 0, WALK_MODAL_DIR_UP_STOP);
201 WM_modalkeymap_add_item(keymap, QKEY, KM_RELEASE, KM_ANY, 0, WALK_MODAL_DIR_DOWN_STOP);
203 WM_modalkeymap_add_item(keymap, UPARROWKEY, KM_PRESS, 0, 0, WALK_MODAL_DIR_FORWARD);
204 WM_modalkeymap_add_item(keymap, DOWNARROWKEY, KM_PRESS, 0, 0, WALK_MODAL_DIR_BACKWARD);
205 WM_modalkeymap_add_item(keymap, LEFTARROWKEY, KM_PRESS, 0, 0, WALK_MODAL_DIR_LEFT);
206 WM_modalkeymap_add_item(keymap, RIGHTARROWKEY, KM_PRESS, 0, 0, WALK_MODAL_DIR_RIGHT);
208 WM_modalkeymap_add_item(keymap, UPARROWKEY, KM_RELEASE, KM_ANY, 0, WALK_MODAL_DIR_FORWARD_STOP);
209 WM_modalkeymap_add_item(keymap, DOWNARROWKEY, KM_RELEASE, KM_ANY, 0, WALK_MODAL_DIR_BACKWARD_STOP);
210 WM_modalkeymap_add_item(keymap, LEFTARROWKEY, KM_RELEASE, KM_ANY, 0, WALK_MODAL_DIR_LEFT_STOP);
211 WM_modalkeymap_add_item(keymap, RIGHTARROWKEY, KM_RELEASE, KM_ANY, 0, WALK_MODAL_DIR_RIGHT_STOP);
213 WM_modalkeymap_add_item(keymap, TABKEY, KM_PRESS, 0, 0, WALK_MODAL_TOGGLE);
214 WM_modalkeymap_add_item(keymap, GKEY, KM_PRESS, 0, 0, WALK_MODAL_TOGGLE);
216 WM_modalkeymap_add_item(keymap, VKEY, KM_PRESS, KM_ANY, 0, WALK_MODAL_JUMP);
217 WM_modalkeymap_add_item(keymap, VKEY, KM_RELEASE, KM_ANY, 0, WALK_MODAL_JUMP_STOP);
219 WM_modalkeymap_add_item(keymap, SPACEKEY, KM_PRESS, KM_ANY, 0, WALK_MODAL_TELEPORT);
220 WM_modalkeymap_add_item(keymap, MIDDLEMOUSE, KM_ANY, KM_ANY, 0, WALK_MODAL_TELEPORT);
222 WM_modalkeymap_add_item(keymap, PADPLUSKEY, KM_PRESS, KM_ANY, 0, WALK_MODAL_ACCELERATE);
223 WM_modalkeymap_add_item(keymap, PADMINUS, KM_PRESS, KM_ANY, 0, WALK_MODAL_DECELERATE);
224 WM_modalkeymap_add_item(keymap, WHEELUPMOUSE, KM_PRESS, KM_ANY, 0, WALK_MODAL_ACCELERATE);
225 WM_modalkeymap_add_item(keymap, WHEELDOWNMOUSE, KM_PRESS, KM_ANY, 0, WALK_MODAL_DECELERATE);
227 /* assign map to operators */
228 WM_modalkeymap_assign(keymap, "VIEW3D_OT_walk");
232 typedef struct WalkTeleport {
233 eWalkTeleportState state;
234 float duration; /* from user preferences */
238 eWalkMethod navigation_mode; /* teleport always set FREE mode on */
242 typedef struct WalkInfo {
249 wmTimer *timer; /* needed for redraws */
254 int prev_mval[2]; /* previous 2D mouse values */
255 int center_mval[2]; /* center mouse values */
257 wmNDOFMotionData *ndof; /* latest 3D mouse values */
259 /* walk state state */
260 float base_speed; /* the base speed without run/slow down modifications */
261 float speed; /* the speed the view is moving per redraw */
262 float grid; /* world scale 1.0 default */
264 /* compare between last state */
265 double time_lastdraw; /* time between draws */
267 void *draw_handle_pixel;
269 /* use for some lag */
270 float dvec_prev[3]; /* old for some lag */
273 eWalkMethod navigation_mode;
276 WalkTeleport teleport;
278 /* look speed factor - user preferences */
281 /* speed adjustments */
288 #ifdef USE_TABLET_SUPPORT
289 /* check if we had a cursor event before */
290 bool is_cursor_first;
292 /* tablet devices (we can't relocate the cursor) */
293 bool is_cursor_absolute;
297 eWalkGravityState gravity_state;
300 /* height to use in walk mode */
303 /* counting system to allow movement to continue if a direction (WASD) key is still pressed */
304 int active_directions;
307 float jump_height; /* maximum jump height */
308 float speed_factor; /* to use for fast/slow speeds */
310 struct SnapObjectContext *snap_context;
312 struct View3DCameraControl *v3d_camera_control;
316 static void drawWalkPixel(const struct bContext *UNUSED(C), ARegion *ar, void *arg)
318 /* draws an aim/cross in the center */
319 WalkInfo *walk = arg;
321 const int outter_length = 24;
322 const int inner_length = 14;
326 if (walk->scene->camera) {
327 ED_view3d_calc_camera_border(walk->scene, ar, walk->v3d, walk->rv3d, &viewborder, false);
328 xoff = viewborder.xmin + BLI_rctf_size_x(&viewborder) * 0.5f;
329 yoff = viewborder.ymin + BLI_rctf_size_y(&viewborder) * 0.5f;
332 xoff = walk->ar->winx / 2;
333 yoff = walk->ar->winy / 2;
336 UI_ThemeColor(TH_VIEW_OVERLAY);
339 glVertex2i(xoff, yoff + inner_length);
340 glVertex2i(xoff, yoff + outter_length);
343 glVertex2i(xoff + inner_length, yoff);
344 glVertex2i(xoff + outter_length, yoff);
347 glVertex2i(xoff, yoff - inner_length);
348 glVertex2i(xoff, yoff - outter_length);
351 glVertex2i(xoff - inner_length, yoff);
352 glVertex2i(xoff - outter_length, yoff);
356 static void walk_update_header(bContext *C, wmOperator *op, WalkInfo *walk)
358 const bool gravity = (walk->navigation_mode == WALK_MODE_GRAVITY) ||
359 ((walk->teleport.state == WALK_TELEPORT_STATE_ON) &&
360 (walk->teleport.navigation_mode == WALK_MODE_GRAVITY));
361 char header[UI_MAX_DRAW_STR];
362 char buf[UI_MAX_DRAW_STR];
365 int available_len = sizeof(buf);
367 #define WM_MODALKEY(_id) \
368 WM_modalkeymap_operator_items_to_string_buf(op->type, (_id), true, UI_MAX_SHORTCUT_STR, &available_len, &p)
370 BLI_snprintf(header, sizeof(header), IFACE_("%s: confirm, %s: cancel, "
372 "%s|%s|%s|%s: move around, "
373 "%s: fast, %s: slow, "
374 "%s|%s: up and down, "
375 "%s: teleport, %s: jump, "
376 "%s: increase speed, %s: decrease speed"),
377 WM_MODALKEY(WALK_MODAL_CONFIRM), WM_MODALKEY(WALK_MODAL_CANCEL),
378 WM_MODALKEY(WALK_MODAL_TOGGLE), WM_bool_as_string(gravity),
379 WM_MODALKEY(WALK_MODAL_DIR_FORWARD), WM_MODALKEY(WALK_MODAL_DIR_LEFT),
380 WM_MODALKEY(WALK_MODAL_DIR_BACKWARD), WM_MODALKEY(WALK_MODAL_DIR_RIGHT),
381 WM_MODALKEY(WALK_MODAL_FAST_ENABLE), WM_MODALKEY(WALK_MODAL_SLOW_ENABLE),
382 WM_MODALKEY(WALK_MODAL_DIR_UP), WM_MODALKEY(WALK_MODAL_DIR_DOWN),
383 WM_MODALKEY(WALK_MODAL_TELEPORT), WM_MODALKEY(WALK_MODAL_JUMP),
384 WM_MODALKEY(WALK_MODAL_ACCELERATE), WM_MODALKEY(WALK_MODAL_DECELERATE));
388 ED_area_headerprint(CTX_wm_area(C), header);
391 static void walk_navigation_mode_set(bContext *C, wmOperator *op, WalkInfo *walk, eWalkMethod mode)
393 if (mode == WALK_MODE_FREE) {
394 walk->navigation_mode = WALK_MODE_FREE;
395 walk->gravity_state = WALK_GRAVITY_STATE_OFF;
397 else { /* WALK_MODE_GRAVITY */
398 walk->navigation_mode = WALK_MODE_GRAVITY;
399 walk->gravity_state = WALK_GRAVITY_STATE_START;
402 walk_update_header(C, op, walk);
406 * \param r_distance Distance to the hit point
408 static bool walk_floor_distance_get(
409 RegionView3D *rv3d, WalkInfo *walk, const float dvec[3],
412 float ray_normal[3] = {0, 0, -1}; /* down */
415 float r_normal_dummy[3];
419 *r_distance = BVH_RAYCAST_DIST_MAX;
421 copy_v3_v3(ray_start, rv3d->viewinv[3]);
423 mul_v3_v3fl(dvec_tmp, dvec, walk->grid);
424 add_v3_v3(ray_start, dvec_tmp);
426 ret = ED_transform_snap_object_project_ray(
428 &(const struct SnapObjectParams){
429 .snap_select = SNAP_ALL,
431 ray_start, ray_normal, r_distance,
432 r_location, r_normal_dummy);
434 /* artifically scale the distance to the scene size */
435 *r_distance /= walk->grid;
440 * \param ray_distance Distance to the hit point
441 * \param r_location Location of the hit point
442 * \param r_normal Normal of the hit surface, transformed to always face the camera
444 static bool walk_ray_cast(
445 RegionView3D *rv3d, WalkInfo *walk,
446 float r_location[3], float r_normal[3], float *ray_distance)
448 float ray_normal[3] = {0, 0, -1}; /* forward */
452 *ray_distance = BVH_RAYCAST_DIST_MAX;
454 copy_v3_v3(ray_start, rv3d->viewinv[3]);
456 mul_mat3_m4_v3(rv3d->viewinv, ray_normal);
458 normalize_v3(ray_normal);
460 ret = ED_transform_snap_object_project_ray(
462 &(const struct SnapObjectParams){
463 .snap_select = SNAP_ALL,
465 ray_start, ray_normal, NULL,
466 r_location, r_normal);
468 /* dot is positive if both rays are facing the same direction */
469 if (dot_v3v3(ray_normal, r_normal) > 0) {
473 /* artifically scale the distance to the scene size */
474 *ray_distance /= walk->grid;
479 /* WalkInfo->state */
486 /* keep the previous speed until user changes userpreferences */
487 static float base_speed = -1.f;
488 static float userdef_speed = -1.f;
490 static bool initWalkInfo(bContext *C, WalkInfo *walk, wmOperator *op)
492 wmWindow *win = CTX_wm_window(C);
494 walk->rv3d = CTX_wm_region_view3d(C);
495 walk->v3d = CTX_wm_view3d(C);
496 walk->ar = CTX_wm_region(C);
497 walk->scene = CTX_data_scene(C);
499 #ifdef NDOF_WALK_DEBUG
500 puts("\n-- walk begin --");
503 /* sanity check: for rare but possible case (if lib-linking the camera fails) */
504 if ((walk->rv3d->persp == RV3D_CAMOB) && (walk->v3d->camera == NULL)) {
505 walk->rv3d->persp = RV3D_PERSP;
508 if (walk->rv3d->persp == RV3D_CAMOB && ID_IS_LINKED_DATABLOCK(walk->v3d->camera)) {
509 BKE_report(op->reports, RPT_ERROR, "Cannot navigate a camera from an external library");
513 if (ED_view3d_offset_lock_check(walk->v3d, walk->rv3d)) {
514 BKE_report(op->reports, RPT_ERROR, "Cannot navigate when the view offset is locked");
518 if (walk->rv3d->persp == RV3D_CAMOB && walk->v3d->camera->constraints.first) {
519 BKE_report(op->reports, RPT_ERROR, "Cannot navigate an object with constraints");
523 walk->state = WALK_RUNNING;
525 if (fabsf(U.walk_navigation.walk_speed - userdef_speed) > 0.1f) {
526 base_speed = U.walk_navigation.walk_speed;
527 userdef_speed = U.walk_navigation.walk_speed;
531 walk->is_fast = false;
532 walk->is_slow = false;
533 walk->grid = (walk->scene->unit.system == USER_UNIT_NONE) ? 1.f : 1.f / walk->scene->unit.scale_length;
535 /* user preference settings */
536 walk->teleport.duration = U.walk_navigation.teleport_time;
537 walk->mouse_speed = U.walk_navigation.mouse_speed;
539 if ((U.walk_navigation.flag & USER_WALK_GRAVITY))
540 walk_navigation_mode_set(C, op, walk, WALK_MODE_GRAVITY);
542 walk_navigation_mode_set(C, op, walk, WALK_MODE_FREE);
544 walk->view_height = U.walk_navigation.view_height;
545 walk->jump_height = U.walk_navigation.jump_height;
546 walk->speed = U.walk_navigation.walk_speed;
547 walk->speed_factor = U.walk_navigation.walk_speed_factor;
549 walk->gravity_state = WALK_GRAVITY_STATE_OFF;
551 if ((walk->scene->physics_settings.flag & PHYS_GLOBAL_GRAVITY)) {
552 walk->gravity = fabsf(walk->scene->physics_settings.gravity[2]);
555 walk->gravity = 9.80668f; /* m/s2 */
558 walk->is_reversed = ((U.walk_navigation.flag & USER_WALK_MOUSE_REVERSE) != 0);
560 #ifdef USE_TABLET_SUPPORT
561 walk->is_cursor_first = true;
563 walk->is_cursor_absolute = false;
566 walk->active_directions = 0;
568 #ifdef NDOF_WALK_DRAW_TOOMUCH
571 zero_v3(walk->dvec_prev);
573 walk->timer = WM_event_add_timer(CTX_wm_manager(C), win, TIMER, 0.01f);
577 walk->time_lastdraw = PIL_check_seconds_timer();
579 walk->draw_handle_pixel = ED_region_draw_cb_activate(walk->ar->type, drawWalkPixel, walk, REGION_DRAW_POST_PIXEL);
581 walk->rv3d->rflag |= RV3D_NAVIGATING;
583 walk->snap_context = ED_transform_snap_object_context_create_view3d(
584 CTX_data_main(C), walk->scene, SNAP_OBJECT_USE_CACHE,
585 walk->ar, walk->v3d);
587 walk->v3d_camera_control = ED_view3d_cameracontrol_acquire(
588 walk->scene, walk->v3d, walk->rv3d,
589 (U.uiflag & USER_CAM_LOCK_NO_PARENT) == 0);
591 /* center the mouse */
592 walk->center_mval[0] = walk->ar->winx * 0.5f;
593 walk->center_mval[1] = walk->ar->winy * 0.5f;
595 #ifdef USE_PIXELSIZE_NATIVE_SUPPORT
596 walk->center_mval[0] += walk->ar->winrct.xmin;
597 walk->center_mval[1] += walk->ar->winrct.ymin;
599 WM_cursor_compatible_xy(win, &walk->center_mval[0], &walk->center_mval[1]);
601 walk->center_mval[0] -= walk->ar->winrct.xmin;
602 walk->center_mval[1] -= walk->ar->winrct.ymin;
605 copy_v2_v2_int(walk->prev_mval, walk->center_mval);
608 walk->ar->winrct.xmin + walk->center_mval[0],
609 walk->ar->winrct.ymin + walk->center_mval[1]);
611 /* remove the mouse cursor temporarily */
612 WM_cursor_modal_set(win, CURSOR_NONE);
617 static int walkEnd(bContext *C, WalkInfo *walk)
622 if (walk->state == WALK_RUNNING)
623 return OPERATOR_RUNNING_MODAL;
625 #ifdef NDOF_WALK_DEBUG
626 puts("\n-- walk end --");
629 win = CTX_wm_window(C);
632 WM_event_remove_timer(CTX_wm_manager(C), win, walk->timer);
634 ED_region_draw_cb_exit(walk->ar->type, walk->draw_handle_pixel);
636 ED_transform_snap_object_context_destroy(walk->snap_context);
638 ED_view3d_cameracontrol_release(walk->v3d_camera_control, walk->state == WALK_CANCEL);
640 rv3d->rflag &= ~RV3D_NAVIGATING;
643 MEM_freeN(walk->ndof);
645 /* restore the cursor */
646 WM_cursor_modal_restore(win);
648 #ifdef USE_TABLET_SUPPORT
649 if (walk->is_cursor_absolute == false)
652 /* center the mouse */
655 walk->ar->winrct.xmin + walk->center_mval[0],
656 walk->ar->winrct.ymin + walk->center_mval[1]);
659 if (walk->state == WALK_CONFIRM) {
661 return OPERATOR_FINISHED;
665 return OPERATOR_CANCELLED;
668 static bool wm_event_is_last_mousemove(const wmEvent *event)
670 while ((event = event->next)) {
671 if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
678 static void walkEvent(bContext *C, wmOperator *op, WalkInfo *walk, const wmEvent *event)
680 if (event->type == TIMER && event->customdata == walk->timer) {
683 else if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
685 #ifdef USE_TABLET_SUPPORT
686 if (walk->is_cursor_first) {
687 /* wait until we get the 'warp' event */
688 if ((walk->center_mval[0] == event->mval[0]) &&
689 (walk->center_mval[1] == event->mval[1]))
691 walk->is_cursor_first = false;
694 /* note, its possible the system isn't giving us the warp event
695 * ideally we shouldn't have to worry about this, see: T45361 */
696 wmWindow *win = CTX_wm_window(C);
698 walk->ar->winrct.xmin + walk->center_mval[0],
699 walk->ar->winrct.ymin + walk->center_mval[1]);
704 if ((walk->is_cursor_absolute == false) && WM_event_is_absolute(event)) {
705 walk->is_cursor_absolute = true;
706 copy_v2_v2_int(walk->prev_mval, event->mval);
707 copy_v2_v2_int(walk->center_mval, event->mval);
708 /* without this we can't turn 180d */
709 CLAMP_MIN(walk->mouse_speed, 4.0f);
711 #endif /* USE_TABLET_SUPPORT */
714 walk->moffset[0] += event->mval[0] - walk->prev_mval[0];
715 walk->moffset[1] += event->mval[1] - walk->prev_mval[1];
717 copy_v2_v2_int(walk->prev_mval, event->mval);
719 if ((walk->center_mval[0] != event->mval[0]) ||
720 (walk->center_mval[1] != event->mval[1]))
724 #ifdef USE_TABLET_SUPPORT
725 if (walk->is_cursor_absolute) {
730 if (wm_event_is_last_mousemove(event)) {
731 wmWindow *win = CTX_wm_window(C);
734 if ((abs(walk->prev_mval[0] - walk->center_mval[0]) > walk->center_mval[0] / 2) ||
735 (abs(walk->prev_mval[1] - walk->center_mval[1]) > walk->center_mval[1] / 2))
739 walk->ar->winrct.xmin + walk->center_mval[0],
740 walk->ar->winrct.ymin + walk->center_mval[1]);
741 copy_v2_v2_int(walk->prev_mval, walk->center_mval);
746 else if (event->type == NDOF_MOTION) {
747 /* do these automagically get delivered? yes. */
748 // puts("ndof motion detected in walk mode!");
749 // static const char *tag_name = "3D mouse position";
751 const wmNDOFMotionData *incoming_ndof = event->customdata;
752 switch (incoming_ndof->progress) {
754 /* start keeping track of 3D mouse position */
755 #ifdef NDOF_WALK_DEBUG
756 puts("start keeping track of 3D mouse position");
760 /* update 3D mouse position */
761 #ifdef NDOF_WALK_DEBUG
762 putchar('.'); fflush(stdout);
764 if (walk->ndof == NULL) {
765 // walk->ndof = MEM_mallocN(sizeof(wmNDOFMotionData), tag_name);
766 walk->ndof = MEM_dupallocN(incoming_ndof);
767 // walk->ndof = malloc(sizeof(wmNDOFMotionData));
770 memcpy(walk->ndof, incoming_ndof, sizeof(wmNDOFMotionData));
774 /* stop keeping track of 3D mouse position */
775 #ifdef NDOF_WALK_DEBUG
776 puts("stop keeping track of 3D mouse position");
779 MEM_freeN(walk->ndof);
784 /* update the time else the view will jump when 2D mouse/timer resume */
785 walk->time_lastdraw = PIL_check_seconds_timer();
789 break; /* should always be one of the above 3 */
792 /* handle modal keymap first */
793 else if (event->type == EVT_MODAL_MAP) {
794 switch (event->val) {
795 case WALK_MODAL_CANCEL:
796 walk->state = WALK_CANCEL;
798 case WALK_MODAL_CONFIRM:
799 walk->state = WALK_CONFIRM;
802 case WALK_MODAL_ACCELERATE:
803 base_speed *= 1.0f + (walk->is_slow ? 0.01f : 0.1f);
805 case WALK_MODAL_DECELERATE:
806 base_speed /= 1.0f + (walk->is_slow ? 0.01f : 0.1f);
809 /* implement WASD keys */
810 case WALK_MODAL_DIR_FORWARD:
811 walk->active_directions |= WALK_BIT_FORWARD;
813 case WALK_MODAL_DIR_BACKWARD:
814 walk->active_directions |= WALK_BIT_BACKWARD;
816 case WALK_MODAL_DIR_LEFT:
817 walk->active_directions |= WALK_BIT_LEFT;
819 case WALK_MODAL_DIR_RIGHT:
820 walk->active_directions |= WALK_BIT_RIGHT;
822 case WALK_MODAL_DIR_UP:
823 walk->active_directions |= WALK_BIT_UP;
825 case WALK_MODAL_DIR_DOWN:
826 walk->active_directions |= WALK_BIT_DOWN;
829 case WALK_MODAL_DIR_FORWARD_STOP:
830 walk->active_directions &= ~WALK_BIT_FORWARD;
832 case WALK_MODAL_DIR_BACKWARD_STOP:
833 walk->active_directions &= ~WALK_BIT_BACKWARD;
835 case WALK_MODAL_DIR_LEFT_STOP:
836 walk->active_directions &= ~WALK_BIT_LEFT;
838 case WALK_MODAL_DIR_RIGHT_STOP:
839 walk->active_directions &= ~WALK_BIT_RIGHT;
841 case WALK_MODAL_DIR_UP_STOP:
842 walk->active_directions &= ~WALK_BIT_UP;
844 case WALK_MODAL_DIR_DOWN_STOP:
845 walk->active_directions &= ~WALK_BIT_DOWN;
848 case WALK_MODAL_FAST_ENABLE:
849 walk->is_fast = true;
851 case WALK_MODAL_FAST_DISABLE:
852 walk->is_fast = false;
854 case WALK_MODAL_SLOW_ENABLE:
855 walk->is_slow = true;
857 case WALK_MODAL_SLOW_DISABLE:
858 walk->is_slow = false;
861 #define JUMP_SPEED_MIN 1.0f
862 #define JUMP_TIME_MAX 0.2f /* s */
863 #define JUMP_SPEED_MAX sqrtf(2.0f * walk->gravity * walk->jump_height)
865 case WALK_MODAL_JUMP_STOP:
866 if (walk->gravity_state == WALK_GRAVITY_STATE_JUMP) {
870 t = (float)(PIL_check_seconds_timer() - walk->teleport.initial_time);
872 /* reduce the veolocity, if JUMP wasn't hold for long enough */
873 t = min_ff(t, JUMP_TIME_MAX);
874 walk->speed_jump = JUMP_SPEED_MIN + t * (JUMP_SPEED_MAX - JUMP_SPEED_MIN) / JUMP_TIME_MAX;
876 /* when jumping, duration is how long it takes before we start going down */
877 walk->teleport.duration = getVelocityZeroTime(walk->gravity, walk->speed_jump);
879 /* no more increase of jump speed */
880 walk->gravity_state = WALK_GRAVITY_STATE_ON;
883 case WALK_MODAL_JUMP:
884 if ((walk->navigation_mode == WALK_MODE_GRAVITY) &&
885 (walk->gravity_state == WALK_GRAVITY_STATE_OFF) &&
886 (walk->teleport.state == WALK_TELEPORT_STATE_OFF))
888 /* no need to check for ground,
889 * walk->gravity wouldn't be off
890 * if we were over a hole */
891 walk->gravity_state = WALK_GRAVITY_STATE_JUMP;
892 walk->speed_jump = JUMP_SPEED_MAX;
894 walk->teleport.initial_time = PIL_check_seconds_timer();
895 copy_v3_v3(walk->teleport.origin, walk->rv3d->viewinv[3]);
897 /* using previous vec because WASD keys are not called when SPACE is */
898 copy_v2_v2(walk->teleport.direction, walk->dvec_prev);
900 /* when jumping, duration is how long it takes before we start going down */
901 walk->teleport.duration = getVelocityZeroTime(walk->gravity, walk->speed_jump);
906 case WALK_MODAL_TELEPORT:
908 float loc[3], nor[3];
910 bool ret = walk_ray_cast(walk->rv3d, walk, loc, nor, &distance);
912 /* in case we are teleporting middle way from a jump */
913 walk->speed_jump = 0.0f;
916 WalkTeleport *teleport = &walk->teleport;
917 teleport->state = WALK_TELEPORT_STATE_ON;
918 teleport->initial_time = PIL_check_seconds_timer();
919 teleport->duration = U.walk_navigation.teleport_time;
921 teleport->navigation_mode = walk->navigation_mode;
922 walk_navigation_mode_set(C, op, walk, WALK_MODE_FREE);
924 copy_v3_v3(teleport->origin, walk->rv3d->viewinv[3]);
926 /* stop the camera from a distance (camera height) */
927 normalize_v3_length(nor, walk->view_height);
930 sub_v3_v3v3(teleport->direction, loc, teleport->origin);
933 walk->teleport.state = WALK_TELEPORT_STATE_OFF;
938 #undef JUMP_SPEED_MAX
940 #undef JUMP_SPEED_MIN
942 case WALK_MODAL_TOGGLE:
943 if (walk->navigation_mode == WALK_MODE_GRAVITY) {
944 walk_navigation_mode_set(C, op, walk, WALK_MODE_FREE);
946 else { /* WALK_MODE_FREE */
947 walk_navigation_mode_set(C, op, walk, WALK_MODE_GRAVITY);
954 static void walkMoveCamera(bContext *C, WalkInfo *walk,
955 const bool do_rotate, const bool do_translate)
957 ED_view3d_cameracontrol_update(walk->v3d_camera_control, true, C, do_rotate, do_translate);
960 static float getFreeFallDistance(const float gravity, const float time)
962 return gravity * (time * time) * 0.5f;
965 static float getVelocityZeroTime(const float gravity, const float velocity)
967 return velocity / gravity;
970 static int walkApply(bContext *C, wmOperator *op, WalkInfo *walk)
972 #define WALK_ROTATE_FAC 2.2f /* more is faster */
973 #define WALK_TOP_LIMIT DEG2RADF(85.0f)
974 #define WALK_BOTTOM_LIMIT DEG2RADF(-80.0f)
975 #define WALK_MOVE_SPEED base_speed
976 #define WALK_BOOST_FACTOR ((void)0, walk->speed_factor)
978 /* walk mode - Ctrl+Shift+F
979 * a walk loop where the user can move move the view as if they are in a walk game
981 RegionView3D *rv3d = walk->rv3d;
982 ARegion *ar = walk->ar;
984 float mat[3][3]; /* 3x3 copy of the view matrix so we can move along the view axis */
985 float dvec[3] = {0.0f, 0.0f, 0.0f}; /* this is the direction that's added to the view offset per redraw */
987 int moffset[2]; /* mouse offset from the views center */
988 float tmp_quat[4]; /* used for rotating the view */
990 #ifdef NDOF_WALK_DEBUG
992 static unsigned int iteration = 1;
993 printf("walk timer %d\n", iteration++);
998 /* mouse offset from the center */
999 copy_v2_v2_int(moffset, walk->moffset);
1001 /* apply moffset so we can re-accumulate */
1002 walk->moffset[0] = 0;
1003 walk->moffset[1] = 0;
1006 if (walk->is_reversed) {
1007 moffset[1] = -moffset[1];
1010 /* Should we redraw? */
1011 if ((walk->active_directions) ||
1012 moffset[0] || moffset[1] ||
1013 walk->teleport.state == WALK_TELEPORT_STATE_ON ||
1014 walk->gravity_state != WALK_GRAVITY_STATE_OFF)
1018 /* time how fast it takes for us to redraw,
1019 * this is so simple scenes don't walk too fast */
1020 double time_current;
1022 #ifdef NDOF_WALK_DRAW_TOOMUCH
1025 time_current = PIL_check_seconds_timer();
1026 time_redraw = (float)(time_current - walk->time_lastdraw);
1028 walk->time_lastdraw = time_current;
1030 /* base speed in m/s */
1031 walk->speed = WALK_MOVE_SPEED;
1033 if (walk->is_fast) {
1034 walk->speed *= WALK_BOOST_FACTOR;
1036 else if (walk->is_slow) {
1037 walk->speed *= 1.0f / WALK_BOOST_FACTOR;
1040 copy_m3_m4(mat, rv3d->viewinv);
1043 /* rotate about the X axis- look up/down */
1049 /* relative offset */
1050 y = (float) moffset[1] / ar->winy;
1053 y *= WALK_ROTATE_FAC;
1055 /* user adjustement factor */
1056 y *= walk->mouse_speed;
1058 /* clamp the angle limits */
1059 /* it ranges from 90.0f to -90.0f */
1060 angle = -asinf(rv3d->viewmat[2][2]);
1062 if (angle > WALK_TOP_LIMIT && y > 0.0f)
1065 else if (angle < WALK_BOTTOM_LIMIT && y < 0.0f)
1068 copy_v3_fl3(upvec, 1.0f, 0.0f, 0.0f);
1069 mul_m3_v3(mat, upvec);
1070 /* Rotate about the relative up vec */
1071 axis_angle_to_quat(tmp_quat, upvec, -y);
1072 mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, tmp_quat);
1075 /* rotate about the Y axis- look left/right */
1080 /* if we're upside down invert the moffset */
1081 copy_v3_fl3(upvec, 0.0f, 1.0f, 0.0f);
1082 mul_m3_v3(mat, upvec);
1084 if (upvec[2] < 0.0f)
1085 moffset[0] = -moffset[0];
1087 /* relative offset */
1088 x = (float) moffset[0] / ar->winx;
1091 x *= WALK_ROTATE_FAC;
1093 /* user adjustement factor */
1094 x *= walk->mouse_speed;
1096 /* Rotate about the relative up vec */
1097 axis_angle_to_quat_single(tmp_quat, 'Z', x);
1098 mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, tmp_quat);
1102 /* WASD - 'move' translation code */
1103 if ((walk->active_directions) &&
1104 (walk->gravity_state == WALK_GRAVITY_STATE_OFF))
1110 if ((walk->active_directions & WALK_BIT_FORWARD) ||
1111 (walk->active_directions & WALK_BIT_BACKWARD))
1116 if ((walk->active_directions & WALK_BIT_FORWARD))
1119 if ((walk->active_directions & WALK_BIT_BACKWARD))
1122 copy_v3_fl3(dvec_tmp, 0.0f, 0.0f, direction);
1123 mul_m3_v3(mat, dvec_tmp);
1125 if (walk->navigation_mode == WALK_MODE_GRAVITY) {
1129 normalize_v3(dvec_tmp);
1130 add_v3_v3(dvec, dvec_tmp);
1134 if ((walk->active_directions & WALK_BIT_LEFT) ||
1135 (walk->active_directions & WALK_BIT_RIGHT))
1140 if ((walk->active_directions & WALK_BIT_LEFT))
1143 if ((walk->active_directions & WALK_BIT_RIGHT))
1146 dvec_tmp[0] = direction * rv3d->viewinv[0][0];
1147 dvec_tmp[1] = direction * rv3d->viewinv[0][1];
1150 normalize_v3(dvec_tmp);
1151 add_v3_v3(dvec, dvec_tmp);
1155 if ((walk->active_directions & WALK_BIT_UP) ||
1156 (walk->active_directions & WALK_BIT_DOWN))
1159 if (walk->navigation_mode == WALK_MODE_FREE) {
1163 if ((walk->active_directions & WALK_BIT_UP))
1166 if ((walk->active_directions & WALK_BIT_DOWN))
1169 copy_v3_fl3(dvec_tmp, 0.0f, 0.0f, direction);
1170 add_v3_v3(dvec, dvec_tmp);
1174 /* apply movement */
1175 mul_v3_fl(dvec, walk->speed * time_redraw);
1178 /* stick to the floor */
1179 if (walk->navigation_mode == WALK_MODE_GRAVITY &&
1180 ELEM(walk->gravity_state,
1181 WALK_GRAVITY_STATE_OFF,
1182 WALK_GRAVITY_STATE_START))
1187 float difference = -100.0f;
1188 float fall_distance;
1190 ret = walk_floor_distance_get(rv3d, walk, dvec, &ray_distance);
1193 difference = walk->view_height - ray_distance;
1196 /* the distance we would fall naturally smoothly enough that we
1197 * can manually drop the object without activating gravity */
1198 fall_distance = time_redraw * walk->speed * WALK_BOOST_FACTOR;
1200 if (fabsf(difference) < fall_distance) {
1202 dvec[2] -= difference;
1204 /* in case we switched from FREE to GRAVITY too close to the ground */
1205 if (walk->gravity_state == WALK_GRAVITY_STATE_START)
1206 walk->gravity_state = WALK_GRAVITY_STATE_OFF;
1209 /* hijack the teleport variables */
1210 walk->teleport.initial_time = PIL_check_seconds_timer();
1211 walk->gravity_state = WALK_GRAVITY_STATE_ON;
1212 walk->teleport.duration = 0.0f;
1214 copy_v3_v3(walk->teleport.origin, walk->rv3d->viewinv[3]);
1215 copy_v2_v2(walk->teleport.direction, dvec);
1220 /* Falling or jumping) */
1221 if (ELEM(walk->gravity_state, WALK_GRAVITY_STATE_ON, WALK_GRAVITY_STATE_JUMP)) {
1225 float ray_distance, difference = -100.0f;
1228 t = (float)(PIL_check_seconds_timer() - walk->teleport.initial_time);
1230 /* keep moving if we were moving */
1231 copy_v2_v2(dvec, walk->teleport.direction);
1233 z_cur = walk->rv3d->viewinv[3][2];
1234 z_new = walk->teleport.origin[2] - getFreeFallDistance(walk->gravity, t) * walk->grid;
1237 z_new += t * walk->speed_jump * walk->grid;
1239 /* duration is the jump duration */
1240 if (t > walk->teleport.duration) {
1242 /* check to see if we are landing */
1243 ret = walk_floor_distance_get(rv3d, walk, dvec, &ray_distance);
1246 difference = walk->view_height - ray_distance;
1249 if (difference > 0.0f) {
1250 /* quit falling, lands at "view_height" from the floor */
1251 dvec[2] -= difference;
1252 walk->gravity_state = WALK_GRAVITY_STATE_OFF;
1253 walk->speed_jump = 0.0f;
1257 dvec[2] = z_cur - z_new;
1261 /* keep going up (jump) */
1262 dvec[2] = z_cur - z_new;
1267 else if (walk->teleport.state == WALK_TELEPORT_STATE_ON) {
1268 float t; /* factor */
1272 /* linear interpolation */
1273 t = (float)(PIL_check_seconds_timer() - walk->teleport.initial_time);
1274 t /= walk->teleport.duration;
1276 /* clamp so we don't go past our limit */
1279 walk->teleport.state = WALK_TELEPORT_STATE_OFF;
1280 walk_navigation_mode_set(C, op, walk, walk->teleport.navigation_mode);
1283 mul_v3_v3fl(new_loc, walk->teleport.direction, t);
1284 add_v3_v3(new_loc, walk->teleport.origin);
1286 copy_v3_v3(cur_loc, walk->rv3d->viewinv[3]);
1287 sub_v3_v3v3(dvec, cur_loc, new_loc);
1290 if (rv3d->persp == RV3D_CAMOB) {
1291 Object *lock_ob = ED_view3d_cameracontrol_object_get(walk->v3d_camera_control);
1292 if (lock_ob->protectflag & OB_LOCK_LOCX) dvec[0] = 0.0f;
1293 if (lock_ob->protectflag & OB_LOCK_LOCY) dvec[1] = 0.0f;
1294 if (lock_ob->protectflag & OB_LOCK_LOCZ) dvec[2] = 0.0f;
1297 /* scale the movement to the scene size */
1298 mul_v3_v3fl(dvec_tmp, dvec, walk->grid);
1299 add_v3_v3(rv3d->ofs, dvec_tmp);
1301 if (rv3d->persp == RV3D_CAMOB) {
1302 const bool do_rotate = (moffset[0] || moffset[1]);
1303 const bool do_translate = (walk->speed != 0.0f);
1304 walkMoveCamera(C, walk, do_rotate, do_translate);
1308 /* we're not redrawing but we need to update the time else the view will jump */
1309 walk->time_lastdraw = PIL_check_seconds_timer();
1312 copy_v3_v3(walk->dvec_prev, dvec);
1315 return OPERATOR_FINISHED;
1316 #undef WALK_ROTATE_FAC
1317 #undef WALK_ZUP_CORRECT_FAC
1318 #undef WALK_ZUP_CORRECT_ACCEL
1319 #undef WALK_SMOOTH_FAC
1320 #undef WALK_TOP_LIMIT
1321 #undef WALK_BOTTOM_LIMIT
1322 #undef WALK_MOVE_SPEED
1323 #undef WALK_BOOST_FACTOR
1326 static void walkApply_ndof(bContext *C, WalkInfo *walk)
1328 Object *lock_ob = ED_view3d_cameracontrol_object_get(walk->v3d_camera_control);
1329 bool has_translate, has_rotate;
1331 view3d_ndof_fly(walk->ndof,
1332 walk->v3d, walk->rv3d,
1333 walk->is_slow, lock_ob ? lock_ob->protectflag : 0,
1334 &has_translate, &has_rotate);
1336 if (has_translate || has_rotate) {
1337 walk->redraw = true;
1339 if (walk->rv3d->persp == RV3D_CAMOB) {
1340 walkMoveCamera(C, walk, has_rotate, has_translate);
1345 /****** walk operator ******/
1346 static int walk_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1348 RegionView3D *rv3d = CTX_wm_region_view3d(C);
1351 if (rv3d->viewlock & RV3D_LOCKED)
1352 return OPERATOR_CANCELLED;
1354 walk = MEM_callocN(sizeof(WalkInfo), "NavigationWalkOperation");
1356 op->customdata = walk;
1358 if (initWalkInfo(C, walk, op) == false) {
1359 MEM_freeN(op->customdata);
1360 return OPERATOR_CANCELLED;
1363 walkEvent(C, op, walk, event);
1365 WM_event_add_modal_handler(C, op);
1367 return OPERATOR_RUNNING_MODAL;
1370 static void walk_cancel(bContext *C, wmOperator *op)
1372 WalkInfo *walk = op->customdata;
1374 walk->state = WALK_CANCEL;
1376 op->customdata = NULL;
1379 static int walk_modal(bContext *C, wmOperator *op, const wmEvent *event)
1382 bool do_draw = false;
1383 WalkInfo *walk = op->customdata;
1384 RegionView3D *rv3d = walk->rv3d;
1385 Object *walk_object = ED_view3d_cameracontrol_object_get(walk->v3d_camera_control);
1387 walk->redraw = false;
1389 walkEvent(C, op, walk, event);
1391 if (walk->ndof) { /* 3D mouse overrules [2D mouse + timer] */
1392 if (event->type == NDOF_MOTION) {
1393 walkApply_ndof(C, walk);
1396 else if (event->type == TIMER && event->customdata == walk->timer) {
1397 walkApply(C, op, walk);
1400 do_draw |= walk->redraw;
1402 exit_code = walkEnd(C, walk);
1404 if (exit_code != OPERATOR_RUNNING_MODAL)
1408 if (rv3d->persp == RV3D_CAMOB) {
1409 WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, walk_object);
1412 // puts("redraw!"); // too frequent, commented with NDOF_WALK_DRAW_TOOMUCH for now
1413 ED_region_tag_redraw(CTX_wm_region(C));
1416 if (ELEM(exit_code, OPERATOR_FINISHED, OPERATOR_CANCELLED))
1417 ED_area_headerprint(CTX_wm_area(C), NULL);
1422 void VIEW3D_OT_walk(wmOperatorType *ot)
1425 ot->name = "Walk Navigation";
1426 ot->description = "Interactively walk around the scene";
1427 ot->idname = "VIEW3D_OT_walk";
1430 ot->invoke = walk_invoke;
1431 ot->cancel = walk_cancel;
1432 ot->modal = walk_modal;
1433 ot->poll = ED_operator_region_view3d_active;
1436 ot->flag = OPTYPE_BLOCKING;