/indra/newview/lljoystickbutton.cpp

https://bitbucket.org/lindenlab/viewer-beta/ · C++ · 642 lines · 450 code · 109 blank · 83 comment · 87 complexity · 7b85dba591576acd98fb6a215ee44177 MD5 · raw file

  1. /**
  2. * @file lljoystickbutton.cpp
  3. * @brief LLJoystick class implementation
  4. *
  5. * $LicenseInfo:firstyear=2001&license=viewerlgpl$
  6. * Second Life Viewer Source Code
  7. * Copyright (C) 2010, Linden Research, Inc.
  8. *
  9. * This library is free software; you can redistribute it and/or
  10. * modify it under the terms of the GNU Lesser General Public
  11. * License as published by the Free Software Foundation;
  12. * version 2.1 of the License only.
  13. *
  14. * This library is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  17. * Lesser General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU Lesser General Public
  20. * License along with this library; if not, write to the Free Software
  21. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  22. *
  23. * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
  24. * $/LicenseInfo$
  25. */
  26. #include "llviewerprecompiledheaders.h"
  27. #include "lljoystickbutton.h"
  28. // Library includes
  29. #include "llcoord.h"
  30. #include "indra_constants.h"
  31. #include "llrender.h"
  32. // Project includes
  33. #include "llui.h"
  34. #include "llagent.h"
  35. #include "llagentcamera.h"
  36. #include "llviewertexture.h"
  37. #include "llviewertexturelist.h"
  38. #include "llviewerwindow.h"
  39. #include "llmoveview.h"
  40. #include "llglheaders.h"
  41. static LLDefaultChildRegistry::Register<LLJoystickAgentSlide> r1("joystick_slide");
  42. static LLDefaultChildRegistry::Register<LLJoystickAgentTurn> r2("joystick_turn");
  43. static LLDefaultChildRegistry::Register<LLJoystickCameraRotate> r3("joystick_rotate");
  44. static LLDefaultChildRegistry::Register<LLJoystickCameraTrack> r5("joystick_track");
  45. const F32 NUDGE_TIME = 0.25f; // in seconds
  46. const F32 ORBIT_NUDGE_RATE = 0.05f; // fraction of normal speed
  47. //
  48. // Public Methods
  49. //
  50. void QuadrantNames::declareValues()
  51. {
  52. declare("origin", JQ_ORIGIN);
  53. declare("up", JQ_UP);
  54. declare("down", JQ_DOWN);
  55. declare("left", JQ_LEFT);
  56. declare("right", JQ_RIGHT);
  57. }
  58. LLJoystick::LLJoystick(const LLJoystick::Params& p)
  59. : LLButton(p),
  60. mInitialOffset(0, 0),
  61. mLastMouse(0, 0),
  62. mFirstMouse(0, 0),
  63. mVertSlopNear(0),
  64. mVertSlopFar(0),
  65. mHorizSlopNear(0),
  66. mHorizSlopFar(0),
  67. mHeldDown(FALSE),
  68. mHeldDownTimer(),
  69. mInitialQuadrant(p.quadrant)
  70. {
  71. setHeldDownCallback(&LLJoystick::onBtnHeldDown, this);
  72. }
  73. void LLJoystick::updateSlop()
  74. {
  75. mVertSlopNear = getRect().getHeight();
  76. mVertSlopFar = getRect().getHeight() * 2;
  77. mHorizSlopNear = getRect().getWidth();
  78. mHorizSlopFar = getRect().getWidth() * 2;
  79. // Compute initial mouse offset based on initial quadrant.
  80. // Place the mouse evenly between the near and far zones.
  81. switch (mInitialQuadrant)
  82. {
  83. case JQ_ORIGIN:
  84. mInitialOffset.set(0, 0);
  85. break;
  86. case JQ_UP:
  87. mInitialOffset.mX = 0;
  88. mInitialOffset.mY = (mVertSlopNear + mVertSlopFar) / 2;
  89. break;
  90. case JQ_DOWN:
  91. mInitialOffset.mX = 0;
  92. mInitialOffset.mY = - (mVertSlopNear + mVertSlopFar) / 2;
  93. break;
  94. case JQ_LEFT:
  95. mInitialOffset.mX = - (mHorizSlopNear + mHorizSlopFar) / 2;
  96. mInitialOffset.mY = 0;
  97. break;
  98. case JQ_RIGHT:
  99. mInitialOffset.mX = (mHorizSlopNear + mHorizSlopFar) / 2;
  100. mInitialOffset.mY = 0;
  101. break;
  102. default:
  103. llerrs << "LLJoystick::LLJoystick() - bad switch case" << llendl;
  104. break;
  105. }
  106. return;
  107. }
  108. bool LLJoystick::pointInCircle(S32 x, S32 y) const
  109. {
  110. if(this->getLocalRect().getHeight() != this->getLocalRect().getWidth())
  111. {
  112. llwarns << "Joystick shape is not square"<<llendl;
  113. return true;
  114. }
  115. //center is x and y coordinates of center of joystick circle, and also its radius
  116. int center = this->getLocalRect().getHeight()/2;
  117. bool in_circle = (x - center) * (x - center) + (y - center) * (y - center) <= center * center;
  118. return in_circle;
  119. }
  120. BOOL LLJoystick::handleMouseDown(S32 x, S32 y, MASK mask)
  121. {
  122. //llinfos << "joystick mouse down " << x << ", " << y << llendl;
  123. bool handles = false;
  124. if(pointInCircle(x, y))
  125. {
  126. mLastMouse.set(x, y);
  127. mFirstMouse.set(x, y);
  128. mMouseDownTimer.reset();
  129. handles = LLButton::handleMouseDown(x, y, mask);
  130. }
  131. return handles;
  132. }
  133. BOOL LLJoystick::handleMouseUp(S32 x, S32 y, MASK mask)
  134. {
  135. // llinfos << "joystick mouse up " << x << ", " << y << llendl;
  136. if( hasMouseCapture() )
  137. {
  138. mLastMouse.set(x, y);
  139. mHeldDown = FALSE;
  140. onMouseUp();
  141. }
  142. return LLButton::handleMouseUp(x, y, mask);
  143. }
  144. BOOL LLJoystick::handleHover(S32 x, S32 y, MASK mask)
  145. {
  146. if( hasMouseCapture() )
  147. {
  148. mLastMouse.set(x, y);
  149. }
  150. return LLButton::handleHover(x, y, mask);
  151. }
  152. F32 LLJoystick::getElapsedHeldDownTime()
  153. {
  154. if( mHeldDown )
  155. {
  156. return getHeldDownTime();
  157. }
  158. else
  159. {
  160. return 0.f;
  161. }
  162. }
  163. // static
  164. void LLJoystick::onBtnHeldDown(void *userdata)
  165. {
  166. LLJoystick *self = (LLJoystick *)userdata;
  167. if (self)
  168. {
  169. self->mHeldDown = TRUE;
  170. self->onHeldDown();
  171. }
  172. }
  173. EJoystickQuadrant LLJoystick::selectQuadrant(LLXMLNodePtr node)
  174. {
  175. EJoystickQuadrant quadrant = JQ_RIGHT;
  176. if (node->hasAttribute("quadrant"))
  177. {
  178. std::string quadrant_name;
  179. node->getAttributeString("quadrant", quadrant_name);
  180. quadrant = quadrantFromName(quadrant_name);
  181. }
  182. return quadrant;
  183. }
  184. std::string LLJoystick::nameFromQuadrant(EJoystickQuadrant quadrant)
  185. {
  186. if (quadrant == JQ_ORIGIN) return std::string("origin");
  187. else if (quadrant == JQ_UP) return std::string("up");
  188. else if (quadrant == JQ_DOWN) return std::string("down");
  189. else if (quadrant == JQ_LEFT) return std::string("left");
  190. else if (quadrant == JQ_RIGHT) return std::string("right");
  191. else return std::string();
  192. }
  193. EJoystickQuadrant LLJoystick::quadrantFromName(const std::string& sQuadrant)
  194. {
  195. EJoystickQuadrant quadrant = JQ_RIGHT;
  196. if (sQuadrant == "origin")
  197. {
  198. quadrant = JQ_ORIGIN;
  199. }
  200. else if (sQuadrant == "up")
  201. {
  202. quadrant = JQ_UP;
  203. }
  204. else if (sQuadrant == "down")
  205. {
  206. quadrant = JQ_DOWN;
  207. }
  208. else if (sQuadrant == "left")
  209. {
  210. quadrant = JQ_LEFT;
  211. }
  212. else if (sQuadrant == "right")
  213. {
  214. quadrant = JQ_RIGHT;
  215. }
  216. return quadrant;
  217. }
  218. //-------------------------------------------------------------------------------
  219. // LLJoystickAgentTurn
  220. //-------------------------------------------------------------------------------
  221. void LLJoystickAgentTurn::onHeldDown()
  222. {
  223. F32 time = getElapsedHeldDownTime();
  224. updateSlop();
  225. //llinfos << "move forward/backward (and/or turn)" << llendl;
  226. S32 dx = mLastMouse.mX - mFirstMouse.mX + mInitialOffset.mX;
  227. S32 dy = mLastMouse.mY - mFirstMouse.mY + mInitialOffset.mY;
  228. float m = (float) (dx)/abs(dy);
  229. if (m > 1) {
  230. m = 1;
  231. }
  232. else if (m < -1) {
  233. m = -1;
  234. }
  235. gAgent.moveYaw(-LLFloaterMove::getYawRate(time)*m);
  236. // handle forward/back movement
  237. if (dy > mVertSlopFar)
  238. {
  239. // ...if mouse is forward of run region run forward
  240. gAgent.moveAt(1);
  241. }
  242. else if (dy > mVertSlopNear)
  243. {
  244. if( time < NUDGE_TIME )
  245. {
  246. gAgent.moveAtNudge(1);
  247. }
  248. else
  249. {
  250. // ...else if mouse is forward of walk region walk forward
  251. // JC 9/5/2002 - Always run / move quickly.
  252. gAgent.moveAt(1);
  253. }
  254. }
  255. else if (dy < -mVertSlopFar)
  256. {
  257. // ...else if mouse is behind run region run backward
  258. gAgent.moveAt(-1);
  259. }
  260. else if (dy < -mVertSlopNear)
  261. {
  262. if( time < NUDGE_TIME )
  263. {
  264. gAgent.moveAtNudge(-1);
  265. }
  266. else
  267. {
  268. // ...else if mouse is behind walk region walk backward
  269. // JC 9/5/2002 - Always run / move quickly.
  270. gAgent.moveAt(-1);
  271. }
  272. }
  273. }
  274. //-------------------------------------------------------------------------------
  275. // LLJoystickAgentSlide
  276. //-------------------------------------------------------------------------------
  277. void LLJoystickAgentSlide::onMouseUp()
  278. {
  279. F32 time = getElapsedHeldDownTime();
  280. if( time < NUDGE_TIME )
  281. {
  282. switch (mInitialQuadrant)
  283. {
  284. case JQ_LEFT:
  285. gAgent.moveLeftNudge(1);
  286. break;
  287. case JQ_RIGHT:
  288. gAgent.moveLeftNudge(-1);
  289. break;
  290. default:
  291. break;
  292. }
  293. }
  294. }
  295. void LLJoystickAgentSlide::onHeldDown()
  296. {
  297. //llinfos << "slide left/right (and/or move forward/backward)" << llendl;
  298. updateSlop();
  299. S32 dx = mLastMouse.mX - mFirstMouse.mX + mInitialOffset.mX;
  300. S32 dy = mLastMouse.mY - mFirstMouse.mY + mInitialOffset.mY;
  301. // handle left-right sliding
  302. if (dx > mHorizSlopNear)
  303. {
  304. gAgent.moveLeft(-1);
  305. }
  306. else if (dx < -mHorizSlopNear)
  307. {
  308. gAgent.moveLeft(1);
  309. }
  310. // handle forward/back movement
  311. if (dy > mVertSlopFar)
  312. {
  313. // ...if mouse is forward of run region run forward
  314. gAgent.moveAt(1);
  315. }
  316. else if (dy > mVertSlopNear)
  317. {
  318. // ...else if mouse is forward of walk region walk forward
  319. gAgent.moveAtNudge(1);
  320. }
  321. else if (dy < -mVertSlopFar)
  322. {
  323. // ...else if mouse is behind run region run backward
  324. gAgent.moveAt(-1);
  325. }
  326. else if (dy < -mVertSlopNear)
  327. {
  328. // ...else if mouse is behind walk region walk backward
  329. gAgent.moveAtNudge(-1);
  330. }
  331. }
  332. //-------------------------------------------------------------------------------
  333. // LLJoystickCameraRotate
  334. //-------------------------------------------------------------------------------
  335. LLJoystickCameraRotate::LLJoystickCameraRotate(const LLJoystickCameraRotate::Params& p)
  336. : LLJoystick(p),
  337. mInLeft( FALSE ),
  338. mInTop( FALSE ),
  339. mInRight( FALSE ),
  340. mInBottom( FALSE )
  341. { }
  342. void LLJoystickCameraRotate::updateSlop()
  343. {
  344. // do the initial offset calculation based on mousedown location
  345. // small fixed slop region
  346. mVertSlopNear = 16;
  347. mVertSlopFar = 32;
  348. mHorizSlopNear = 16;
  349. mHorizSlopFar = 32;
  350. return;
  351. }
  352. BOOL LLJoystickCameraRotate::handleMouseDown(S32 x, S32 y, MASK mask)
  353. {
  354. updateSlop();
  355. // Set initial offset based on initial click location
  356. S32 horiz_center = getRect().getWidth() / 2;
  357. S32 vert_center = getRect().getHeight() / 2;
  358. S32 dx = x - horiz_center;
  359. S32 dy = y - vert_center;
  360. if (dy > dx && dy > -dx)
  361. {
  362. // top
  363. mInitialOffset.mX = 0;
  364. mInitialOffset.mY = (mVertSlopNear + mVertSlopFar) / 2;
  365. mInitialQuadrant = JQ_UP;
  366. }
  367. else if (dy > dx && dy <= -dx)
  368. {
  369. // left
  370. mInitialOffset.mX = - (mHorizSlopNear + mHorizSlopFar) / 2;
  371. mInitialOffset.mY = 0;
  372. mInitialQuadrant = JQ_LEFT;
  373. }
  374. else if (dy <= dx && dy <= -dx)
  375. {
  376. // bottom
  377. mInitialOffset.mX = 0;
  378. mInitialOffset.mY = - (mVertSlopNear + mVertSlopFar) / 2;
  379. mInitialQuadrant = JQ_DOWN;
  380. }
  381. else
  382. {
  383. // right
  384. mInitialOffset.mX = (mHorizSlopNear + mHorizSlopFar) / 2;
  385. mInitialOffset.mY = 0;
  386. mInitialQuadrant = JQ_RIGHT;
  387. }
  388. return LLJoystick::handleMouseDown(x, y, mask);
  389. }
  390. void LLJoystickCameraRotate::onHeldDown()
  391. {
  392. updateSlop();
  393. S32 dx = mLastMouse.mX - mFirstMouse.mX + mInitialOffset.mX;
  394. S32 dy = mLastMouse.mY - mFirstMouse.mY + mInitialOffset.mY;
  395. // left-right rotation
  396. if (dx > mHorizSlopNear)
  397. {
  398. gAgentCamera.unlockView();
  399. gAgentCamera.setOrbitLeftKey(getOrbitRate());
  400. }
  401. else if (dx < -mHorizSlopNear)
  402. {
  403. gAgentCamera.unlockView();
  404. gAgentCamera.setOrbitRightKey(getOrbitRate());
  405. }
  406. // over/under rotation
  407. if (dy > mVertSlopNear)
  408. {
  409. gAgentCamera.unlockView();
  410. gAgentCamera.setOrbitUpKey(getOrbitRate());
  411. }
  412. else if (dy < -mVertSlopNear)
  413. {
  414. gAgentCamera.unlockView();
  415. gAgentCamera.setOrbitDownKey(getOrbitRate());
  416. }
  417. }
  418. F32 LLJoystickCameraRotate::getOrbitRate()
  419. {
  420. F32 time = getElapsedHeldDownTime();
  421. if( time < NUDGE_TIME )
  422. {
  423. F32 rate = ORBIT_NUDGE_RATE + time * (1 - ORBIT_NUDGE_RATE)/ NUDGE_TIME;
  424. //llinfos << rate << llendl;
  425. return rate;
  426. }
  427. else
  428. {
  429. return 1;
  430. }
  431. }
  432. // Only used for drawing
  433. void LLJoystickCameraRotate::setToggleState( BOOL left, BOOL top, BOOL right, BOOL bottom )
  434. {
  435. mInLeft = left;
  436. mInTop = top;
  437. mInRight = right;
  438. mInBottom = bottom;
  439. }
  440. void LLJoystickCameraRotate::draw()
  441. {
  442. LLGLSUIDefault gls_ui;
  443. getImageUnselected()->draw( 0, 0 );
  444. LLPointer<LLUIImage> image = getImageSelected();
  445. if( mInTop )
  446. {
  447. drawRotatedImage( getImageSelected(), 0 );
  448. }
  449. if( mInRight )
  450. {
  451. drawRotatedImage( getImageSelected(), 1 );
  452. }
  453. if( mInBottom )
  454. {
  455. drawRotatedImage( getImageSelected(), 2 );
  456. }
  457. if( mInLeft )
  458. {
  459. drawRotatedImage( getImageSelected(), 3 );
  460. }
  461. }
  462. // Draws image rotated by multiples of 90 degrees
  463. void LLJoystickCameraRotate::drawRotatedImage( LLPointer<LLUIImage> image, S32 rotations )
  464. {
  465. S32 width = image->getWidth();
  466. S32 height = image->getHeight();
  467. LLTexture* texture = image->getImage();
  468. /*
  469. * Scale texture coordinate system
  470. * to handle the different between image size and size of texture.
  471. * If we will use default matrix,
  472. * it may break texture mapping after rotation.
  473. * see EXT-2023 Camera floater: arrows became shifted when pressed.
  474. */
  475. F32 uv[][2] =
  476. {
  477. { (F32)width/texture->getWidth(), (F32)height/texture->getHeight() },
  478. { 0.f, (F32)height/texture->getHeight() },
  479. { 0.f, 0.f },
  480. { (F32)width/texture->getWidth(), 0.f }
  481. };
  482. gGL.getTexUnit(0)->bind(texture);
  483. gGL.color4fv(UI_VERTEX_COLOR.mV);
  484. gGL.begin(LLRender::QUADS);
  485. {
  486. gGL.texCoord2fv( uv[ (rotations + 0) % 4]);
  487. gGL.vertex2i(width, height );
  488. gGL.texCoord2fv( uv[ (rotations + 1) % 4]);
  489. gGL.vertex2i(0, height );
  490. gGL.texCoord2fv( uv[ (rotations + 2) % 4]);
  491. gGL.vertex2i(0, 0);
  492. gGL.texCoord2fv( uv[ (rotations + 3) % 4]);
  493. gGL.vertex2i(width, 0);
  494. }
  495. gGL.end();
  496. }
  497. //-------------------------------------------------------------------------------
  498. // LLJoystickCameraTrack
  499. //-------------------------------------------------------------------------------
  500. LLJoystickCameraTrack::Params::Params()
  501. {
  502. held_down_delay.seconds(0.0);
  503. }
  504. LLJoystickCameraTrack::LLJoystickCameraTrack(const LLJoystickCameraTrack::Params& p)
  505. : LLJoystickCameraRotate(p)
  506. {}
  507. void LLJoystickCameraTrack::onHeldDown()
  508. {
  509. updateSlop();
  510. S32 dx = mLastMouse.mX - mFirstMouse.mX + mInitialOffset.mX;
  511. S32 dy = mLastMouse.mY - mFirstMouse.mY + mInitialOffset.mY;
  512. if (dx > mVertSlopNear)
  513. {
  514. gAgentCamera.unlockView();
  515. gAgentCamera.setPanRightKey(getOrbitRate());
  516. }
  517. else if (dx < -mVertSlopNear)
  518. {
  519. gAgentCamera.unlockView();
  520. gAgentCamera.setPanLeftKey(getOrbitRate());
  521. }
  522. // over/under rotation
  523. if (dy > mVertSlopNear)
  524. {
  525. gAgentCamera.unlockView();
  526. gAgentCamera.setPanUpKey(getOrbitRate());
  527. }
  528. else if (dy < -mVertSlopNear)
  529. {
  530. gAgentCamera.unlockView();
  531. gAgentCamera.setPanDownKey(getOrbitRate());
  532. }
  533. }