lltoolgrab.cpp 31 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157
  1. /**
  2. * @file lltoolgrab.cpp
  3. * @brief LLToolGrab class implementation
  4. *
  5. * $LicenseInfo:firstyear=2001&license=viewergpl$
  6. *
  7. * Copyright (c) 2001-2009, Linden Research, Inc.
  8. *
  9. * Second Life Viewer Source Code
  10. * The source code in this file ("Source Code") is provided by Linden Lab
  11. * to you under the terms of the GNU General Public License, version 2.0
  12. * ("GPL"), unless you have obtained a separate licensing agreement
  13. * ("Other License"), formally executed by you and Linden Lab. Terms of
  14. * the GPL can be found in doc/GPL-license.txt in this distribution, or
  15. * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
  16. *
  17. * There are special exceptions to the terms and conditions of the GPL as
  18. * it is applied to this Source Code. View the full text of the exception
  19. * in the file doc/FLOSS-exception.txt in this software distribution, or
  20. * online at
  21. * http://secondlifegrid.net/programs/open_source/licensing/flossexception
  22. *
  23. * By copying, modifying or distributing this software, you acknowledge
  24. * that you have read and understood your obligations described above,
  25. * and agree to abide by those obligations.
  26. *
  27. * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
  28. * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
  29. * COMPLETENESS OR PERFORMANCE.
  30. * $/LicenseInfo$
  31. */
  32. #include "llviewerprecompiledheaders.h"
  33. #include "lltoolgrab.h"
  34. #include "llmessage.h"
  35. #include "llwindow.h"
  36. #include "llagent.h"
  37. #include "llappviewer.h" // For gFPSClamped
  38. #include "lldrawable.h"
  39. #include "llfloatertools.h"
  40. #include "llhudeffect.h"
  41. #include "llhudmanager.h"
  42. //MK
  43. #include "mkrlinterface.h"
  44. //mk
  45. #include "llselectmgr.h"
  46. #include "llstatusbar.h"
  47. #include "lltoolmgr.h"
  48. #include "lltoolpie.h"
  49. #include "llviewercamera.h"
  50. #include "llviewercontrol.h"
  51. #include "llviewerobjectlist.h"
  52. #include "llviewerregion.h"
  53. #include "llvoavatarself.h"
  54. #include "llworld.h"
  55. constexpr S32 SLOP_DIST_SQ = 4;
  56. LLToolGrab gToolGrab;
  57. LLTool* gGrabTransientTool = NULL;
  58. // Override modifier key behavior with these buttons
  59. bool gGrabBtnVertical = false;
  60. bool gGrabBtnSpin = false;
  61. LLToolGrabBase::LLToolGrabBase(LLToolComposite* composite)
  62. : LLTool("Grab", composite),
  63. mMode(GRAB_INACTIVE),
  64. mVerticalDragging(false),
  65. mHasMoved(false),
  66. mOutsideSlop(false),
  67. mSpinGrabbing(false),
  68. mSpinRotation(),
  69. mClickedInMouselook(false)
  70. {
  71. }
  72. // virtual
  73. void LLToolGrabBase::handleSelect()
  74. {
  75. if (gFloaterToolsp)
  76. {
  77. // Viewer can crash during startup if we do not check.
  78. gFloaterToolsp->setStatusText("grab");
  79. }
  80. gGrabBtnVertical = false;
  81. gGrabBtnSpin = false;
  82. }
  83. void LLToolGrabBase::handleDeselect()
  84. {
  85. if (hasMouseCapture())
  86. {
  87. setMouseCapture(false);
  88. }
  89. }
  90. bool LLToolGrabBase::handleDoubleClick(S32 x, S32 y, MASK mask)
  91. {
  92. if (gDebugClicks)
  93. {
  94. llinfos << "Double click becoming mouse-down" << llendl;
  95. }
  96. return false;
  97. }
  98. bool LLToolGrabBase::handleMouseDown(S32 x, S32 y, MASK mask)
  99. {
  100. if (gDebugClicks)
  101. {
  102. llinfos << "Mouse down" << llendl;
  103. }
  104. // Call the base class to propogate info to sim
  105. LLTool::handleMouseDown(x, y, mask);
  106. if (!gAgent.leftButtonGrabbed())
  107. {
  108. // Can grab transparent objects (how touch event propagates, scripters
  109. // rely on this) but not particles
  110. gViewerWindowp->pickAsync(x, y, mask, pickCallback, true, false, true);
  111. }
  112. mClickedInMouselook = gAgent.cameraMouselook();
  113. return true;
  114. }
  115. void LLToolGrabBase::pickCallback(const LLPickInfo& pick_info)
  116. {
  117. LLViewerObject* objectp = pick_info.getObject();
  118. gToolGrab.mGrabPick = pick_info;
  119. bool extend_select = (pick_info.mKeyMask & MASK_SHIFT) != 0;
  120. if (!extend_select && !gSelectMgr.getSelection()->isEmpty())
  121. {
  122. gSelectMgr.deselectAll();
  123. }
  124. // If not over object, do nothing
  125. if (!objectp)
  126. {
  127. gToolGrab.setMouseCapture(true);
  128. gToolGrab.mMode = GRAB_NOOBJECT;
  129. gToolGrab.mGrabPick.mObjectID.setNull();
  130. }
  131. else
  132. {
  133. gToolGrab.handleObjectHit(gToolGrab.mGrabPick);
  134. }
  135. }
  136. bool LLToolGrabBase::handleObjectHit(const LLPickInfo& info)
  137. {
  138. mGrabPick = info;
  139. LLViewerObject* objectp = mGrabPick.getObject();
  140. //MK
  141. if (gRLenabled &&
  142. !gRLInterface.canTouch(objectp, mGrabPick.mIntersection))
  143. {
  144. // hide grab tool immediately
  145. if (gGrabTransientTool)
  146. {
  147. gBasicToolset->selectTool(gGrabTransientTool);
  148. gGrabTransientTool = NULL;
  149. }
  150. return true;
  151. }
  152. //mk
  153. if (gDebugClicks)
  154. {
  155. llinfos << "Object hit at " << info.mMousePt.mX << ","
  156. << info.mMousePt.mY << llendl;
  157. }
  158. if (!objectp) // unexpected
  159. {
  160. llwarns << "Objectp was NULL, aborting" << llendl;
  161. return false;
  162. }
  163. if (objectp->isAvatar())
  164. {
  165. if (gGrabTransientTool)
  166. {
  167. gBasicToolset->selectTool(gGrabTransientTool);
  168. gGrabTransientTool = NULL;
  169. }
  170. return true;
  171. }
  172. setMouseCapture(true);
  173. bool script_touch = objectp->flagHandleTouch();
  174. if (!script_touch)
  175. {
  176. LLViewerObject* parent = objectp->getRootEdit();
  177. script_touch = parent && parent->flagHandleTouch();
  178. }
  179. if (!objectp->flagUsePhysics())
  180. {
  181. if (script_touch)
  182. {
  183. // Script-touch object: let's touch it !
  184. mMode = GRAB_NONPHYSICAL;
  185. }
  186. else if (gAgent.cameraMouselook())
  187. {
  188. // In mouselook, we should not be able to grab non-physical,
  189. // non-touchable objects. If it has a touch handler, we do grab it
  190. // (so llDetectedGrab works), but movement is blocked on the server
  191. // side. JC
  192. mMode = GRAB_LOCKED;
  193. gViewerWindowp->hideCursor();
  194. gViewerWindowp->moveCursorToCenter();
  195. }
  196. else if (objectp->permMove() && !objectp->isPermanentEnforced())
  197. {
  198. mMode = GRAB_ACTIVE_CENTER;
  199. gViewerWindowp->hideCursor();
  200. gViewerWindowp->moveCursorToCenter();
  201. }
  202. else
  203. {
  204. mMode = GRAB_LOCKED;
  205. }
  206. // Do not bail out here, go on and grab so buttons can get their
  207. // "touched" event.
  208. }
  209. else if (!objectp->permMove() || objectp->flagCharacter() ||
  210. objectp->isPermanentEnforced())
  211. {
  212. // If mouse is over a physical object without move permission, show
  213. // feedback if user tries to move it.
  214. mMode = GRAB_LOCKED;
  215. // Do not bail out here, go on and grab so buttons can get their
  216. // "touched" event.
  217. }
  218. else
  219. {
  220. // If mouse is over a physical object with move permission,
  221. // select it and enter "grab" mode (hiding cursor, etc.)
  222. mMode = GRAB_ACTIVE_CENTER;
  223. gViewerWindowp->hideCursor();
  224. gViewerWindowp->moveCursorToCenter();
  225. }
  226. // Always send "touched" message
  227. mLastMouseX = gViewerWindowp->getCurrentMouseX();
  228. mLastMouseY = gViewerWindowp->getCurrentMouseY();
  229. mAccumDeltaX = 0;
  230. mAccumDeltaY = 0;
  231. mHasMoved = false;
  232. mOutsideSlop = false;
  233. mVerticalDragging = info.mKeyMask == MASK_VERTICAL || gGrabBtnVertical;
  234. startGrab();
  235. if (info.mKeyMask == MASK_SPIN || gGrabBtnSpin)
  236. {
  237. startSpin();
  238. }
  239. gSelectMgr.updateSelectionCenter(); // Update selection beam
  240. // Update point at
  241. LLViewerObject* edit_object = info.getObject();
  242. if (edit_object && info.mPickType != LLPickInfo::PICK_FLORA)
  243. {
  244. LLVector3 local_edit_pt = gAgent.getPosAgentFromGlobal(info.mPosGlobal);
  245. local_edit_pt -= edit_object->getPositionAgent();
  246. local_edit_pt = local_edit_pt * ~edit_object->getRenderRotation();
  247. gAgent.setPointAt(POINTAT_TARGET_GRAB, edit_object, local_edit_pt);
  248. gAgent.setLookAt(LOOKAT_TARGET_SELECT, edit_object, local_edit_pt);
  249. }
  250. // On transient grabs (clicks on world objects), kill the grab immediately
  251. if (!gViewerWindowp->getLeftMouseDown() && gGrabTransientTool &&
  252. (mMode == GRAB_NONPHYSICAL || mMode == GRAB_LOCKED))
  253. {
  254. gBasicToolset->selectTool(gGrabTransientTool);
  255. gGrabTransientTool = NULL;
  256. }
  257. return true;
  258. }
  259. void LLToolGrabBase::startSpin()
  260. {
  261. LLViewerObject* objectp = mGrabPick.getObject();
  262. if (!objectp)
  263. {
  264. return;
  265. }
  266. mSpinGrabbing = true;
  267. // Was saveSelectedObjectTransform()
  268. LLViewerObject* rootp = (LLViewerObject*)objectp->getRoot();
  269. mSpinRotation = rootp->getRotation();
  270. //MK
  271. if (gRLenabled &&
  272. (gRLInterface.mContainsEdit ||
  273. !gRLInterface.canTouch(objectp, mGrabPick.mIntersection)))
  274. {
  275. return;
  276. }
  277. //mk
  278. LLMessageSystem* msg = gMessageSystemp;
  279. msg->newMessageFast(_PREHASH_ObjectSpinStart);
  280. msg->nextBlockFast(_PREHASH_AgentData);
  281. msg->addUUIDFast(_PREHASH_AgentID, gAgentID);
  282. msg->addUUIDFast(_PREHASH_SessionID, gAgentSessionID);
  283. msg->nextBlockFast(_PREHASH_ObjectData);
  284. msg->addUUIDFast(_PREHASH_ObjectID, mGrabPick.mObjectID);
  285. msg->sendMessage(objectp->getRegion()->getHost());
  286. }
  287. void LLToolGrabBase::stopSpin()
  288. {
  289. mSpinGrabbing = false;
  290. LLViewerObject* objectp = mGrabPick.getObject();
  291. if (!objectp)
  292. {
  293. return;
  294. }
  295. LLMessageSystem* msg = gMessageSystemp;
  296. switch (mMode)
  297. {
  298. case GRAB_ACTIVE_CENTER:
  299. case GRAB_NONPHYSICAL:
  300. case GRAB_LOCKED:
  301. msg->newMessageFast(_PREHASH_ObjectSpinStop);
  302. msg->nextBlockFast(_PREHASH_AgentData);
  303. msg->addUUIDFast(_PREHASH_AgentID, gAgentID);
  304. msg->addUUIDFast(_PREHASH_SessionID, gAgentSessionID);
  305. msg->nextBlockFast(_PREHASH_ObjectData);
  306. msg->addUUIDFast(_PREHASH_ObjectID, objectp->getID());
  307. msg->sendMessage(objectp->getRegion()->getHost());
  308. break;
  309. case GRAB_NOOBJECT:
  310. case GRAB_INACTIVE:
  311. default:
  312. // do nothing
  313. break;
  314. }
  315. }
  316. void LLToolGrabBase::startGrab()
  317. {
  318. // Compute grab_offset in the OBJECT's root's coordinate frame
  319. // (sometimes root == object)
  320. LLViewerObject* objectp = mGrabPick.getObject();
  321. if (!objectp)
  322. {
  323. return;
  324. }
  325. LLViewerObject* rootp = (LLViewerObject*)objectp->getRoot();
  326. // drag from center
  327. LLVector3d grab_start_global = rootp->getPositionGlobal();
  328. //MK
  329. if (gRLenabled &&
  330. (gRLInterface.mContainsEdit ||
  331. !gRLInterface.canTouch(objectp, mGrabPick.mIntersection)))
  332. {
  333. return;
  334. }
  335. //mk
  336. // Where the grab starts, relative to the center of the root object of the
  337. // set. JC - This code looks wonky, but I believe it does the right thing.
  338. // Otherwise, when you grab a linked object set, it "pops" on the start of
  339. // the drag.
  340. LLVector3d grab_offsetd = rootp->getPositionGlobal() -
  341. objectp->getPositionGlobal();
  342. LLVector3 grab_offset;
  343. grab_offset.set(grab_offsetd);
  344. LLQuaternion rotation = rootp->getRotation();
  345. rotation.transpose();
  346. grab_offset = grab_offset * rotation;
  347. // This planar drag starts at the grab point
  348. mDragStartPointGlobal = grab_start_global;
  349. mDragStartFromCamera = grab_start_global - gAgent.getCameraPositionGlobal();
  350. LLMessageSystem* msg = gMessageSystemp;
  351. msg->newMessageFast(_PREHASH_ObjectGrab);
  352. msg->nextBlockFast(_PREHASH_AgentData);
  353. msg->addUUIDFast(_PREHASH_AgentID, gAgentID);
  354. msg->addUUIDFast(_PREHASH_SessionID, gAgentSessionID);
  355. msg->nextBlockFast(_PREHASH_ObjectData);
  356. msg->addU32Fast(_PREHASH_LocalID, objectp->mLocalID);
  357. msg->addVector3Fast(_PREHASH_GrabOffset, grab_offset);
  358. msg->nextBlock("SurfaceInfo");
  359. msg->addVector3("UVCoord", LLVector3(mGrabPick.mUVCoords));
  360. msg->addVector3("STCoord", LLVector3(mGrabPick.mSTCoords));
  361. msg->addS32Fast(_PREHASH_FaceIndex, mGrabPick.mObjectFace);
  362. msg->addVector3("Position", mGrabPick.mIntersection);
  363. msg->addVector3("Normal", mGrabPick.mNormal);
  364. msg->addVector3("Binormal", mGrabPick.mBinormal);
  365. msg->sendMessage(objectp->getRegion()->getHost());
  366. mGrabOffsetFromCenterInitial = grab_offset;
  367. mGrabHiddenOffsetFromCamera = mDragStartFromCamera;
  368. mGrabTimer.reset();
  369. mLastUVCoords = mGrabPick.mUVCoords;
  370. mLastSTCoords = mGrabPick.mSTCoords;
  371. mLastFace = mGrabPick.mObjectFace;
  372. mLastIntersection = mGrabPick.mIntersection;
  373. mLastNormal = mGrabPick.mNormal;
  374. mLastBinormal = mGrabPick.mBinormal;
  375. mLastGrabPos = LLVector3(-1.f, -1.f, -1.f);
  376. }
  377. bool LLToolGrabBase::handleHover(S32 x, S32 y, MASK mask)
  378. {
  379. if (!gViewerWindowp->getLeftMouseDown())
  380. {
  381. gViewerWindowp->setCursor(UI_CURSOR_TOOLGRAB);
  382. setMouseCapture(false);
  383. return true;
  384. }
  385. // Do the right hover based on mode
  386. switch (mMode)
  387. {
  388. case GRAB_ACTIVE_CENTER:
  389. handleHoverActive(x, y, mask); // cursor hidden
  390. break;
  391. case GRAB_NONPHYSICAL:
  392. handleHoverNonPhysical(x, y, mask);
  393. break;
  394. case GRAB_INACTIVE:
  395. handleHoverInactive(x, y, mask); // cursor set here
  396. break;
  397. case GRAB_NOOBJECT:
  398. case GRAB_LOCKED:
  399. handleHoverFailed(x, y, mask);
  400. }
  401. mLastMouseX = x;
  402. mLastMouseY = y;
  403. return true;
  404. }
  405. constexpr F32 GRAB_SENSITIVITY_X = 0.0075f;
  406. constexpr F32 GRAB_SENSITIVITY_Y = 0.0075f;
  407. // Dragging.
  408. void LLToolGrabBase::handleHoverActive(S32 x, S32 y, MASK mask)
  409. {
  410. LLViewerObject* objectp = mGrabPick.getObject();
  411. if (!objectp || !hasMouseCapture()) return;
  412. if (objectp->isDead())
  413. {
  414. // Bail out of drag because object has been killed
  415. setMouseCapture(false);
  416. return;
  417. }
  418. //MK
  419. if (gRLenabled &&
  420. (gRLInterface.mContainsEdit ||
  421. !gRLInterface.canTouch(objectp, mGrabPick.mIntersection)))
  422. {
  423. return;
  424. }
  425. //mk
  426. //--------------------------------------------------
  427. // Determine target mode
  428. //--------------------------------------------------
  429. bool vertical_dragging = false;
  430. bool spin_grabbing = false;
  431. if (mask == MASK_VERTICAL || (gGrabBtnVertical && mask != MASK_SPIN))
  432. {
  433. vertical_dragging = true;
  434. }
  435. else if (mask == MASK_SPIN || (gGrabBtnSpin && mask != MASK_VERTICAL))
  436. {
  437. spin_grabbing = true;
  438. }
  439. //--------------------------------------------------
  440. // Toggle spinning
  441. //--------------------------------------------------
  442. if (mSpinGrabbing && !spin_grabbing)
  443. {
  444. // User released or switched mask key(s), stop spinning
  445. stopSpin();
  446. }
  447. else if (!mSpinGrabbing && spin_grabbing)
  448. {
  449. // User pressed mask key(s), start spinning
  450. startSpin();
  451. }
  452. mSpinGrabbing = spin_grabbing;
  453. //--------------------------------------------------
  454. // Toggle vertical dragging
  455. //--------------------------------------------------
  456. if (mVerticalDragging && !vertical_dragging)
  457. {
  458. // Switch to horizontal dragging
  459. mDragStartPointGlobal =
  460. gViewerWindowp->clickPointInWorldGlobal(x, y, objectp);
  461. mDragStartFromCamera = mDragStartPointGlobal -
  462. gAgent.getCameraPositionGlobal();
  463. }
  464. else if (!mVerticalDragging && vertical_dragging)
  465. {
  466. // Switch to vertical dragging
  467. mDragStartPointGlobal =
  468. gViewerWindowp->clickPointInWorldGlobal(x, y, objectp);
  469. mDragStartFromCamera = mDragStartPointGlobal -
  470. gAgent.getCameraPositionGlobal();
  471. }
  472. mVerticalDragging = vertical_dragging;
  473. constexpr F32 RADIANS_PER_PIXEL_X = 0.01f;
  474. constexpr F32 RADIANS_PER_PIXEL_Y = 0.01f;
  475. S32 dx = x - gViewerWindowp->getWindowWidth() / 2;
  476. S32 dy = y - gViewerWindowp->getWindowHeight() / 2;
  477. if (dx != 0 || dy != 0)
  478. {
  479. mAccumDeltaX += dx;
  480. mAccumDeltaY += dy;
  481. S32 dist_sq = mAccumDeltaX * mAccumDeltaX +
  482. mAccumDeltaY * mAccumDeltaY;
  483. if (dist_sq > SLOP_DIST_SQ)
  484. {
  485. mOutsideSlop = true;
  486. }
  487. // Mouse has moved outside center
  488. mHasMoved = true;
  489. if (mSpinGrabbing)
  490. {
  491. //------------------------------------------------------
  492. // Handle spinning
  493. //------------------------------------------------------
  494. // X motion maps to rotation around vertical axis
  495. LLQuaternion rot_around_vert(dx * RADIANS_PER_PIXEL_X,
  496. LLVector3::z_axis);
  497. // Y motion maps to rotation around left axis
  498. const LLVector3& agent_left = gViewerCamera.getLeftAxis();
  499. LLQuaternion rot_around_left(dy * RADIANS_PER_PIXEL_Y, agent_left);
  500. // Compose with current rotation
  501. mSpinRotation = mSpinRotation * rot_around_vert;
  502. mSpinRotation = mSpinRotation * rot_around_left;
  503. // *TODO: throttle these
  504. LLMessageSystem* msg = gMessageSystemp;
  505. msg->newMessageFast(_PREHASH_ObjectSpinUpdate);
  506. msg->nextBlockFast(_PREHASH_AgentData);
  507. msg->addUUIDFast(_PREHASH_AgentID, gAgentID);
  508. msg->addUUIDFast(_PREHASH_SessionID, gAgentSessionID);
  509. msg->nextBlockFast(_PREHASH_ObjectData);
  510. msg->addUUIDFast(_PREHASH_ObjectID, objectp->getID());
  511. msg->addQuatFast(_PREHASH_Rotation, mSpinRotation);
  512. msg->sendMessage(objectp->getRegion()->getHost());
  513. }
  514. else
  515. {
  516. //------------------------------------------------------
  517. // Handle grabbing
  518. //------------------------------------------------------
  519. LLVector3d x_part;
  520. x_part.set(gViewerCamera.getLeftAxis());
  521. x_part.mdV[VZ] = 0.0;
  522. x_part.normalize();
  523. LLVector3d y_part;
  524. if (mVerticalDragging)
  525. {
  526. y_part.set(gViewerCamera.getUpAxis());
  527. }
  528. else
  529. {
  530. // Drag toward camera
  531. y_part = x_part % LLVector3d::z_axis;
  532. y_part.mdV[VZ] = 0.0;
  533. y_part.normalize();
  534. }
  535. mGrabHiddenOffsetFromCamera = mGrabHiddenOffsetFromCamera +
  536. x_part * (-dx * GRAB_SENSITIVITY_X) +
  537. y_part * (dy * GRAB_SENSITIVITY_Y);
  538. // Send the message to the viewer.
  539. F32 dt = mGrabTimer.getElapsedTimeAndResetF32();
  540. U32 dt_milliseconds = (U32) (1000.f * dt);
  541. // Need to return offset from mGrabStartPoint
  542. LLVector3d grab_pt_global;
  543. grab_pt_global = gAgent.getCameraPositionGlobal() +
  544. mGrabHiddenOffsetFromCamera;
  545. #if 0 // Snap to grid disabled for grab tool: very confusing.
  546. // Handle snapping to grid, but only when the tool is formally
  547. // selected.
  548. if (!gGrabTransientTool && gSavedSettings.getBool("SnapEnabled")
  549. {
  550. F64 snap_size = gSavedSettings.getF32("GridResolution");
  551. U8 snap_dimensions = mVerticalDragging ? 3 : 2;
  552. for (U8 i = 0; i < snap_dimensions; ++i)
  553. {
  554. grab_pt_global.mdV[i] += snap_size * 0.5;
  555. grab_pt_global.mdV[i] -= fmod(grab_pt_global.mdV[i],
  556. snap_size);
  557. }
  558. }
  559. #endif
  560. // Do not let object centers go underground.
  561. F32 land_height = gWorld.resolveLandHeightGlobal(grab_pt_global);
  562. if (grab_pt_global.mdV[VZ] < land_height)
  563. {
  564. grab_pt_global.mdV[VZ] = land_height;
  565. }
  566. // For safety, cap heights where objects can be dragged
  567. if (grab_pt_global.mdV[VZ] > MAX_OBJECT_Z)
  568. {
  569. grab_pt_global.mdV[VZ] = MAX_OBJECT_Z;
  570. }
  571. grab_pt_global = gWorld.clipToVisibleRegions(mDragStartPointGlobal,
  572. grab_pt_global);
  573. // Propagate constrained grab point back to grab offset
  574. mGrabHiddenOffsetFromCamera = grab_pt_global -
  575. gAgent.getCameraPositionGlobal();
  576. // Handle auto-rotation at screen edge.
  577. LLVector3 grab_pos_agent =
  578. gAgent.getPosAgentFromGlobal(grab_pt_global);
  579. LLCoordGL grab_center_gl(gViewerWindowp->getWindowWidth() / 2,
  580. gViewerWindowp->getWindowHeight() / 2);
  581. gViewerCamera.projectPosAgentToScreen(grab_pos_agent,
  582. grab_center_gl);
  583. const S32 rotate_h_margin = gViewerWindowp->getWindowWidth() / 20;
  584. constexpr F32 ROTATE_ANGLE_PER_SECOND = 30.f * DEG_TO_RAD;
  585. const F32 rotate_angle = ROTATE_ANGLE_PER_SECOND / gFPSClamped;
  586. // Build mode moves camera about focus point
  587. if (grab_center_gl.mX < rotate_h_margin)
  588. {
  589. if (gAgent.getFocusOnAvatar())
  590. {
  591. gAgent.yaw(rotate_angle);
  592. }
  593. else
  594. {
  595. gAgent.cameraOrbitAround(rotate_angle);
  596. }
  597. }
  598. else if (grab_center_gl.mX >
  599. gViewerWindowp->getWindowWidth() - rotate_h_margin)
  600. {
  601. if (gAgent.getFocusOnAvatar())
  602. {
  603. gAgent.yaw(-rotate_angle);
  604. }
  605. else
  606. {
  607. gAgent.cameraOrbitAround(-rotate_angle);
  608. }
  609. }
  610. // Do not move above top of screen or below bottom
  611. if (grab_center_gl.mY < gViewerWindowp->getWindowHeight() - 6 &&
  612. grab_center_gl.mY > 24)
  613. {
  614. // Transmit update to simulator
  615. LLVector3 grab_pos_region =
  616. objectp->getRegion()->getPosRegionFromGlobal(grab_pt_global);
  617. LLMessageSystem* msg = gMessageSystemp;
  618. msg->newMessageFast(_PREHASH_ObjectGrabUpdate);
  619. msg->nextBlockFast(_PREHASH_AgentData);
  620. msg->addUUIDFast(_PREHASH_AgentID, gAgentID);
  621. msg->addUUIDFast(_PREHASH_SessionID, gAgentSessionID);
  622. msg->nextBlockFast(_PREHASH_ObjectData);
  623. msg->addUUIDFast(_PREHASH_ObjectID, objectp->getID());
  624. msg->addVector3Fast(_PREHASH_GrabOffsetInitial,
  625. mGrabOffsetFromCenterInitial);
  626. msg->addVector3Fast(_PREHASH_GrabPosition, grab_pos_region);
  627. msg->addU32Fast(_PREHASH_TimeSinceLast, dt_milliseconds);
  628. msg->nextBlock("SurfaceInfo");
  629. msg->addVector3("UVCoord", LLVector3(mGrabPick.mUVCoords));
  630. msg->addVector3("STCoord", LLVector3(mGrabPick.mSTCoords));
  631. msg->addS32Fast(_PREHASH_FaceIndex, mGrabPick.mObjectFace);
  632. msg->addVector3("Position", mGrabPick.mIntersection);
  633. msg->addVector3("Normal", mGrabPick.mNormal);
  634. msg->addVector3("Binormal", mGrabPick.mBinormal);
  635. msg->sendMessage(objectp->getRegion()->getHost());
  636. }
  637. }
  638. gViewerWindowp->moveCursorToCenter();
  639. gSelectMgr.updateSelectionCenter();
  640. }
  641. // Once we have initiated a drag, lock the camera down
  642. if (mHasMoved)
  643. {
  644. if (!gAgent.cameraMouselook() && !objectp->isHUDAttachment() &&
  645. objectp->getRoot() == gAgentAvatarp->getRoot())
  646. {
  647. // Force focus to point in space where we were looking previously
  648. gAgent.setFocusGlobal(gAgent.calcFocusPositionTargetGlobal(),
  649. LLUUID::null);
  650. gAgent.setFocusOnAvatar(false);
  651. }
  652. else
  653. {
  654. gAgent.clearFocusObject();
  655. }
  656. }
  657. // *HACK: to avoid assert: error checking system makes sure that the cursor
  658. // is set during every handleHover. This is actually a no-op since the
  659. // cursor is hidden.
  660. gViewerWindowp->setCursor(UI_CURSOR_ARROW);
  661. LL_DEBUGS("UserInput") << "Hover handled by LLToolGrab (active) [cursor hidden]"
  662. << LL_ENDL;
  663. }
  664. void LLToolGrabBase::handleHoverNonPhysical(S32 x, S32 y, MASK mask)
  665. {
  666. LLViewerObject* objectp = mGrabPick.getObject();
  667. if (!objectp || !hasMouseCapture()) return;
  668. if (objectp->isDead())
  669. {
  670. // Bail out of drag because object has been killed
  671. setMouseCapture(false);
  672. return;
  673. }
  674. LLPickInfo pick = mGrabPick;
  675. pick.mMousePt = LLCoordGL(x, y);
  676. pick.getSurfaceInfo();
  677. // Compute elapsed time
  678. F32 dt = mGrabTimer.getElapsedTimeAndResetF32();
  679. U32 dt_milliseconds = (U32) (1000.f * dt);
  680. // I am not a big fan of the following code - it has been culled from the
  681. // physical grab case. Ideally these two would be nicely integrated - but
  682. // the code in that method is a serious mess of spaghetti. So here we go:
  683. #if 1 // *TODO: remove ? See BUG-100806.
  684. //--------------------------------------------------
  685. // Toggle vertical dragging
  686. //--------------------------------------------------
  687. if (!gGrabBtnVertical && mask != MASK_VERTICAL)
  688. {
  689. mVerticalDragging = false;
  690. }
  691. else if (gGrabBtnVertical || mask == MASK_VERTICAL)
  692. {
  693. mVerticalDragging = true;
  694. }
  695. S32 dx = x - mLastMouseX;
  696. S32 dy = y - mLastMouseY;
  697. if (dx != 0 || dy != 0)
  698. {
  699. mAccumDeltaX += dx;
  700. mAccumDeltaY += dy;
  701. S32 dist_sq = mAccumDeltaX * mAccumDeltaX +
  702. mAccumDeltaY * mAccumDeltaY;
  703. if (dist_sq > SLOP_DIST_SQ)
  704. {
  705. mOutsideSlop = true;
  706. }
  707. // Mouse has moved
  708. mHasMoved = true;
  709. //------------------------------------------------------
  710. // Handle grabbing
  711. //------------------------------------------------------
  712. LLVector3d x_part;
  713. x_part.set(gViewerCamera.getLeftAxis());
  714. x_part.mdV[VZ] = 0.0;
  715. x_part.normalize();
  716. LLVector3d y_part;
  717. if (mVerticalDragging)
  718. {
  719. y_part.set(gViewerCamera.getUpAxis());
  720. }
  721. else
  722. {
  723. // Drag toward camera
  724. y_part = x_part % LLVector3d::z_axis;
  725. y_part.mdV[VZ] = 0.0;
  726. y_part.normalize();
  727. }
  728. mGrabHiddenOffsetFromCamera = mGrabHiddenOffsetFromCamera +
  729. x_part * (-dx * GRAB_SENSITIVITY_X) +
  730. y_part * (dy * GRAB_SENSITIVITY_Y);
  731. }
  732. // Need to return offset from mGrabStartPoint
  733. LLVector3d grab_pt_global = gAgent.getCameraPositionGlobal() +
  734. mGrabHiddenOffsetFromCamera;
  735. LLVector3 grab_pos_region =
  736. objectp->getRegion()->getPosRegionFromGlobal(grab_pt_global);
  737. #else
  738. LLVector3 grab_pos_region = mLastGrabPos;
  739. #endif
  740. // Only send message if something has changed since last message
  741. if (grab_pos_region != mLastGrabPos || pick.mObjectFace != mLastFace ||
  742. pick.mUVCoords != mLastUVCoords || pick.mSTCoords != mLastSTCoords ||
  743. pick.mNormal != mLastNormal || pick.mBinormal != mLastBinormal ||
  744. pick.mIntersection != mLastIntersection)
  745. {
  746. LLMessageSystem* msg = gMessageSystemp;
  747. msg->newMessageFast(_PREHASH_ObjectGrabUpdate);
  748. msg->nextBlockFast(_PREHASH_AgentData);
  749. msg->addUUIDFast(_PREHASH_AgentID, gAgentID);
  750. msg->addUUIDFast(_PREHASH_SessionID, gAgentSessionID);
  751. msg->nextBlockFast(_PREHASH_ObjectData);
  752. msg->addUUIDFast(_PREHASH_ObjectID, objectp->getID());
  753. msg->addVector3Fast(_PREHASH_GrabOffsetInitial,
  754. mGrabOffsetFromCenterInitial);
  755. msg->addVector3Fast(_PREHASH_GrabPosition, grab_pos_region);
  756. msg->addU32Fast(_PREHASH_TimeSinceLast, dt_milliseconds);
  757. msg->nextBlock("SurfaceInfo");
  758. msg->addVector3("UVCoord", LLVector3(pick.mUVCoords));
  759. msg->addVector3("STCoord", LLVector3(pick.mSTCoords));
  760. msg->addS32Fast(_PREHASH_FaceIndex, pick.mObjectFace);
  761. msg->addVector3("Position", pick.mIntersection);
  762. msg->addVector3("Normal", pick.mNormal);
  763. msg->addVector3("Binormal", pick.mBinormal);
  764. msg->sendMessage(objectp->getRegion()->getHost());
  765. mLastUVCoords = pick.mUVCoords;
  766. mLastSTCoords = pick.mSTCoords;
  767. mLastFace = pick.mObjectFace;
  768. mLastIntersection = pick.mIntersection;
  769. mLastNormal = pick.mNormal;
  770. mLastBinormal = pick.mBinormal;
  771. mLastGrabPos = grab_pos_region;
  772. }
  773. // Update point-at / look-at
  774. // If the intersection was on the surface of the object:
  775. if (pick.mObjectFace != -1)
  776. {
  777. LLVector3 local_edit_pt = pick.mIntersection;
  778. local_edit_pt -= objectp->getPositionAgent();
  779. local_edit_pt = local_edit_pt * ~objectp->getRenderRotation();
  780. gAgent.setPointAt(POINTAT_TARGET_GRAB, objectp, local_edit_pt);
  781. gAgent.setLookAt(LOOKAT_TARGET_SELECT, objectp, local_edit_pt);
  782. }
  783. gViewerWindowp->setCursor(UI_CURSOR_HAND);
  784. }
  785. // Not dragging, just showing affordances
  786. void LLToolGrabBase::handleHoverInactive(S32 x, S32 y, MASK mask)
  787. {
  788. constexpr F32 ROTATE_ANGLE_PER_SECOND = 40.f * DEG_TO_RAD;
  789. const F32 rotate_angle = ROTATE_ANGLE_PER_SECOND / gFPSClamped;
  790. // Look for cursor against the edge of the screen. Only works in fullscreen
  791. if (gWindowp && gWindowp->getFullscreen())
  792. {
  793. if (gAgent.cameraThirdPerson())
  794. {
  795. if (x == 0)
  796. {
  797. gAgent.yaw(rotate_angle);
  798. }
  799. else if (x == gViewerWindowp->getWindowWidth() - 1)
  800. {
  801. gAgent.yaw(-rotate_angle);
  802. }
  803. }
  804. }
  805. // JC - *TODO: change cursor based on gGrabBtnVertical, gGrabBtnSpin
  806. LL_DEBUGS("UserInput") << "Hover handled by LLToolGrab (inactive-not over editable object)"
  807. << LL_ENDL;
  808. gViewerWindowp->setCursor(UI_CURSOR_TOOLGRAB);
  809. }
  810. // User is trying to do something that is not allowed.
  811. void LLToolGrabBase::handleHoverFailed(S32 x, S32 y, MASK mask)
  812. {
  813. if (mMode == GRAB_NOOBJECT)
  814. {
  815. gViewerWindowp->setCursor(UI_CURSOR_NO);
  816. LL_DEBUGS("UserInput") << "Hover handled by LLToolGrab (not on object)"
  817. << LL_ENDL;
  818. }
  819. else
  820. {
  821. S32 dist_sq = (x - mGrabPick.mMousePt.mX) *
  822. (x - mGrabPick.mMousePt.mX) +
  823. (y - mGrabPick.mMousePt.mY) *
  824. (y - mGrabPick.mMousePt.mY);
  825. if (mOutsideSlop || dist_sq > SLOP_DIST_SQ)
  826. {
  827. mOutsideSlop = true;
  828. switch (mMode)
  829. {
  830. case GRAB_LOCKED:
  831. gViewerWindowp->setCursor(UI_CURSOR_GRABLOCKED);
  832. LL_DEBUGS("UserInput") << "Hover handled by LLToolGrab (grab failed, no move permission)"
  833. << LL_ENDL;
  834. break;
  835. #if 0 // Non physical now handled by handleHoverActive - CRO
  836. case GRAB_NONPHYSICAL:
  837. gViewerWindowp->setCursor(UI_CURSOR_ARROW);
  838. LL_DEBUGS("UserInput") << "Hover handled by LLToolGrab (grab failed, non-physical)"
  839. << LL_ENDL;
  840. break;
  841. #endif
  842. default:
  843. llassert(false);
  844. }
  845. }
  846. else
  847. {
  848. gViewerWindowp->setCursor(UI_CURSOR_ARROW);
  849. LL_DEBUGS("UserInput") << "Hover handled by LLToolGrab (grab failed but within slop)"
  850. << LL_ENDL;
  851. }
  852. }
  853. }
  854. bool LLToolGrabBase::handleMouseUp(S32 x, S32 y, MASK mask)
  855. {
  856. // Call the base class to propogate info to sim
  857. LLTool::handleMouseUp(x, y, mask);
  858. if (hasMouseCapture())
  859. {
  860. setMouseCapture(false);
  861. }
  862. mMode = GRAB_INACTIVE;
  863. if (mClickedInMouselook && !gAgent.cameraMouselook())
  864. {
  865. mClickedInMouselook = false;
  866. }
  867. else
  868. {
  869. // *HACK: Make some grabs temporary
  870. if (gGrabTransientTool)
  871. {
  872. gBasicToolset->selectTool(gGrabTransientTool);
  873. gGrabTransientTool = NULL;
  874. }
  875. }
  876. #if 0
  877. gAgent.setObjectTracking(gSavedSettings.getBool("TrackFocusObject"));
  878. #endif
  879. return true;
  880. }
  881. void LLToolGrabBase::stopEditing()
  882. {
  883. if (hasMouseCapture())
  884. {
  885. setMouseCapture(false);
  886. }
  887. }
  888. void LLToolGrabBase::onMouseCaptureLost()
  889. {
  890. LLViewerObject* objectp = mGrabPick.getObject();
  891. if (!objectp)
  892. {
  893. gViewerWindowp->showCursor();
  894. return;
  895. }
  896. // First, fix cursor placement
  897. if (!gAgent.cameraMouselook() && GRAB_ACTIVE_CENTER == mMode)
  898. {
  899. if (objectp->isHUDAttachment())
  900. {
  901. // Move cursor "naturally", as if it had moved when hidden
  902. S32 x = mGrabPick.mMousePt.mX + mAccumDeltaX;
  903. S32 y = mGrabPick.mMousePt.mY + mAccumDeltaY;
  904. LLUI::setCursorPositionScreen(x, y);
  905. }
  906. else if (mHasMoved)
  907. {
  908. // Move cursor back to the center of the object
  909. LLVector3 grab_pt_agent = objectp->getRenderPosition();
  910. LLCoordGL gl_point;
  911. if (gViewerCamera.projectPosAgentToScreen(grab_pt_agent, gl_point))
  912. {
  913. LLUI::setCursorPositionScreen(gl_point.mX, gl_point.mY);
  914. }
  915. }
  916. else
  917. {
  918. // Move cursor back to click position
  919. LLUI::setCursorPositionScreen(mGrabPick.mMousePt.mX,
  920. mGrabPick.mMousePt.mY);
  921. }
  922. gViewerWindowp->showCursor();
  923. }
  924. stopGrab();
  925. if (mSpinGrabbing)
  926. {
  927. stopSpin();
  928. }
  929. mMode = GRAB_INACTIVE;
  930. mGrabPick.mObjectID.setNull();
  931. gSelectMgr.updateSelectionCenter();
  932. gAgent.setPointAt(POINTAT_TARGET_CLEAR);
  933. gAgent.setLookAt(LOOKAT_TARGET_CLEAR);
  934. dialog_refresh_all();
  935. }
  936. void LLToolGrabBase::stopGrab()
  937. {
  938. LLViewerObject* objectp = mGrabPick.getObject();
  939. if (!objectp)
  940. {
  941. return;
  942. }
  943. LLPickInfo pick = mGrabPick;
  944. if (mMode == GRAB_NONPHYSICAL)
  945. {
  946. // For non-physical (touch) grabs, gather surface info for this de-grab
  947. // (mouse-up)
  948. S32 x = gViewerWindowp->getCurrentMouseX();
  949. S32 y = gViewerWindowp->getCurrentMouseY();
  950. pick.mMousePt = LLCoordGL(x, y);
  951. pick.getSurfaceInfo();
  952. }
  953. // Next, send messages to simulator
  954. LLMessageSystem* msg = gMessageSystemp;
  955. switch (mMode)
  956. {
  957. case GRAB_ACTIVE_CENTER:
  958. case GRAB_NONPHYSICAL:
  959. case GRAB_LOCKED:
  960. msg->newMessageFast(_PREHASH_ObjectDeGrab);
  961. msg->nextBlockFast(_PREHASH_AgentData);
  962. msg->addUUIDFast(_PREHASH_AgentID, gAgentID);
  963. msg->addUUIDFast(_PREHASH_SessionID, gAgentSessionID);
  964. msg->nextBlockFast(_PREHASH_ObjectData);
  965. msg->addU32Fast(_PREHASH_LocalID, objectp->mLocalID);
  966. msg->nextBlock("SurfaceInfo");
  967. msg->addVector3("UVCoord", LLVector3(pick.mUVCoords));
  968. msg->addVector3("STCoord", LLVector3(pick.mSTCoords));
  969. msg->addS32Fast(_PREHASH_FaceIndex, pick.mObjectFace);
  970. msg->addVector3("Position", pick.mIntersection);
  971. msg->addVector3("Normal", pick.mNormal);
  972. msg->addVector3("Binormal", pick.mBinormal);
  973. msg->sendMessage(objectp->getRegion()->getHost());
  974. mVerticalDragging = false;
  975. break;
  976. case GRAB_NOOBJECT:
  977. case GRAB_INACTIVE:
  978. default:
  979. // Do nothing
  980. break;
  981. }
  982. }
  983. bool LLToolGrabBase::isEditing()
  984. {
  985. return mGrabPick.getObject().notNull();
  986. }
  987. LLViewerObject* LLToolGrabBase::getEditingObject()
  988. {
  989. return mGrabPick.getObject();
  990. }
  991. LLVector3d LLToolGrabBase::getEditingPointGlobal()
  992. {
  993. return getGrabPointGlobal();
  994. }
  995. LLVector3d LLToolGrabBase::getGrabPointGlobal()
  996. {
  997. switch (mMode)
  998. {
  999. case GRAB_ACTIVE_CENTER:
  1000. case GRAB_NONPHYSICAL:
  1001. case GRAB_LOCKED:
  1002. return gAgent.getCameraPositionGlobal() +
  1003. mGrabHiddenOffsetFromCamera;
  1004. case GRAB_NOOBJECT:
  1005. case GRAB_INACTIVE:
  1006. default:
  1007. return gAgent.getPositionGlobal();
  1008. }
  1009. }