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.
This commit is contained in:
Campbell Barton 2015-03-21 15:11:30 +11:00
parent e88cfc28b2
commit e0611ca832
4 changed files with 130 additions and 53 deletions

View File

@ -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;
}

View File

@ -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]);

View File

@ -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(&params);
params.clipsta = v3d->near;
params.clipend = v3d->far;
BKE_camera_params_from_object(&params, 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,

View File

@ -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:
*
* <pre>
* +
* pt --> + /^ radius
* / |
* / |
* view + +
* \ |
* \ |
* \|
* +
* </pre>
*
* \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(&params);
params.clipsta = v3d->near;
params.clipend = v3d->far;
BKE_camera_params_from_object(&params, 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 */