fix view3d_persp_mat4, which gave incorrect translation, rename to ED_view3d_to_m4...
[blender-staging.git] / source / blender / editors / space_view3d / view3d_fly.c
1 /*
2  * $Id$
3  *
4  * ***** BEGIN GPL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version. 
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19  * 
20  * Contributor(s): Campbell Barton
21  *
22  * ***** END GPL LICENSE BLOCK *****
23  */
24
25 /** \file blender/editors/space_view3d/view3d_fly.c
26  *  \ingroup spview3d
27  */
28
29
30 /* defines VIEW3D_OT_fly modal operator */
31
32 #include "DNA_anim_types.h"
33 #include "DNA_scene_types.h"
34 #include "DNA_object_types.h"
35
36 #include "MEM_guardedalloc.h"
37
38 #include "BLI_math.h"
39 #include "BLI_blenlib.h"
40 #include "BLI_utildefines.h"
41
42 #include "BKE_context.h"
43 #include "BKE_object.h"
44 #include "BKE_report.h"
45
46 #include "BKE_depsgraph.h" /* for fly mode updating */
47
48 #include "BIF_gl.h"
49
50 #include "WM_api.h"
51 #include "WM_types.h"
52
53 #include "ED_keyframing.h"
54 #include "ED_screen.h"
55 #include "ED_space_api.h"
56
57 #include "PIL_time.h" /* smoothview */
58
59 #include "view3d_intern.h"      // own include
60
61 /* NOTE: these defines are saved in keymap files, do not change values but just add new ones */
62 #define FLY_MODAL_CANCEL                        1
63 #define FLY_MODAL_CONFIRM                       2
64 #define FLY_MODAL_ACCELERATE            3
65 #define FLY_MODAL_DECELERATE            4
66 #define FLY_MODAL_PAN_ENABLE            5
67 #define FLY_MODAL_PAN_DISABLE           6
68 #define FLY_MODAL_DIR_FORWARD           7
69 #define FLY_MODAL_DIR_BACKWARD          8
70 #define FLY_MODAL_DIR_LEFT                      9
71 #define FLY_MODAL_DIR_RIGHT                     10
72 #define FLY_MODAL_DIR_UP                        11
73 #define FLY_MODAL_DIR_DOWN                      12
74 #define FLY_MODAL_AXIS_LOCK_X           13
75 #define FLY_MODAL_AXIS_LOCK_Z           14
76 #define FLY_MODAL_PRECISION_ENABLE      15
77 #define FLY_MODAL_PRECISION_DISABLE     16
78
79 /* called in transform_ops.c, on each regeneration of keymaps  */
80 void fly_modal_keymap(wmKeyConfig *keyconf)
81 {
82         static EnumPropertyItem modal_items[] = {
83         {FLY_MODAL_CANCEL,      "CANCEL", 0, "Cancel", ""},
84         {FLY_MODAL_CONFIRM,     "CONFIRM", 0, "Confirm", ""},
85         {FLY_MODAL_ACCELERATE, "ACCELERATE", 0, "Accelerate", ""},
86         {FLY_MODAL_DECELERATE, "DECELERATE", 0, "Decelerate", ""},
87
88         {FLY_MODAL_PAN_ENABLE,  "PAN_ENABLE", 0, "Pan Enable", ""},
89         {FLY_MODAL_PAN_DISABLE, "PAN_DISABLE", 0, "Pan Disable", ""},
90
91         {FLY_MODAL_DIR_FORWARD, "FORWARD", 0, "Fly Forward", ""},
92         {FLY_MODAL_DIR_BACKWARD,"BACKWARD", 0, "Fly Backward", ""},
93         {FLY_MODAL_DIR_LEFT,    "LEFT", 0, "Fly Left", ""},
94         {FLY_MODAL_DIR_RIGHT,   "RIGHT", 0, "Fly Right", ""},
95         {FLY_MODAL_DIR_UP,              "UP", 0, "Fly Up", ""},
96         {FLY_MODAL_DIR_DOWN,    "DOWN", 0, "Fly Down", ""},
97
98         {FLY_MODAL_AXIS_LOCK_X, "AXIS_LOCK_X", 0, "X Axis Correction", "X axis correction (toggle)"},
99         {FLY_MODAL_AXIS_LOCK_Z, "AXIS_LOCK_Z", 0, "X Axis Correction", "Z axis correction (toggle)"},
100
101         {FLY_MODAL_PRECISION_ENABLE,    "PRECISION_ENABLE", 0, "Precision Enable", ""},
102         {FLY_MODAL_PRECISION_DISABLE,   "PRECISION_DISABLE", 0, "Precision Disable", ""},
103
104         {0, NULL, 0, NULL, NULL}};
105
106         wmKeyMap *keymap= WM_modalkeymap_get(keyconf, "View3D Fly Modal");
107
108         /* this function is called for each spacetype, only needs to add map once */
109         if(keymap) return;
110
111         keymap= WM_modalkeymap_add(keyconf, "View3D Fly Modal", modal_items);
112
113         /* items for modal map */
114         WM_modalkeymap_add_item(keymap, ESCKEY,    KM_PRESS, KM_ANY, 0, FLY_MODAL_CANCEL);
115         WM_modalkeymap_add_item(keymap, RIGHTMOUSE, KM_ANY, KM_ANY, 0, FLY_MODAL_CANCEL);
116
117         WM_modalkeymap_add_item(keymap, LEFTMOUSE, KM_ANY, KM_ANY, 0, FLY_MODAL_CONFIRM);
118         WM_modalkeymap_add_item(keymap, RETKEY, KM_PRESS, KM_ANY, 0, FLY_MODAL_CONFIRM);
119         WM_modalkeymap_add_item(keymap, SPACEKEY, KM_PRESS, KM_ANY, 0, FLY_MODAL_CONFIRM);
120         WM_modalkeymap_add_item(keymap, PADENTER, KM_PRESS, KM_ANY, 0, FLY_MODAL_CONFIRM);
121
122         WM_modalkeymap_add_item(keymap, PADPLUSKEY, KM_PRESS, 0, 0, FLY_MODAL_ACCELERATE);
123         WM_modalkeymap_add_item(keymap, PADMINUS, KM_PRESS, 0, 0, FLY_MODAL_DECELERATE);
124         WM_modalkeymap_add_item(keymap, WHEELUPMOUSE, KM_PRESS, 0, 0, FLY_MODAL_ACCELERATE);
125         WM_modalkeymap_add_item(keymap, WHEELDOWNMOUSE, KM_PRESS, 0, 0, FLY_MODAL_DECELERATE);
126
127         WM_modalkeymap_add_item(keymap, MIDDLEMOUSE, KM_PRESS, KM_ANY, 0, FLY_MODAL_PAN_ENABLE);
128         WM_modalkeymap_add_item(keymap, MIDDLEMOUSE, KM_RELEASE, KM_ANY, 0, FLY_MODAL_PAN_DISABLE); /* XXX - Bug in the event system, middle mouse release doesnt work */
129
130         /* WASD */
131         WM_modalkeymap_add_item(keymap, WKEY, KM_PRESS, 0, 0, FLY_MODAL_DIR_FORWARD);
132         WM_modalkeymap_add_item(keymap, SKEY, KM_PRESS, 0, 0, FLY_MODAL_DIR_BACKWARD);
133         WM_modalkeymap_add_item(keymap, AKEY, KM_PRESS, 0, 0, FLY_MODAL_DIR_LEFT);
134         WM_modalkeymap_add_item(keymap, DKEY, KM_PRESS, 0, 0, FLY_MODAL_DIR_RIGHT);
135         WM_modalkeymap_add_item(keymap, RKEY, KM_PRESS, 0, 0, FLY_MODAL_DIR_UP);
136         WM_modalkeymap_add_item(keymap, FKEY, KM_PRESS, 0, 0, FLY_MODAL_DIR_DOWN);
137
138         WM_modalkeymap_add_item(keymap, XKEY, KM_PRESS, 0, 0, FLY_MODAL_AXIS_LOCK_X);
139         WM_modalkeymap_add_item(keymap, ZKEY, KM_PRESS, 0, 0, FLY_MODAL_AXIS_LOCK_Z);
140
141         WM_modalkeymap_add_item(keymap, LEFTSHIFTKEY, KM_PRESS, KM_ANY, 0, FLY_MODAL_PRECISION_ENABLE);
142         WM_modalkeymap_add_item(keymap, LEFTSHIFTKEY, KM_RELEASE, KM_ANY, 0, FLY_MODAL_PRECISION_DISABLE);
143
144         /* assign map to operators */
145         WM_modalkeymap_assign(keymap, "VIEW3D_OT_fly");
146
147 }
148
149 typedef struct FlyInfo {
150         /* context stuff */
151         RegionView3D *rv3d;
152         View3D *v3d;
153         ARegion *ar;
154         Scene *scene;
155
156         wmTimer *timer; /* needed for redraws */
157
158         short state;
159         short use_precision;
160         short redraw;
161         int mval[2];
162
163         /* fly state state */
164         float speed; /* the speed the view is moving per redraw */
165         short axis; /* Axis index to move allong by default Z to move allong the view */
166         short pan_view; /* when true, pan the view instead of rotating */
167
168         /* relative view axis locking - xlock, zlock
169         0; disabled
170         1; enabled but not checking because mouse hasnt moved outside the margin since locking was checked an not needed
171            when the mouse moves, locking is set to 2 so checks are done.
172         2; mouse moved and checking needed, if no view altering is donem its changed back to 1 */
173         short xlock, zlock;
174         float xlock_momentum, zlock_momentum; /* nicer dynamics */
175         float grid; /* world scale 1.0 default */
176
177         /* root most parent */
178         Object *root_parent;
179
180         /* backup values */
181         float dist_backup; /* backup the views distance since we use a zero dist for fly mode */
182         float ofs_backup[3]; /* backup the views offset incase the user cancels flying in non camera mode */
183         float rot_backup[4]; /* backup the views quat incase the user cancels flying in non camera mode. (quat for view, eul for camera) */
184         short persp_backup; /* remember if were ortho or not, only used for restoring the view if it was a ortho view */
185
186         void *obtfm; /* backup the objects transform */
187
188         /* compare between last state */
189         double time_lastwheel; /* used to accelerate when using the mousewheel a lot */
190         double time_lastdraw; /* time between draws */
191
192         void *draw_handle_pixel;
193
194         /* use for some lag */
195         float dvec_prev[3]; /* old for some lag */
196
197 } FlyInfo;
198
199 static void drawFlyPixel(const struct bContext *UNUSED(C), struct ARegion *UNUSED(ar), void *arg)
200 {
201         FlyInfo *fly = arg;
202
203         /* draws 4 edge brackets that frame the safe area where the
204         mouse can move during fly mode without spinning the view */
205         float x1, x2, y1, y2;
206         
207         x1= 0.45f * (float)fly->ar->winx;
208         y1= 0.45f * (float)fly->ar->winy;
209         x2= 0.55f * (float)fly->ar->winx;
210         y2= 0.55f * (float)fly->ar->winy;
211         cpack(0);
212         
213         
214         glBegin(GL_LINES);
215         /* bottom left */
216         glVertex2f(x1,y1); 
217         glVertex2f(x1,y1+5);
218         
219         glVertex2f(x1,y1); 
220         glVertex2f(x1+5,y1);
221         
222         /* top right */
223         glVertex2f(x2,y2); 
224         glVertex2f(x2,y2-5);
225         
226         glVertex2f(x2,y2); 
227         glVertex2f(x2-5,y2);
228         
229         /* top left */
230         glVertex2f(x1,y2); 
231         glVertex2f(x1,y2-5);
232         
233         glVertex2f(x1,y2); 
234         glVertex2f(x1+5,y2);
235         
236         /* bottom right */
237         glVertex2f(x2,y1); 
238         glVertex2f(x2,y1+5);
239         
240         glVertex2f(x2,y1); 
241         glVertex2f(x2-5,y1);
242         glEnd();
243 }
244
245 /* FlyInfo->state */
246 #define FLY_RUNNING             0
247 #define FLY_CANCEL              1
248 #define FLY_CONFIRM             2
249
250 static int initFlyInfo (bContext *C, FlyInfo *fly, wmOperator *op, wmEvent *event)
251 {
252         float upvec[3]; // tmp
253         float mat[3][3];
254
255         fly->rv3d= CTX_wm_region_view3d(C);
256         fly->v3d = CTX_wm_view3d(C);
257         fly->ar = CTX_wm_region(C);
258         fly->scene= CTX_data_scene(C);
259
260         if(fly->rv3d->persp==RV3D_CAMOB && fly->v3d->camera->id.lib) {
261                 BKE_report(op->reports, RPT_ERROR, "Cannot fly a camera from an external library");
262                 return FALSE;
263         }
264
265         if(fly->v3d->ob_centre) {
266                 BKE_report(op->reports, RPT_ERROR, "Cannot fly when the view is locked to an object");
267                 return FALSE;
268         }
269
270         if(fly->rv3d->persp==RV3D_CAMOB && fly->v3d->camera->constraints.first) {
271                 BKE_report(op->reports, RPT_ERROR, "Cannot fly an object with constraints");
272                 return FALSE;
273         }
274
275         fly->state= FLY_RUNNING;
276         fly->speed= 0.0f;
277         fly->axis= 2;
278         fly->pan_view= FALSE;
279         fly->xlock= FALSE;
280         fly->zlock= FALSE;
281         fly->xlock_momentum=0.0f;
282         fly->zlock_momentum=0.0f;
283         fly->grid= 1.0f;
284         fly->use_precision= 0;
285
286         fly->dvec_prev[0]= fly->dvec_prev[1]= fly->dvec_prev[2]= 0.0f;
287
288         fly->timer= WM_event_add_timer(CTX_wm_manager(C), CTX_wm_window(C), TIMER, 0.01f);
289
290         VECCOPY2D(fly->mval, event->mval)
291
292         fly->time_lastdraw= fly->time_lastwheel= PIL_check_seconds_timer();
293
294         fly->draw_handle_pixel = ED_region_draw_cb_activate(fly->ar->type, drawFlyPixel, fly, REGION_DRAW_POST_PIXEL);
295
296         fly->rv3d->rflag |= RV3D_NAVIGATING; /* so we draw the corner margins */
297
298         /* detect weather to start with Z locking */
299         upvec[0]=1.0f; upvec[1]=0.0f; upvec[2]=0.0f;
300         copy_m3_m4(mat, fly->rv3d->viewinv);
301         mul_m3_v3(mat, upvec);
302         if (fabs(upvec[2]) < 0.1)
303                 fly->zlock = 1;
304         upvec[0]=0; upvec[1]=0; upvec[2]=0;
305
306         fly->persp_backup= fly->rv3d->persp;
307         fly->dist_backup= fly->rv3d->dist;
308         if (fly->rv3d->persp==RV3D_CAMOB) {
309                 Object *ob_back;
310                 if((fly->root_parent=fly->v3d->camera->parent)) {
311                         while(fly->root_parent->parent)
312                                 fly->root_parent= fly->root_parent->parent;
313                         ob_back= fly->root_parent;
314                 }
315                 else {
316                         ob_back= fly->v3d->camera;
317                 }
318
319                 /* store the original camera loc and rot */
320                 /* TODO. axis angle etc */
321
322                 fly->obtfm= object_tfm_backup(ob_back);
323
324                 where_is_object(fly->scene, fly->v3d->camera);
325                 negate_v3_v3(fly->rv3d->ofs, fly->v3d->camera->obmat[3]);
326
327                 fly->rv3d->dist=0.0;
328         } else {
329                 /* perspective or ortho */
330                 if (fly->rv3d->persp==RV3D_ORTHO)
331                         fly->rv3d->persp= RV3D_PERSP; /*if ortho projection, make perspective */
332                 copy_qt_qt(fly->rot_backup, fly->rv3d->viewquat);
333                 copy_v3_v3(fly->ofs_backup, fly->rv3d->ofs);
334                 fly->rv3d->dist= 0.0f;
335
336                 upvec[2]= fly->dist_backup; /*x and y are 0*/
337                 mul_m3_v3(mat, upvec);
338                 sub_v3_v3(fly->rv3d->ofs, upvec);
339                 /*Done with correcting for the dist*/
340         }
341
342         
343         /* center the mouse, probably the UI mafia are against this but without its quite annoying */
344         WM_cursor_warp(CTX_wm_window(C), fly->ar->winrct.xmin + fly->ar->winx/2, fly->ar->winrct.ymin + fly->ar->winy/2);
345         
346         return 1;
347 }
348
349 static int flyEnd(bContext *C, FlyInfo *fly)
350 {
351         RegionView3D *rv3d= fly->rv3d;
352         View3D *v3d = fly->v3d;
353
354         float upvec[3];
355
356         if(fly->state == FLY_RUNNING)
357                 return OPERATOR_RUNNING_MODAL;
358
359         WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), fly->timer);
360
361         ED_region_draw_cb_exit(fly->ar->type, fly->draw_handle_pixel);
362
363         rv3d->dist= fly->dist_backup;
364
365         if (fly->state == FLY_CANCEL) {
366         /* Revert to original view? */
367                 if (fly->persp_backup==RV3D_CAMOB) { /* a camera view */
368                         Object *ob_back;
369                         if(fly->root_parent)ob_back= fly->root_parent;
370                         else                            ob_back= fly->v3d->camera;
371
372                         /* store the original camera loc and rot */
373                         object_tfm_restore(ob_back, fly->obtfm);
374
375                         DAG_id_tag_update(&ob_back->id, OB_RECALC_OB);
376                 } else {
377                         /* Non Camera we need to reset the view back to the original location bacause the user canceled*/
378                         copy_qt_qt(rv3d->viewquat, fly->rot_backup);
379                         copy_v3_v3(rv3d->ofs, fly->ofs_backup);
380                         rv3d->persp= fly->persp_backup;
381                 }
382         }
383         else if (fly->persp_backup==RV3D_CAMOB) {       /* camera */
384                 DAG_id_tag_update(fly->root_parent ? &fly->root_parent->id : &v3d->camera->id, OB_RECALC_OB);
385         }
386         else { /* not camera */
387                 /* Apply the fly mode view */
388                 /*restore the dist*/
389                 float mat[3][3];
390                 upvec[0]= upvec[1]= 0;
391                 upvec[2]= fly->dist_backup; /*x and y are 0*/
392                 copy_m3_m4(mat, rv3d->viewinv);
393                 mul_m3_v3(mat, upvec);
394                 add_v3_v3(rv3d->ofs, upvec);
395                 /*Done with correcting for the dist */
396         }
397
398         rv3d->rflag &= ~RV3D_NAVIGATING;
399 //XXX2.5        BIF_view3d_previewrender_signal(fly->sa, PR_DBASE|PR_DISPRECT); /* not working at the moment not sure why */
400
401         if(fly->obtfm)
402                 MEM_freeN(fly->obtfm);
403
404         if(fly->state == FLY_CONFIRM) {
405                 MEM_freeN(fly);
406                 return OPERATOR_FINISHED;
407         }
408
409         MEM_freeN(fly);
410         return OPERATOR_CANCELLED;
411 }
412
413 static void flyEvent(FlyInfo *fly, wmEvent *event)
414 {
415         if (event->type == TIMER && event->customdata == fly->timer) {
416                 fly->redraw = 1;
417         }
418         else if (event->type == MOUSEMOVE) {
419                 VECCOPY2D(fly->mval, event->mval);
420         } /* handle modal keymap first */
421         else if (event->type == EVT_MODAL_MAP) {
422                 switch (event->val) {
423                         case FLY_MODAL_CANCEL:
424                                 fly->state = FLY_CANCEL;
425                                 break;
426                         case FLY_MODAL_CONFIRM:
427                                 fly->state = FLY_CONFIRM;
428                                 break;
429
430                         case FLY_MODAL_ACCELERATE:
431                         {
432                                 double time_currwheel;
433                                 float time_wheel;
434
435                                 time_currwheel= PIL_check_seconds_timer();
436                                 time_wheel = (float)(time_currwheel - fly->time_lastwheel);
437                                 fly->time_lastwheel = time_currwheel;
438                                 /*printf("Wheel %f\n", time_wheel);*/
439                                 /*Mouse wheel delays range from 0.5==slow to 0.01==fast*/
440                                 time_wheel = 1.0f + (10.0f - (20.0f * MIN2(time_wheel, 0.5f))); /* 0-0.5 -> 0-5.0 */
441
442                                 if (fly->speed<0.0f) fly->speed= 0.0f;
443                                 else {
444                                         if (event->shift)
445                                                 fly->speed += fly->grid*time_wheel * 0.1f;
446                                         else
447                                                 fly->speed += fly->grid*time_wheel;
448                                 }
449                                 break;
450                         }
451                         case FLY_MODAL_DECELERATE:
452                         {
453                                 double time_currwheel;
454                                 float time_wheel;
455
456                                 time_currwheel= PIL_check_seconds_timer();
457                                 time_wheel = (float)(time_currwheel - fly->time_lastwheel);
458                                 fly->time_lastwheel = time_currwheel;
459                                 time_wheel = 1.0f + (10.0f - (20.0f * MIN2(time_wheel, 0.5f))); /* 0-0.5 -> 0-5.0 */
460
461                                 if (fly->speed>0) fly->speed=0;
462                                 else {
463                                         if (event->shift)
464                                                 fly->speed-= fly->grid*time_wheel * 0.1f;
465                                         else
466                                                 fly->speed-= fly->grid*time_wheel;
467                                 }
468                                 break;
469                         }
470                         case FLY_MODAL_PAN_ENABLE:
471                                 fly->pan_view= TRUE;
472                                 break;
473                         case FLY_MODAL_PAN_DISABLE:
474 //XXX2.5                warp_pointer(cent_orig[0], cent_orig[1]);
475                                 fly->pan_view= FALSE;
476                                 break;
477
478                                 /* impliment WASD keys */
479                         case FLY_MODAL_DIR_FORWARD:
480                                 if (fly->speed < 0.0f) fly->speed= -fly->speed; /* flip speed rather then stopping, game like motion */
481                                 else if (fly->axis==2) fly->speed += fly->grid; /* increse like mousewheel if were already moving in that difection*/
482                                 fly->axis= 2;
483                                 break;
484                         case FLY_MODAL_DIR_BACKWARD:
485                                 if (fly->speed > 0.0f) fly->speed= -fly->speed;
486                                 else if (fly->axis==2) fly->speed -= fly->grid;
487                                 fly->axis= 2;
488                                 break;
489                         case FLY_MODAL_DIR_LEFT:
490                                 if (fly->speed < 0.0f) fly->speed= -fly->speed;
491                                 else if (fly->axis==0) fly->speed += fly->grid;
492                                 fly->axis= 0;
493                                 break;
494                         case FLY_MODAL_DIR_RIGHT:
495                                 if (fly->speed > 0.0f) fly->speed= -fly->speed;
496                                 else if (fly->axis==0) fly->speed -= fly->grid;
497                                 fly->axis= 0;
498                                 break;
499                         case FLY_MODAL_DIR_DOWN:
500                                 if (fly->speed < 0.0f) fly->speed= -fly->speed;
501                                 else if (fly->axis==1) fly->speed += fly->grid;
502                                 fly->axis= 1;
503                                 break;
504                         case FLY_MODAL_DIR_UP:
505                                 if (fly->speed > 0.0f) fly->speed= -fly->speed;
506                                 else if (fly->axis==1) fly->speed -= fly->grid;
507                                 fly->axis= 1;
508                                 break;
509
510                         case FLY_MODAL_AXIS_LOCK_X:
511                                 if (fly->xlock) fly->xlock=0;
512                                 else {
513                                         fly->xlock = 2;
514                                         fly->xlock_momentum = 0.0;
515                                 }
516                                 break;
517                         case FLY_MODAL_AXIS_LOCK_Z:
518                                 if (fly->zlock) fly->zlock=0;
519                                 else {
520                                         fly->zlock = 2;
521                                         fly->zlock_momentum = 0.0;
522                                 }
523                                 break;
524
525                         case FLY_MODAL_PRECISION_ENABLE:
526                                 fly->use_precision= TRUE;
527                                 break;
528                         case FLY_MODAL_PRECISION_DISABLE:
529                                 fly->use_precision= FALSE;
530                                 break;
531
532                 }
533         }
534 }
535
536 static int flyApply(bContext *C, FlyInfo *fly)
537 {
538
539 #define FLY_ROTATE_FAC 2.5f /* more is faster */
540 #define FLY_ZUP_CORRECT_FAC 0.1f /* amount to correct per step */
541 #define FLY_ZUP_CORRECT_ACCEL 0.05f /* increase upright momentum each step */
542
543         /*
544         fly mode - Shift+F
545         a fly loop where the user can move move the view as if they are flying
546         */
547         RegionView3D *rv3d= fly->rv3d;
548         View3D *v3d = fly->v3d;
549         ARegion *ar = fly->ar;
550         Scene *scene= fly->scene;
551
552         float prev_view_mat[4][4];
553
554         float mat[3][3], /* 3x3 copy of the view matrix so we can move allong the view axis */
555         dvec[3]={0,0,0}, /* this is the direction thast added to the view offset per redraw */
556
557         /* Camera Uprighting variables */
558         upvec[3]={0,0,0}, /* stores the view's up vector */
559
560         moffset[2], /* mouse offset from the views center */
561         tmp_quat[4]; /* used for rotating the view */
562
563         int
564 //      cent_orig[2], /* view center */
565 //XXX- can avoid using //       cent[2], /* view center modified */
566         xmargin, ymargin; /* x and y margin are define the safe area where the mouses movement wont rotate the view */
567         unsigned char
568         apply_rotation= 1; /* if the user presses shift they can look about without movinf the direction there looking*/
569
570         if(fly->root_parent)
571                 ED_view3d_to_m4(prev_view_mat, fly->rv3d->ofs, fly->rv3d->viewquat, fly->rv3d->dist);
572
573         /* the dist defines a vector that is infront of the offset
574         to rotate the view about.
575         this is no good for fly mode because we
576         want to rotate about the viewers center.
577         but to correct the dist removal we must
578         alter offset so the view doesn't jump. */
579
580         xmargin= ar->winx/20.0f;
581         ymargin= ar->winy/20.0f;
582
583         // UNUSED
584         // cent_orig[0]= ar->winrct.xmin + ar->winx/2;
585         // cent_orig[1]= ar->winrct.ymin + ar->winy/2;
586
587         {
588
589                 /* mouse offset from the center */
590                 moffset[0]= fly->mval[0]- ar->winx/2;
591                 moffset[1]= fly->mval[1]- ar->winy/2;
592
593                 /* enforce a view margin */
594                 if (moffset[0]>xmargin)                 moffset[0]-=xmargin;
595                 else if (moffset[0] < -xmargin) moffset[0]+=xmargin;
596                 else                                                    moffset[0]=0;
597
598                 if (moffset[1]>ymargin)                 moffset[1]-=ymargin;
599                 else if (moffset[1] < -ymargin) moffset[1]+=ymargin;
600                 else                                                    moffset[1]=0;
601
602
603                 /* scale the mouse movement by this value - scales mouse movement to the view size
604                  * moffset[0]/(ar->winx-xmargin*2) - window size minus margin (same for y)
605                  *
606                  * the mouse moves isnt linear */
607
608                 if(moffset[0]) {
609                         moffset[0] /= ar->winx - (xmargin*2);
610                         moffset[0] *= fabsf(moffset[0]);
611                 }
612
613                 if(moffset[1]) {
614                         moffset[1] /= ar->winy - (ymargin*2);
615                         moffset[1] *= fabsf(moffset[1]);
616                 }
617
618                 /* Should we redraw? */
619                 if(fly->speed != 0.0f || moffset[0] || moffset[1] || fly->zlock || fly->xlock || dvec[0] || dvec[1] || dvec[2] ) {
620                         float dvec_tmp[3];
621                         double time_current; /*time how fast it takes for us to redraw, this is so simple scenes dont fly too fast */
622                         float time_redraw;
623                         float time_redraw_clamped;
624
625                         time_current= PIL_check_seconds_timer();
626                         time_redraw= (float)(time_current - fly->time_lastdraw);
627                         time_redraw_clamped= MIN2(0.05f, time_redraw); /* clamt the redraw time to avoid jitter in roll correction */
628                         fly->time_lastdraw= time_current;
629                         /*fprintf(stderr, "%f\n", time_redraw);*/ /* 0.002 is a small redraw 0.02 is larger */
630
631                         /* Scale the time to use shift to scale the speed down- just like
632                         shift slows many other areas of blender down */
633                         if (fly->use_precision)
634                                 fly->speed= fly->speed * (1.0f-time_redraw_clamped);
635
636                         copy_m3_m4(mat, rv3d->viewinv);
637
638                         if (fly->pan_view==TRUE) {
639                                 /* pan only */
640                                 dvec_tmp[0]= -moffset[0];
641                                 dvec_tmp[1]= -moffset[1];
642                                 dvec_tmp[2]= 0;
643
644                                 if (fly->use_precision) {
645                                         dvec_tmp[0] *= 0.1f;
646                                         dvec_tmp[1] *= 0.1f;
647                                 }
648
649                                 mul_m3_v3(mat, dvec_tmp);
650                                 mul_v3_fl(dvec_tmp, time_redraw * 200.0f * fly->grid);
651
652                         } else {
653                                 float roll; /* similar to the angle between the camera's up and the Z-up, but its very rough so just roll*/
654
655                                 /* rotate about the X axis- look up/down */
656                                 if (moffset[1]) {
657                                         upvec[0]=1;
658                                         upvec[1]=0;
659                                         upvec[2]=0;
660                                         mul_m3_v3(mat, upvec);
661                                         axis_angle_to_quat( tmp_quat, upvec, (float)moffset[1] * time_redraw * -FLY_ROTATE_FAC); /* Rotate about the relative up vec */
662                                         mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, tmp_quat);
663
664                                         if (fly->xlock) fly->xlock = 2; /*check for rotation*/
665                                         if (fly->zlock) fly->zlock = 2;
666                                         fly->xlock_momentum= 0.0f;
667                                 }
668
669                                 /* rotate about the Y axis- look left/right */
670                                 if (moffset[0]) {
671
672                                         /* if we're upside down invert the moffset */
673                                         upvec[0]=0;
674                                         upvec[1]=1;
675                                         upvec[2]=0;
676                                         mul_m3_v3(mat, upvec);
677
678                                         if(upvec[2] < 0.0f)
679                                                 moffset[0]= -moffset[0];
680
681                                         /* make the lock vectors */
682                                         if (fly->zlock) {
683                                                 upvec[0]=0;
684                                                 upvec[1]=0;
685                                                 upvec[2]=1;
686                                         } else {
687                                                 upvec[0]=0;
688                                                 upvec[1]=1;
689                                                 upvec[2]=0;
690                                                 mul_m3_v3(mat, upvec);
691                                         }
692
693                                         axis_angle_to_quat( tmp_quat, upvec, (float)moffset[0] * time_redraw * FLY_ROTATE_FAC); /* Rotate about the relative up vec */
694                                         mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, tmp_quat);
695
696                                         if (fly->xlock) fly->xlock = 2;/*check for rotation*/
697                                         if (fly->zlock) fly->zlock = 2;
698                                 }
699
700                                 if (fly->zlock==2) {
701                                         upvec[0]=1;
702                                         upvec[1]=0;
703                                         upvec[2]=0;
704                                         mul_m3_v3(mat, upvec);
705
706                                         /*make sure we have some z rolling*/
707                                         if (fabsf(upvec[2]) > 0.00001f) {
708                                                 roll= upvec[2]*5;
709                                                 upvec[0]=0; /*rotate the view about this axis*/
710                                                 upvec[1]=0;
711                                                 upvec[2]=1;
712
713                                                 mul_m3_v3(mat, upvec);
714                                                 axis_angle_to_quat( tmp_quat, upvec, roll*time_redraw_clamped*fly->zlock_momentum * FLY_ZUP_CORRECT_FAC); /* Rotate about the relative up vec */
715                                                 mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, tmp_quat);
716
717                                                 fly->zlock_momentum += FLY_ZUP_CORRECT_ACCEL;
718                                         } else {
719                                                 fly->zlock=1; /* dont check until the view rotates again */
720                                                 fly->zlock_momentum= 0.0f;
721                                         }
722                                 }
723
724                                 if (fly->xlock==2 && moffset[1]==0) { /*only apply xcorrect when mouse isnt applying x rot*/
725                                         upvec[0]=0;
726                                         upvec[1]=0;
727                                         upvec[2]=1;
728                                         mul_m3_v3(mat, upvec);
729                                         /*make sure we have some z rolling*/
730                                         if (fabs(upvec[2]) > 0.00001) {
731                                                 roll= upvec[2] * -5;
732
733                                                 upvec[0]= 1.0f; /*rotate the view about this axis*/
734                                                 upvec[1]= 0.0f;
735                                                 upvec[2]= 0.0f;
736
737                                                 mul_m3_v3(mat, upvec);
738
739                                                 axis_angle_to_quat( tmp_quat, upvec, roll*time_redraw_clamped*fly->xlock_momentum*0.1f); /* Rotate about the relative up vec */
740                                                 mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, tmp_quat);
741
742                                                 fly->xlock_momentum += 0.05f;
743                                         } else {
744                                                 fly->xlock=1; /* see above */
745                                                 fly->xlock_momentum= 0.0f;
746                                         }
747                                 }
748
749
750                                 if (apply_rotation) {
751                                         /* Normal operation */
752                                         /* define dvec, view direction vector */
753                                         dvec_tmp[0]= dvec_tmp[1]= dvec_tmp[2]= 0.0f;
754                                         /* move along the current axis */
755                                         dvec_tmp[fly->axis]= 1.0f;
756
757                                         mul_m3_v3(mat, dvec_tmp);
758
759                                         mul_v3_fl(dvec_tmp, fly->speed * time_redraw * 0.25f);
760                                 }
761                         }
762
763                         /* impose a directional lag */
764                         interp_v3_v3v3(dvec, dvec_tmp, fly->dvec_prev, (1.0f/(1.0f+(time_redraw*5.0f))));
765
766                         if (rv3d->persp==RV3D_CAMOB) {
767                                 Object *lock_ob= fly->root_parent ? fly->root_parent : fly->v3d->camera;
768                                 if (lock_ob->protectflag & OB_LOCK_LOCX) dvec[0] = 0.0;
769                                 if (lock_ob->protectflag & OB_LOCK_LOCY) dvec[1] = 0.0;
770                                 if (lock_ob->protectflag & OB_LOCK_LOCZ) dvec[2] = 0.0;
771                         }
772
773                         add_v3_v3(rv3d->ofs, dvec);
774
775                         /* todo, dynamic keys */
776 #if 0
777                         if (fly->zlock && fly->xlock)
778                                 ED_area_headerprint(fly->ar, "FlyKeys  Speed:(+/- | Wheel),  Upright Axis:X  on/Z on,   Slow:Shift,  Direction:WASDRF,  Ok:LMB,  Pan:MMB,  Cancel:RMB");
779                         else if (fly->zlock)
780                                 ED_area_headerprint(fly->ar, "FlyKeys  Speed:(+/- | Wheel),  Upright Axis:X off/Z on,   Slow:Shift,  Direction:WASDRF,  Ok:LMB,  Pan:MMB,  Cancel:RMB");
781                         else if (fly->xlock)
782                                 ED_area_headerprint(fly->ar, "FlyKeys  Speed:(+/- | Wheel),  Upright Axis:X  on/Z off,  Slow:Shift,  Direction:WASDRF,  Ok:LMB,  Pan:MMB,  Cancel:RMB");
783                         else
784                                 ED_area_headerprint(fly->ar, "FlyKeys  Speed:(+/- | Wheel),  Upright Axis:X off/Z off,  Slow:Shift,  Direction:WASDRF,  Ok:LMB,  Pan:MMB,  Cancel:RMB");
785 #endif
786
787                         /* we are in camera view so apply the view ofs and quat to the view matrix and set the camera to the view */
788                         if (rv3d->persp==RV3D_CAMOB) {
789                                 ID *id_key;
790                                 /* transform the parent or the camera? */
791                                 if(fly->root_parent) {
792                                         Object *ob_update;
793
794                                         float view_mat[4][4];
795                                         float prev_view_imat[4][4];
796                                         float diff_mat[4][4];
797                                         float parent_mat[4][4];
798
799                                         invert_m4_m4(prev_view_imat, prev_view_mat);
800                                         ED_view3d_to_m4(view_mat, rv3d->ofs, rv3d->viewquat, rv3d->dist);
801                                         mul_m4_m4m4(diff_mat, prev_view_imat, view_mat);
802                                         mul_m4_m4m4(parent_mat, fly->root_parent->obmat, diff_mat);
803                                         object_apply_mat4(fly->root_parent, parent_mat, TRUE, FALSE);
804
805                                         // where_is_object(scene, fly->root_parent);
806
807                                         ob_update= v3d->camera->parent;
808                                         while(ob_update) {
809                                                 DAG_id_tag_update(&ob_update->id, OB_RECALC_OB);
810                                                 ob_update= ob_update->parent;
811                                         }
812
813                                         copy_m4_m4(prev_view_mat, view_mat);
814
815                                         id_key= &fly->root_parent->id;
816
817                                 }
818                                 else {
819                                         float view_mat[4][4];
820                                         ED_view3d_to_m4(view_mat, rv3d->ofs, rv3d->viewquat, rv3d->dist);
821                                         object_apply_mat4(v3d->camera, view_mat, TRUE, FALSE);
822                                         id_key= &v3d->camera->id;
823                                 }
824
825                                 /* record the motion */
826                                 if (autokeyframe_cfra_can_key(scene, id_key)) {
827                                         ListBase dsources = {NULL, NULL};
828                                         
829                                         /* add datasource override for the camera object */
830                                         ANIM_relative_keyingset_add_source(&dsources, id_key, NULL, NULL); 
831                                         
832                                         /* insert keyframes 
833                                          *      1) on the first frame
834                                          *      2) on each subsequent frame
835                                          *              TODO: need to check in future that frame changed before doing this 
836                                          */
837                                         if (fly->xlock || fly->zlock || moffset[0] || moffset[1]) {
838                                                 KeyingSet *ks= ANIM_builtin_keyingset_get_named(NULL, "Rotation");
839                                                 ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, (float)CFRA);
840                                         }
841                                         if (fly->speed) {
842                                                 KeyingSet *ks= ANIM_builtin_keyingset_get_named(NULL, "Location");
843                                                 ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, (float)CFRA);
844                                         }
845                                         
846                                         /* free temp data */
847                                         BLI_freelistN(&dsources);
848                                 }
849                         }
850                 } else
851                         /*were not redrawing but we need to update the time else the view will jump */
852                         fly->time_lastdraw= PIL_check_seconds_timer();
853                 /* end drawing */
854                 copy_v3_v3(fly->dvec_prev, dvec);
855         }
856
857 /* moved to flyEnd() */
858
859         return OPERATOR_FINISHED;
860 }
861
862
863
864 static int fly_invoke(bContext *C, wmOperator *op, wmEvent *event)
865 {
866         RegionView3D *rv3d= CTX_wm_region_view3d(C);
867         FlyInfo *fly;
868
869         if(rv3d->viewlock)
870                 return OPERATOR_CANCELLED;
871
872         fly= MEM_callocN(sizeof(FlyInfo), "FlyOperation");
873
874         op->customdata= fly;
875
876         if(initFlyInfo(C, fly, op, event)==FALSE) {
877                 MEM_freeN(op->customdata);
878                 return OPERATOR_CANCELLED;
879         }
880
881         flyEvent(fly, event);
882
883         WM_event_add_modal_handler(C, op);
884
885         return OPERATOR_RUNNING_MODAL;
886 }
887
888 static int fly_cancel(bContext *C, wmOperator *op)
889 {
890         FlyInfo *fly = op->customdata;
891
892         fly->state = FLY_CANCEL;
893         flyEnd(C, fly);
894         op->customdata= NULL;
895
896         return OPERATOR_CANCELLED;
897 }
898
899 static int fly_modal(bContext *C, wmOperator *op, wmEvent *event)
900 {
901         int exit_code;
902         short do_draw= FALSE;
903         FlyInfo *fly= op->customdata;
904         RegionView3D *rv3d= fly->rv3d;
905         Object *fly_object= fly->root_parent ? fly->root_parent : fly->v3d->camera;
906
907         fly->redraw= 0;
908
909         flyEvent(fly, event);
910
911         if(event->type==TIMER && event->customdata == fly->timer)
912                 flyApply(C, fly);
913
914         do_draw |= fly->redraw;
915
916         exit_code = flyEnd(C, fly);
917
918         if(exit_code!=OPERATOR_RUNNING_MODAL)
919                 do_draw= TRUE;
920
921         if(do_draw) {
922                 if(rv3d->persp==RV3D_CAMOB) {
923                         WM_event_add_notifier(C, NC_OBJECT|ND_TRANSFORM, fly_object);
924                 }
925
926                 ED_region_tag_redraw(CTX_wm_region(C));
927         }
928
929         return exit_code;
930 }
931
932 void VIEW3D_OT_fly(wmOperatorType *ot)
933 {
934         /* identifiers */
935         ot->name= "Fly Navigation";
936         ot->description= "Interactively fly around the scene";
937         ot->idname= "VIEW3D_OT_fly";
938
939         /* api callbacks */
940         ot->invoke= fly_invoke;
941         ot->cancel= fly_cancel;
942         ot->modal= fly_modal;
943         ot->poll= ED_operator_view3d_active;
944
945         /* flags */
946         ot->flag= OPTYPE_BLOCKING;
947 }