Blender  V2.59
view3d_fly.c
Go to the documentation of this file.
00001 /*
00002  * $Id: view3d_fly.c 39111 2011-08-06 23:25:13Z merwin $
00003  *
00004  * ***** BEGIN GPL LICENSE BLOCK *****
00005  *
00006  * This program is free software; you can redistribute it and/or
00007  * modify it under the terms of the GNU General Public License
00008  * as published by the Free Software Foundation; either version 2
00009  * of the License, or (at your option) any later version. 
00010  *
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU General Public License
00017  * along with this program; if not, write to the Free Software Foundation,
00018  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00019  * 
00020  * Contributor(s): Campbell Barton
00021  *
00022  * ***** END GPL LICENSE BLOCK *****
00023  */
00024 
00030 /* defines VIEW3D_OT_fly modal operator */
00031 
00032 //#define NDOF_FLY_DEBUG
00033 //#define NDOF_FLY_DRAW_TOOMUCH // is this needed for ndof? - commented so redraw doesnt thrash - campbell
00034 
00035 #include "DNA_anim_types.h"
00036 #include "DNA_scene_types.h"
00037 #include "DNA_object_types.h"
00038 
00039 #include "MEM_guardedalloc.h"
00040 
00041 #include "BLI_math.h"
00042 #include "BLI_blenlib.h"
00043 #include "BLI_utildefines.h"
00044 
00045 #include "BKE_context.h"
00046 #include "BKE_object.h"
00047 #include "BKE_report.h"
00048 
00049 #include "BKE_depsgraph.h" /* for fly mode updating */
00050 
00051 #include "BIF_gl.h"
00052 
00053 #include "WM_api.h"
00054 #include "WM_types.h"
00055 
00056 #include "ED_keyframing.h"
00057 #include "ED_screen.h"
00058 #include "ED_space_api.h"
00059 
00060 #include "PIL_time.h" /* smoothview */
00061 
00062 #include "view3d_intern.h"      // own include
00063 
00064 /* NOTE: these defines are saved in keymap files, do not change values but just add new ones */
00065 #define FLY_MODAL_CANCEL                        1
00066 #define FLY_MODAL_CONFIRM                       2
00067 #define FLY_MODAL_ACCELERATE            3
00068 #define FLY_MODAL_DECELERATE            4
00069 #define FLY_MODAL_PAN_ENABLE            5
00070 #define FLY_MODAL_PAN_DISABLE           6
00071 #define FLY_MODAL_DIR_FORWARD           7
00072 #define FLY_MODAL_DIR_BACKWARD          8
00073 #define FLY_MODAL_DIR_LEFT                      9
00074 #define FLY_MODAL_DIR_RIGHT                     10
00075 #define FLY_MODAL_DIR_UP                        11
00076 #define FLY_MODAL_DIR_DOWN                      12
00077 #define FLY_MODAL_AXIS_LOCK_X           13
00078 #define FLY_MODAL_AXIS_LOCK_Z           14
00079 #define FLY_MODAL_PRECISION_ENABLE      15
00080 #define FLY_MODAL_PRECISION_DISABLE     16
00081 
00082 /* called in transform_ops.c, on each regeneration of keymaps  */
00083 void fly_modal_keymap(wmKeyConfig *keyconf)
00084 {
00085         static EnumPropertyItem modal_items[] = {
00086         {FLY_MODAL_CANCEL,      "CANCEL", 0, "Cancel", ""},
00087         {FLY_MODAL_CONFIRM,     "CONFIRM", 0, "Confirm", ""},
00088         {FLY_MODAL_ACCELERATE, "ACCELERATE", 0, "Accelerate", ""},
00089         {FLY_MODAL_DECELERATE, "DECELERATE", 0, "Decelerate", ""},
00090 
00091         {FLY_MODAL_PAN_ENABLE,  "PAN_ENABLE", 0, "Pan Enable", ""},
00092         {FLY_MODAL_PAN_DISABLE, "PAN_DISABLE", 0, "Pan Disable", ""},
00093 
00094         {FLY_MODAL_DIR_FORWARD, "FORWARD", 0, "Fly Forward", ""},
00095         {FLY_MODAL_DIR_BACKWARD,"BACKWARD", 0, "Fly Backward", ""},
00096         {FLY_MODAL_DIR_LEFT,    "LEFT", 0, "Fly Left", ""},
00097         {FLY_MODAL_DIR_RIGHT,   "RIGHT", 0, "Fly Right", ""},
00098         {FLY_MODAL_DIR_UP,              "UP", 0, "Fly Up", ""},
00099         {FLY_MODAL_DIR_DOWN,    "DOWN", 0, "Fly Down", ""},
00100 
00101         {FLY_MODAL_AXIS_LOCK_X, "AXIS_LOCK_X", 0, "X Axis Correction", "X axis correction (toggle)"},
00102         {FLY_MODAL_AXIS_LOCK_Z, "AXIS_LOCK_Z", 0, "X Axis Correction", "Z axis correction (toggle)"},
00103 
00104         {FLY_MODAL_PRECISION_ENABLE,    "PRECISION_ENABLE", 0, "Precision Enable", ""},
00105         {FLY_MODAL_PRECISION_DISABLE,   "PRECISION_DISABLE", 0, "Precision Disable", ""},
00106 
00107         {0, NULL, 0, NULL, NULL}};
00108 
00109         wmKeyMap *keymap= WM_modalkeymap_get(keyconf, "View3D Fly Modal");
00110 
00111         /* this function is called for each spacetype, only needs to add map once */
00112         if (keymap) return;
00113 
00114         keymap= WM_modalkeymap_add(keyconf, "View3D Fly Modal", modal_items);
00115 
00116         /* items for modal map */
00117         WM_modalkeymap_add_item(keymap, ESCKEY,    KM_PRESS, KM_ANY, 0, FLY_MODAL_CANCEL);
00118         WM_modalkeymap_add_item(keymap, RIGHTMOUSE, KM_ANY, KM_ANY, 0, FLY_MODAL_CANCEL);
00119 
00120         WM_modalkeymap_add_item(keymap, LEFTMOUSE, KM_ANY, KM_ANY, 0, FLY_MODAL_CONFIRM);
00121         WM_modalkeymap_add_item(keymap, RETKEY, KM_PRESS, KM_ANY, 0, FLY_MODAL_CONFIRM);
00122         WM_modalkeymap_add_item(keymap, SPACEKEY, KM_PRESS, KM_ANY, 0, FLY_MODAL_CONFIRM);
00123         WM_modalkeymap_add_item(keymap, PADENTER, KM_PRESS, KM_ANY, 0, FLY_MODAL_CONFIRM);
00124 
00125         WM_modalkeymap_add_item(keymap, PADPLUSKEY, KM_PRESS, 0, 0, FLY_MODAL_ACCELERATE);
00126         WM_modalkeymap_add_item(keymap, PADMINUS, KM_PRESS, 0, 0, FLY_MODAL_DECELERATE);
00127         WM_modalkeymap_add_item(keymap, WHEELUPMOUSE, KM_PRESS, 0, 0, FLY_MODAL_ACCELERATE);
00128         WM_modalkeymap_add_item(keymap, WHEELDOWNMOUSE, KM_PRESS, 0, 0, FLY_MODAL_DECELERATE);
00129 
00130         WM_modalkeymap_add_item(keymap, MIDDLEMOUSE, KM_PRESS, KM_ANY, 0, FLY_MODAL_PAN_ENABLE);
00131         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 */
00132 
00133         /* WASD */
00134         WM_modalkeymap_add_item(keymap, WKEY, KM_PRESS, 0, 0, FLY_MODAL_DIR_FORWARD);
00135         WM_modalkeymap_add_item(keymap, SKEY, KM_PRESS, 0, 0, FLY_MODAL_DIR_BACKWARD);
00136         WM_modalkeymap_add_item(keymap, AKEY, KM_PRESS, 0, 0, FLY_MODAL_DIR_LEFT);
00137         WM_modalkeymap_add_item(keymap, DKEY, KM_PRESS, 0, 0, FLY_MODAL_DIR_RIGHT);
00138         WM_modalkeymap_add_item(keymap, RKEY, KM_PRESS, 0, 0, FLY_MODAL_DIR_UP);
00139         WM_modalkeymap_add_item(keymap, FKEY, KM_PRESS, 0, 0, FLY_MODAL_DIR_DOWN);
00140 
00141         WM_modalkeymap_add_item(keymap, XKEY, KM_PRESS, 0, 0, FLY_MODAL_AXIS_LOCK_X);
00142         WM_modalkeymap_add_item(keymap, ZKEY, KM_PRESS, 0, 0, FLY_MODAL_AXIS_LOCK_Z);
00143 
00144         WM_modalkeymap_add_item(keymap, LEFTSHIFTKEY, KM_PRESS, KM_ANY, 0, FLY_MODAL_PRECISION_ENABLE);
00145         WM_modalkeymap_add_item(keymap, LEFTSHIFTKEY, KM_RELEASE, KM_ANY, 0, FLY_MODAL_PRECISION_DISABLE);
00146 
00147         /* assign map to operators */
00148         WM_modalkeymap_assign(keymap, "VIEW3D_OT_fly");
00149 }
00150 
00151 typedef struct FlyInfo {
00152         /* context stuff */
00153         RegionView3D *rv3d;
00154         View3D *v3d;
00155         ARegion *ar;
00156         Scene *scene;
00157 
00158         wmTimer *timer; /* needed for redraws */
00159 
00160         short state;
00161         short use_precision;
00162         short redraw;
00163 
00164         int mval[2]; /* latest 2D mouse values */
00165         wmNDOFMotionData* ndof; /* latest 3D mouse values */
00166 
00167         /* fly state state */
00168         float speed; /* the speed the view is moving per redraw */
00169         short axis; /* Axis index to move allong by default Z to move allong the view */
00170         short pan_view; /* when true, pan the view instead of rotating */
00171 
00172         /* relative view axis locking - xlock, zlock
00173         0; disabled
00174         1; enabled but not checking because mouse hasnt moved outside the margin since locking was checked an not needed
00175            when the mouse moves, locking is set to 2 so checks are done.
00176         2; mouse moved and checking needed, if no view altering is donem its changed back to 1 */
00177         short xlock, zlock;
00178         float xlock_momentum, zlock_momentum; /* nicer dynamics */
00179         float grid; /* world scale 1.0 default */
00180 
00181         /* root most parent */
00182         Object *root_parent;
00183 
00184         /* backup values */
00185         float dist_backup; /* backup the views distance since we use a zero dist for fly mode */
00186         float ofs_backup[3]; /* backup the views offset incase the user cancels flying in non camera mode */
00187         float rot_backup[4]; /* backup the views quat incase the user cancels flying in non camera mode. (quat for view, eul for camera) */
00188         short persp_backup; /* remember if were ortho or not, only used for restoring the view if it was a ortho view */
00189 
00190         void *obtfm; /* backup the objects transform */
00191 
00192         /* compare between last state */
00193         double time_lastwheel; /* used to accelerate when using the mousewheel a lot */
00194         double time_lastdraw; /* time between draws */
00195 
00196         void *draw_handle_pixel;
00197 
00198         /* use for some lag */
00199         float dvec_prev[3]; /* old for some lag */
00200 
00201 } FlyInfo;
00202 
00203 static void drawFlyPixel(const struct bContext *UNUSED(C), struct ARegion *UNUSED(ar), void *arg)
00204 {
00205         FlyInfo *fly = arg;
00206 
00207         /* draws 4 edge brackets that frame the safe area where the
00208         mouse can move during fly mode without spinning the view */
00209         float x1, x2, y1, y2;
00210         
00211         x1= 0.45f * (float)fly->ar->winx;
00212         y1= 0.45f * (float)fly->ar->winy;
00213         x2= 0.55f * (float)fly->ar->winx;
00214         y2= 0.55f * (float)fly->ar->winy;
00215         cpack(0);
00216         
00217         
00218         glBegin(GL_LINES);
00219         /* bottom left */
00220         glVertex2f(x1,y1); 
00221         glVertex2f(x1,y1+5);
00222         
00223         glVertex2f(x1,y1); 
00224         glVertex2f(x1+5,y1);
00225         
00226         /* top right */
00227         glVertex2f(x2,y2); 
00228         glVertex2f(x2,y2-5);
00229         
00230         glVertex2f(x2,y2); 
00231         glVertex2f(x2-5,y2);
00232         
00233         /* top left */
00234         glVertex2f(x1,y2); 
00235         glVertex2f(x1,y2-5);
00236         
00237         glVertex2f(x1,y2); 
00238         glVertex2f(x1+5,y2);
00239         
00240         /* bottom right */
00241         glVertex2f(x2,y1); 
00242         glVertex2f(x2,y1+5);
00243         
00244         glVertex2f(x2,y1); 
00245         glVertex2f(x2-5,y1);
00246         glEnd();
00247 }
00248 
00249 /* FlyInfo->state */
00250 #define FLY_RUNNING             0
00251 #define FLY_CANCEL              1
00252 #define FLY_CONFIRM             2
00253 
00254 static int initFlyInfo (bContext *C, FlyInfo *fly, wmOperator *op, wmEvent *event)
00255 {
00256         float upvec[3]; // tmp
00257         float mat[3][3];
00258 
00259         fly->rv3d= CTX_wm_region_view3d(C);
00260         fly->v3d = CTX_wm_view3d(C);
00261         fly->ar = CTX_wm_region(C);
00262         fly->scene= CTX_data_scene(C);
00263 
00264 #ifdef NDOF_FLY_DEBUG
00265         puts("\n-- fly begin --");
00266 #endif
00267 
00268         if (fly->rv3d->persp==RV3D_CAMOB && fly->v3d->camera->id.lib) {
00269                 BKE_report(op->reports, RPT_ERROR, "Cannot fly a camera from an external library");
00270                 return FALSE;
00271         }
00272 
00273         if (fly->v3d->ob_centre) {
00274                 BKE_report(op->reports, RPT_ERROR, "Cannot fly when the view is locked to an object");
00275                 return FALSE;
00276         }
00277 
00278         if (fly->rv3d->persp==RV3D_CAMOB && fly->v3d->camera->constraints.first) {
00279                 BKE_report(op->reports, RPT_ERROR, "Cannot fly an object with constraints");
00280                 return FALSE;
00281         }
00282 
00283         fly->state= FLY_RUNNING;
00284         fly->speed= 0.0f;
00285         fly->axis= 2;
00286         fly->pan_view= FALSE;
00287         fly->xlock= FALSE;
00288         fly->zlock= FALSE;
00289         fly->xlock_momentum=0.0f;
00290         fly->zlock_momentum=0.0f;
00291         fly->grid= 1.0f;
00292         fly->use_precision= 0;
00293 
00294 #ifdef NDOF_FLY_DRAW_TOOMUCH
00295         fly->redraw= 1;
00296 #endif
00297         fly->dvec_prev[0]= fly->dvec_prev[1]= fly->dvec_prev[2]= 0.0f;
00298 
00299         fly->timer= WM_event_add_timer(CTX_wm_manager(C), CTX_wm_window(C), TIMER, 0.01f);
00300 
00301         VECCOPY2D(fly->mval, event->mval)
00302         fly->ndof = NULL;
00303 
00304         fly->time_lastdraw= fly->time_lastwheel= PIL_check_seconds_timer();
00305 
00306         fly->draw_handle_pixel = ED_region_draw_cb_activate(fly->ar->type, drawFlyPixel, fly, REGION_DRAW_POST_PIXEL);
00307 
00308         fly->rv3d->rflag |= RV3D_NAVIGATING; /* so we draw the corner margins */
00309 
00310         /* detect weather to start with Z locking */
00311         upvec[0]=1.0f; upvec[1]=0.0f; upvec[2]=0.0f;
00312         copy_m3_m4(mat, fly->rv3d->viewinv);
00313         mul_m3_v3(mat, upvec);
00314         if (fabs(upvec[2]) < 0.1)
00315                 fly->zlock = 1;
00316         upvec[0]=0; upvec[1]=0; upvec[2]=0;
00317 
00318         fly->persp_backup= fly->rv3d->persp;
00319         fly->dist_backup= fly->rv3d->dist;
00320         if (fly->rv3d->persp==RV3D_CAMOB) {
00321                 Object *ob_back;
00322                 if ((U.uiflag & USER_CAM_LOCK_NO_PARENT)==0 && (fly->root_parent=fly->v3d->camera->parent)) {
00323                         while(fly->root_parent->parent)
00324                                 fly->root_parent= fly->root_parent->parent;
00325                         ob_back= fly->root_parent;
00326                 }
00327                 else {
00328                         ob_back= fly->v3d->camera;
00329                 }
00330 
00331                 /* store the original camera loc and rot */
00332                 /* TODO. axis angle etc */
00333 
00334                 fly->obtfm= object_tfm_backup(ob_back);
00335 
00336                 where_is_object(fly->scene, fly->v3d->camera);
00337                 negate_v3_v3(fly->rv3d->ofs, fly->v3d->camera->obmat[3]);
00338 
00339                 fly->rv3d->dist=0.0;
00340         }
00341         else {
00342                 /* perspective or ortho */
00343                 if (fly->rv3d->persp==RV3D_ORTHO)
00344                         fly->rv3d->persp= RV3D_PERSP; /*if ortho projection, make perspective */
00345 
00346                 copy_qt_qt(fly->rot_backup, fly->rv3d->viewquat);
00347                 copy_v3_v3(fly->ofs_backup, fly->rv3d->ofs);
00348 
00349                 /* the dist defines a vector that is infront of the offset
00350                 to rotate the view about.
00351                 this is no good for fly mode because we
00352                 want to rotate about the viewers center.
00353                 but to correct the dist removal we must
00354                 alter offset so the view doesn't jump. */
00355 
00356                 fly->rv3d->dist= 0.0f;
00357 
00358                 upvec[2]= fly->dist_backup; /*x and y are 0*/
00359                 mul_m3_v3(mat, upvec);
00360                 sub_v3_v3(fly->rv3d->ofs, upvec);
00361                 /*Done with correcting for the dist*/
00362         }
00363         
00364         /* center the mouse, probably the UI mafia are against this but without its quite annoying */
00365         WM_cursor_warp(CTX_wm_window(C), fly->ar->winrct.xmin + fly->ar->winx/2, fly->ar->winrct.ymin + fly->ar->winy/2);
00366         
00367         return 1;
00368 }
00369 
00370 static int flyEnd(bContext *C, FlyInfo *fly)
00371 {
00372         RegionView3D *rv3d= fly->rv3d;
00373         View3D *v3d = fly->v3d;
00374 
00375         float upvec[3];
00376 
00377         if (fly->state == FLY_RUNNING)
00378                 return OPERATOR_RUNNING_MODAL;
00379 
00380 #ifdef NDOF_FLY_DEBUG
00381         puts("\n-- fly end --");
00382 #endif
00383 
00384         WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), fly->timer);
00385 
00386         ED_region_draw_cb_exit(fly->ar->type, fly->draw_handle_pixel);
00387 
00388         rv3d->dist= fly->dist_backup;
00389 
00390         if (fly->state == FLY_CANCEL) {
00391         /* Revert to original view? */
00392                 if (fly->persp_backup==RV3D_CAMOB) { /* a camera view */
00393                         Object *ob_back;
00394                         ob_back= (fly->root_parent) ? fly->root_parent : fly->v3d->camera;
00395 
00396                         /* store the original camera loc and rot */
00397                         object_tfm_restore(ob_back, fly->obtfm);
00398 
00399                         DAG_id_tag_update(&ob_back->id, OB_RECALC_OB);
00400                 }
00401                 else {
00402                         /* Non Camera we need to reset the view back to the original location bacause the user canceled*/
00403                         copy_qt_qt(rv3d->viewquat, fly->rot_backup);
00404                         copy_v3_v3(rv3d->ofs, fly->ofs_backup);
00405                         rv3d->persp= fly->persp_backup;
00406                 }
00407         }
00408         else if (fly->persp_backup==RV3D_CAMOB) {       /* camera */
00409                 DAG_id_tag_update(fly->root_parent ? &fly->root_parent->id : &v3d->camera->id, OB_RECALC_OB);
00410         }
00411         else { /* not camera */
00412                 /* Apply the fly mode view */
00413                 /*restore the dist*/
00414                 float mat[3][3];
00415                 upvec[0]= upvec[1]= 0;
00416                 upvec[2]= fly->dist_backup; /*x and y are 0*/
00417                 copy_m3_m4(mat, rv3d->viewinv);
00418                 mul_m3_v3(mat, upvec);
00419                 add_v3_v3(rv3d->ofs, upvec);
00420                 /*Done with correcting for the dist */
00421         }
00422 
00423         rv3d->rflag &= ~RV3D_NAVIGATING;
00424 //XXX2.5        BIF_view3d_previewrender_signal(fly->sa, PR_DBASE|PR_DISPRECT); /* not working at the moment not sure why */
00425 
00426         if (fly->obtfm)
00427                 MEM_freeN(fly->obtfm);
00428 
00429         if (fly->ndof)
00430                 MEM_freeN(fly->ndof);
00431 
00432         if (fly->state == FLY_CONFIRM) {
00433                 MEM_freeN(fly);
00434                 return OPERATOR_FINISHED;
00435         }
00436 
00437         MEM_freeN(fly);
00438         return OPERATOR_CANCELLED;
00439 }
00440 
00441 static void flyEvent(FlyInfo *fly, wmEvent *event)
00442 {
00443         if (event->type == TIMER && event->customdata == fly->timer) {
00444                 fly->redraw = 1;
00445         }
00446         else if (event->type == MOUSEMOVE) {
00447                 VECCOPY2D(fly->mval, event->mval);
00448         }
00449         else if (event->type == NDOF_MOTION) {
00450                 // do these automagically get delivered? yes.
00451                 // puts("ndof motion detected in fly mode!");
00452                 // static const char* tag_name = "3D mouse position";
00453 
00454                 wmNDOFMotionData* incoming_ndof = (wmNDOFMotionData*) event->customdata;
00455                 switch (incoming_ndof->progress) {
00456                         case P_STARTING:
00457                                 // start keeping track of 3D mouse position
00458 #ifdef NDOF_FLY_DEBUG
00459                                 puts("start keeping track of 3D mouse position");
00460 #endif
00461                                 // fall through...
00462                         case P_IN_PROGRESS:
00463                                 // update 3D mouse position
00464 #ifdef NDOF_FLY_DEBUG
00465                                 putchar('.'); fflush(stdout);
00466 #endif
00467                                 if (fly->ndof == NULL) {
00468                                         // fly->ndof = MEM_mallocN(sizeof(wmNDOFMotionData), tag_name);
00469                                         fly->ndof = MEM_dupallocN(incoming_ndof);
00470                                         // fly->ndof = malloc(sizeof(wmNDOFMotionData));
00471                                 }
00472                                 else {
00473                                         memcpy(fly->ndof, incoming_ndof, sizeof(wmNDOFMotionData));
00474                                 }
00475                                 break;
00476                         case P_FINISHING:
00477                                 // stop keeping track of 3D mouse position
00478 #ifdef NDOF_FLY_DEBUG
00479                                 puts("stop keeping track of 3D mouse position");
00480 #endif
00481                                 if (fly->ndof) {
00482                                         MEM_freeN(fly->ndof);
00483                                         // free(fly->ndof);
00484                                         fly->ndof = NULL;
00485                                 }
00486                                 /* update the time else the view will jump when 2D mouse/timer resume */
00487                                 fly->time_lastdraw= PIL_check_seconds_timer();
00488                                 break;
00489                         default:
00490                                 ; // should always be one of the above 3
00491                         }
00492                 }
00493         /* handle modal keymap first */
00494         else if (event->type == EVT_MODAL_MAP) {
00495                 switch (event->val) {
00496                         case FLY_MODAL_CANCEL:
00497                                 fly->state = FLY_CANCEL;
00498                                 break;
00499                         case FLY_MODAL_CONFIRM:
00500                                 fly->state = FLY_CONFIRM;
00501                                 break;
00502 
00503                         case FLY_MODAL_ACCELERATE:
00504                         {
00505                                 double time_currwheel;
00506                                 float time_wheel;
00507 
00508                                 time_currwheel= PIL_check_seconds_timer();
00509                                 time_wheel = (float)(time_currwheel - fly->time_lastwheel);
00510                                 fly->time_lastwheel = time_currwheel;
00511                                 /*printf("Wheel %f\n", time_wheel);*/
00512                                 /*Mouse wheel delays range from 0.5==slow to 0.01==fast*/
00513                                 time_wheel = 1.0f + (10.0f - (20.0f * MIN2(time_wheel, 0.5f))); /* 0-0.5 -> 0-5.0 */
00514 
00515                                 if (fly->speed < 0.0f) {
00516                                         fly->speed= 0.0f;
00517                                 }
00518                                 else {
00519                                         if (event->shift)
00520                                                 fly->speed += fly->grid*time_wheel * 0.1f;
00521                                         else
00522                                                 fly->speed += fly->grid*time_wheel;
00523                                 }
00524                                 break;
00525                         }
00526                         case FLY_MODAL_DECELERATE:
00527                         {
00528                                 double time_currwheel;
00529                                 float time_wheel;
00530 
00531                                 time_currwheel= PIL_check_seconds_timer();
00532                                 time_wheel = (float)(time_currwheel - fly->time_lastwheel);
00533                                 fly->time_lastwheel = time_currwheel;
00534                                 time_wheel = 1.0f + (10.0f - (20.0f * MIN2(time_wheel, 0.5f))); /* 0-0.5 -> 0-5.0 */
00535 
00536                                 if (fly->speed > 0.0f) {
00537                                         fly->speed=0;
00538                                 }
00539                                 else {
00540                                         if (event->shift)
00541                                                 fly->speed-= fly->grid*time_wheel * 0.1f;
00542                                         else
00543                                                 fly->speed-= fly->grid*time_wheel;
00544                                 }
00545                                 break;
00546                         }
00547                         case FLY_MODAL_PAN_ENABLE:
00548                                 fly->pan_view= TRUE;
00549                                 break;
00550                         case FLY_MODAL_PAN_DISABLE:
00551 //XXX2.5                warp_pointer(cent_orig[0], cent_orig[1]);
00552                                 fly->pan_view= FALSE;
00553                                 break;
00554 
00555                                 /* impliment WASD keys */
00556                         case FLY_MODAL_DIR_FORWARD:
00557                                 if (fly->speed < 0.0f) fly->speed= -fly->speed; /* flip speed rather than stopping, game like motion */
00558                                 else if (fly->axis==2) fly->speed += fly->grid; /* increse like mousewheel if were already moving in that difection*/
00559                                 fly->axis= 2;
00560                                 break;
00561                         case FLY_MODAL_DIR_BACKWARD:
00562                                 if (fly->speed > 0.0f) fly->speed= -fly->speed;
00563                                 else if (fly->axis==2) fly->speed -= fly->grid;
00564                                 fly->axis= 2;
00565                                 break;
00566                         case FLY_MODAL_DIR_LEFT:
00567                                 if (fly->speed < 0.0f) fly->speed= -fly->speed;
00568                                 else if (fly->axis==0) fly->speed += fly->grid;
00569                                 fly->axis= 0;
00570                                 break;
00571                         case FLY_MODAL_DIR_RIGHT:
00572                                 if (fly->speed > 0.0f) fly->speed= -fly->speed;
00573                                 else if (fly->axis==0) fly->speed -= fly->grid;
00574                                 fly->axis= 0;
00575                                 break;
00576                         case FLY_MODAL_DIR_DOWN:
00577                                 if (fly->speed < 0.0f) fly->speed= -fly->speed;
00578                                 else if (fly->axis==1) fly->speed += fly->grid;
00579                                 fly->axis= 1;
00580                                 break;
00581                         case FLY_MODAL_DIR_UP:
00582                                 if (fly->speed > 0.0f) fly->speed= -fly->speed;
00583                                 else if (fly->axis==1) fly->speed -= fly->grid;
00584                                 fly->axis= 1;
00585                                 break;
00586 
00587                         case FLY_MODAL_AXIS_LOCK_X:
00588                                 if (fly->xlock) fly->xlock=0;
00589                                 else {
00590                                         fly->xlock = 2;
00591                                         fly->xlock_momentum = 0.0;
00592                                 }
00593                                 break;
00594                         case FLY_MODAL_AXIS_LOCK_Z:
00595                                 if (fly->zlock) fly->zlock=0;
00596                                 else {
00597                                         fly->zlock = 2;
00598                                         fly->zlock_momentum = 0.0;
00599                                 }
00600                                 break;
00601 
00602                         case FLY_MODAL_PRECISION_ENABLE:
00603                                 fly->use_precision= TRUE;
00604                                 break;
00605                         case FLY_MODAL_PRECISION_DISABLE:
00606                                 fly->use_precision= FALSE;
00607                                 break;
00608                 }
00609         }
00610 }
00611 
00612 
00613 static void move_camera(bContext* C, RegionView3D* rv3d, FlyInfo* fly, int orientationChanged, int positionChanged)
00614 {
00615         /* we are in camera view so apply the view ofs and quat to the view matrix and set the camera to the view */
00616 
00617         View3D* v3d = fly->v3d;
00618         Scene *scene= fly->scene;
00619         ID *id_key;
00620 
00621         /* transform the parent or the camera? */
00622         if (fly->root_parent) {
00623                 Object *ob_update;
00624 
00625                 float view_mat[4][4];
00626                 float prev_view_mat[4][4];
00627                 float prev_view_imat[4][4];
00628                 float diff_mat[4][4];
00629                 float parent_mat[4][4];
00630 
00631                 ED_view3d_to_m4(prev_view_mat, fly->rv3d->ofs, fly->rv3d->viewquat, fly->rv3d->dist);
00632                 invert_m4_m4(prev_view_imat, prev_view_mat);
00633                 ED_view3d_to_m4(view_mat, rv3d->ofs, rv3d->viewquat, rv3d->dist);
00634                 mul_m4_m4m4(diff_mat, prev_view_imat, view_mat);
00635                 mul_m4_m4m4(parent_mat, fly->root_parent->obmat, diff_mat);
00636                 object_apply_mat4(fly->root_parent, parent_mat, TRUE, FALSE);
00637 
00638                 // where_is_object(scene, fly->root_parent);
00639 
00640                 ob_update= v3d->camera->parent;
00641                 while(ob_update) {
00642                         DAG_id_tag_update(&ob_update->id, OB_RECALC_OB);
00643                         ob_update= ob_update->parent;
00644                 }
00645 
00646                 id_key= &fly->root_parent->id;
00647         }
00648         else {
00649                 float view_mat[4][4];
00650                 ED_view3d_to_m4(view_mat, rv3d->ofs, rv3d->viewquat, rv3d->dist);
00651                 object_apply_mat4(v3d->camera, view_mat, TRUE, FALSE);
00652                 id_key= &v3d->camera->id;
00653         }
00654 
00655         /* record the motion */
00656         if (autokeyframe_cfra_can_key(scene, id_key)) {
00657                 ListBase dsources = {NULL, NULL};
00658                 
00659                 /* add datasource override for the camera object */
00660                 ANIM_relative_keyingset_add_source(&dsources, id_key, NULL, NULL); 
00661                 
00662                 /* insert keyframes 
00663                  *      1) on the first frame
00664                  *      2) on each subsequent frame
00665                  *              TODO: need to check in future that frame changed before doing this 
00666                  */
00667                 if (orientationChanged) {
00668                         KeyingSet *ks= ANIM_builtin_keyingset_get_named(NULL, "Rotation");
00669                         ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, (float)CFRA);
00670                 }
00671                 if (positionChanged) {
00672                         KeyingSet *ks= ANIM_builtin_keyingset_get_named(NULL, "Location");
00673                         ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, (float)CFRA);
00674                 }
00675                 
00676                 /* free temp data */
00677                 BLI_freelistN(&dsources);
00678         }
00679 }
00680 
00681 static int flyApply(bContext *C, FlyInfo *fly)
00682 {
00683 #define FLY_ROTATE_FAC 2.5f /* more is faster */
00684 #define FLY_ZUP_CORRECT_FAC 0.1f /* amount to correct per step */
00685 #define FLY_ZUP_CORRECT_ACCEL 0.05f /* increase upright momentum each step */
00686 
00687         /*
00688         fly mode - Shift+F
00689         a fly loop where the user can move move the view as if they are flying
00690         */
00691         RegionView3D *rv3d= fly->rv3d;
00692         ARegion *ar = fly->ar;
00693 
00694         float mat[3][3], /* 3x3 copy of the view matrix so we can move allong the view axis */
00695         dvec[3]={0,0,0}, /* this is the direction thast added to the view offset per redraw */
00696 
00697         /* Camera Uprighting variables */
00698         upvec[3]={0,0,0}, /* stores the view's up vector */
00699 
00700         moffset[2], /* mouse offset from the views center */
00701         tmp_quat[4]; /* used for rotating the view */
00702 
00703         int
00704 //      cent_orig[2], /* view center */
00705 //XXX- can avoid using //       cent[2], /* view center modified */
00706         xmargin, ymargin; /* x and y margin are define the safe area where the mouses movement wont rotate the view */
00707         unsigned char
00708         apply_rotation= 1; /* if the user presses shift they can look about without movinf the direction there looking*/
00709 
00710 #ifdef NDOF_FLY_DEBUG
00711         static unsigned int iteration = 1;
00712         printf("fly timer %d\n", iteration++);
00713 #endif
00714 
00715 
00716         xmargin= ar->winx/20.0f;
00717         ymargin= ar->winy/20.0f;
00718 
00719         // UNUSED
00720         // cent_orig[0]= ar->winrct.xmin + ar->winx/2;
00721         // cent_orig[1]= ar->winrct.ymin + ar->winy/2;
00722 
00723         {
00724 
00725                 /* mouse offset from the center */
00726                 moffset[0]= fly->mval[0]- ar->winx/2;
00727                 moffset[1]= fly->mval[1]- ar->winy/2;
00728 
00729                 /* enforce a view margin */
00730                 if (moffset[0]>xmargin)                 moffset[0]-=xmargin;
00731                 else if (moffset[0] < -xmargin) moffset[0]+=xmargin;
00732                 else                                                    moffset[0]=0;
00733 
00734                 if (moffset[1]>ymargin)                 moffset[1]-=ymargin;
00735                 else if (moffset[1] < -ymargin) moffset[1]+=ymargin;
00736                 else                                                    moffset[1]=0;
00737 
00738 
00739                 /* scale the mouse movement by this value - scales mouse movement to the view size
00740                  * moffset[0]/(ar->winx-xmargin*2) - window size minus margin (same for y)
00741                  *
00742                  * the mouse moves isnt linear */
00743 
00744                 if (moffset[0]) {
00745                         moffset[0] /= ar->winx - (xmargin*2);
00746                         moffset[0] *= fabsf(moffset[0]);
00747                 }
00748 
00749                 if (moffset[1]) {
00750                         moffset[1] /= ar->winy - (ymargin*2);
00751                         moffset[1] *= fabsf(moffset[1]);
00752                 }
00753 
00754                 /* Should we redraw? */
00755                 if (fly->speed != 0.0f || moffset[0] || moffset[1] || fly->zlock || fly->xlock || dvec[0] || dvec[1] || dvec[2] ) {
00756                         float dvec_tmp[3];
00757                         double time_current; /*time how fast it takes for us to redraw, this is so simple scenes dont fly too fast */
00758                         float time_redraw;
00759                         float time_redraw_clamped;
00760 #ifdef NDOF_FLY_DRAW_TOOMUCH
00761                         fly->redraw= 1;
00762 #endif
00763                         time_current= PIL_check_seconds_timer();
00764                         time_redraw= (float)(time_current - fly->time_lastdraw);
00765                         time_redraw_clamped= MIN2(0.05f, time_redraw); /* clamt the redraw time to avoid jitter in roll correction */
00766                         fly->time_lastdraw= time_current;
00767                         /*fprintf(stderr, "%f\n", time_redraw);*/ /* 0.002 is a small redraw 0.02 is larger */
00768 
00769                         /* Scale the time to use shift to scale the speed down- just like
00770                         shift slows many other areas of blender down */
00771                         if (fly->use_precision)
00772                                 fly->speed= fly->speed * (1.0f-time_redraw_clamped);
00773 
00774                         copy_m3_m4(mat, rv3d->viewinv);
00775 
00776                         if (fly->pan_view==TRUE) {
00777                                 /* pan only */
00778                                 dvec_tmp[0]= -moffset[0];
00779                                 dvec_tmp[1]= -moffset[1];
00780                                 dvec_tmp[2]= 0;
00781 
00782                                 if (fly->use_precision) {
00783                                         dvec_tmp[0] *= 0.1f;
00784                                         dvec_tmp[1] *= 0.1f;
00785                                 }
00786 
00787                                 mul_m3_v3(mat, dvec_tmp);
00788                                 mul_v3_fl(dvec_tmp, time_redraw * 200.0f * fly->grid);
00789                         }
00790                         else {
00791                                 float roll; /* similar to the angle between the camera's up and the Z-up, but its very rough so just roll*/
00792 
00793                                 /* rotate about the X axis- look up/down */
00794                                 if (moffset[1]) {
00795                                         upvec[0]=1;
00796                                         upvec[1]=0;
00797                                         upvec[2]=0;
00798                                         mul_m3_v3(mat, upvec);
00799                                         axis_angle_to_quat( tmp_quat, upvec, (float)moffset[1] * time_redraw * -FLY_ROTATE_FAC); /* Rotate about the relative up vec */
00800                                         mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, tmp_quat);
00801 
00802                                         if (fly->xlock) fly->xlock = 2; /*check for rotation*/
00803                                         if (fly->zlock) fly->zlock = 2;
00804                                         fly->xlock_momentum= 0.0f;
00805                                 }
00806 
00807                                 /* rotate about the Y axis- look left/right */
00808                                 if (moffset[0]) {
00809 
00810                                         /* if we're upside down invert the moffset */
00811                                         upvec[0]= 0.0f;
00812                                         upvec[1]= 1.0f;
00813                                         upvec[2]= 0.0f;
00814                                         mul_m3_v3(mat, upvec);
00815 
00816                                         if (upvec[2] < 0.0f)
00817                                                 moffset[0]= -moffset[0];
00818 
00819                                         /* make the lock vectors */
00820                                         if (fly->zlock) {
00821                                                 upvec[0]= 0.0f;
00822                                                 upvec[1]= 0.0f;
00823                                                 upvec[2]= 1.0f;
00824                                         }
00825                                         else {
00826                                                 upvec[0]= 0.0f;
00827                                                 upvec[1]= 1.0f;
00828                                                 upvec[2]= 0.0f;
00829                                                 mul_m3_v3(mat, upvec);
00830                                         }
00831 
00832                                         axis_angle_to_quat(tmp_quat, upvec, (float)moffset[0] * time_redraw * FLY_ROTATE_FAC); /* Rotate about the relative up vec */
00833                                         mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, tmp_quat);
00834 
00835                                         if (fly->xlock) fly->xlock = 2;/*check for rotation*/
00836                                         if (fly->zlock) fly->zlock = 2;
00837                                 }
00838 
00839                                 if (fly->zlock==2) {
00840                                         upvec[0]= 1.0f;
00841                                         upvec[1]= 0.0f;
00842                                         upvec[2]= 0.0f;
00843                                         mul_m3_v3(mat, upvec);
00844 
00845                                         /*make sure we have some z rolling*/
00846                                         if (fabsf(upvec[2]) > 0.00001f) {
00847                                                 roll= upvec[2] * 5.0f;
00848                                                 upvec[0]= 0.0f; /*rotate the view about this axis*/
00849                                                 upvec[1]= 0.0f;
00850                                                 upvec[2]= 1.0f;
00851 
00852                                                 mul_m3_v3(mat, upvec);
00853                                                 axis_angle_to_quat( tmp_quat, upvec, roll*time_redraw_clamped*fly->zlock_momentum * FLY_ZUP_CORRECT_FAC); /* Rotate about the relative up vec */
00854                                                 mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, tmp_quat);
00855 
00856                                                 fly->zlock_momentum += FLY_ZUP_CORRECT_ACCEL;
00857                                         }
00858                                         else {
00859                                                 fly->zlock= 1; /* dont check until the view rotates again */
00860                                                 fly->zlock_momentum= 0.0f;
00861                                         }
00862                                 }
00863 
00864                                 if (fly->xlock==2 && moffset[1]==0) { /*only apply xcorrect when mouse isnt applying x rot*/
00865                                         upvec[0]=0;
00866                                         upvec[1]=0;
00867                                         upvec[2]=1;
00868                                         mul_m3_v3(mat, upvec);
00869                                         /*make sure we have some z rolling*/
00870                                         if (fabs(upvec[2]) > 0.00001f) {
00871                                                 roll= upvec[2] * -5.0f;
00872 
00873                                                 upvec[0]= 1.0f; /*rotate the view about this axis*/
00874                                                 upvec[1]= 0.0f;
00875                                                 upvec[2]= 0.0f;
00876 
00877                                                 mul_m3_v3(mat, upvec);
00878 
00879                                                 axis_angle_to_quat( tmp_quat, upvec, roll*time_redraw_clamped*fly->xlock_momentum*0.1f); /* Rotate about the relative up vec */
00880                                                 mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, tmp_quat);
00881 
00882                                                 fly->xlock_momentum += 0.05f;
00883                                         }
00884                                         else {
00885                                                 fly->xlock=1; /* see above */
00886                                                 fly->xlock_momentum= 0.0f;
00887                                         }
00888                                 }
00889 
00890 
00891                                 if (apply_rotation) {
00892                                         /* Normal operation */
00893                                         /* define dvec, view direction vector */
00894                                         dvec_tmp[0]= dvec_tmp[1]= dvec_tmp[2]= 0.0f;
00895                                         /* move along the current axis */
00896                                         dvec_tmp[fly->axis]= 1.0f;
00897 
00898                                         mul_m3_v3(mat, dvec_tmp);
00899 
00900                                         mul_v3_fl(dvec_tmp, fly->speed * time_redraw * 0.25f);
00901                                 }
00902                         }
00903 
00904                         /* impose a directional lag */
00905                         interp_v3_v3v3(dvec, dvec_tmp, fly->dvec_prev, (1.0f/(1.0f+(time_redraw*5.0f))));
00906 
00907                         if (rv3d->persp==RV3D_CAMOB) {
00908                                 Object *lock_ob= fly->root_parent ? fly->root_parent : fly->v3d->camera;
00909                                 if (lock_ob->protectflag & OB_LOCK_LOCX) dvec[0] = 0.0;
00910                                 if (lock_ob->protectflag & OB_LOCK_LOCY) dvec[1] = 0.0;
00911                                 if (lock_ob->protectflag & OB_LOCK_LOCZ) dvec[2] = 0.0;
00912                         }
00913 
00914                         add_v3_v3(rv3d->ofs, dvec);
00915 
00916                         /* todo, dynamic keys */
00917 #if 0
00918                         if (fly->zlock && fly->xlock)
00919                                 ED_area_headerprint(fly->ar, "FlyKeys  Speed:(+/- | Wheel),  Upright Axis:X  on/Z on,   Slow:Shift,  Direction:WASDRF,  Ok:LMB,  Pan:MMB,  Cancel:RMB");
00920                         else if (fly->zlock)
00921                                 ED_area_headerprint(fly->ar, "FlyKeys  Speed:(+/- | Wheel),  Upright Axis:X off/Z on,   Slow:Shift,  Direction:WASDRF,  Ok:LMB,  Pan:MMB,  Cancel:RMB");
00922                         else if (fly->xlock)
00923                                 ED_area_headerprint(fly->ar, "FlyKeys  Speed:(+/- | Wheel),  Upright Axis:X  on/Z off,  Slow:Shift,  Direction:WASDRF,  Ok:LMB,  Pan:MMB,  Cancel:RMB");
00924                         else
00925                                 ED_area_headerprint(fly->ar, "FlyKeys  Speed:(+/- | Wheel),  Upright Axis:X off/Z off,  Slow:Shift,  Direction:WASDRF,  Ok:LMB,  Pan:MMB,  Cancel:RMB");
00926 #endif
00927 
00928                         if (rv3d->persp==RV3D_CAMOB)
00929                                 move_camera(C, rv3d, fly, (fly->xlock || fly->zlock || moffset[0] || moffset[1]), fly->speed);
00930 
00931                 }
00932                 else {
00933                         /* we're not redrawing but we need to update the time else the view will jump */
00934                         fly->time_lastdraw= PIL_check_seconds_timer();
00935                 }
00936                 /* end drawing */
00937                 copy_v3_v3(fly->dvec_prev, dvec);
00938         }
00939 
00940         return OPERATOR_FINISHED;
00941 }
00942 
00943 static int flyApply_ndof(bContext *C, FlyInfo *fly)
00944 {
00945         /* shorthand for oft-used variables */
00946         wmNDOFMotionData* ndof = fly->ndof;
00947         const float dt = ndof->dt;
00948         RegionView3D* rv3d = fly->rv3d;
00949         const int flag = U.ndof_flag;
00950 
00951 /*      int shouldRotate = (flag & NDOF_SHOULD_ROTATE) && (fly->pan_view == FALSE),
00952             shouldTranslate = (flag & (NDOF_SHOULD_PAN | NDOF_SHOULD_ZOOM)); */
00953 
00954         int shouldRotate = (fly->pan_view == FALSE),
00955             shouldTranslate = TRUE;
00956 
00957         float view_inv[4];
00958         invert_qt_qt(view_inv, rv3d->viewquat);
00959 
00960         rv3d->rot_angle = 0.f; // disable onscreen rotation doo-dad
00961 
00962         if (shouldTranslate) {
00963                 const float forward_sensitivity = 1.f;
00964                 const float vertical_sensitivity = 0.4f;
00965                 const float lateral_sensitivity = 0.6f;
00966 
00967                 float speed = 10.f; /* blender units per second */
00968                 /* ^^ this is ok for default cube scene, but should scale with.. something */
00969 
00970                 float trans[3] = {
00971                         lateral_sensitivity * ndof->tvec[0],
00972                         vertical_sensitivity * ndof->tvec[1],
00973                         forward_sensitivity * ndof->tvec[2]
00974                         };
00975 
00976                 if (fly->use_precision)
00977                         speed *= 0.2f;
00978 
00979                 mul_v3_fl(trans, speed * dt);
00980 
00981                 // transform motion from view to world coordinates
00982                 mul_qt_v3(view_inv, trans);
00983 
00984                 if (flag & NDOF_FLY_HELICOPTER) {
00985                         /* replace world z component with device y (yes it makes sense) */
00986                         trans[2] = speed * dt * vertical_sensitivity * ndof->tvec[1];
00987                 }
00988 
00989                 if (rv3d->persp==RV3D_CAMOB) {
00990                         // respect camera position locks
00991                         Object *lock_ob= fly->root_parent ? fly->root_parent : fly->v3d->camera;
00992                         if (lock_ob->protectflag & OB_LOCK_LOCX) trans[0] = 0.f;
00993                         if (lock_ob->protectflag & OB_LOCK_LOCY) trans[1] = 0.f;
00994                         if (lock_ob->protectflag & OB_LOCK_LOCZ) trans[2] = 0.f;
00995                 }
00996 
00997                 if (!is_zero_v3(trans)) {
00998                         // move center of view opposite of hand motion (this is camera mode, not object mode)
00999                         sub_v3_v3(rv3d->ofs, trans);
01000                         shouldTranslate = TRUE;
01001                 }
01002                 else {
01003                         shouldTranslate = FALSE;
01004                 }
01005         }
01006 
01007         if (shouldRotate) {
01008                 const float turn_sensitivity = 1.f;
01009 
01010                 float rotation[4];
01011                 float axis[3];
01012                 float angle = turn_sensitivity * ndof_to_axis_angle(ndof, axis);
01013 
01014                 if (fabsf(angle) > 0.0001f) {
01015                         shouldRotate = TRUE;
01016 
01017                         if (fly->use_precision)
01018                                 angle *= 0.2f;
01019 
01020                         /* transform rotation axis from view to world coordinates */
01021                         mul_qt_v3(view_inv, axis);
01022 
01023                         // apply rotation to view
01024                         axis_angle_to_quat(rotation, axis, angle);
01025                         mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, rotation);
01026 
01027                         if (flag & NDOF_LOCK_HORIZON) {
01028                                 /* force an upright viewpoint
01029                                  * TODO: make this less... sudden */
01030                                 float view_horizon[3] = {1.f, 0.f, 0.f}; /* view +x */
01031                                 float view_direction[3] = {0.f, 0.f, -1.f}; /* view -z (into screen) */
01032 
01033                                 /* find new inverse since viewquat has changed */
01034                                 invert_qt_qt(view_inv, rv3d->viewquat);
01035                                 /* could apply reverse rotation to existing view_inv to save a few cycles */
01036 
01037                                 /* transform view vectors to world coordinates */
01038                                 mul_qt_v3(view_inv, view_horizon);
01039                                 mul_qt_v3(view_inv, view_direction);
01040 
01041                                 /* find difference between view & world horizons
01042                                  * true horizon lives in world xy plane, so look only at difference in z */
01043                                 angle = -asinf(view_horizon[2]);
01044 
01045 #ifdef NDOF_FLY_DEBUG
01046                                 printf("lock horizon: adjusting %.1f degrees\n\n", RAD2DEG(angle));
01047 #endif
01048 
01049                                 /* rotate view so view horizon = world horizon */
01050                                 axis_angle_to_quat(rotation, view_direction, angle);
01051                                 mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, rotation);
01052                         }
01053 
01054                         rv3d->view = RV3D_VIEW_USER;
01055                 }
01056                 else {
01057                         shouldRotate = FALSE;
01058                 }
01059         }
01060 
01061         if (shouldTranslate || shouldRotate) {
01062                 fly->redraw = TRUE;
01063 
01064                 if (rv3d->persp==RV3D_CAMOB) {
01065                         move_camera(C, rv3d, fly, shouldRotate, shouldTranslate);
01066                 }
01067         }
01068 
01069         return OPERATOR_FINISHED;
01070 }
01071 
01072 
01073 static int fly_invoke(bContext *C, wmOperator *op, wmEvent *event)
01074 {
01075         RegionView3D *rv3d= CTX_wm_region_view3d(C);
01076         FlyInfo *fly;
01077 
01078         if (rv3d->viewlock)
01079                 return OPERATOR_CANCELLED;
01080 
01081         fly= MEM_callocN(sizeof(FlyInfo), "FlyOperation");
01082 
01083         op->customdata= fly;
01084 
01085         if (initFlyInfo(C, fly, op, event)==FALSE) {
01086                 MEM_freeN(op->customdata);
01087                 return OPERATOR_CANCELLED;
01088         }
01089 
01090         flyEvent(fly, event);
01091 
01092         WM_event_add_modal_handler(C, op);
01093 
01094         return OPERATOR_RUNNING_MODAL;
01095 }
01096 
01097 static int fly_cancel(bContext *C, wmOperator *op)
01098 {
01099         FlyInfo *fly = op->customdata;
01100 
01101         fly->state = FLY_CANCEL;
01102         flyEnd(C, fly);
01103         op->customdata= NULL;
01104 
01105         return OPERATOR_CANCELLED;
01106 }
01107 
01108 static int fly_modal(bContext *C, wmOperator *op, wmEvent *event)
01109 {
01110         int exit_code;
01111         short do_draw= FALSE;
01112         FlyInfo *fly= op->customdata;
01113         RegionView3D *rv3d= fly->rv3d;
01114         Object *fly_object= fly->root_parent ? fly->root_parent : fly->v3d->camera;
01115 
01116         fly->redraw= 0;
01117 
01118         flyEvent(fly, event);
01119 
01120         if (fly->ndof) { /* 3D mouse overrules [2D mouse + timer] */
01121                 if (event->type==NDOF_MOTION) {
01122                         flyApply_ndof(C, fly);
01123                 }
01124         }
01125         else if (event->type==TIMER && event->customdata == fly->timer) {
01126                 flyApply(C, fly);
01127         }
01128 
01129         do_draw |= fly->redraw;
01130 
01131         exit_code = flyEnd(C, fly);
01132 
01133         if (exit_code!=OPERATOR_RUNNING_MODAL)
01134                 do_draw= TRUE;
01135 
01136         if (do_draw) {
01137                 if (rv3d->persp==RV3D_CAMOB) {
01138                         WM_event_add_notifier(C, NC_OBJECT|ND_TRANSFORM, fly_object);
01139                 }
01140 
01141                 // puts("redraw!"); // too frequent, commented with NDOF_FLY_DRAW_TOOMUCH for now
01142                 ED_region_tag_redraw(CTX_wm_region(C));
01143         }
01144 
01145         return exit_code;
01146 }
01147 
01148 void VIEW3D_OT_fly(wmOperatorType *ot)
01149 {
01150         /* identifiers */
01151         ot->name= "Fly Navigation";
01152         ot->description= "Interactively fly around the scene";
01153         ot->idname= "VIEW3D_OT_fly";
01154 
01155         /* api callbacks */
01156         ot->invoke= fly_invoke;
01157         ot->cancel= fly_cancel;
01158         ot->modal= fly_modal;
01159         ot->poll= ED_operator_view3d_active;
01160 
01161         /* flags */
01162         ot->flag= OPTYPE_BLOCKING;
01163 }