/** * @file lltoolfocus.cpp * @brief A tool to set the build focus point. * * $LicenseInfo:firstyear=2001&license=viewergpl$ * * Copyright (c) 2001-2007, Linden Research, Inc. * * Second Life Viewer Source Code * The source code in this file ("Source Code") is provided by Linden Lab * to you under the terms of the GNU General Public License, version 2.0 * ("GPL"), unless you have obtained a separate licensing agreement * ("Other License"), formally executed by you and Linden Lab. Terms of * the GPL can be found in doc/GPL-license.txt in this distribution, or * online at http://secondlife.com/developers/opensource/gplv2 * * There are special exceptions to the terms and conditions of the GPL as * it is applied to this Source Code. View the full text of the exception * in the file doc/FLOSS-exception.txt in this software distribution, or * online at http://secondlife.com/developers/opensource/flossexception * * By copying, modifying or distributing this software, you acknowledge * that you have read and understood your obligations described above, * and agree to abide by those obligations. * * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, * COMPLETENESS OR PERFORMANCE. * $/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 "llbutton.h" #include "llviewercontrol.h" #include "lldrawable.h" #include "llhoverview.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 "llvoavatar.h" #include "llmorphview.h" // Globals BOOL gCameraBtnOrbit = FALSE; BOOL gCameraBtnPan = FALSE; const S32 SLOP_RANGE = 4; const F32 FOCUS_OFFSET_FACTOR = 1.f; // // Camera - shared functionality // LLToolCamera::LLToolCamera() : LLTool("Camera"), mAccumX(0), mAccumY(0), mMouseDownX(0), mMouseDownY(0), mOutsideSlopX(FALSE), mOutsideSlopY(FALSE), mValidClickPoint(FALSE), mMouseSteering(FALSE), mMouseUpX(0), mMouseUpY(0), mMouseUpMask(MASK_NONE) { } LLToolCamera::~LLToolCamera() { } // virtual void LLToolCamera::handleSelect() { if (gFloaterTools) { gFloaterTools->setStatusText("camera"); } } // virtual void LLToolCamera::handleDeselect() { // gAgent.setLookingAtAvatar(FALSE); } 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; // 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->hitObjectOrLandGlobalAsync(x, y, mask, pickCallback); // don't steal focus from UI return FALSE; } void LLToolCamera::pickCallback(S32 x, S32 y, MASK mask) { if (!LLToolCamera::getInstance()->hasMouseCapture()) { return; } LLToolCamera::getInstance()->mMouseDownX = x; LLToolCamera::getInstance()->mMouseDownY = y; gViewerWindow->moveCursorToCenter(); // Potentially recenter if click outside rectangle LLViewerObject* hit_obj = gViewerWindow->lastObjectHit(); // Check for hit the sky, or some other invalid point if (!hit_obj && gLastHitPosGlobal.isExactlyZero()) { LLToolCamera::getInstance()->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) { LLToolCamera::getInstance()->mValidClickPoint = FALSE; return; } } if( CAMERA_MODE_CUSTOMIZE_AVATAR == gAgent.getCameraMode() ) { BOOL good_customize_avatar_hit = FALSE; if( hit_obj ) { LLVOAvatar* avatar = gAgent.getAvatarObject(); if( hit_obj == avatar) { // 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 ) { LLToolCamera::getInstance()->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 (mask & MASK_ALT || (LLToolMgr::getInstance()->getCurrentTool()->getName() == "Camera")) { LLViewerObject* hit_obj = gViewerWindow->lastObjectHit(); if (hit_obj) { // ...clicked on a world object, so focus at its position // Use "gLastHitPosGlobal" because it's correct for avatar heads, // not pelvis. if (!hit_obj->isHUDAttachment()) { gAgent.setFocusOnAvatar(FALSE, ANIMATE); gAgent.setFocusGlobal( gLastHitObjectOffset + gLastHitPosGlobal, gLastHitObjectID); } } else if (!gLastHitPosGlobal.isExactlyZero()) { // Hit the ground gAgent.setFocusOnAvatar(FALSE, ANIMATE); gAgent.setFocusGlobal( gLastHitPosGlobal, gLastHitObjectID); } if (!(mask & MASK_ALT) && gAgent.cameraThirdPerson() && gViewerWindow->getLeftMouseDown() && !gSavedSettings.getBOOL("FreezeTime") && (hit_obj == gAgent.getAvatarObject() || (hit_obj && hit_obj->isAttachment() && LLVOAvatar::findAvatarFromAttachment(hit_obj)->mIsSelf))) { LLToolCamera::getInstance()->mMouseSteering = TRUE; } } LLToolCamera::getInstance()->mValidClickPoint = TRUE; if( CAMERA_MODE_CUSTOMIZE_AVATAR == gAgent.getCameraMode() ) { gAgent.setFocusOnAvatar(FALSE, FALSE); LLVector3d cam_pos = gAgent.getCameraPositionGlobal(); cam_pos -= LLVector3d(LLViewerCamera::getInstance()->getLeftAxis() * gAgent.calcCustomizeAvatarUIOffset( cam_pos )); gAgent.setCameraPosAndFocusGlobal( cam_pos, gLastHitObjectOffset + gLastHitPosGlobal, gLastHitObjectID); } } // "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(); 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()) { if (mValidClickPoint) { if( CAMERA_MODE_CUSTOMIZE_AVATAR == gAgent.getCameraMode() ) { LLCoordGL mouse_pos; LLVector3 focus_pos = gAgent.getPosAgentFromGlobal(gAgent.getFocusGlobal()); BOOL success = LLViewerCamera::getInstance()->projectPosAgentToScreen(focus_pos, mouse_pos); if (success) { LLUI::setCursorPositionScreen(mouse_pos.mX, mouse_pos.mY); } } else if (mMouseSteering) { LLUI::setCursorPositionScreen(mMouseDownX, mMouseDownY); } else { gViewerWindow->moveCursorToCenter(); } } else { // not a valid zoomable object LLUI::setCursorPositionScreen(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(); BOOL moved_outside_slop = FALSE; if (hasMouseCapture() && mValidClickPoint) { mAccumX += llabs(dx); mAccumY += llabs(dy); if (mAccumX >= SLOP_RANGE) { if (!mOutsideSlopX) { moved_outside_slop = TRUE; } mOutsideSlopX = TRUE; } if (mAccumY >= SLOP_RANGE) { if (!mOutsideSlopY) { moved_outside_slop = TRUE; } mOutsideSlopY = TRUE; } } if (mOutsideSlopX || mOutsideSlopY) { if (!mValidClickPoint) { lldebugst(LLERR_USER_INPUT) << "hover handled by LLToolFocus [invalid point]" << llendl; 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->getWindowWidth(); if (dx != 0) { gAgent.cameraOrbitAround( -dx * RADIANS_PER_PIXEL ); } if (dy != 0) { gAgent.cameraOrbitOver( -dy * RADIANS_PER_PIXEL ); } gViewerWindow->moveCursorToCenter(); } lldebugst(LLERR_USER_INPUT) << "hover handled by LLToolFocus [active]" << llendl; } else if ( gCameraBtnPan || mask == MASK_PAN || mask == (MASK_PAN | MASK_ALT) ) { // Pan tool if (hasMouseCapture()) { LLVector3d camera_to_focus = gAgent.getCameraPositionGlobal(); camera_to_focus -= gAgent.getFocusGlobal(); F32 dist = (F32) camera_to_focus.normVec(); // Fudge factor for pan F32 meters_per_pixel = 3.f * dist / gViewerWindow->getWindowWidth(); if (dx != 0) { gAgent.cameraPanLeft( dx * meters_per_pixel ); } if (dy != 0) { gAgent.cameraPanUp( -dy * meters_per_pixel ); } gViewerWindow->moveCursorToCenter(); } lldebugst(LLERR_USER_INPUT) << "hover handled by LLToolPan" << llendl; } else { // Zoom tool if (hasMouseCapture()) { const F32 RADIANS_PER_PIXEL = 360.f * DEG_TO_RAD / gViewerWindow->getWindowWidth(); if (dx != 0) { gAgent.cameraOrbitAround( -dx * RADIANS_PER_PIXEL ); } const F32 IN_FACTOR = 0.99f; if (dy != 0 && mOutsideSlopY ) { if (mMouseSteering) { gAgent.cameraOrbitOver( -dy * RADIANS_PER_PIXEL ); } else { gAgent.cameraZoomIn( pow( IN_FACTOR, dy ) ); } } gViewerWindow->moveCursorToCenter(); } lldebugst(LLERR_USER_INPUT) << "hover handled by LLToolZoom" << llendl; } } 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(); }