From e0611ca832d793694cbda0fbae1a4388b81fc589 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Sat, 21 Mar 2015 15:11:30 +1100 Subject: View3D: fix view-selected zoom logic - Correct logic converting radius to view distance. - Wasn't taking view-zoom into account converting lens to angle. - Support framing the selection in the camera bounds (for camera locked views). Add ED_view3d_radius_to_dist to handles these details. --- source/blender/blenkernel/intern/camera.c | 2 +- source/blender/editors/include/ED_view3d.h | 8 +- source/blender/editors/space_view3d/view3d_edit.c | 39 ++----- source/blender/editors/space_view3d/view3d_view.c | 134 ++++++++++++++++++---- 4 files changed, 130 insertions(+), 53 deletions(-) diff --git a/source/blender/blenkernel/intern/camera.c b/source/blender/blenkernel/intern/camera.c index 4588c00b2f6..ed400bcf565 100644 --- a/source/blender/blenkernel/intern/camera.c +++ b/source/blender/blenkernel/intern/camera.c @@ -283,7 +283,7 @@ void BKE_camera_params_from_view3d(CameraParams *params, View3D *v3d, RegionView params->clipsta = -params->clipend; params->is_ortho = true; - /* make sure any changes to this match ED_view3d_radius_to_ortho_dist() */ + /* make sure any changes to this match ED_view3d_radius_to_dist_ortho() */ params->ortho_scale = rv3d->dist * sensor_size / v3d->lens; params->zoom = CAMERA_PARAM_ZOOM_INIT_PERSP; } diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h index 11f961605af..ec53bce2eb7 100644 --- a/source/blender/editors/include/ED_view3d.h +++ b/source/blender/editors/include/ED_view3d.h @@ -252,8 +252,12 @@ void ED_view3d_clipping_disable(void); float ED_view3d_pixel_size(struct RegionView3D *rv3d, const float co[3]); -float ED_view3d_radius_to_persp_dist(const float angle, const float radius); -float ED_view3d_radius_to_ortho_dist(const float lens, const float radius); +float ED_view3d_radius_to_dist_persp(const float angle, const float radius); +float ED_view3d_radius_to_dist_ortho(const float lens, const float radius); +float ED_view3d_radius_to_dist( + const struct View3D *v3d, const struct ARegion *ar, + const char persp, const bool use_aspect, + const float radius); void drawcircball(int mode, const float cent[3], float rad, float tmat[4][4]); diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c index 51915e05a9e..0bced34e465 100644 --- a/source/blender/editors/space_view3d/view3d_edit.c +++ b/source/blender/editors/space_view3d/view3d_edit.c @@ -2748,29 +2748,15 @@ static void view3d_from_minmax(bContext *C, View3D *v3d, ARegion *ar, size = max_fff(afm[0], afm[1], afm[2]); if (ok_dist) { - /* fix up zoom distance if needed */ + char persp; if (rv3d->is_persp) { - float lens, sensor_size; - /* offset the view based on the lens */ if (rv3d->persp == RV3D_CAMOB && ED_view3d_camera_lock_check(v3d, rv3d)) { - CameraParams params; - BKE_camera_params_init(¶ms); - params.clipsta = v3d->near; - params.clipend = v3d->far; - BKE_camera_params_from_object(¶ms, v3d->camera); - - lens = params.lens; - sensor_size = BKE_camera_sensor_size(params.sensor_fit, params.sensor_x, params.sensor_y); + persp = RV3D_CAMOB; } else { - lens = v3d->lens; - sensor_size = DEFAULT_SENSOR_WIDTH; + persp = RV3D_PERSP; } - size = ED_view3d_radius_to_persp_dist(focallength_to_fov(lens, sensor_size), size / 2.0f) * VIEW3D_MARGIN; - - /* do not zoom closer than the near clipping plane */ - size = max_ff(size, v3d->near * 1.5f); } else { /* ortho */ if (size < 0.0001f) { @@ -2779,7 +2765,15 @@ static void view3d_from_minmax(bContext *C, View3D *v3d, ARegion *ar, } else { /* adjust zoom so it looks nicer */ - size = ED_view3d_radius_to_ortho_dist(v3d->lens, size / 2.0f) * VIEW3D_MARGIN; + persp = RV3D_ORTHO; + } + } + + if (ok_dist) { + new_dist = ED_view3d_radius_to_dist(v3d, ar, persp, true, (size / 2) * VIEW3D_MARGIN); + if (rv3d->is_persp) { + /* don't zoom closer than the near clipping plane */ + new_dist = max_ff(new_dist, v3d->near * 1.5f); } } } @@ -2787,15 +2781,6 @@ static void view3d_from_minmax(bContext *C, View3D *v3d, ARegion *ar, mid_v3_v3v3(new_ofs, min, max); negate_v3(new_ofs); - new_dist = size; - - /* correction for window aspect ratio */ - if (ar->winy > 2 && ar->winx > 2) { - size = (float)ar->winx / (float)ar->winy; - if (size < 1.0f) size = 1.0f / size; - new_dist *= size; - } - if (rv3d->persp == RV3D_CAMOB && !ED_view3d_camera_lock_check(v3d, rv3d)) { rv3d->persp = RV3D_PERSP; ED_view3d_smooth_view(C, v3d, ar, v3d->camera, NULL, diff --git a/source/blender/editors/space_view3d/view3d_view.c b/source/blender/editors/space_view3d/view3d_view.c index 4888c6846d2..1c10a857179 100644 --- a/source/blender/editors/space_view3d/view3d_view.c +++ b/source/blender/editors/space_view3d/view3d_view.c @@ -1214,7 +1214,7 @@ static bool view3d_localview_init( View3D *v3d = sa->spacedata.first; Base *base; float min[3], max[3], box[3], mid[3]; - float size = 0.0f, size_persp = 0.0f, size_ortho = 0.0f; + float size = 0.0f; unsigned int locallay; bool ok = false; @@ -1252,13 +1252,6 @@ static bool view3d_localview_init( sub_v3_v3v3(box, max, min); size = max_fff(box[0], box[1], box[2]); - - /* do not zoom closer than the near clipping plane */ - size = max_ff(size, v3d->near * 1.5f); - - /* perspective size (we always switch out of camera view so no need to use its lens size) */ - size_persp = ED_view3d_radius_to_persp_dist(focallength_to_fov(v3d->lens, DEFAULT_SENSOR_WIDTH), size / 2.0f) * VIEW3D_MARGIN; - size_ortho = ED_view3d_radius_to_ortho_dist(v3d->lens, size / 2.0f) * VIEW3D_MARGIN; } if (ok == true) { @@ -1275,6 +1268,7 @@ static bool view3d_localview_init( for (ar = sa->regionbase.first; ar; ar = ar->next) { if (ar->regiontype == RGN_TYPE_WINDOW) { RegionView3D *rv3d = ar->regiondata; + bool ok_dist = true; /* new view values */ Object *camera_old = NULL; @@ -1290,25 +1284,24 @@ static bool view3d_localview_init( camera_old = v3d->camera; } - /* perspective should be a bit farther away to look nice */ - if (rv3d->persp != RV3D_ORTHO) { - dist_new = size_persp; - } - else { - dist_new = size_ortho; + if (rv3d->persp == RV3D_ORTHO) { + if (size < 0.0001) { + ok_dist = false; + } } - /* correction for window aspect ratio */ - if (ar->winy > 2 && ar->winx > 2) { - float asp = (float)ar->winx / (float)ar->winy; - if (asp < 1.0f) asp = 1.0f / asp; - dist_new *= asp; + if (ok_dist) { + dist_new = ED_view3d_radius_to_dist(v3d, ar, rv3d->persp, true, (size / 2) * VIEW3D_MARGIN); + if (rv3d->persp == RV3D_PERSP) { + /* don't zoom closer than the near clipping plane */ + dist_new = max_ff(dist_new, v3d->near * 1.5f); + } } ED_view3d_smooth_view_ex( wm, win, sa, v3d, ar, camera_old, NULL, - ofs_new, NULL, &dist_new, NULL, + ofs_new, NULL, ok_dist ? &dist_new : NULL, NULL, smooth_viewtx); } } @@ -1720,16 +1713,111 @@ float ED_view3d_pixel_size(RegionView3D *rv3d, const float co[3]) return mul_project_m4_v3_zfac(rv3d->persmat, co) * rv3d->pixsize * U.pixelsize; } -float ED_view3d_radius_to_persp_dist(const float angle, const float radius) +float ED_view3d_radius_to_dist_persp(const float angle, const float radius) { - return (radius / 2.0f) * fabsf(1.0f / cosf((((float)M_PI) - angle) / 2.0f)); + return radius * (1.0f / tanf(angle / 2.0f)); } -float ED_view3d_radius_to_ortho_dist(const float lens, const float radius) +float ED_view3d_radius_to_dist_ortho(const float lens, const float radius) { return radius / (DEFAULT_SENSOR_WIDTH / lens); } +/** + * Return a new RegionView3D.dist value to fit the \a radius. + * + * \note Depth isn't taken into account, this will fit a flat plane exactly, + * but points towards the view (with a perspective projection), + * may be within the radius but outside the view. eg: + * + *
+ *           +
+ * pt --> + /^ radius
+ *         / |
+ *        /  |
+ * view  +   +
+ *        \  |
+ *         \ |
+ *          \|
+ *           +
+ * 
+ * + * \param ar Can be NULL if \a use_aspect is false. + * \param persp Allow the caller to tell what kind of perspective to use (ortho/view/camera) + * \param use_aspect Increase the distance to account for non 1:1 view aspect. + * \param radius The radius will be fitted exactly, typically pre-scaled by a margin (#VIEW3D_MARGIN). + */ +float ED_view3d_radius_to_dist( + const View3D *v3d, const ARegion *ar, + const char persp, const bool use_aspect, + const float radius) +{ + float dist; + + BLI_assert(ELEM(persp, RV3D_ORTHO, RV3D_PERSP, RV3D_CAMOB)); + BLI_assert((persp != RV3D_CAMOB) || v3d->camera); + + if (persp == RV3D_ORTHO) { + dist = ED_view3d_radius_to_dist_ortho(v3d->lens, radius); + } + else { + float lens, sensor_size, zoom; + float angle; + + if (persp == RV3D_CAMOB) { + CameraParams params; + BKE_camera_params_init(¶ms); + params.clipsta = v3d->near; + params.clipend = v3d->far; + BKE_camera_params_from_object(¶ms, v3d->camera); + + lens = params.lens; + sensor_size = BKE_camera_sensor_size(params.sensor_fit, params.sensor_x, params.sensor_y); + + /* ignore 'rv3d->camzoom' because we wan't to fit to the cameras frame */ + zoom = CAMERA_PARAM_ZOOM_INIT_CAMOB; + } + else { + lens = v3d->lens; + sensor_size = DEFAULT_SENSOR_WIDTH; + zoom = CAMERA_PARAM_ZOOM_INIT_PERSP; + } + + angle = focallength_to_fov(lens, sensor_size); + + /* zoom influences lens, correct this by scaling the angle as a distance (by the zoom-level) */ + angle = ((atanf(tanf(angle / 2.0f) * zoom) * 2.0f)); + + dist = ED_view3d_radius_to_dist_persp(angle, radius); + } + + if (use_aspect) { + const RegionView3D *rv3d = ar->regiondata; + + float winx, winy; + + if (persp == RV3D_CAMOB) { + /* camera frame x/y in pixels */ + winx = ar->winx / rv3d->viewcamtexcofac[0]; + winy = ar->winy / rv3d->viewcamtexcofac[1]; + } + else { + winx = ar->winx; + winy = ar->winy; + } + + if (winx && winy) { + float aspect = winx / winy; + if (aspect < 1.0f) { + aspect = 1.0f / aspect; + } + dist *= aspect; + } + } + + return dist; +} + /* view matrix properties utilities */ /* unused */ -- cgit v1.2.3