diff options
author | Ansariel <ansariel.hiller@phoenixviewer.com> | 2024-05-22 19:04:52 +0200 |
---|---|---|
committer | Ansariel <ansariel.hiller@phoenixviewer.com> | 2024-05-22 19:04:52 +0200 |
commit | 1b67dd855c41f5a0cda7ec2a68d98071986ca703 (patch) | |
tree | ab243607f74f78200787bba5b9b88f07ef1b966f /indra/newview/llviewermediafocus.cpp | |
parent | 6d6eabca44d08d5b97bfe3e941d2b9687c2246ea (diff) | |
parent | e1623bb276f83a43ce7a197e388720c05bdefe61 (diff) |
Merge remote-tracking branch 'origin/main' into DRTVWR-600-maint-A
# Conflicts:
# autobuild.xml
# indra/cmake/CMakeLists.txt
# indra/cmake/GoogleMock.cmake
# indra/llaudio/llaudioengine_fmodstudio.cpp
# indra/llaudio/llaudioengine_fmodstudio.h
# indra/llaudio/lllistener_fmodstudio.cpp
# indra/llaudio/lllistener_fmodstudio.h
# indra/llaudio/llstreamingaudio_fmodstudio.cpp
# indra/llaudio/llstreamingaudio_fmodstudio.h
# indra/llcharacter/llmultigesture.cpp
# indra/llcharacter/llmultigesture.h
# indra/llimage/llimage.cpp
# indra/llimage/llimagepng.cpp
# indra/llimage/llimageworker.cpp
# indra/llimage/tests/llimageworker_test.cpp
# indra/llmessage/tests/llmockhttpclient.h
# indra/llprimitive/llgltfmaterial.h
# indra/llrender/llfontfreetype.cpp
# indra/llui/llcombobox.cpp
# indra/llui/llfolderview.cpp
# indra/llui/llfolderviewmodel.h
# indra/llui/lllineeditor.cpp
# indra/llui/lllineeditor.h
# indra/llui/lltextbase.cpp
# indra/llui/lltextbase.h
# indra/llui/lltexteditor.cpp
# indra/llui/lltextvalidate.cpp
# indra/llui/lltextvalidate.h
# indra/llui/lluictrl.h
# indra/llui/llview.cpp
# indra/llwindow/llwindowmacosx.cpp
# indra/newview/app_settings/settings.xml
# indra/newview/llappearancemgr.cpp
# indra/newview/llappearancemgr.h
# indra/newview/llavatarpropertiesprocessor.cpp
# indra/newview/llavatarpropertiesprocessor.h
# indra/newview/llbreadcrumbview.cpp
# indra/newview/llbreadcrumbview.h
# indra/newview/llbreastmotion.cpp
# indra/newview/llbreastmotion.h
# indra/newview/llconversationmodel.h
# indra/newview/lldensityctrl.cpp
# indra/newview/lldensityctrl.h
# indra/newview/llface.inl
# indra/newview/llfloatereditsky.cpp
# indra/newview/llfloatereditwater.cpp
# indra/newview/llfloateremojipicker.h
# indra/newview/llfloaterimsessiontab.cpp
# indra/newview/llfloaterprofiletexture.cpp
# indra/newview/llfloaterprofiletexture.h
# indra/newview/llgesturemgr.cpp
# indra/newview/llgesturemgr.h
# indra/newview/llimpanel.cpp
# indra/newview/llimpanel.h
# indra/newview/llinventorybridge.cpp
# indra/newview/llinventorybridge.h
# indra/newview/llinventoryclipboard.cpp
# indra/newview/llinventoryclipboard.h
# indra/newview/llinventoryfunctions.cpp
# indra/newview/llinventoryfunctions.h
# indra/newview/llinventorygallery.cpp
# indra/newview/lllistbrowser.cpp
# indra/newview/lllistbrowser.h
# indra/newview/llpanelobjectinventory.cpp
# indra/newview/llpanelprofile.cpp
# indra/newview/llpanelprofile.h
# indra/newview/llpreviewgesture.cpp
# indra/newview/llsavedsettingsglue.cpp
# indra/newview/llsavedsettingsglue.h
# indra/newview/lltooldraganddrop.cpp
# indra/newview/llurllineeditorctrl.cpp
# indra/newview/llvectorperfoptions.cpp
# indra/newview/llvectorperfoptions.h
# indra/newview/llviewerparceloverlay.cpp
# indra/newview/llviewertexlayer.cpp
# indra/newview/llviewertexturelist.cpp
# indra/newview/macmain.h
# indra/test/test.cpp
Diffstat (limited to 'indra/newview/llviewermediafocus.cpp')
-rw-r--r-- | indra/newview/llviewermediafocus.cpp | 1278 |
1 files changed, 639 insertions, 639 deletions
diff --git a/indra/newview/llviewermediafocus.cpp b/indra/newview/llviewermediafocus.cpp index bd309a340e..cc638bb248 100644 --- a/indra/newview/llviewermediafocus.cpp +++ b/indra/newview/llviewermediafocus.cpp @@ -1,639 +1,639 @@ -/** - * @file llviewermediafocus.cpp - * @brief Governs focus on Media prims - * - * $LicenseInfo:firstyear=2003&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "llviewerprecompiledheaders.h" - -#include "llviewermediafocus.h" - -//LLViewerMediaFocus -#include "llviewerobjectlist.h" -#include "llpanelprimmediacontrols.h" -#include "llpluginclassmedia.h" -#include "llagent.h" -#include "llagentcamera.h" -#include "lltoolpie.h" -#include "llviewercamera.h" -#include "llviewermedia.h" -#include "llhudview.h" -#include "lluictrlfactory.h" -#include "lldrawable.h" -#include "llparcel.h" -#include "llviewerparcelmgr.h" -#include "llweb.h" -#include "llmediaentry.h" -#include "llkeyboard.h" -#include "lltoolmgr.h" -#include "llvovolume.h" -#include "llhelp.h" - -// -// LLViewerMediaFocus -// - -LLViewerMediaFocus::LLViewerMediaFocus() -: mFocusedObjectFace(0), - mHoverObjectFace(0) -{ -} - -LLViewerMediaFocus::~LLViewerMediaFocus() -{ - // The destructor for LLSingletons happens at atexit() time, which is too late to do much. - // Clean up in cleanupClass() instead. - gFocusMgr.removeKeyboardFocusWithoutCallback(this); -} - -void LLViewerMediaFocus::setFocusFace(LLPointer<LLViewerObject> objectp, S32 face, viewer_media_t media_impl, LLVector3 pick_normal) -{ - LLParcel *parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); - - LLViewerMediaImpl *old_media_impl = getFocusedMediaImpl(); - if(old_media_impl) - { - old_media_impl->focus(false); - } - - // Always clear the current selection. If we're setting focus on a face, we'll reselect the correct object below. - LLSelectMgr::getInstance()->deselectAll(); - mSelection = NULL; - - if (media_impl.notNull() && objectp.notNull()) - { - bool face_auto_zoom = false; - mPrevFocusedImplID = LLUUID::null; - mFocusedImplID = media_impl->getMediaTextureID(); - mFocusedObjectID = objectp->getID(); - mFocusedObjectFace = face; - mFocusedObjectNormal = pick_normal; - - // Set the selection in the selection manager so we can draw the focus ring. - mSelection = LLSelectMgr::getInstance()->selectObjectOnly(objectp, face); - - // Focusing on a media face clears its disable flag. - media_impl->setDisabled(false); - - LLTextureEntry* tep = objectp->getTE(face); - if(tep->hasMedia()) - { - LLMediaEntry* mep = tep->getMediaData(); - face_auto_zoom = mep->getAutoZoom(); - if(!media_impl->hasMedia()) - { - std::string url = mep->getCurrentURL().empty() ? mep->getHomeURL() : mep->getCurrentURL(); - media_impl->navigateTo(url, "", true); - } - } - else - { - // This should never happen. - LL_WARNS() << "Can't find media entry for focused face" << LL_ENDL; - } - - media_impl->focus(true); - gFocusMgr.setKeyboardFocus(this); - LLViewerMediaImpl* impl = getFocusedMediaImpl(); - if (impl) - { - LLEditMenuHandler::gEditMenuHandler = impl; - } - - // We must do this before processing the media HUD zoom, or it may zoom to the wrong face. - update(); - - if(mMediaControls.get()) - { - if(face_auto_zoom && !static_cast<bool>(parcel->getMediaPreventCameraZoom())) - { - // Zoom in on this face - mMediaControls.get()->resetZoomLevel(false); - mMediaControls.get()->nextZoomLevel(); - } - else - { - // Reset the controls' zoom level without moving the camera. - // This fixes the case where clicking focus between two non-autozoom faces doesn't change the zoom-out button back to a zoom-in button. - mMediaControls.get()->resetZoomLevel(false); - } - } - } - else - { - if(hasFocus()) - { - gFocusMgr.setKeyboardFocus(NULL); - } - - LLViewerMediaImpl* impl = getFocusedMediaImpl(); - if (LLEditMenuHandler::gEditMenuHandler == impl) - { - LLEditMenuHandler::gEditMenuHandler = NULL; - } - - - mFocusedImplID = LLUUID::null; - if (objectp.notNull()) - { - // Still record the focused object...it may mean we need to load media data. - // This will aid us in determining this object is "important enough" - mFocusedObjectID = objectp->getID(); - mFocusedObjectFace = face; - } - else { - mFocusedObjectID = LLUUID::null; - mFocusedObjectFace = 0; - } - } -} - -void LLViewerMediaFocus::clearFocus() -{ - setFocusFace(NULL, 0, NULL); -} - -void LLViewerMediaFocus::setHoverFace(LLPointer<LLViewerObject> objectp, S32 face, viewer_media_t media_impl, LLVector3 pick_normal) -{ - if (media_impl.notNull()) - { - mHoverImplID = media_impl->getMediaTextureID(); - mHoverObjectID = objectp->getID(); - mHoverObjectFace = face; - mHoverObjectNormal = pick_normal; - } - else - { - mHoverObjectID = LLUUID::null; - mHoverObjectFace = 0; - mHoverImplID = LLUUID::null; - } -} - -void LLViewerMediaFocus::clearHover() -{ - setHoverFace(NULL, 0, NULL); -} - - -bool LLViewerMediaFocus::getFocus() -{ - if (gFocusMgr.getKeyboardFocus() == this) - { - return true; - } - return false; -} - -// This function selects an ideal viewing distance based on the focused object, pick normal, and padding value -LLVector3d LLViewerMediaFocus::setCameraZoom(LLViewerObject* object, LLVector3 normal, F32 padding_factor, bool zoom_in_only) -{ - LLVector3d camera_pos; - 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(); - - LL_DEBUGS() << "normal = " << normal << ", aspect_ratio = " << aspect_ratio << ", camera_aspect = " << camera_aspect << LL_ENDL; - - // 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 ); - - LL_DEBUGS() << "using width (" << width << "), angle_of_view = " << angle_of_view << ", distance = " << distance << LL_ENDL; - } - else - { - angle_of_view = llmax(0.1f, LLViewerCamera::getInstance()->getView()); - distance = height * 0.5 * padding_factor / tan(angle_of_view * 0.5f ); - - LL_DEBUGS() << "using height (" << height << "), angle_of_view = " << angle_of_view << ", distance = " << distance << LL_ENDL; - } - - distance += depth * 0.5; - - // Finally animate the camera to this new position and focal point - LLVector3d 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 camera_pos; - } - - gAgentCamera.setCameraPosAndFocusGlobal(camera_pos, target_pos, object->getID() ); - - } - else - { - // If we have no object, focus back on the avatar. - gAgentCamera.setFocusOnAvatar(true, ANIMATE); - } - return camera_pos; -} -void LLViewerMediaFocus::onFocusReceived() -{ - LLViewerMediaImpl* media_impl = getFocusedMediaImpl(); - if(media_impl) - media_impl->focus(true); - - LLFocusableElement::onFocusReceived(); -} - -void LLViewerMediaFocus::onFocusLost() -{ - LLViewerMediaImpl* media_impl = getFocusedMediaImpl(); - if(media_impl) - media_impl->focus(false); - - gViewerWindow->focusClient(); - LLFocusableElement::onFocusLost(); -} - -bool LLViewerMediaFocus::handleKey(KEY key, MASK mask, bool called_from_parent) -{ - LLViewerMediaImpl* media_impl = getFocusedMediaImpl(); - if(media_impl) - { - media_impl->handleKeyHere(key, mask); - - if (KEY_ESCAPE == key) - { - // Reset camera zoom in this case. - if(mFocusedImplID.notNull()) - { - if(mMediaControls.get()) - { - mMediaControls.get()->resetZoomLevel(true); - } - } - - clearFocus(); - } - } - - return true; -} - -bool LLViewerMediaFocus::handleKeyUp(KEY key, MASK mask, bool called_from_parent) -{ - LLViewerMediaImpl* media_impl = getFocusedMediaImpl(); - if (media_impl) - { - media_impl->handleKeyUpHere(key, mask); - } - return true; -} - - - -bool LLViewerMediaFocus::handleUnicodeChar(llwchar uni_char, bool called_from_parent) -{ - LLViewerMediaImpl* media_impl = getFocusedMediaImpl(); - if(media_impl) - media_impl->handleUnicodeCharHere(uni_char); - return true; -} - -bool LLViewerMediaFocus::handleScrollWheel(const LLVector2& texture_coords, S32 clicks_x, S32 clicks_y) -{ - bool retval = false; - LLViewerMediaImpl* media_impl = getFocusedMediaImpl(); - if (media_impl && media_impl->hasMedia()) - { - media_impl->scrollWheel(texture_coords, clicks_x, clicks_y, gKeyboard->currentMask(true)); - retval = true; - } - return retval; -} - -bool LLViewerMediaFocus::handleScrollWheel(S32 x, S32 y, S32 clicks_x, S32 clicks_y) -{ - bool retval = false; - LLViewerMediaImpl* media_impl = getFocusedMediaImpl(); - if(media_impl && media_impl->hasMedia()) - { - media_impl->scrollWheel(x, y, clicks_x, clicks_y, gKeyboard->currentMask(true)); - retval = true; - } - return retval; -} - -void LLViewerMediaFocus::update() -{ - if(mFocusedImplID.notNull()) - { - // We have a focused impl/face. - if(!getFocus()) - { - // We've lost keyboard focus -- check to see whether the media controls have it - if(mMediaControls.get() && mMediaControls.get()->hasFocus()) - { - // the media controls have focus -- don't clear. - } - else - { - // Someone else has focus -- back off. - mPrevFocusedImplID = mFocusedImplID; - clearFocus(); - } - } - else if(LLToolMgr::getInstance()->inBuildMode()) - { - // Build tools are selected -- clear focus. - clearFocus(); - } - } - - - LLViewerMediaImpl *media_impl = getFocusedMediaImpl(); - LLViewerObject *viewer_object = getFocusedObject(); - S32 face = mFocusedObjectFace; - LLVector3 normal = mFocusedObjectNormal; - - if(!media_impl || !viewer_object) - { - media_impl = getHoverMediaImpl(); - viewer_object = getHoverObject(); - face = mHoverObjectFace; - normal = mHoverObjectNormal; - } - - if(media_impl && viewer_object) - { - // We have an object and impl to point at. - - // Make sure the media HUD object exists. - if(! mMediaControls.get()) - { - LLPanelPrimMediaControls* media_controls = new LLPanelPrimMediaControls(); - mMediaControls = media_controls->getHandle(); - gHUDView->addChild(media_controls); - } - mMediaControls.get()->setMediaFace(viewer_object, face, media_impl, normal); - } - else - { - // The media HUD is no longer needed. - if(mMediaControls.get()) - { - mMediaControls.get()->setMediaFace(NULL, 0, NULL); - } - } -} - - -// This function calculates the aspect ratio and the world aligned components of a selection bounding box. -F32 LLViewerMediaFocus::getBBoxAspectRatio(const LLBBox& bbox, const LLVector3& normal, F32* height, F32* width, F32* depth) -{ - // Convert the selection normal and an up vector to local coordinate space of the bbox - LLVector3 local_normal = bbox.agentToLocalBasis(normal); - LLVector3 z_vec = bbox.agentToLocalBasis(LLVector3(0.0f, 0.0f, 1.0f)); - - LLVector3 comp1(0.f,0.f,0.f); - LLVector3 comp2(0.f,0.f,0.f); - LLVector3 bbox_max = bbox.getExtentLocal(); - F32 dot1 = 0.f; - F32 dot2 = 0.f; - - LL_DEBUGS() << "bounding box local size = " << bbox_max << ", local_normal = " << local_normal << LL_ENDL; - - // The largest component of the localized normal vector is the depth component - // meaning that the other two are the legs of the rectangle. - local_normal.abs(); - - // Using temporary variables for these makes the logic a bit more readable. - bool XgtY = (local_normal.mV[VX] > local_normal.mV[VY]); - bool XgtZ = (local_normal.mV[VX] > local_normal.mV[VZ]); - bool YgtZ = (local_normal.mV[VY] > local_normal.mV[VZ]); - - if(XgtY && XgtZ) - { - LL_DEBUGS() << "x component of normal is longest, using y and z" << LL_ENDL; - comp1.mV[VY] = bbox_max.mV[VY]; - comp2.mV[VZ] = bbox_max.mV[VZ]; - *depth = bbox_max.mV[VX]; - } - else if(!XgtY && YgtZ) - { - LL_DEBUGS() << "y component of normal is longest, using x and z" << LL_ENDL; - comp1.mV[VX] = bbox_max.mV[VX]; - comp2.mV[VZ] = bbox_max.mV[VZ]; - *depth = bbox_max.mV[VY]; - } - else - { - LL_DEBUGS() << "z component of normal is longest, using x and y" << LL_ENDL; - comp1.mV[VX] = bbox_max.mV[VX]; - comp2.mV[VY] = bbox_max.mV[VY]; - *depth = bbox_max.mV[VZ]; - } - - // The height is the vector closest to vertical in the bbox coordinate space (highest dot product value) - dot1 = comp1 * z_vec; - dot2 = comp2 * z_vec; - if(fabs(dot1) > fabs(dot2)) - { - *height = comp1.length(); - *width = comp2.length(); - - LL_DEBUGS() << "comp1 = " << comp1 << ", height = " << *height << LL_ENDL; - LL_DEBUGS() << "comp2 = " << comp2 << ", width = " << *width << LL_ENDL; - } - else - { - *height = comp2.length(); - *width = comp1.length(); - - LL_DEBUGS() << "comp2 = " << comp2 << ", height = " << *height << LL_ENDL; - LL_DEBUGS() << "comp1 = " << comp1 << ", width = " << *width << LL_ENDL; - } - - LL_DEBUGS() << "returning " << (*width / *height) << LL_ENDL; - - // Return the aspect ratio. - return *width / *height; -} - -bool LLViewerMediaFocus::isFocusedOnFace(LLPointer<LLViewerObject> objectp, S32 face) -{ - return objectp->getID() == mFocusedObjectID && face == mFocusedObjectFace; -} - -bool LLViewerMediaFocus::isHoveringOverFace(LLPointer<LLViewerObject> objectp, S32 face) -{ - return objectp->getID() == mHoverObjectID && face == mHoverObjectFace; -} - - -LLViewerMediaImpl* LLViewerMediaFocus::getFocusedMediaImpl() -{ - return LLViewerMedia::getInstance()->getMediaImplFromTextureID(mFocusedImplID); -} - -LLViewerObject* LLViewerMediaFocus::getFocusedObject() -{ - return gObjectList.findObject(mFocusedObjectID); -} - -LLViewerMediaImpl* LLViewerMediaFocus::getHoverMediaImpl() -{ - return LLViewerMedia::getInstance()->getMediaImplFromTextureID(mHoverImplID); -} - -LLViewerObject* LLViewerMediaFocus::getHoverObject() -{ - return gObjectList.findObject(mHoverObjectID); -} - -void LLViewerMediaFocus::focusZoomOnMedia(LLUUID media_id) -{ - LLViewerMediaImpl* impl = LLViewerMedia::getInstance()->getMediaImplFromTextureID(media_id); - - if(impl) - { - // Get the first object from the media impl's object list. This is completely arbitrary, but should suffice. - LLVOVolume *obj = impl->getSomeObject(); - if(obj) - { - // This media is attached to at least one object. Figure out which face it's on. - S32 face = obj->getFaceIndexWithMediaImpl(impl, -1); - - // We don't have a proper pick normal here, and finding a face's real normal is... complicated. - LLVector3 normal = obj->getApproximateFaceNormal(face); - if(normal.isNull()) - { - // If that didn't work, use the inverse of the camera "look at" axis, which should keep the camera pointed in the same direction. -// LL_INFOS() << "approximate face normal invalid, using camera direction." << LL_ENDL; - normal = LLViewerCamera::getInstance()->getAtAxis(); - normal *= (F32)-1.0f; - } - - // Attempt to focus/zoom on that face. - setFocusFace(obj, face, impl, normal); - - if(mMediaControls.get()) - { - mMediaControls.get()->resetZoomLevel(); - mMediaControls.get()->nextZoomLevel(); - } - } - } -} - -void LLViewerMediaFocus::unZoom() -{ - if(mMediaControls.get()) - { - mMediaControls.get()->resetZoomLevel(); - } -} - -bool LLViewerMediaFocus::isZoomed() const -{ - return (mMediaControls.get() && mMediaControls.get()->getZoomLevel() != LLPanelPrimMediaControls::ZOOM_NONE); -} - -bool LLViewerMediaFocus::isZoomedOnMedia(LLUUID media_id) -{ - if (isZoomed()) - { - return (mFocusedImplID == media_id) || (mPrevFocusedImplID == media_id); - } - return false; -} - -LLUUID LLViewerMediaFocus::getControlsMediaID() -{ - if(getFocusedMediaImpl()) - { - return mFocusedImplID; - } - else if(getHoverMediaImpl()) - { - return mHoverImplID; - } - - return LLUUID::null; -} - -bool LLViewerMediaFocus::wantsKeyUpKeyDown() const -{ - return true; -} - -bool LLViewerMediaFocus::wantsReturnKey() const -{ - return true; -} +/**
+ * @file llviewermediafocus.cpp
+ * @brief Governs focus on Media prims
+ *
+ * $LicenseInfo:firstyear=2003&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llviewermediafocus.h"
+
+//LLViewerMediaFocus
+#include "llviewerobjectlist.h"
+#include "llpanelprimmediacontrols.h"
+#include "llpluginclassmedia.h"
+#include "llagent.h"
+#include "llagentcamera.h"
+#include "lltoolpie.h"
+#include "llviewercamera.h"
+#include "llviewermedia.h"
+#include "llhudview.h"
+#include "lluictrlfactory.h"
+#include "lldrawable.h"
+#include "llparcel.h"
+#include "llviewerparcelmgr.h"
+#include "llweb.h"
+#include "llmediaentry.h"
+#include "llkeyboard.h"
+#include "lltoolmgr.h"
+#include "llvovolume.h"
+#include "llhelp.h"
+
+//
+// LLViewerMediaFocus
+//
+
+LLViewerMediaFocus::LLViewerMediaFocus()
+: mFocusedObjectFace(0),
+ mHoverObjectFace(0)
+{
+}
+
+LLViewerMediaFocus::~LLViewerMediaFocus()
+{
+ // The destructor for LLSingletons happens at atexit() time, which is too late to do much.
+ // Clean up in cleanupClass() instead.
+ gFocusMgr.removeKeyboardFocusWithoutCallback(this);
+}
+
+void LLViewerMediaFocus::setFocusFace(LLPointer<LLViewerObject> objectp, S32 face, viewer_media_t media_impl, LLVector3 pick_normal)
+{
+ LLParcel *parcel = LLViewerParcelMgr::getInstance()->getAgentParcel();
+
+ LLViewerMediaImpl *old_media_impl = getFocusedMediaImpl();
+ if(old_media_impl)
+ {
+ old_media_impl->focus(false);
+ }
+
+ // Always clear the current selection. If we're setting focus on a face, we'll reselect the correct object below.
+ LLSelectMgr::getInstance()->deselectAll();
+ mSelection = NULL;
+
+ if (media_impl.notNull() && objectp.notNull())
+ {
+ bool face_auto_zoom = false;
+ mPrevFocusedImplID = LLUUID::null;
+ mFocusedImplID = media_impl->getMediaTextureID();
+ mFocusedObjectID = objectp->getID();
+ mFocusedObjectFace = face;
+ mFocusedObjectNormal = pick_normal;
+
+ // Set the selection in the selection manager so we can draw the focus ring.
+ mSelection = LLSelectMgr::getInstance()->selectObjectOnly(objectp, face);
+
+ // Focusing on a media face clears its disable flag.
+ media_impl->setDisabled(false);
+
+ LLTextureEntry* tep = objectp->getTE(face);
+ if(tep->hasMedia())
+ {
+ LLMediaEntry* mep = tep->getMediaData();
+ face_auto_zoom = mep->getAutoZoom();
+ if(!media_impl->hasMedia())
+ {
+ std::string url = mep->getCurrentURL().empty() ? mep->getHomeURL() : mep->getCurrentURL();
+ media_impl->navigateTo(url, "", true);
+ }
+ }
+ else
+ {
+ // This should never happen.
+ LL_WARNS() << "Can't find media entry for focused face" << LL_ENDL;
+ }
+
+ media_impl->focus(true);
+ gFocusMgr.setKeyboardFocus(this);
+ LLViewerMediaImpl* impl = getFocusedMediaImpl();
+ if (impl)
+ {
+ LLEditMenuHandler::gEditMenuHandler = impl;
+ }
+
+ // We must do this before processing the media HUD zoom, or it may zoom to the wrong face.
+ update();
+
+ if(mMediaControls.get())
+ {
+ if(face_auto_zoom && !static_cast<bool>(parcel->getMediaPreventCameraZoom()))
+ {
+ // Zoom in on this face
+ mMediaControls.get()->resetZoomLevel(false);
+ mMediaControls.get()->nextZoomLevel();
+ }
+ else
+ {
+ // Reset the controls' zoom level without moving the camera.
+ // This fixes the case where clicking focus between two non-autozoom faces doesn't change the zoom-out button back to a zoom-in button.
+ mMediaControls.get()->resetZoomLevel(false);
+ }
+ }
+ }
+ else
+ {
+ if(hasFocus())
+ {
+ gFocusMgr.setKeyboardFocus(NULL);
+ }
+
+ LLViewerMediaImpl* impl = getFocusedMediaImpl();
+ if (LLEditMenuHandler::gEditMenuHandler == impl)
+ {
+ LLEditMenuHandler::gEditMenuHandler = NULL;
+ }
+
+
+ mFocusedImplID = LLUUID::null;
+ if (objectp.notNull())
+ {
+ // Still record the focused object...it may mean we need to load media data.
+ // This will aid us in determining this object is "important enough"
+ mFocusedObjectID = objectp->getID();
+ mFocusedObjectFace = face;
+ }
+ else {
+ mFocusedObjectID = LLUUID::null;
+ mFocusedObjectFace = 0;
+ }
+ }
+}
+
+void LLViewerMediaFocus::clearFocus()
+{
+ setFocusFace(NULL, 0, NULL);
+}
+
+void LLViewerMediaFocus::setHoverFace(LLPointer<LLViewerObject> objectp, S32 face, viewer_media_t media_impl, LLVector3 pick_normal)
+{
+ if (media_impl.notNull())
+ {
+ mHoverImplID = media_impl->getMediaTextureID();
+ mHoverObjectID = objectp->getID();
+ mHoverObjectFace = face;
+ mHoverObjectNormal = pick_normal;
+ }
+ else
+ {
+ mHoverObjectID = LLUUID::null;
+ mHoverObjectFace = 0;
+ mHoverImplID = LLUUID::null;
+ }
+}
+
+void LLViewerMediaFocus::clearHover()
+{
+ setHoverFace(NULL, 0, NULL);
+}
+
+
+bool LLViewerMediaFocus::getFocus()
+{
+ if (gFocusMgr.getKeyboardFocus() == this)
+ {
+ return true;
+ }
+ return false;
+}
+
+// This function selects an ideal viewing distance based on the focused object, pick normal, and padding value
+LLVector3d LLViewerMediaFocus::setCameraZoom(LLViewerObject* object, LLVector3 normal, F32 padding_factor, bool zoom_in_only)
+{
+ LLVector3d camera_pos;
+ 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();
+
+ LL_DEBUGS() << "normal = " << normal << ", aspect_ratio = " << aspect_ratio << ", camera_aspect = " << camera_aspect << LL_ENDL;
+
+ // 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 );
+
+ LL_DEBUGS() << "using width (" << width << "), angle_of_view = " << angle_of_view << ", distance = " << distance << LL_ENDL;
+ }
+ else
+ {
+ angle_of_view = llmax(0.1f, LLViewerCamera::getInstance()->getView());
+ distance = height * 0.5 * padding_factor / tan(angle_of_view * 0.5f );
+
+ LL_DEBUGS() << "using height (" << height << "), angle_of_view = " << angle_of_view << ", distance = " << distance << LL_ENDL;
+ }
+
+ distance += depth * 0.5;
+
+ // Finally animate the camera to this new position and focal point
+ LLVector3d 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 camera_pos;
+ }
+
+ gAgentCamera.setCameraPosAndFocusGlobal(camera_pos, target_pos, object->getID() );
+
+ }
+ else
+ {
+ // If we have no object, focus back on the avatar.
+ gAgentCamera.setFocusOnAvatar(true, ANIMATE);
+ }
+ return camera_pos;
+}
+void LLViewerMediaFocus::onFocusReceived()
+{
+ LLViewerMediaImpl* media_impl = getFocusedMediaImpl();
+ if(media_impl)
+ media_impl->focus(true);
+
+ LLFocusableElement::onFocusReceived();
+}
+
+void LLViewerMediaFocus::onFocusLost()
+{
+ LLViewerMediaImpl* media_impl = getFocusedMediaImpl();
+ if(media_impl)
+ media_impl->focus(false);
+
+ gViewerWindow->focusClient();
+ LLFocusableElement::onFocusLost();
+}
+
+bool LLViewerMediaFocus::handleKey(KEY key, MASK mask, bool called_from_parent)
+{
+ LLViewerMediaImpl* media_impl = getFocusedMediaImpl();
+ if(media_impl)
+ {
+ media_impl->handleKeyHere(key, mask);
+
+ if (KEY_ESCAPE == key)
+ {
+ // Reset camera zoom in this case.
+ if(mFocusedImplID.notNull())
+ {
+ if(mMediaControls.get())
+ {
+ mMediaControls.get()->resetZoomLevel(true);
+ }
+ }
+
+ clearFocus();
+ }
+ }
+
+ return true;
+}
+
+bool LLViewerMediaFocus::handleKeyUp(KEY key, MASK mask, bool called_from_parent)
+{
+ LLViewerMediaImpl* media_impl = getFocusedMediaImpl();
+ if (media_impl)
+ {
+ media_impl->handleKeyUpHere(key, mask);
+ }
+ return true;
+}
+
+
+
+bool LLViewerMediaFocus::handleUnicodeChar(llwchar uni_char, bool called_from_parent)
+{
+ LLViewerMediaImpl* media_impl = getFocusedMediaImpl();
+ if(media_impl)
+ media_impl->handleUnicodeCharHere(uni_char);
+ return true;
+}
+
+bool LLViewerMediaFocus::handleScrollWheel(const LLVector2& texture_coords, S32 clicks_x, S32 clicks_y)
+{
+ bool retval = false;
+ LLViewerMediaImpl* media_impl = getFocusedMediaImpl();
+ if (media_impl && media_impl->hasMedia())
+ {
+ media_impl->scrollWheel(texture_coords, clicks_x, clicks_y, gKeyboard->currentMask(true));
+ retval = true;
+ }
+ return retval;
+}
+
+bool LLViewerMediaFocus::handleScrollWheel(S32 x, S32 y, S32 clicks_x, S32 clicks_y)
+{
+ bool retval = false;
+ LLViewerMediaImpl* media_impl = getFocusedMediaImpl();
+ if(media_impl && media_impl->hasMedia())
+ {
+ media_impl->scrollWheel(x, y, clicks_x, clicks_y, gKeyboard->currentMask(true));
+ retval = true;
+ }
+ return retval;
+}
+
+void LLViewerMediaFocus::update()
+{
+ if(mFocusedImplID.notNull())
+ {
+ // We have a focused impl/face.
+ if(!getFocus())
+ {
+ // We've lost keyboard focus -- check to see whether the media controls have it
+ if(mMediaControls.get() && mMediaControls.get()->hasFocus())
+ {
+ // the media controls have focus -- don't clear.
+ }
+ else
+ {
+ // Someone else has focus -- back off.
+ mPrevFocusedImplID = mFocusedImplID;
+ clearFocus();
+ }
+ }
+ else if(LLToolMgr::getInstance()->inBuildMode())
+ {
+ // Build tools are selected -- clear focus.
+ clearFocus();
+ }
+ }
+
+
+ LLViewerMediaImpl *media_impl = getFocusedMediaImpl();
+ LLViewerObject *viewer_object = getFocusedObject();
+ S32 face = mFocusedObjectFace;
+ LLVector3 normal = mFocusedObjectNormal;
+
+ if(!media_impl || !viewer_object)
+ {
+ media_impl = getHoverMediaImpl();
+ viewer_object = getHoverObject();
+ face = mHoverObjectFace;
+ normal = mHoverObjectNormal;
+ }
+
+ if(media_impl && viewer_object)
+ {
+ // We have an object and impl to point at.
+
+ // Make sure the media HUD object exists.
+ if(! mMediaControls.get())
+ {
+ LLPanelPrimMediaControls* media_controls = new LLPanelPrimMediaControls();
+ mMediaControls = media_controls->getHandle();
+ gHUDView->addChild(media_controls);
+ }
+ mMediaControls.get()->setMediaFace(viewer_object, face, media_impl, normal);
+ }
+ else
+ {
+ // The media HUD is no longer needed.
+ if(mMediaControls.get())
+ {
+ mMediaControls.get()->setMediaFace(NULL, 0, NULL);
+ }
+ }
+}
+
+
+// This function calculates the aspect ratio and the world aligned components of a selection bounding box.
+F32 LLViewerMediaFocus::getBBoxAspectRatio(const LLBBox& bbox, const LLVector3& normal, F32* height, F32* width, F32* depth)
+{
+ // Convert the selection normal and an up vector to local coordinate space of the bbox
+ LLVector3 local_normal = bbox.agentToLocalBasis(normal);
+ LLVector3 z_vec = bbox.agentToLocalBasis(LLVector3(0.0f, 0.0f, 1.0f));
+
+ LLVector3 comp1(0.f,0.f,0.f);
+ LLVector3 comp2(0.f,0.f,0.f);
+ LLVector3 bbox_max = bbox.getExtentLocal();
+ F32 dot1 = 0.f;
+ F32 dot2 = 0.f;
+
+ LL_DEBUGS() << "bounding box local size = " << bbox_max << ", local_normal = " << local_normal << LL_ENDL;
+
+ // The largest component of the localized normal vector is the depth component
+ // meaning that the other two are the legs of the rectangle.
+ local_normal.abs();
+
+ // Using temporary variables for these makes the logic a bit more readable.
+ bool XgtY = (local_normal.mV[VX] > local_normal.mV[VY]);
+ bool XgtZ = (local_normal.mV[VX] > local_normal.mV[VZ]);
+ bool YgtZ = (local_normal.mV[VY] > local_normal.mV[VZ]);
+
+ if(XgtY && XgtZ)
+ {
+ LL_DEBUGS() << "x component of normal is longest, using y and z" << LL_ENDL;
+ comp1.mV[VY] = bbox_max.mV[VY];
+ comp2.mV[VZ] = bbox_max.mV[VZ];
+ *depth = bbox_max.mV[VX];
+ }
+ else if(!XgtY && YgtZ)
+ {
+ LL_DEBUGS() << "y component of normal is longest, using x and z" << LL_ENDL;
+ comp1.mV[VX] = bbox_max.mV[VX];
+ comp2.mV[VZ] = bbox_max.mV[VZ];
+ *depth = bbox_max.mV[VY];
+ }
+ else
+ {
+ LL_DEBUGS() << "z component of normal is longest, using x and y" << LL_ENDL;
+ comp1.mV[VX] = bbox_max.mV[VX];
+ comp2.mV[VY] = bbox_max.mV[VY];
+ *depth = bbox_max.mV[VZ];
+ }
+
+ // The height is the vector closest to vertical in the bbox coordinate space (highest dot product value)
+ dot1 = comp1 * z_vec;
+ dot2 = comp2 * z_vec;
+ if(fabs(dot1) > fabs(dot2))
+ {
+ *height = comp1.length();
+ *width = comp2.length();
+
+ LL_DEBUGS() << "comp1 = " << comp1 << ", height = " << *height << LL_ENDL;
+ LL_DEBUGS() << "comp2 = " << comp2 << ", width = " << *width << LL_ENDL;
+ }
+ else
+ {
+ *height = comp2.length();
+ *width = comp1.length();
+
+ LL_DEBUGS() << "comp2 = " << comp2 << ", height = " << *height << LL_ENDL;
+ LL_DEBUGS() << "comp1 = " << comp1 << ", width = " << *width << LL_ENDL;
+ }
+
+ LL_DEBUGS() << "returning " << (*width / *height) << LL_ENDL;
+
+ // Return the aspect ratio.
+ return *width / *height;
+}
+
+bool LLViewerMediaFocus::isFocusedOnFace(LLPointer<LLViewerObject> objectp, S32 face)
+{
+ return objectp->getID() == mFocusedObjectID && face == mFocusedObjectFace;
+}
+
+bool LLViewerMediaFocus::isHoveringOverFace(LLPointer<LLViewerObject> objectp, S32 face)
+{
+ return objectp->getID() == mHoverObjectID && face == mHoverObjectFace;
+}
+
+
+LLViewerMediaImpl* LLViewerMediaFocus::getFocusedMediaImpl()
+{
+ return LLViewerMedia::getInstance()->getMediaImplFromTextureID(mFocusedImplID);
+}
+
+LLViewerObject* LLViewerMediaFocus::getFocusedObject()
+{
+ return gObjectList.findObject(mFocusedObjectID);
+}
+
+LLViewerMediaImpl* LLViewerMediaFocus::getHoverMediaImpl()
+{
+ return LLViewerMedia::getInstance()->getMediaImplFromTextureID(mHoverImplID);
+}
+
+LLViewerObject* LLViewerMediaFocus::getHoverObject()
+{
+ return gObjectList.findObject(mHoverObjectID);
+}
+
+void LLViewerMediaFocus::focusZoomOnMedia(LLUUID media_id)
+{
+ LLViewerMediaImpl* impl = LLViewerMedia::getInstance()->getMediaImplFromTextureID(media_id);
+
+ if(impl)
+ {
+ // Get the first object from the media impl's object list. This is completely arbitrary, but should suffice.
+ LLVOVolume *obj = impl->getSomeObject();
+ if(obj)
+ {
+ // This media is attached to at least one object. Figure out which face it's on.
+ S32 face = obj->getFaceIndexWithMediaImpl(impl, -1);
+
+ // We don't have a proper pick normal here, and finding a face's real normal is... complicated.
+ LLVector3 normal = obj->getApproximateFaceNormal(face);
+ if(normal.isNull())
+ {
+ // If that didn't work, use the inverse of the camera "look at" axis, which should keep the camera pointed in the same direction.
+// LL_INFOS() << "approximate face normal invalid, using camera direction." << LL_ENDL;
+ normal = LLViewerCamera::getInstance()->getAtAxis();
+ normal *= (F32)-1.0f;
+ }
+
+ // Attempt to focus/zoom on that face.
+ setFocusFace(obj, face, impl, normal);
+
+ if(mMediaControls.get())
+ {
+ mMediaControls.get()->resetZoomLevel();
+ mMediaControls.get()->nextZoomLevel();
+ }
+ }
+ }
+}
+
+void LLViewerMediaFocus::unZoom()
+{
+ if(mMediaControls.get())
+ {
+ mMediaControls.get()->resetZoomLevel();
+ }
+}
+
+bool LLViewerMediaFocus::isZoomed() const
+{
+ return (mMediaControls.get() && mMediaControls.get()->getZoomLevel() != LLPanelPrimMediaControls::ZOOM_NONE);
+}
+
+bool LLViewerMediaFocus::isZoomedOnMedia(LLUUID media_id)
+{
+ if (isZoomed())
+ {
+ return (mFocusedImplID == media_id) || (mPrevFocusedImplID == media_id);
+ }
+ return false;
+}
+
+LLUUID LLViewerMediaFocus::getControlsMediaID()
+{
+ if(getFocusedMediaImpl())
+ {
+ return mFocusedImplID;
+ }
+ else if(getHoverMediaImpl())
+ {
+ return mHoverImplID;
+ }
+
+ return LLUUID::null;
+}
+
+bool LLViewerMediaFocus::wantsKeyUpKeyDown() const
+{
+ return true;
+}
+
+bool LLViewerMediaFocus::wantsReturnKey() const
+{
+ return true;
+}
|