/** * @file lltoolgrab.cpp * @brief LLToolGrab class implementation * * $LicenseInfo:firstyear=2001&license=viewergpl$ * * Copyright (c) 2001-2008, 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://secondlifegrid.net/programs/open_source/licensing/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://secondlifegrid.net/programs/open_source/licensing/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" #include "lltoolgrab.h" // library headers #include "indra_constants.h" // for agent control flags #include "llviewercontrol.h" #include "llquaternion.h" #include "llbox.h" #include "message.h" #include "llview.h" #include "llfontgl.h" #include "llui.h" // newview headers #include "llagent.h" //#include "llfloateravatarinfo.h" #include "lldrawable.h" #include "llfloatertools.h" #include "llhudeffect.h" #include "llhudmanager.h" #include "llregionposition.h" #include "llselectmgr.h" #include "llstatusbar.h" #include "lltoolmgr.h" #include "lltoolpie.h" #include "llviewercamera.h" #include "llviewerobject.h" #include "llviewerobjectlist.h" #include "llviewerregion.h" #include "llviewerwindow.h" #include "llvoavatar.h" #include "llworld.h" const S32 SLOP_DIST_SQ = 4; // Override modifier key behavior with these buttons BOOL gGrabBtnVertical = FALSE; BOOL gGrabBtnSpin = FALSE; LLTool* gGrabTransientTool = NULL; extern BOOL gDebugClicks; // // Methods // LLToolGrab::LLToolGrab( LLToolComposite* composite ) : LLTool( std::string("Grab"), composite ), mMode( GRAB_INACTIVE ), mVerticalDragging( FALSE ), mHasMoved( FALSE ), mOutsideSlop(FALSE), mDeselectedThisClick(FALSE), mSpinGrabbing( FALSE ), mSpinRotation(), mHideBuildHighlight(FALSE) { } LLToolGrab::~LLToolGrab() { } // virtual void LLToolGrab::handleSelect() { if(gFloaterTools) { // viewer can crash during startup if we don't check. gFloaterTools->setStatusText("grab"); } gGrabBtnVertical = FALSE; gGrabBtnSpin = FALSE; } void LLToolGrab::handleDeselect() { if( hasMouseCapture() ) { setMouseCapture( FALSE ); } } BOOL LLToolGrab::handleDoubleClick(S32 x, S32 y, MASK mask) { if (gDebugClicks) { llinfos << "LLToolGrab handleDoubleClick (becoming mouseDown)" << llendl; } return FALSE; } BOOL LLToolGrab::handleMouseDown(S32 x, S32 y, MASK mask) { if (gDebugClicks) { llinfos << "LLToolGrab handleMouseDown" << llendl; } // call the base class to propogate info to sim LLTool::handleMouseDown(x, y, mask); if (!gAgent.leftButtonGrabbed()) { // can grab transparent objects (how touch event propagates, scripters rely on this) gViewerWindow->pickAsync(x, y, mask, pickCallback, TRUE, TRUE); } return TRUE; } void LLToolGrab::pickCallback(const LLPickInfo& pick_info) { LLToolGrab::getInstance()->mGrabPick = pick_info; LLViewerObject *objectp = pick_info.getObject(); BOOL extend_select = (pick_info.mKeyMask & MASK_SHIFT); if (!extend_select && !LLSelectMgr::getInstance()->getSelection()->isEmpty()) { LLSelectMgr::getInstance()->deselectAll(); LLToolGrab::getInstance()->mDeselectedThisClick = TRUE; } else { LLToolGrab::getInstance()->mDeselectedThisClick = FALSE; } // if not over object, do nothing if (!objectp) { LLToolGrab::getInstance()->setMouseCapture(TRUE); LLToolGrab::getInstance()->mMode = GRAB_NOOBJECT; LLToolGrab::getInstance()->mGrabPick.mObjectID.setNull(); } else { LLToolGrab::getInstance()->handleObjectHit(LLToolGrab::getInstance()->mGrabPick); } } BOOL LLToolGrab::handleObjectHit(const LLPickInfo& info) { mGrabPick = info; LLViewerObject* objectp = mGrabPick.getObject(); if (gDebugClicks) { llinfos << "LLToolGrab handleObjectHit " << info.mMousePt.mX << "," << info.mMousePt.mY << llendl; } if (NULL == objectp) // unexpected { llwarns << "objectp was NULL; returning FALSE" << llendl; return FALSE; } if (objectp->isAvatar()) { if (gGrabTransientTool) { gBasicToolset->selectTool( gGrabTransientTool ); gGrabTransientTool = NULL; } return TRUE; } setMouseCapture( TRUE ); // Grabs always start from the root // objectp = (LLViewerObject *)objectp->getRoot(); LLViewerObject* parent = objectp->getRootEdit(); BOOL script_touch = (objectp->flagHandleTouch()) || (parent && parent->flagHandleTouch()); // Clicks on scripted or physical objects are temporary grabs, so // not "Build mode" mHideBuildHighlight = script_touch || objectp->usePhysics(); if (!objectp->usePhysics()) { // In mouselook, we shouldn't be able to grab non-physical, // non-touchable objects. If it has a touch handler, we // do grab it (so llDetectedGrab works), but movement is // blocked on the server side. JC if (gAgent.cameraMouselook() && !script_touch) { mMode = GRAB_LOCKED; gViewerWindow->hideCursor(); gViewerWindow->moveCursorToCenter(); } else { mMode = GRAB_NONPHYSICAL; } // Don't bail out here, go on and grab so buttons can get // their "touched" event. } else if( !objectp->permMove() ) { // if mouse is over a physical object without move permission, show feedback if user tries to move it. mMode = GRAB_LOCKED; // Don't bail out here, go on and grab so buttons can get // their "touched" event. } else { // if mouse is over a physical object with move permission, // select it and enter "grab" mode (hiding cursor, etc.) mMode = GRAB_ACTIVE_CENTER; gViewerWindow->hideCursor(); gViewerWindow->moveCursorToCenter(); } // Always send "touched" message mLastMouseX = gViewerWindow->getCurrentMouseX(); mLastMouseY = gViewerWindow->getCurrentMouseY(); mAccumDeltaX = 0; mAccumDeltaY = 0; mHasMoved = FALSE; mOutsideSlop = FALSE; mVerticalDragging = (info.mKeyMask == MASK_VERTICAL) || gGrabBtnVertical; startGrab(); if ((info.mKeyMask == MASK_SPIN) || gGrabBtnSpin) { startSpin(); } LLSelectMgr::getInstance()->updateSelectionCenter(); // update selection beam // update point at LLViewerObject *edit_object = info.getObject(); if (edit_object && info.mPickType != LLPickInfo::PICK_FLORA) { LLVector3 local_edit_point = gAgent.getPosAgentFromGlobal(info.mPosGlobal); local_edit_point -= edit_object->getPositionAgent(); local_edit_point = local_edit_point * ~edit_object->getRenderRotation(); gAgent.setPointAt(POINTAT_TARGET_GRAB, edit_object, local_edit_point ); gAgent.setLookAt(LOOKAT_TARGET_SELECT, edit_object, local_edit_point ); } // on transient grabs (clicks on world objects), kill the grab immediately if (!gViewerWindow->getLeftMouseDown() && gGrabTransientTool && (mMode == GRAB_NONPHYSICAL || mMode == GRAB_LOCKED)) { gBasicToolset->selectTool( gGrabTransientTool ); gGrabTransientTool = NULL; } return TRUE; } void LLToolGrab::startSpin() { LLViewerObject* objectp = mGrabPick.getObject(); if (!objectp) { return; } mSpinGrabbing = TRUE; // Was saveSelectedObjectTransform() LLViewerObject *root = (LLViewerObject *)objectp->getRoot(); mSpinRotation = root->getRotation(); LLMessageSystem *msg = gMessageSystem; msg->newMessageFast(_PREHASH_ObjectSpinStart); msg->nextBlockFast(_PREHASH_AgentData); msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID() ); msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); msg->nextBlockFast(_PREHASH_ObjectData); msg->addUUIDFast(_PREHASH_ObjectID, mGrabPick.mObjectID ); msg->sendMessage( objectp->getRegion()->getHost() ); } void LLToolGrab::stopSpin() { mSpinGrabbing = FALSE; LLViewerObject* objectp = mGrabPick.getObject(); if (!objectp) { return; } LLMessageSystem *msg = gMessageSystem; switch(mMode) { case GRAB_ACTIVE_CENTER: case GRAB_NONPHYSICAL: case GRAB_LOCKED: msg->newMessageFast(_PREHASH_ObjectSpinStop); msg->nextBlockFast(_PREHASH_AgentData); msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID() ); msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); msg->nextBlockFast(_PREHASH_ObjectData); msg->addUUIDFast(_PREHASH_ObjectID, objectp->getID() ); msg->sendMessage( objectp->getRegion()->getHost() ); break; case GRAB_NOOBJECT: case GRAB_INACTIVE: default: // do nothing break; } } void LLToolGrab::startGrab() { // Compute grab_offset in the OBJECT's root's coordinate frame // (sometimes root == object) LLViewerObject* objectp = mGrabPick.getObject(); if (!objectp) { return; } LLViewerObject *root = (LLViewerObject *)objectp->getRoot(); // drag from center LLVector3d grab_start_global = root->getPositionGlobal(); // Where the grab starts, relative to the center of the root object of the set. // JC - This code looks wonky, but I believe it does the right thing. // Otherwise, when you grab a linked object set, it "pops" on the start // of the drag. LLVector3d grab_offsetd = root->getPositionGlobal() - objectp->getPositionGlobal(); LLVector3 grab_offset; grab_offset.setVec(grab_offsetd); LLQuaternion rotation = root->getRotation(); rotation.conjQuat(); grab_offset = grab_offset * rotation; // This planar drag starts at the grab point mDragStartPointGlobal = grab_start_global; mDragStartFromCamera = grab_start_global - gAgent.getCameraPositionGlobal(); LLMessageSystem *msg = gMessageSystem; msg->newMessageFast(_PREHASH_ObjectGrab); msg->nextBlockFast(_PREHASH_AgentData); msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); msg->nextBlockFast(_PREHASH_ObjectData); msg->addU32Fast(_PREHASH_LocalID, objectp->mLocalID); msg->addVector3Fast(_PREHASH_GrabOffset, grab_offset ); msg->nextBlock("SurfaceInfo"); msg->addVector3("UVCoord", LLVector3(mGrabPick.mUVCoords)); msg->addVector3("STCoord", LLVector3(mGrabPick.mSTCoords)); msg->addS32Fast(_PREHASH_FaceIndex, mGrabPick.mObjectFace); msg->addVector3("Position", mGrabPick.mIntersection); msg->addVector3("Normal", mGrabPick.mNormal); msg->addVector3("Binormal", mGrabPick.mBinormal); msg->sendMessage( objectp->getRegion()->getHost()); mGrabOffsetFromCenterInitial = grab_offset; mGrabHiddenOffsetFromCamera = mDragStartFromCamera; mGrabTimer.reset(); mLastUVCoords = mGrabPick.mUVCoords; mLastSTCoords = mGrabPick.mSTCoords; mLastFace = mGrabPick.mObjectFace; mLastIntersection = mGrabPick.mIntersection; mLastNormal = mGrabPick.mNormal; mLastBinormal = mGrabPick.mBinormal; mLastGrabPos = LLVector3(-1.f, -1.f, -1.f); } BOOL LLToolGrab::handleHover(S32 x, S32 y, MASK mask) { if (!gViewerWindow->getLeftMouseDown()) { gViewerWindow->setCursor(UI_CURSOR_TOOLGRAB); setMouseCapture(FALSE); return TRUE; } // Do the right hover based on mode switch( mMode ) { case GRAB_ACTIVE_CENTER: handleHoverActive( x, y, mask ); // cursor hidden break; case GRAB_NONPHYSICAL: handleHoverNonPhysical(x, y, mask); break; case GRAB_INACTIVE: handleHoverInactive( x, y, mask ); // cursor set here break; case GRAB_NOOBJECT: case GRAB_LOCKED: handleHoverFailed( x, y, mask ); break; } mLastMouseX = x; mLastMouseY = y; return TRUE; } const F32 GRAB_SENSITIVITY_X = 0.0075f; const F32 GRAB_SENSITIVITY_Y = 0.0075f; // Dragging. void LLToolGrab::handleHoverActive(S32 x, S32 y, MASK mask) { LLViewerObject* objectp = mGrabPick.getObject(); if (!objectp || !hasMouseCapture() ) return; if (objectp->isDead()) { // Bail out of drag because object has been killed setMouseCapture(FALSE); return; } //-------------------------------------------------- // Toggle spinning //-------------------------------------------------- if (mSpinGrabbing && !(mask == MASK_SPIN) && !gGrabBtnSpin) { // user released ALT key, stop spinning stopSpin(); } else if (!mSpinGrabbing && (mask == MASK_SPIN) ) { // user pressed ALT key, start spinning startSpin(); } //-------------------------------------------------- // Toggle vertical dragging //-------------------------------------------------- if (mVerticalDragging && !(mask == MASK_VERTICAL) && !gGrabBtnVertical) { // ...switch to horizontal dragging mVerticalDragging = FALSE; mDragStartPointGlobal = gViewerWindow->clickPointInWorldGlobal(x, y, objectp); mDragStartFromCamera = mDragStartPointGlobal - gAgent.getCameraPositionGlobal(); } else if (!mVerticalDragging && (mask == MASK_VERTICAL) ) { // ...switch to vertical dragging mVerticalDragging = TRUE; mDragStartPointGlobal = gViewerWindow->clickPointInWorldGlobal(x, y, objectp); mDragStartFromCamera = mDragStartPointGlobal - gAgent.getCameraPositionGlobal(); } const F32 RADIANS_PER_PIXEL_X = 0.01f; const F32 RADIANS_PER_PIXEL_Y = 0.01f; S32 dx = x - (gViewerWindow->getWindowWidth() / 2); S32 dy = y - (gViewerWindow->getWindowHeight() / 2); if (dx != 0 || dy != 0) { mAccumDeltaX += dx; mAccumDeltaY += dy; S32 dist_sq = mAccumDeltaX * mAccumDeltaX + mAccumDeltaY * mAccumDeltaY; if (dist_sq > SLOP_DIST_SQ) { mOutsideSlop = TRUE; } // mouse has moved outside center mHasMoved = TRUE; if (mSpinGrabbing) { //------------------------------------------------------ // Handle spinning //------------------------------------------------------ // x motion maps to rotation around vertical axis LLVector3 up(0.f, 0.f, 1.f); LLQuaternion rotation_around_vertical( dx*RADIANS_PER_PIXEL_X, up ); // y motion maps to rotation around left axis const LLVector3 &agent_left = LLViewerCamera::getInstance()->getLeftAxis(); LLQuaternion rotation_around_left( dy*RADIANS_PER_PIXEL_Y, agent_left ); // compose with current rotation mSpinRotation = mSpinRotation * rotation_around_vertical; mSpinRotation = mSpinRotation * rotation_around_left; // TODO: Throttle these LLMessageSystem *msg = gMessageSystem; msg->newMessageFast(_PREHASH_ObjectSpinUpdate); msg->nextBlockFast(_PREHASH_AgentData); msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID() ); msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); msg->nextBlockFast(_PREHASH_ObjectData); msg->addUUIDFast(_PREHASH_ObjectID, objectp->getID() ); msg->addQuatFast(_PREHASH_Rotation, mSpinRotation ); msg->sendMessage( objectp->getRegion()->getHost() ); } else { //------------------------------------------------------ // Handle grabbing //------------------------------------------------------ LLVector3d x_part; x_part.setVec(LLViewerCamera::getInstance()->getLeftAxis()); x_part.mdV[VZ] = 0.0; x_part.normVec(); LLVector3d y_part; if( mVerticalDragging ) { y_part.setVec(LLViewerCamera::getInstance()->getUpAxis()); // y_part.setVec(0.f, 0.f, 1.f); } else { // drag toward camera y_part = x_part % LLVector3d::z_axis; y_part.mdV[VZ] = 0.0; y_part.normVec(); } mGrabHiddenOffsetFromCamera = mGrabHiddenOffsetFromCamera + (x_part * (-dx * GRAB_SENSITIVITY_X)) + (y_part * ( dy * GRAB_SENSITIVITY_Y)); // Send the message to the viewer. F32 dt = mGrabTimer.getElapsedTimeAndResetF32(); U32 dt_milliseconds = (U32) (1000.f * dt); // need to return offset from mGrabStartPoint LLVector3d grab_point_global; grab_point_global = gAgent.getCameraPositionGlobal() + mGrabHiddenOffsetFromCamera; /* Snap to grid disabled for grab tool - very confusing // Handle snapping to grid, but only when the tool is formally selected. BOOL snap_on = gSavedSettings.getBOOL("SnapEnabled"); if (snap_on && !gGrabTransientTool) { F64 snap_size = gSavedSettings.getF32("GridResolution"); U8 snap_dimensions = (mVerticalDragging ? 3 : 2); for (U8 i = 0; i < snap_dimensions; i++) { grab_point_global.mdV[i] += snap_size / 2; grab_point_global.mdV[i] -= fmod(grab_point_global.mdV[i], snap_size); } } */ // Don't let object centers go underground. F32 land_height = LLWorld::getInstance()->resolveLandHeightGlobal(grab_point_global); if (grab_point_global.mdV[VZ] < land_height) { grab_point_global.mdV[VZ] = land_height; } // For safety, cap heights where objects can be dragged if (grab_point_global.mdV[VZ] > MAX_OBJECT_Z) { grab_point_global.mdV[VZ] = MAX_OBJECT_Z; } grab_point_global = LLWorld::getInstance()->clipToVisibleRegions(mDragStartPointGlobal, grab_point_global); // propagate constrained grab point back to grab offset mGrabHiddenOffsetFromCamera = grab_point_global - gAgent.getCameraPositionGlobal(); // Handle auto-rotation at screen edge. LLVector3 grab_pos_agent = gAgent.getPosAgentFromGlobal( grab_point_global ); LLCoordGL grab_center_gl( gViewerWindow->getWindowWidth() / 2, gViewerWindow->getWindowHeight() / 2); LLViewerCamera::getInstance()->projectPosAgentToScreen(grab_pos_agent, grab_center_gl); const S32 ROTATE_H_MARGIN = gViewerWindow->getWindowWidth() / 20; const F32 ROTATE_ANGLE_PER_SECOND = 30.f * DEG_TO_RAD; const F32 rotate_angle = ROTATE_ANGLE_PER_SECOND / gFPSClamped; // ...build mode moves camera about focus point if (grab_center_gl.mX < ROTATE_H_MARGIN) { if (gAgent.getFocusOnAvatar()) { gAgent.yaw(rotate_angle); } else { gAgent.cameraOrbitAround(rotate_angle); } } else if (grab_center_gl.mX > gViewerWindow->getWindowWidth() - ROTATE_H_MARGIN) { if (gAgent.getFocusOnAvatar()) { gAgent.yaw(-rotate_angle); } else { gAgent.cameraOrbitAround(-rotate_angle); } } // Don't move above top of screen or below bottom if ((grab_center_gl.mY < gViewerWindow->getWindowHeight() - 6) && (grab_center_gl.mY > 24)) { // Transmit update to simulator LLVector3 grab_pos_region = objectp->getRegion()->getPosRegionFromGlobal( grab_point_global ); LLMessageSystem *msg = gMessageSystem; msg->newMessageFast(_PREHASH_ObjectGrabUpdate); msg->nextBlockFast(_PREHASH_AgentData); msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); msg->nextBlockFast(_PREHASH_ObjectData); msg->addUUIDFast(_PREHASH_ObjectID, objectp->getID() ); msg->addVector3Fast(_PREHASH_GrabOffsetInitial, mGrabOffsetFromCenterInitial ); msg->addVector3Fast(_PREHASH_GrabPosition, grab_pos_region ); msg->addU32Fast(_PREHASH_TimeSinceLast, dt_milliseconds ); msg->nextBlock("SurfaceInfo"); msg->addVector3("UVCoord", LLVector3(mGrabPick.mUVCoords)); msg->addVector3("STCoord", LLVector3(mGrabPick.mSTCoords)); msg->addS32Fast(_PREHASH_FaceIndex, mGrabPick.mObjectFace); msg->addVector3("Position", mGrabPick.mIntersection); msg->addVector3("Normal", mGrabPick.mNormal); msg->addVector3("Binormal", mGrabPick.mBinormal); msg->sendMessage( objectp->getRegion()->getHost() ); } } gViewerWindow->moveCursorToCenter(); LLSelectMgr::getInstance()->updateSelectionCenter(); } // once we've initiated a drag, lock the camera down if (mHasMoved) { if (!gAgent.cameraMouselook() && !objectp->isHUDAttachment() && objectp->getRoot() == gAgent.getAvatarObject()->getRoot()) { // force focus to point in space where we were looking previously gAgent.setFocusGlobal(gAgent.calcFocusPositionTargetGlobal(), LLUUID::null); gAgent.setFocusOnAvatar(FALSE, ANIMATE); } else { gAgent.clearFocusObject(); } } // HACK to avoid assert: error checking system makes sure that the cursor is set during every handleHover. This is actually a no-op since the cursor is hidden. gViewerWindow->setCursor(UI_CURSOR_ARROW); lldebugst(LLERR_USER_INPUT) << "hover handled by LLToolGrab (active) [cursor hidden]" << llendl; } void LLToolGrab::handleHoverNonPhysical(S32 x, S32 y, MASK mask) { LLViewerObject* objectp = mGrabPick.getObject(); if (!objectp || !hasMouseCapture() ) return; if (objectp->isDead()) { // Bail out of drag because object has been killed setMouseCapture(FALSE); return; } LLPickInfo pick = mGrabPick; pick.mMousePt = LLCoordGL(x, y); pick.getSurfaceInfo(); // compute elapsed time F32 dt = mGrabTimer.getElapsedTimeAndResetF32(); U32 dt_milliseconds = (U32) (1000.f * dt); // i'm not a big fan of the following code - it's been culled from the physical grab case. // ideally these two would be nicely integrated - but the code in that method is a serious // mess of spaghetti. so here we go: LLVector3 grab_pos_region(0,0,0); const BOOL SUPPORT_LLDETECTED_GRAB = TRUE; if (SUPPORT_LLDETECTED_GRAB) { //-------------------------------------------------- // Toggle vertical dragging //-------------------------------------------------- if (mVerticalDragging && !(mask == MASK_VERTICAL) && !gGrabBtnVertical) { mVerticalDragging = FALSE; } else if (!mVerticalDragging && (mask == MASK_VERTICAL) ) { mVerticalDragging = TRUE; } S32 dx = x - mLastMouseX; S32 dy = y - mLastMouseY; if (dx != 0 || dy != 0) { mAccumDeltaX += dx; mAccumDeltaY += dy; S32 dist_sq = mAccumDeltaX * mAccumDeltaX + mAccumDeltaY * mAccumDeltaY; if (dist_sq > SLOP_DIST_SQ) { mOutsideSlop = TRUE; } // mouse has moved mHasMoved = TRUE; //------------------------------------------------------ // Handle grabbing //------------------------------------------------------ LLVector3d x_part; x_part.setVec(LLViewerCamera::getInstance()->getLeftAxis()); x_part.mdV[VZ] = 0.0; x_part.normVec(); LLVector3d y_part; if( mVerticalDragging ) { y_part.setVec(LLViewerCamera::getInstance()->getUpAxis()); // y_part.setVec(0.f, 0.f, 1.f); } else { // drag toward camera y_part = x_part % LLVector3d::z_axis; y_part.mdV[VZ] = 0.0; y_part.normVec(); } mGrabHiddenOffsetFromCamera = mGrabHiddenOffsetFromCamera + (x_part * (-dx * GRAB_SENSITIVITY_X)) + (y_part * ( dy * GRAB_SENSITIVITY_Y)); } // need to return offset from mGrabStartPoint LLVector3d grab_point_global = gAgent.getCameraPositionGlobal() + mGrabHiddenOffsetFromCamera; grab_pos_region = objectp->getRegion()->getPosRegionFromGlobal( grab_point_global ); } // only send message if something has changed since last message BOOL changed_since_last_update = FALSE; // test if touch data needs to be updated if ((pick.mObjectFace != mLastFace) || (pick.mUVCoords != mLastUVCoords) || (pick.mSTCoords != mLastSTCoords) || (pick.mIntersection != mLastIntersection) || (pick.mNormal != mLastNormal) || (pick.mBinormal != mLastBinormal) || (grab_pos_region != mLastGrabPos)) { changed_since_last_update = TRUE; } if (changed_since_last_update) { LLMessageSystem *msg = gMessageSystem; msg->newMessageFast(_PREHASH_ObjectGrabUpdate); msg->nextBlockFast(_PREHASH_AgentData); msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); msg->nextBlockFast(_PREHASH_ObjectData); msg->addUUIDFast(_PREHASH_ObjectID, objectp->getID() ); msg->addVector3Fast(_PREHASH_GrabOffsetInitial, mGrabOffsetFromCenterInitial ); msg->addVector3Fast(_PREHASH_GrabPosition, grab_pos_region ); msg->addU32Fast(_PREHASH_TimeSinceLast, dt_milliseconds ); msg->nextBlock("SurfaceInfo"); msg->addVector3("UVCoord", LLVector3(pick.mUVCoords)); msg->addVector3("STCoord", LLVector3(pick.mSTCoords)); msg->addS32Fast(_PREHASH_FaceIndex, pick.mObjectFace); msg->addVector3("Position", pick.mIntersection); msg->addVector3("Normal", pick.mNormal); msg->addVector3("Binormal", pick.mBinormal); msg->sendMessage( objectp->getRegion()->getHost() ); mLastUVCoords = pick.mUVCoords; mLastSTCoords = pick.mSTCoords; mLastFace = pick.mObjectFace; mLastIntersection = pick.mIntersection; mLastNormal= pick.mNormal; mLastBinormal= pick.mBinormal; mLastGrabPos = grab_pos_region; } // update point-at / look-at if (pick.mObjectFace != -1) // if the intersection was on the surface of the obejct { LLVector3 local_edit_point = pick.mIntersection; local_edit_point -= objectp->getPositionAgent(); local_edit_point = local_edit_point * ~objectp->getRenderRotation(); gAgent.setPointAt(POINTAT_TARGET_GRAB, objectp, local_edit_point ); gAgent.setLookAt(LOOKAT_TARGET_SELECT, objectp, local_edit_point ); } gViewerWindow->setCursor(UI_CURSOR_HAND); } // Not dragging. Just showing affordances void LLToolGrab::handleHoverInactive(S32 x, S32 y, MASK mask) { const F32 ROTATE_ANGLE_PER_SECOND = 40.f * DEG_TO_RAD; const F32 rotate_angle = ROTATE_ANGLE_PER_SECOND / gFPSClamped; // Look for cursor against the edge of the screen // Only works in fullscreen if (gSavedSettings.getBOOL("FullScreen")) { if (gAgent.cameraThirdPerson() ) { if (x == 0) { gAgent.yaw(rotate_angle); //gAgent.setControlFlags(AGENT_CONTROL_YAW_POS); } else if (x == (gViewerWindow->getWindowWidth() - 1) ) { gAgent.yaw(-rotate_angle); //gAgent.setControlFlags(AGENT_CONTROL_YAW_NEG); } } } // JC - TODO - change cursor based on gGrabBtnVertical, gGrabBtnSpin lldebugst(LLERR_USER_INPUT) << "hover handled by LLToolGrab (inactive-not over editable object)" << llendl; gViewerWindow->setCursor(UI_CURSOR_TOOLGRAB); } // User is trying to do something that's not allowed. void LLToolGrab::handleHoverFailed(S32 x, S32 y, MASK mask) { if( GRAB_NOOBJECT == mMode ) { gViewerWindow->setCursor(UI_CURSOR_NO); lldebugst(LLERR_USER_INPUT) << "hover handled by LLToolGrab (not on object)" << llendl; } else { S32 dist_sq = (x-mGrabPick.mMousePt.mX) * (x-mGrabPick.mMousePt.mX) + (y-mGrabPick.mMousePt.mY) * (y-mGrabPick.mMousePt.mY); if( mOutsideSlop || dist_sq > SLOP_DIST_SQ ) { mOutsideSlop = TRUE; switch( mMode ) { case GRAB_LOCKED: gViewerWindow->setCursor(UI_CURSOR_GRABLOCKED); lldebugst(LLERR_USER_INPUT) << "hover handled by LLToolGrab (grab failed, no move permission)" << llendl; break; // Non physical now handled by handleHoverActive - CRO // case GRAB_NONPHYSICAL: // gViewerWindow->setCursor(UI_CURSOR_ARROW); // lldebugst(LLERR_USER_INPUT) << "hover handled by LLToolGrab (grab failed, nonphysical)" << llendl; // break; default: llassert(0); } } else { gViewerWindow->setCursor(UI_CURSOR_ARROW); lldebugst(LLERR_USER_INPUT) << "hover handled by LLToolGrab (grab failed but within slop)" << llendl; } } } BOOL LLToolGrab::handleMouseUp(S32 x, S32 y, MASK mask) { // call the base class to propogate info to sim LLTool::handleMouseUp(x, y, mask); if( hasMouseCapture() ) { setMouseCapture( FALSE ); } mMode = GRAB_INACTIVE; // HACK: Make some grabs temporary if (gGrabTransientTool) { gBasicToolset->selectTool( gGrabTransientTool ); gGrabTransientTool = NULL; } //gAgent.setObjectTracking(gSavedSettings.getBOOL("TrackFocusObject")); return TRUE; } void LLToolGrab::stopEditing() { if( hasMouseCapture() ) { setMouseCapture( FALSE ); } } void LLToolGrab::onMouseCaptureLost() { LLViewerObject* objectp = mGrabPick.getObject(); if (!objectp) { gViewerWindow->showCursor(); return; } // First, fix cursor placement if( !gAgent.cameraMouselook() && (GRAB_ACTIVE_CENTER == mMode)) { if (objectp->isHUDAttachment()) { // ...move cursor "naturally", as if it had moved when hidden S32 x = mGrabPick.mMousePt.mX + mAccumDeltaX; S32 y = mGrabPick.mMousePt.mY + mAccumDeltaY; LLUI::setCursorPositionScreen(x, y); } else if (mHasMoved) { // ...move cursor back to the center of the object LLVector3 grab_point_agent = objectp->getRenderPosition(); LLCoordGL gl_point; if (LLViewerCamera::getInstance()->projectPosAgentToScreen(grab_point_agent, gl_point)) { LLUI::setCursorPositionScreen(gl_point.mX, gl_point.mY); } } else { // ...move cursor back to click position LLUI::setCursorPositionScreen(mGrabPick.mMousePt.mX, mGrabPick.mMousePt.mY); } gViewerWindow->showCursor(); } stopGrab(); if (mSpinGrabbing) stopSpin(); mMode = GRAB_INACTIVE; mHideBuildHighlight = FALSE; mGrabPick.mObjectID.setNull(); LLSelectMgr::getInstance()->updateSelectionCenter(); gAgent.setPointAt(POINTAT_TARGET_CLEAR); gAgent.setLookAt(LOOKAT_TARGET_CLEAR); dialog_refresh_all(); } void LLToolGrab::stopGrab() { LLViewerObject* objectp = mGrabPick.getObject(); if (!objectp) { return; } LLPickInfo pick = mGrabPick; if (mMode == GRAB_NONPHYSICAL) { // for non-physical (touch) grabs, // gather surface info for this degrab (mouse-up) S32 x = gViewerWindow->getCurrentMouseX(); S32 y = gViewerWindow->getCurrentMouseY(); pick.mMousePt = LLCoordGL(x, y); pick.getSurfaceInfo(); } // Next, send messages to simulator LLMessageSystem *msg = gMessageSystem; switch(mMode) { case GRAB_ACTIVE_CENTER: case GRAB_NONPHYSICAL: case GRAB_LOCKED: msg->newMessageFast(_PREHASH_ObjectDeGrab); msg->nextBlockFast(_PREHASH_AgentData); msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); msg->nextBlockFast(_PREHASH_ObjectData); msg->addU32Fast(_PREHASH_LocalID, objectp->mLocalID); msg->nextBlock("SurfaceInfo"); msg->addVector3("UVCoord", LLVector3(pick.mUVCoords)); msg->addVector3("STCoord", LLVector3(pick.mSTCoords)); msg->addS32Fast(_PREHASH_FaceIndex, pick.mObjectFace); msg->addVector3("Position", pick.mIntersection); msg->addVector3("Normal", pick.mNormal); msg->addVector3("Binormal", pick.mBinormal); msg->sendMessage(objectp->getRegion()->getHost()); mVerticalDragging = FALSE; break; case GRAB_NOOBJECT: case GRAB_INACTIVE: default: // do nothing break; } mHideBuildHighlight = FALSE; } void LLToolGrab::draw() { } void LLToolGrab::render() { } BOOL LLToolGrab::isEditing() { return (mGrabPick.getObject().notNull()); } LLViewerObject* LLToolGrab::getEditingObject() { return mGrabPick.getObject(); } LLVector3d LLToolGrab::getEditingPointGlobal() { return getGrabPointGlobal(); } LLVector3d LLToolGrab::getGrabPointGlobal() { switch(mMode) { case GRAB_ACTIVE_CENTER: case GRAB_NONPHYSICAL: case GRAB_LOCKED: return gAgent.getCameraPositionGlobal() + mGrabHiddenOffsetFromCamera; case GRAB_NOOBJECT: case GRAB_INACTIVE: default: return gAgent.getPositionGlobal(); } }