// This function selects an ideal viewing distance given a selection bounding box, normal, and padding value void LLViewerMediaFocus::setCameraZoom(F32 padding_factor) { LLPickInfo& pick = LLToolPie::getInstance()->getPick(); if(LLSelectMgr::getInstance()->getSelection()->isEmpty()) { pick = mPickInfo; setFocusFace(true, pick.getObject(), pick.mObjectFace, mMediaImpl); } if (!LLSelectMgr::getInstance()->getSelection()->isEmpty()) { gAgentCamera.setFocusOnAvatar(FALSE, ANIMATE); LLBBox selection_bbox = LLSelectMgr::getInstance()->getBBoxOfSelection(); F32 height; F32 width; F32 depth; F32 angle_of_view; F32 distance; // We need the aspect ratio, and the 3 components of the bbox as height, width, and depth. F32 aspect_ratio = getBBoxAspectRatio(selection_bbox, pick.mNormal, &height, &width, &depth); F32 camera_aspect = LLViewerCamera::getInstance()->getAspect(); // We will normally use the side of the volume aligned with the short side of the screen (i.e. the height for // a screen in a landscape aspect ratio), however there is an edge case where the aspect ratio of the object is // more extreme than the screen. In this case we invert the logic, using the longer component of both the object // and the screen. bool invert = (camera_aspect > 1.0f && aspect_ratio > camera_aspect) || (camera_aspect < 1.0f && aspect_ratio < camera_aspect); // To calculate the optimum viewing distance we will need the angle of the shorter side of the view rectangle. // In portrait mode this is the width, and in landscape it is the height. // We then calculate the distance based on the corresponding side of the object bbox (width for portrait, height for landscape) // We will add half the depth of the bounding box, as the distance projection uses the center point of the bbox. if(camera_aspect < 1.0f || invert) { angle_of_view = llmax(0.1f, LLViewerCamera::getInstance()->getView() * LLViewerCamera::getInstance()->getAspect()); distance = width * 0.5 * padding_factor / tan(angle_of_view * 0.5f ); } else { angle_of_view = llmax(0.1f, LLViewerCamera::getInstance()->getView()); distance = height * 0.5 * padding_factor / tan(angle_of_view * 0.5f ); } distance += depth * 0.5; // Finally animate the camera to this new position and focal point gAgentCamera.setCameraPosAndFocusGlobal(LLSelectMgr::getInstance()->getSelectionCenterGlobal() + LLVector3d(pick.mNormal * distance), LLSelectMgr::getInstance()->getSelectionCenterGlobal(), LLSelectMgr::getInstance()->getSelection()->getFirstObject()->mID ); } }
// This function selects an ideal viewing distance based on the focused object, pick normal, and padding value void LLViewerMediaFocus::setCameraZoom(LLViewerObject* object, LLVector3 normal, F32 padding_factor, bool zoom_in_only) { if (object) { gAgentCamera.setFocusOnAvatar(FALSE, ANIMATE); LLBBox bbox = object->getBoundingBoxAgent(); LLVector3d center = gAgent.getPosGlobalFromAgent(bbox.getCenterAgent()); F32 height; F32 width; F32 depth; F32 angle_of_view; F32 distance; // We need the aspect ratio, and the 3 components of the bbox as height, width, and depth. F32 aspect_ratio = getBBoxAspectRatio(bbox, normal, &height, &width, &depth); F32 camera_aspect = LLViewerCamera::getInstance()->getAspect(); lldebugs << "normal = " << normal << ", aspect_ratio = " << aspect_ratio << ", camera_aspect = " << camera_aspect << llendl; // We will normally use the side of the volume aligned with the short side of the screen (i.e. the height for // a screen in a landscape aspect ratio), however there is an edge case where the aspect ratio of the object is // more extreme than the screen. In this case we invert the logic, using the longer component of both the object // and the screen. bool invert = (camera_aspect > 1.0f && aspect_ratio > camera_aspect) || (camera_aspect < 1.0f && aspect_ratio < camera_aspect); // To calculate the optimum viewing distance we will need the angle of the shorter side of the view rectangle. // In portrait mode this is the width, and in landscape it is the height. // We then calculate the distance based on the corresponding side of the object bbox (width for portrait, height for landscape) // We will add half the depth of the bounding box, as the distance projection uses the center point of the bbox. if(camera_aspect < 1.0f || invert) { angle_of_view = llmax(0.1f, LLViewerCamera::getInstance()->getView() * LLViewerCamera::getInstance()->getAspect()); distance = width * 0.5 * padding_factor / tan(angle_of_view * 0.5f ); lldebugs << "using width (" << width << "), angle_of_view = " << angle_of_view << ", distance = " << distance << llendl; } else { angle_of_view = llmax(0.1f, LLViewerCamera::getInstance()->getView()); distance = height * 0.5 * padding_factor / tan(angle_of_view * 0.5f ); lldebugs << "using height (" << height << "), angle_of_view = " << angle_of_view << ", distance = " << distance << llendl; } distance += depth * 0.5; // Finally animate the camera to this new position and focal point LLVector3d camera_pos, target_pos; // The target lookat position is the center of the selection (in global coords) target_pos = center; // Target look-from (camera) position is "distance" away from the target along the normal LLVector3d pickNormal = LLVector3d(normal); pickNormal.normalize(); camera_pos = target_pos + pickNormal * distance; if (pickNormal == LLVector3d::z_axis || pickNormal == LLVector3d::z_axis_neg) { // If the normal points directly up, the camera will "flip" around. // We try to avoid this by adjusting the target camera position a // smidge towards current camera position // *NOTE: this solution is not perfect. All it attempts to solve is the // "looking down" problem where the camera flips around when it animates // to that position. You still are not guaranteed to be looking at the // media in the correct orientation. What this solution does is it will // put the camera into position keeping as best it can the current // orientation with respect to the face. In other words, if before zoom // the media appears "upside down" from the camera, after zooming it will // still be upside down, but at least it will not flip. LLVector3d cur_camera_pos = LLVector3d(gAgentCamera.getCameraPositionGlobal()); LLVector3d delta = (cur_camera_pos - camera_pos); F64 len = delta.length(); delta.normalize(); // Move 1% of the distance towards original camera location camera_pos += 0.01 * len * delta; } // If we are not allowing zooming out and the old camera position is closer to // the center then the new intended camera position, don't move camera and return if (zoom_in_only && (dist_vec_squared(gAgentCamera.getCameraPositionGlobal(), target_pos) < dist_vec_squared(camera_pos, target_pos))) { return; } gAgentCamera.setCameraPosAndFocusGlobal(camera_pos, target_pos, object->getID() ); } else { // If we have no object, focus back on the avatar. gAgentCamera.setFocusOnAvatar(TRUE, ANIMATE); } }