static void walkApply_ndof(bContext *C, WalkInfo *walk) { Object *lock_ob = ED_view3d_cameracontrol_object_get(walk->v3d_camera_control); bool has_translate, has_rotate; view3d_ndof_fly(walk->ndof, walk->v3d, walk->rv3d, walk->is_slow, lock_ob ? lock_ob->protectflag : 0, &has_translate, &has_rotate); if (has_translate || has_rotate) { walk->redraw = true; if (walk->rv3d->persp == RV3D_CAMOB) { walkMoveCamera(C, walk, has_rotate, has_translate); } } }
static int walkApply(bContext *C, wmOperator *op, WalkInfo *walk) { #define WALK_ROTATE_FAC 2.2f /* more is faster */ #define WALK_TOP_LIMIT DEG2RADF(85.0f) #define WALK_BOTTOM_LIMIT DEG2RADF(-80.0f) #define WALK_MOVE_SPEED base_speed #define WALK_BOOST_FACTOR ((void)0, walk->speed_factor) /* walk mode - Ctrl+Shift+F * a walk loop where the user can move move the view as if they are in a walk game */ RegionView3D *rv3d = walk->rv3d; ARegion *ar = walk->ar; float mat[3][3]; /* 3x3 copy of the view matrix so we can move along the view axis */ float dvec[3] = {0.0f, 0.0f, 0.0f}; /* this is the direction that's added to the view offset per redraw */ /* Camera Uprighting variables */ float upvec[3] = {0.0f, 0.0f, 0.0f}; /* stores the view's up vector */ int moffset[2]; /* mouse offset from the views center */ float tmp_quat[4]; /* used for rotating the view */ #ifdef NDOF_WALK_DEBUG { static unsigned int iteration = 1; printf("walk timer %d\n", iteration++); } #endif { /* mouse offset from the center */ copy_v2_v2_int(moffset, walk->moffset); /* apply moffset so we can re-accumulate */ walk->moffset[0] = 0; walk->moffset[1] = 0; /* revert mouse */ if (walk->is_reversed) { moffset[1] = -moffset[1]; } /* Should we redraw? */ if ((walk->active_directions) || moffset[0] || moffset[1] || walk->teleport.state == WALK_TELEPORT_STATE_ON || walk->gravity_state != WALK_GRAVITY_STATE_OFF) { float dvec_tmp[3]; /* time how fast it takes for us to redraw, * this is so simple scenes don't walk too fast */ double time_current; float time_redraw; #ifdef NDOF_WALK_DRAW_TOOMUCH walk->redraw = 1; #endif time_current = PIL_check_seconds_timer(); time_redraw = (float)(time_current - walk->time_lastdraw); walk->time_lastdraw = time_current; /* base speed in m/s */ walk->speed = WALK_MOVE_SPEED; if (walk->is_fast) { walk->speed *= WALK_BOOST_FACTOR; } else if (walk->is_slow) { walk->speed *= 1.0f / WALK_BOOST_FACTOR; } copy_m3_m4(mat, rv3d->viewinv); { /* rotate about the X axis- look up/down */ if (moffset[1]) { float angle; float y; /* relative offset */ y = (float) moffset[1] / ar->winy; /* speed factor */ y *= WALK_ROTATE_FAC; /* user adjustement factor */ y *= walk->mouse_speed; /* clamp the angle limits */ /* it ranges from 90.0f to -90.0f */ angle = -asinf(rv3d->viewmat[2][2]); if (angle > WALK_TOP_LIMIT && y > 0.0f) y = 0.0f; else if (angle < WALK_BOTTOM_LIMIT && y < 0.0f) y = 0.0f; copy_v3_fl3(upvec, 1.0f, 0.0f, 0.0f); mul_m3_v3(mat, upvec); /* Rotate about the relative up vec */ axis_angle_to_quat(tmp_quat, upvec, -y); mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, tmp_quat); } /* rotate about the Y axis- look left/right */ if (moffset[0]) { float x; /* if we're upside down invert the moffset */ copy_v3_fl3(upvec, 0.0f, 1.0f, 0.0f); mul_m3_v3(mat, upvec); if (upvec[2] < 0.0f) moffset[0] = -moffset[0]; /* relative offset */ x = (float) moffset[0] / ar->winx; /* speed factor */ x *= WALK_ROTATE_FAC; /* user adjustement factor */ x *= walk->mouse_speed; copy_v3_fl3(upvec, 0.0f, 0.0f, 1.0f); /* Rotate about the relative up vec */ axis_angle_normalized_to_quat(tmp_quat, upvec, x); mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, tmp_quat); } } /* WASD - 'move' translation code */ if ((walk->active_directions) && (walk->gravity_state == WALK_GRAVITY_STATE_OFF)) { short direction; zero_v3(dvec); if ((walk->active_directions & WALK_BIT_FORWARD) || (walk->active_directions & WALK_BIT_BACKWARD)) { direction = 0; if ((walk->active_directions & WALK_BIT_FORWARD)) direction += 1; if ((walk->active_directions & WALK_BIT_BACKWARD)) direction -= 1; copy_v3_fl3(dvec_tmp, 0.0f, 0.0f, direction); mul_m3_v3(mat, dvec_tmp); if (walk->navigation_mode == WALK_MODE_GRAVITY) { dvec_tmp[2] = 0.0f; } normalize_v3(dvec_tmp); add_v3_v3(dvec, dvec_tmp); } if ((walk->active_directions & WALK_BIT_LEFT) || (walk->active_directions & WALK_BIT_RIGHT)) { direction = 0; if ((walk->active_directions & WALK_BIT_LEFT)) direction += 1; if ((walk->active_directions & WALK_BIT_RIGHT)) direction -= 1; dvec_tmp[0] = direction * rv3d->viewinv[0][0]; dvec_tmp[1] = direction * rv3d->viewinv[0][1]; dvec_tmp[2] = 0.0f; normalize_v3(dvec_tmp); add_v3_v3(dvec, dvec_tmp); } if ((walk->active_directions & WALK_BIT_UP) || (walk->active_directions & WALK_BIT_DOWN)) { if (walk->navigation_mode == WALK_MODE_FREE) { direction = 0; if ((walk->active_directions & WALK_BIT_UP)) direction -= 1; if ((walk->active_directions & WALK_BIT_DOWN)) direction = 1; copy_v3_fl3(dvec_tmp, 0.0f, 0.0f, direction); add_v3_v3(dvec, dvec_tmp); } } /* apply movement */ mul_v3_fl(dvec, walk->speed * time_redraw); } /* stick to the floor */ if (walk->navigation_mode == WALK_MODE_GRAVITY && ELEM(walk->gravity_state, WALK_GRAVITY_STATE_OFF, WALK_GRAVITY_STATE_START)) { bool ret; float ray_distance; float difference = -100.0f; float fall_distance; ret = walk_floor_distance_get(C, rv3d, walk, dvec, &ray_distance); if (ret) { difference = walk->view_height - ray_distance; } /* the distance we would fall naturally smoothly enough that we * can manually drop the object without activating gravity */ fall_distance = time_redraw * walk->speed * WALK_BOOST_FACTOR; if (fabsf(difference) < fall_distance) { /* slope/stairs */ dvec[2] -= difference; /* in case we switched from FREE to GRAVITY too close to the ground */ if (walk->gravity_state == WALK_GRAVITY_STATE_START) walk->gravity_state = WALK_GRAVITY_STATE_OFF; } else { /* hijack the teleport variables */ walk->teleport.initial_time = PIL_check_seconds_timer(); walk->gravity_state = WALK_GRAVITY_STATE_ON; walk->teleport.duration = 0.0f; copy_v3_v3(walk->teleport.origin, walk->rv3d->viewinv[3]); copy_v2_v2(walk->teleport.direction, dvec); } } /* Falling or jumping) */ if (ELEM(walk->gravity_state, WALK_GRAVITY_STATE_ON, WALK_GRAVITY_STATE_JUMP)) { float t; float z_cur, z_new; bool ret; float ray_distance, difference = -100.0f; /* delta time */ t = (float)(PIL_check_seconds_timer() - walk->teleport.initial_time); /* keep moving if we were moving */ copy_v2_v2(dvec, walk->teleport.direction); z_cur = walk->rv3d->viewinv[3][2]; z_new = walk->teleport.origin[2] - getFreeFallDistance(walk->gravity, t) * walk->grid; /* jump */ z_new += t * walk->speed_jump * walk->grid; /* duration is the jump duration */ if (t > walk->teleport.duration) { /* check to see if we are landing */ ret = walk_floor_distance_get(C, rv3d, walk, dvec, &ray_distance); if (ret) { difference = walk->view_height - ray_distance; } if (difference > 0.0f) { /* quit falling, lands at "view_height" from the floor */ dvec[2] -= difference; walk->gravity_state = WALK_GRAVITY_STATE_OFF; walk->speed_jump = 0.0f; } else { /* keep falling */ dvec[2] = z_cur - z_new; } } else { /* keep going up (jump) */ dvec[2] = z_cur - z_new; } } /* Teleport */ else if (walk->teleport.state == WALK_TELEPORT_STATE_ON) { float t; /* factor */ float new_loc[3]; float cur_loc[3]; /* linear interpolation */ t = (float)(PIL_check_seconds_timer() - walk->teleport.initial_time); t /= walk->teleport.duration; /* clamp so we don't go past our limit */ if (t >= 1.0f) { t = 1.0f; walk->teleport.state = WALK_TELEPORT_STATE_OFF; walk_navigation_mode_set(C, op, walk, walk->teleport.navigation_mode); } mul_v3_v3fl(new_loc, walk->teleport.direction, t); add_v3_v3(new_loc, walk->teleport.origin); copy_v3_v3(cur_loc, walk->rv3d->viewinv[3]); sub_v3_v3v3(dvec, cur_loc, new_loc); } if (rv3d->persp == RV3D_CAMOB) { Object *lock_ob = ED_view3d_cameracontrol_object_get(walk->v3d_camera_control); if (lock_ob->protectflag & OB_LOCK_LOCX) dvec[0] = 0.0f; if (lock_ob->protectflag & OB_LOCK_LOCY) dvec[1] = 0.0f; if (lock_ob->protectflag & OB_LOCK_LOCZ) dvec[2] = 0.0f; } /* scale the movement to the scene size */ mul_v3_v3fl(dvec_tmp, dvec, walk->grid); add_v3_v3(rv3d->ofs, dvec_tmp); if (rv3d->persp == RV3D_CAMOB) { const bool do_rotate = (moffset[0] || moffset[1]); const bool do_translate = (walk->speed != 0.0f); walkMoveCamera(C, walk, do_rotate, do_translate); } } else { /* we're not redrawing but we need to update the time else the view will jump */ walk->time_lastdraw = PIL_check_seconds_timer(); } /* end drawing */ copy_v3_v3(walk->dvec_prev, dvec); } return OPERATOR_FINISHED; #undef WALK_ROTATE_FAC #undef WALK_ZUP_CORRECT_FAC #undef WALK_ZUP_CORRECT_ACCEL #undef WALK_SMOOTH_FAC #undef WALK_TOP_LIMIT #undef WALK_BOTTOM_LIMIT #undef WALK_MOVE_SPEED #undef WALK_BOOST_FACTOR }
static int walkApply_ndof(bContext *C, WalkInfo *walk) { /* shorthand for oft-used variables */ wmNDOFMotionData *ndof = walk->ndof; const float dt = ndof->dt; RegionView3D *rv3d = walk->rv3d; const int flag = U.ndof_flag; #if 0 bool do_rotate = (flag & NDOF_SHOULD_ROTATE) && (walk->pan_view == false); bool do_translate = (flag & (NDOF_SHOULD_PAN | NDOF_SHOULD_ZOOM)) != 0; #endif bool do_rotate = true; bool do_translate = true; float view_inv[4]; invert_qt_qt(view_inv, rv3d->viewquat); rv3d->rot_angle = 0.0f; /* disable onscreen rotation doo-dad */ if (do_translate) { const float forward_sensitivity = 1.0f; const float vertical_sensitivity = 0.4f; const float lateral_sensitivity = 0.6f; float speed = 10.0f; /* blender units per second */ /* ^^ this is ok for default cube scene, but should scale with.. something */ float trans[3] = {lateral_sensitivity * ndof->tvec[0], vertical_sensitivity * ndof->tvec[1], forward_sensitivity * ndof->tvec[2]}; if (walk->is_slow) 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->tvec[1]; } if (rv3d->persp == RV3D_CAMOB) { /* respect camera position locks */ Object *lock_ob = ED_view3d_cameracontrol_object_get(walk->v3d_camera_control); if (lock_ob->protectflag & OB_LOCK_LOCX) trans[0] = 0.0f; if (lock_ob->protectflag & OB_LOCK_LOCY) trans[1] = 0.0f; if (lock_ob->protectflag & OB_LOCK_LOCZ) trans[2] = 0.0f; } if (!is_zero_v3(trans)) { /* move center of view opposite of hand motion (this is camera mode, not object mode) */ sub_v3_v3(rv3d->ofs, trans); do_translate = true; } else { do_translate = false; } } if (do_rotate) { const float turn_sensitivity = 1.0f; float rotation[4]; float axis[3]; float angle = turn_sensitivity * ndof_to_axis_angle(ndof, axis); if (fabsf(angle) > 0.0001f) { do_rotate = true; if (walk->is_slow) 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.0f, 0.0f, 0.0f}; /* view +x */ float view_direction[3] = {0.0f, 0.0f, -1.0f}; /* 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_WALK_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 { do_rotate = false; } } if (do_translate || do_rotate) { walk->redraw = true; if (rv3d->persp == RV3D_CAMOB) { walkMoveCamera(C, walk, do_rotate, do_translate); } } return OPERATOR_FINISHED; }