/** * @file lltoolfocus.cpp * @brief A tool to set the build focus point. * * $LicenseInfo:firstyear=2001&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" // File includes #include "lltoolfocus.h" // Library includes #include "v3math.h" #include "llfontgl.h" #include "llui.h" // Viewer includes #include "llagent.h" #include "llagentcamera.h" #include "llbutton.h" #include "llviewercontrol.h" #include "lldrawable.h" #include "lltooltip.h" #include "llhudmanager.h" #include "llfloatertools.h" #include "llselectmgr.h" #include "llstatusbar.h" #include "lltoolmgr.h" #include "llviewercamera.h" #include "llviewerobject.h" #include "llviewerwindow.h" #include "llvoavatarself.h" #include "llmorphview.h" #include "llfloaterreg.h" #include "llfloatercamera.h" #include "llmenugl.h" // Globals BOOL gCameraBtnZoom = TRUE; BOOL gCameraBtnOrbit = FALSE; BOOL gCameraBtnPan = FALSE; const S32 SLOP_RANGE = 4; // // Camera - shared functionality // LLToolCamera::LLToolCamera() : LLTool(std::string("Camera")), mAccumX(0), mAccumY(0), mMouseDownX(0), mMouseDownY(0), mOutsideSlopX(FALSE), mOutsideSlopY(FALSE), mValidClickPoint(FALSE), mClickPickPending(false), mValidSelection(FALSE), mMouseSteering(FALSE), mMouseUpX(0), mMouseUpY(0), mMouseUpMask(MASK_NONE) { } LLToolCamera::~LLToolCamera() { } // virtual void LLToolCamera::handleSelect() { if (gFloaterTools) { gFloaterTools->setStatusText("camera"); // in case we start from tools floater, we count any selection as valid mValidSelection = gFloaterTools->getVisible(); } } // virtual void LLToolCamera::handleDeselect() { // gAgent.setLookingAtAvatar(FALSE); // Make sure that temporary selection won't pass anywhere except pie tool. MASK override_mask = gKeyboard ? gKeyboard->currentMask(TRUE) : 0; if (!mValidSelection && (override_mask != MASK_NONE || (gFloaterTools && gFloaterTools->getVisible()))) { LLMenuGL::sMenuContainer->hideMenus(); LLSelectMgr::getInstance()->validateSelection(); } } bool LLToolCamera::handleMouseDown(S32 x, S32 y, MASK mask) { // Ensure a mouseup setMouseCapture(TRUE); // call the base class to propogate info to sim LLTool::handleMouseDown(x, y, mask); mAccumX = 0; mAccumY = 0; mOutsideSlopX = FALSE; mOutsideSlopY = FALSE; mValidClickPoint = FALSE; // Sometimes Windows issues down and up events near simultaneously // without giving async pick a chance to trigged // Ex: mouse from numlock emulation mClickPickPending = true; // If mouse capture gets ripped away, claim we moused up // at the point we moused down. JC mMouseUpX = x; mMouseUpY = y; mMouseUpMask = mask; gViewerWindow->hideCursor(); gViewerWindow->pickAsync(x, y, mask, pickCallback, /*BOOL pick_transparent*/ FALSE, /*BOOL pick_rigged*/ FALSE, /*BOOL pick_unselectable*/ TRUE); return true; } void LLToolCamera::pickCallback(const LLPickInfo& pick_info) { LLToolCamera* camera = LLToolCamera::getInstance(); if (!camera->mClickPickPending) { return; } camera->mClickPickPending = false; camera->mMouseDownX = pick_info.mMousePt.mX; camera->mMouseDownY = pick_info.mMousePt.mY; gViewerWindow->moveCursorToCenter(); // Potentially recenter if click outside rectangle LLViewerObject* hit_obj = pick_info.getObject(); // Check for hit the sky, or some other invalid point if (!hit_obj && pick_info.mPosGlobal.isExactlyZero()) { camera->mValidClickPoint = FALSE; return; } // check for hud attachments if (hit_obj && hit_obj->isHUDAttachment()) { LLObjectSelectionHandle selection = LLSelectMgr::getInstance()->getSelection(); if (!selection->getObjectCount() || selection->getSelectType() != SELECT_TYPE_HUD) { camera->mValidClickPoint = FALSE; return; } } if( CAMERA_MODE_CUSTOMIZE_AVATAR == gAgentCamera.getCameraMode() ) { BOOL good_customize_avatar_hit = FALSE; if( hit_obj ) { if (isAgentAvatarValid() && (hit_obj == gAgentAvatarp)) { // It's you good_customize_avatar_hit = TRUE; } else if (hit_obj->isAttachment() && hit_obj->permYouOwner()) { // It's an attachment that you're wearing good_customize_avatar_hit = TRUE; } } if( !good_customize_avatar_hit ) { camera->mValidClickPoint = FALSE; return; } if( gMorphView ) { gMorphView->setCameraDrivenByKeys( FALSE ); } } //RN: check to see if this is mouse-driving as opposed to ALT-zoom or Focus tool else if (pick_info.mKeyMask & MASK_ALT || (LLToolMgr::getInstance()->getCurrentTool()->getName() == "Camera")) { LLViewerObject* hit_obj = pick_info.getObject(); if (hit_obj) { // ...clicked on a world object, so focus at its position if (!hit_obj->isHUDAttachment()) { gAgentCamera.setFocusOnAvatar(FALSE, ANIMATE); gAgentCamera.setFocusGlobal(pick_info); } } else if (!pick_info.mPosGlobal.isExactlyZero()) { // Hit the ground gAgentCamera.setFocusOnAvatar(FALSE, ANIMATE); gAgentCamera.setFocusGlobal(pick_info); } BOOL zoom_tool = gCameraBtnZoom && (LLToolMgr::getInstance()->getBaseTool() == LLToolCamera::getInstance()); if (!(pick_info.mKeyMask & MASK_ALT) && !LLFloaterCamera::inFreeCameraMode() && !zoom_tool && gAgentCamera.cameraThirdPerson() && gViewerWindow->getLeftMouseDown() && !gSavedSettings.getBOOL("FreezeTime") && (hit_obj == gAgentAvatarp || (hit_obj && hit_obj->isAttachment() && LLVOAvatar::findAvatarFromAttachment(hit_obj)->isSelf()))) { LLToolCamera::getInstance()->mMouseSteering = TRUE; } } camera->mValidClickPoint = TRUE; if( CAMERA_MODE_CUSTOMIZE_AVATAR == gAgentCamera.getCameraMode() ) { gAgentCamera.setFocusOnAvatar(FALSE, FALSE); LLVector3d cam_pos = gAgentCamera.getCameraPositionGlobal(); gAgentCamera.setCameraPosAndFocusGlobal( cam_pos, pick_info.mPosGlobal, pick_info.mObjectID); } } // "Let go" of the mouse, for example on mouse up or when // we lose mouse capture. This ensures that cursor becomes visible // if a modal dialog pops up during Alt-Zoom. JC void LLToolCamera::releaseMouse() { // Need to tell the sim that the mouse button is up, since this // tool is no longer working and cursor is visible (despite actual // mouse button status). LLTool::handleMouseUp(mMouseUpX, mMouseUpY, mMouseUpMask); gViewerWindow->showCursor(); //for the situation when left click was performed on the Agent if (!LLFloaterCamera::inFreeCameraMode()) { LLToolMgr::getInstance()->clearTransientTool(); } mMouseSteering = FALSE; mValidClickPoint = FALSE; mOutsideSlopX = FALSE; mOutsideSlopY = FALSE; } bool LLToolCamera::handleMouseUp(S32 x, S32 y, MASK mask) { // Claim that we're mousing up somewhere mMouseUpX = x; mMouseUpY = y; mMouseUpMask = mask; if (hasMouseCapture()) { // Do not move camera if we haven't gotten a pick if (!mClickPickPending) { if (mValidClickPoint) { if (CAMERA_MODE_CUSTOMIZE_AVATAR == gAgentCamera.getCameraMode()) { LLCoordGL mouse_pos; LLVector3 focus_pos = gAgent.getPosAgentFromGlobal(gAgentCamera.getFocusGlobal()); BOOL success = LLViewerCamera::getInstance()->projectPosAgentToScreen(focus_pos, mouse_pos); if (success) { LLUI::getInstance()->setMousePositionScreen(mouse_pos.mX, mouse_pos.mY); } } else if (mMouseSteering) { LLUI::getInstance()->setMousePositionScreen(mMouseDownX, mMouseDownY); } else { gViewerWindow->moveCursorToCenter(); } } else { // not a valid zoomable object LLUI::getInstance()->setMousePositionScreen(mMouseDownX, mMouseDownY); } } // calls releaseMouse() internally setMouseCapture(FALSE); } else { releaseMouse(); } return true; } bool LLToolCamera::handleHover(S32 x, S32 y, MASK mask) { S32 dx = gViewerWindow->getCurrentMouseDX(); S32 dy = gViewerWindow->getCurrentMouseDY(); if (hasMouseCapture() && mValidClickPoint) { mAccumX += llabs(dx); mAccumY += llabs(dy); if (mAccumX >= SLOP_RANGE) { mOutsideSlopX = TRUE; } if (mAccumY >= SLOP_RANGE) { mOutsideSlopY = TRUE; } } if (mOutsideSlopX || mOutsideSlopY) { if (!mValidClickPoint) { LL_DEBUGS("UserInput") << "hover handled by LLToolFocus [invalid point]" << LL_ENDL; gViewerWindow->setCursor(UI_CURSOR_NO); gViewerWindow->showCursor(); return TRUE; } if (gCameraBtnOrbit || mask == MASK_ORBIT || mask == (MASK_ALT | MASK_ORBIT)) { // Orbit tool if (hasMouseCapture()) { const F32 RADIANS_PER_PIXEL = 360.f * DEG_TO_RAD / gViewerWindow->getWorldViewWidthScaled(); if (dx != 0) { gAgentCamera.cameraOrbitAround( -dx * RADIANS_PER_PIXEL ); } if (dy != 0) { gAgentCamera.cameraOrbitOver( -dy * RADIANS_PER_PIXEL ); } gViewerWindow->moveCursorToCenter(); } LL_DEBUGS("UserInput") << "hover handled by LLToolFocus [active]" << LL_ENDL; } else if ( gCameraBtnPan || mask == MASK_PAN || mask == (MASK_PAN | MASK_ALT) ) { // Pan tool if (hasMouseCapture()) { LLVector3d camera_to_focus = gAgentCamera.getCameraPositionGlobal(); camera_to_focus -= gAgentCamera.getFocusGlobal(); F32 dist = (F32) camera_to_focus.normVec(); // Fudge factor for pan F32 meters_per_pixel = 3.f * dist / gViewerWindow->getWorldViewWidthScaled(); if (dx != 0) { gAgentCamera.cameraPanLeft( dx * meters_per_pixel ); } if (dy != 0) { gAgentCamera.cameraPanUp( -dy * meters_per_pixel ); } gViewerWindow->moveCursorToCenter(); } LL_DEBUGS("UserInput") << "hover handled by LLToolPan" << LL_ENDL; } else if (gCameraBtnZoom) { // Zoom tool if (hasMouseCapture()) { const F32 RADIANS_PER_PIXEL = 360.f * DEG_TO_RAD / gViewerWindow->getWorldViewWidthScaled(); if (dx != 0) { gAgentCamera.cameraOrbitAround( -dx * RADIANS_PER_PIXEL ); } const F32 IN_FACTOR = 0.99f; if (dy != 0 && mOutsideSlopY ) { if (mMouseSteering) { gAgentCamera.cameraOrbitOver( -dy * RADIANS_PER_PIXEL ); } else { gAgentCamera.cameraZoomIn( pow( IN_FACTOR, dy ) ); } } gViewerWindow->moveCursorToCenter(); } LL_DEBUGS("UserInput") << "hover handled by LLToolZoom" << LL_ENDL; } } if (gCameraBtnOrbit || mask == MASK_ORBIT || mask == (MASK_ALT | MASK_ORBIT)) { gViewerWindow->setCursor(UI_CURSOR_TOOLCAMERA); } else if ( gCameraBtnPan || mask == MASK_PAN || mask == (MASK_PAN | MASK_ALT) ) { gViewerWindow->setCursor(UI_CURSOR_TOOLPAN); } else { gViewerWindow->setCursor(UI_CURSOR_TOOLZOOMIN); } return true; } void LLToolCamera::onMouseCaptureLost() { releaseMouse(); }