Blender V2.61 - r43446

view3d_fly.c

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