diff options
Diffstat (limited to 'source/blender/editors/space_view3d/view3d_fly.c')
-rw-r--r-- | source/blender/editors/space_view3d/view3d_fly.c | 377 |
1 files changed, 290 insertions, 87 deletions
diff --git a/source/blender/editors/space_view3d/view3d_fly.c b/source/blender/editors/space_view3d/view3d_fly.c index ed1ed5b3881..38d93ab59f3 100644 --- a/source/blender/editors/space_view3d/view3d_fly.c +++ b/source/blender/editors/space_view3d/view3d_fly.c @@ -29,6 +29,8 @@ /* defines VIEW3D_OT_fly modal operator */ +//#define NDOF_FLY_DEBUG + #include "DNA_anim_types.h" #include "DNA_scene_types.h" #include "DNA_object_types.h" @@ -143,7 +145,6 @@ void fly_modal_keymap(wmKeyConfig *keyconf) /* assign map to operators */ WM_modalkeymap_assign(keymap, "VIEW3D_OT_fly"); - } typedef struct FlyInfo { @@ -158,7 +159,9 @@ typedef struct FlyInfo { short state; short use_precision; short redraw; - int mval[2]; + + int mval[2]; /* latest 2D mouse values */ + wmNDOFMotionData* ndof; /* latest 3D mouse values */ /* fly state state */ float speed; /* the speed the view is moving per redraw */ @@ -257,6 +260,10 @@ static int initFlyInfo (bContext *C, FlyInfo *fly, wmOperator *op, wmEvent *even fly->ar = CTX_wm_region(C); fly->scene= CTX_data_scene(C); + #ifdef NDOF_FLY_DEBUG + puts("\n-- fly begin --"); + #endif + if(fly->rv3d->persp==RV3D_CAMOB && fly->v3d->camera->id.lib) { BKE_report(op->reports, RPT_ERROR, "Cannot fly a camera from an external library"); return FALSE; @@ -282,12 +289,14 @@ static int initFlyInfo (bContext *C, FlyInfo *fly, wmOperator *op, wmEvent *even fly->zlock_momentum=0.0f; fly->grid= 1.0f; fly->use_precision= 0; + fly->redraw= 1; fly->dvec_prev[0]= fly->dvec_prev[1]= fly->dvec_prev[2]= 0.0f; fly->timer= WM_event_add_timer(CTX_wm_manager(C), CTX_wm_window(C), TIMER, 0.01f); VECCOPY2D(fly->mval, event->mval) + fly->ndof = NULL; fly->time_lastdraw= fly->time_lastwheel= PIL_check_seconds_timer(); @@ -329,8 +338,17 @@ static int initFlyInfo (bContext *C, FlyInfo *fly, wmOperator *op, wmEvent *even /* perspective or ortho */ if (fly->rv3d->persp==RV3D_ORTHO) fly->rv3d->persp= RV3D_PERSP; /*if ortho projection, make perspective */ + copy_qt_qt(fly->rot_backup, fly->rv3d->viewquat); copy_v3_v3(fly->ofs_backup, fly->rv3d->ofs); + + /* the dist defines a vector that is infront of the offset + to rotate the view about. + this is no good for fly mode because we + want to rotate about the viewers center. + but to correct the dist removal we must + alter offset so the view doesn't jump. */ + fly->rv3d->dist= 0.0f; upvec[2]= fly->dist_backup; /*x and y are 0*/ @@ -338,7 +356,6 @@ static int initFlyInfo (bContext *C, FlyInfo *fly, wmOperator *op, wmEvent *even sub_v3_v3(fly->rv3d->ofs, upvec); /*Done with correcting for the dist*/ } - /* center the mouse, probably the UI mafia are against this but without its quite annoying */ WM_cursor_warp(CTX_wm_window(C), fly->ar->winrct.xmin + fly->ar->winx/2, fly->ar->winrct.ymin + fly->ar->winy/2); @@ -356,6 +373,10 @@ static int flyEnd(bContext *C, FlyInfo *fly) if(fly->state == FLY_RUNNING) return OPERATOR_RUNNING_MODAL; + #ifdef NDOF_FLY_DEBUG + puts("\n-- fly end --"); + #endif + WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), fly->timer); ED_region_draw_cb_exit(fly->ar->type, fly->draw_handle_pixel); @@ -401,6 +422,9 @@ static int flyEnd(bContext *C, FlyInfo *fly) if(fly->obtfm) MEM_freeN(fly->obtfm); + if(fly->ndof) + MEM_freeN(fly->ndof); + if(fly->state == FLY_CONFIRM) { MEM_freeN(fly); return OPERATOR_FINISHED; @@ -412,12 +436,54 @@ static int flyEnd(bContext *C, FlyInfo *fly) static void flyEvent(FlyInfo *fly, wmEvent *event) { - if (event->type == TIMER && event->customdata == fly->timer) { - fly->redraw = 1; - } - else if (event->type == MOUSEMOVE) { + if (event->type == MOUSEMOVE) { VECCOPY2D(fly->mval, event->mval); - } /* handle modal keymap first */ + } + else if (event->type == NDOF_MOTION) { + // do these automagically get delivered? yes. + // puts("ndof motion detected in fly mode!"); + // static const char* tag_name = "3D mouse position"; + + wmNDOFMotionData* incoming_ndof = (wmNDOFMotionData*) event->customdata; + switch (incoming_ndof->progress) + { + case P_STARTING: + // start keeping track of 3D mouse position + #ifdef NDOF_FLY_DEBUG + puts("start keeping track of 3D mouse position"); + #endif + // fall through... + case P_IN_PROGRESS: + // update 3D mouse position + #ifdef NDOF_FLY_DEBUG + putchar('.'); fflush(stdout); + #endif + if (fly->ndof == NULL) + // fly->ndof = MEM_mallocN(sizeof(wmNDOFMotionData), tag_name); + fly->ndof = MEM_dupallocN(incoming_ndof); + // fly->ndof = malloc(sizeof(wmNDOFMotionData)); + else + memcpy(fly->ndof, incoming_ndof, sizeof(wmNDOFMotionData)); + break; + case P_FINISHING: + // stop keeping track of 3D mouse position + #ifdef NDOF_FLY_DEBUG + puts("stop keeping track of 3D mouse position"); + #endif + if (fly->ndof) + { + MEM_freeN(fly->ndof); + // free(fly->ndof); + fly->ndof = NULL; + } + /* update the time else the view will jump when 2D mouse/timer resume */ + fly->time_lastdraw= PIL_check_seconds_timer(); + break; + default: + ; // should always be one of the above 3 + } + } + /* handle modal keymap first */ else if (event->type == EVT_MODAL_MAP) { switch (event->val) { case FLY_MODAL_CANCEL: @@ -528,14 +594,81 @@ static void flyEvent(FlyInfo *fly, wmEvent *event) case FLY_MODAL_PRECISION_DISABLE: fly->use_precision= FALSE; break; + } + } +} + + +static void move_camera(bContext* C, RegionView3D* rv3d, FlyInfo* fly, int orientationChanged, int positionChanged) +{ + /* we are in camera view so apply the view ofs and quat to the view matrix and set the camera to the view */ + View3D* v3d = fly->v3d; + Scene *scene= fly->scene; + ID *id_key; + + /* transform the parent or the camera? */ + if(fly->root_parent) { + Object *ob_update; + + float view_mat[4][4]; + float prev_view_mat[4][4]; + float prev_view_imat[4][4]; + float diff_mat[4][4]; + float parent_mat[4][4]; + + ED_view3d_to_m4(prev_view_mat, fly->rv3d->ofs, fly->rv3d->viewquat, fly->rv3d->dist); + invert_m4_m4(prev_view_imat, prev_view_mat); + ED_view3d_to_m4(view_mat, rv3d->ofs, rv3d->viewquat, rv3d->dist); + mul_m4_m4m4(diff_mat, prev_view_imat, view_mat); + mul_m4_m4m4(parent_mat, fly->root_parent->obmat, diff_mat); + object_apply_mat4(fly->root_parent, parent_mat, TRUE, FALSE); + + // where_is_object(scene, fly->root_parent); + + ob_update= v3d->camera->parent; + while(ob_update) { + DAG_id_tag_update(&ob_update->id, OB_RECALC_OB); + ob_update= ob_update->parent; } + + id_key= &fly->root_parent->id; + } + else { + float view_mat[4][4]; + ED_view3d_to_m4(view_mat, rv3d->ofs, rv3d->viewquat, rv3d->dist); + object_apply_mat4(v3d->camera, view_mat, TRUE, FALSE); + id_key= &v3d->camera->id; + } + + /* record the motion */ + if (autokeyframe_cfra_can_key(scene, id_key)) { + ListBase dsources = {NULL, NULL}; + + /* add datasource override for the camera object */ + ANIM_relative_keyingset_add_source(&dsources, id_key, NULL, NULL); + + /* insert keyframes + * 1) on the first frame + * 2) on each subsequent frame + * TODO: need to check in future that frame changed before doing this + */ + if (orientationChanged) { + KeyingSet *ks= ANIM_builtin_keyingset_get_named(NULL, "Rotation"); + ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, (float)CFRA); + } + if (positionChanged) { + KeyingSet *ks= ANIM_builtin_keyingset_get_named(NULL, "Location"); + ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, (float)CFRA); + } + + /* free temp data */ + BLI_freelistN(&dsources); } } static int flyApply(bContext *C, FlyInfo *fly) { - #define FLY_ROTATE_FAC 2.5f /* more is faster */ #define FLY_ZUP_CORRECT_FAC 0.1f /* amount to correct per step */ #define FLY_ZUP_CORRECT_ACCEL 0.05f /* increase upright momentum each step */ @@ -545,11 +678,7 @@ static int flyApply(bContext *C, FlyInfo *fly) a fly loop where the user can move move the view as if they are flying */ RegionView3D *rv3d= fly->rv3d; - View3D *v3d = fly->v3d; ARegion *ar = fly->ar; - Scene *scene= fly->scene; - - float prev_view_mat[4][4]; float mat[3][3], /* 3x3 copy of the view matrix so we can move allong the view axis */ dvec[3]={0,0,0}, /* this is the direction thast added to the view offset per redraw */ @@ -567,15 +696,11 @@ static int flyApply(bContext *C, FlyInfo *fly) unsigned char apply_rotation= 1; /* if the user presses shift they can look about without movinf the direction there looking*/ - if(fly->root_parent) - ED_view3d_to_m4(prev_view_mat, fly->rv3d->ofs, fly->rv3d->viewquat, fly->rv3d->dist); + #ifdef NDOF_FLY_DEBUG + static unsigned int iteration = 1; + printf("fly timer %d\n", iteration++); + #endif - /* the dist defines a vector that is infront of the offset - to rotate the view about. - this is no good for fly mode because we - want to rotate about the viewers center. - but to correct the dist removal we must - alter offset so the view doesn't jump. */ xmargin= ar->winx/20.0f; ymargin= ar->winy/20.0f; @@ -622,6 +747,8 @@ static int flyApply(bContext *C, FlyInfo *fly) float time_redraw; float time_redraw_clamped; + fly->redraw= 1; + time_current= PIL_check_seconds_timer(); time_redraw= (float)(time_current - fly->time_lastdraw); time_redraw_clamped= MIN2(0.05f, time_redraw); /* clamt the redraw time to avoid jitter in roll correction */ @@ -690,7 +817,7 @@ static int flyApply(bContext *C, FlyInfo *fly) mul_m3_v3(mat, upvec); } - axis_angle_to_quat( tmp_quat, upvec, (float)moffset[0] * time_redraw * FLY_ROTATE_FAC); /* Rotate about the relative up vec */ + axis_angle_to_quat(tmp_quat, upvec, (float)moffset[0] * time_redraw * FLY_ROTATE_FAC); /* Rotate about the relative up vec */ mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, tmp_quat); if (fly->xlock) fly->xlock = 2;/*check for rotation*/ @@ -784,69 +911,9 @@ static int flyApply(bContext *C, FlyInfo *fly) ED_area_headerprint(fly->ar, "FlyKeys Speed:(+/- | Wheel), Upright Axis:X off/Z off, Slow:Shift, Direction:WASDRF, Ok:LMB, Pan:MMB, Cancel:RMB"); #endif - /* we are in camera view so apply the view ofs and quat to the view matrix and set the camera to the view */ - if (rv3d->persp==RV3D_CAMOB) { - ID *id_key; - /* transform the parent or the camera? */ - if(fly->root_parent) { - Object *ob_update; - - float view_mat[4][4]; - float prev_view_imat[4][4]; - float diff_mat[4][4]; - float parent_mat[4][4]; - - invert_m4_m4(prev_view_imat, prev_view_mat); - ED_view3d_to_m4(view_mat, rv3d->ofs, rv3d->viewquat, rv3d->dist); - mul_m4_m4m4(diff_mat, prev_view_imat, view_mat); - mul_m4_m4m4(parent_mat, fly->root_parent->obmat, diff_mat); - object_apply_mat4(fly->root_parent, parent_mat, TRUE, FALSE); - - // where_is_object(scene, fly->root_parent); - - ob_update= v3d->camera->parent; - while(ob_update) { - DAG_id_tag_update(&ob_update->id, OB_RECALC_OB); - ob_update= ob_update->parent; - } - - copy_m4_m4(prev_view_mat, view_mat); - - id_key= &fly->root_parent->id; - - } - else { - float view_mat[4][4]; - ED_view3d_to_m4(view_mat, rv3d->ofs, rv3d->viewquat, rv3d->dist); - object_apply_mat4(v3d->camera, view_mat, TRUE, FALSE); - id_key= &v3d->camera->id; - } + if (rv3d->persp==RV3D_CAMOB) + move_camera(C, rv3d, fly, (fly->xlock || fly->zlock || moffset[0] || moffset[1]), fly->speed); - /* record the motion */ - if (autokeyframe_cfra_can_key(scene, id_key)) { - ListBase dsources = {NULL, NULL}; - - /* add datasource override for the camera object */ - ANIM_relative_keyingset_add_source(&dsources, id_key, NULL, NULL); - - /* insert keyframes - * 1) on the first frame - * 2) on each subsequent frame - * TODO: need to check in future that frame changed before doing this - */ - if (fly->xlock || fly->zlock || moffset[0] || moffset[1]) { - KeyingSet *ks= ANIM_builtin_keyingset_get_named(NULL, "Rotation"); - ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, (float)CFRA); - } - if (fly->speed) { - KeyingSet *ks= ANIM_builtin_keyingset_get_named(NULL, "Location"); - ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, (float)CFRA); - } - - /* free temp data */ - BLI_freelistN(&dsources); - } - } } else /*were not redrawing but we need to update the time else the view will jump */ fly->time_lastdraw= PIL_check_seconds_timer(); @@ -854,11 +921,141 @@ static int flyApply(bContext *C, FlyInfo *fly) copy_v3_v3(fly->dvec_prev, dvec); } -/* moved to flyEnd() */ - return OPERATOR_FINISHED; } +static int flyApply_ndof(bContext *C, FlyInfo *fly) +{ + // shorthand for oft-used variables + wmNDOFMotionData* ndof = fly->ndof; + const float dt = ndof->dt; + RegionView3D* rv3d = fly->rv3d; + const int flag = U.ndof_flag; + +// int shouldRotate = (flag & NDOF_SHOULD_ROTATE) && (fly->pan_view == FALSE), +// shouldTranslate = (flag & (NDOF_SHOULD_PAN | NDOF_SHOULD_ZOOM)); + + int shouldRotate = (fly->pan_view == FALSE), + shouldTranslate = TRUE; + + float view_inv[4]; + invert_qt_qt(view_inv, rv3d->viewquat); + + rv3d->rot_angle = 0.f; // disable onscreen rotation doo-dad + + if (shouldTranslate) + { + const float forward_sensitivity = 1.f; + const float vertical_sensitivity = 0.4f; + const float lateral_sensitivity = 0.6f; + + float speed = 10.f; // blender units per second + // ^^ this is ok for default cube scene, but should scale with.. something + + float trans[3] = { + lateral_sensitivity * ndof->tx, + vertical_sensitivity * ndof->ty, + forward_sensitivity * ndof->tz + }; + + if (fly->use_precision) + speed *= 0.2f; + + mul_v3_fl(trans, speed * dt); + + // transform motion from view to world coordinates + mul_qt_v3(view_inv, trans); + + if (flag & NDOF_FLY_HELICOPTER) + { + // replace world z component with device y (yes it makes sense) + trans[2] = speed * dt * vertical_sensitivity * ndof->ty; + } + + if (rv3d->persp==RV3D_CAMOB) { + // respect camera position locks + Object *lock_ob= fly->root_parent ? fly->root_parent : fly->v3d->camera; + if (lock_ob->protectflag & OB_LOCK_LOCX) trans[0] = 0.f; + if (lock_ob->protectflag & OB_LOCK_LOCY) trans[1] = 0.f; + if (lock_ob->protectflag & OB_LOCK_LOCZ) trans[2] = 0.f; + } + + if (trans[0] || trans[1] || trans[2]) + { + // move center of view opposite of hand motion (this is camera mode, not object mode) + sub_v3_v3(rv3d->ofs, trans); + shouldTranslate = TRUE; + } + else + shouldTranslate = FALSE; + } + + if (shouldRotate) + { + const float turn_sensitivity = 1.f; + + float rotation[4]; + float axis[3]; + float angle = turn_sensitivity * ndof_to_angle_axis(ndof, axis); + + if (fabsf(angle) > 0.0001f) + { + shouldRotate = TRUE; + + if (fly->use_precision) + angle *= 0.2f; + + // transform rotation axis from view to world coordinates + mul_qt_v3(view_inv, axis); + + // apply rotation to view + axis_angle_to_quat(rotation, axis, angle); + mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, rotation); + + if (flag & NDOF_LOCK_HORIZON) + // force an upright viewpoint + // TODO: make this less... sudden + { + float view_horizon[3] = {1.f, 0.f, 0.f}; // view +x + float view_direction[3] = {0.f, 0.f, -1.f}; // view -z (into screen) + + // find new inverse since viewquat has changed + invert_qt_qt(view_inv, rv3d->viewquat); + // could apply reverse rotation to existing view_inv to save a few cycles + + // transform view vectors to world coordinates + mul_qt_v3(view_inv, view_horizon); + mul_qt_v3(view_inv, view_direction); + + // find difference between view & world horizons + // true horizon lives in world xy plane, so look only at difference in z + angle = -asinf(view_horizon[2]); + + #ifdef NDOF_FLY_DEBUG + printf("lock horizon: adjusting %.1f degrees\n\n", RAD2DEG(angle)); + #endif + + // rotate view so view horizon = world horizon + axis_angle_to_quat(rotation, view_direction, angle); + mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, rotation); + } + + rv3d->view = RV3D_VIEW_USER; + } + else + shouldRotate = FALSE; + } + + if (shouldTranslate || shouldRotate) + { + fly->redraw = TRUE; + + if (rv3d->persp==RV3D_CAMOB) + move_camera(C, rv3d, fly, shouldRotate, shouldTranslate); + } + + return OPERATOR_FINISHED; +} static int fly_invoke(bContext *C, wmOperator *op, wmEvent *event) @@ -908,7 +1105,12 @@ static int fly_modal(bContext *C, wmOperator *op, wmEvent *event) flyEvent(fly, event); - if(event->type==TIMER && event->customdata == fly->timer) + if (fly->ndof) // 3D mouse overrules [2D mouse + timer] + { + if (event->type==NDOF_MOTION) + flyApply_ndof(C, fly); + } + else if (event->type==TIMER && event->customdata == fly->timer) flyApply(C, fly); do_draw |= fly->redraw; @@ -923,6 +1125,7 @@ static int fly_modal(bContext *C, wmOperator *op, wmEvent *event) WM_event_add_notifier(C, NC_OBJECT|ND_TRANSFORM, fly_object); } + // puts("redraw!"); // too frequent, fix tomorrow. ED_region_tag_redraw(CTX_wm_region(C)); } |