/indra/newview/lljoystickbutton.cpp
C++ | 642 lines | 450 code | 109 blank | 83 comment | 87 complexity | 7b85dba591576acd98fb6a215ee44177 MD5 | raw file
Possible License(s): LGPL-2.1
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 27#include "llviewerprecompiledheaders.h" 28 29#include "lljoystickbutton.h" 30 31// Library includes 32#include "llcoord.h" 33#include "indra_constants.h" 34#include "llrender.h" 35 36// Project includes 37#include "llui.h" 38#include "llagent.h" 39#include "llagentcamera.h" 40#include "llviewertexture.h" 41#include "llviewertexturelist.h" 42#include "llviewerwindow.h" 43#include "llmoveview.h" 44 45#include "llglheaders.h" 46 47static LLDefaultChildRegistry::Register<LLJoystickAgentSlide> r1("joystick_slide"); 48static LLDefaultChildRegistry::Register<LLJoystickAgentTurn> r2("joystick_turn"); 49static LLDefaultChildRegistry::Register<LLJoystickCameraRotate> r3("joystick_rotate"); 50static LLDefaultChildRegistry::Register<LLJoystickCameraTrack> r5("joystick_track"); 51 52 53 54const F32 NUDGE_TIME = 0.25f; // in seconds 55const F32 ORBIT_NUDGE_RATE = 0.05f; // fraction of normal speed 56 57// 58// Public Methods 59// 60void QuadrantNames::declareValues() 61{ 62 declare("origin", JQ_ORIGIN); 63 declare("up", JQ_UP); 64 declare("down", JQ_DOWN); 65 declare("left", JQ_LEFT); 66 declare("right", JQ_RIGHT); 67} 68 69 70LLJoystick::LLJoystick(const LLJoystick::Params& p) 71: LLButton(p), 72 mInitialOffset(0, 0), 73 mLastMouse(0, 0), 74 mFirstMouse(0, 0), 75 mVertSlopNear(0), 76 mVertSlopFar(0), 77 mHorizSlopNear(0), 78 mHorizSlopFar(0), 79 mHeldDown(FALSE), 80 mHeldDownTimer(), 81 mInitialQuadrant(p.quadrant) 82{ 83 setHeldDownCallback(&LLJoystick::onBtnHeldDown, this); 84} 85 86 87void LLJoystick::updateSlop() 88{ 89 mVertSlopNear = getRect().getHeight(); 90 mVertSlopFar = getRect().getHeight() * 2; 91 92 mHorizSlopNear = getRect().getWidth(); 93 mHorizSlopFar = getRect().getWidth() * 2; 94 95 // Compute initial mouse offset based on initial quadrant. 96 // Place the mouse evenly between the near and far zones. 97 switch (mInitialQuadrant) 98 { 99 case JQ_ORIGIN: 100 mInitialOffset.set(0, 0); 101 break; 102 103 case JQ_UP: 104 mInitialOffset.mX = 0; 105 mInitialOffset.mY = (mVertSlopNear + mVertSlopFar) / 2; 106 break; 107 108 case JQ_DOWN: 109 mInitialOffset.mX = 0; 110 mInitialOffset.mY = - (mVertSlopNear + mVertSlopFar) / 2; 111 break; 112 113 case JQ_LEFT: 114 mInitialOffset.mX = - (mHorizSlopNear + mHorizSlopFar) / 2; 115 mInitialOffset.mY = 0; 116 break; 117 118 case JQ_RIGHT: 119 mInitialOffset.mX = (mHorizSlopNear + mHorizSlopFar) / 2; 120 mInitialOffset.mY = 0; 121 break; 122 123 default: 124 llerrs << "LLJoystick::LLJoystick() - bad switch case" << llendl; 125 break; 126 } 127 128 return; 129} 130 131bool LLJoystick::pointInCircle(S32 x, S32 y) const 132{ 133 if(this->getLocalRect().getHeight() != this->getLocalRect().getWidth()) 134 { 135 llwarns << "Joystick shape is not square"<<llendl; 136 return true; 137 } 138 //center is x and y coordinates of center of joystick circle, and also its radius 139 int center = this->getLocalRect().getHeight()/2; 140 bool in_circle = (x - center) * (x - center) + (y - center) * (y - center) <= center * center; 141 return in_circle; 142} 143 144BOOL LLJoystick::handleMouseDown(S32 x, S32 y, MASK mask) 145{ 146 //llinfos << "joystick mouse down " << x << ", " << y << llendl; 147 bool handles = false; 148 149 if(pointInCircle(x, y)) 150 { 151 mLastMouse.set(x, y); 152 mFirstMouse.set(x, y); 153 mMouseDownTimer.reset(); 154 handles = LLButton::handleMouseDown(x, y, mask); 155 } 156 157 return handles; 158} 159 160 161BOOL LLJoystick::handleMouseUp(S32 x, S32 y, MASK mask) 162{ 163 // llinfos << "joystick mouse up " << x << ", " << y << llendl; 164 165 if( hasMouseCapture() ) 166 { 167 mLastMouse.set(x, y); 168 mHeldDown = FALSE; 169 onMouseUp(); 170 } 171 172 return LLButton::handleMouseUp(x, y, mask); 173} 174 175 176BOOL LLJoystick::handleHover(S32 x, S32 y, MASK mask) 177{ 178 if( hasMouseCapture() ) 179 { 180 mLastMouse.set(x, y); 181 } 182 183 return LLButton::handleHover(x, y, mask); 184} 185 186F32 LLJoystick::getElapsedHeldDownTime() 187{ 188 if( mHeldDown ) 189 { 190 return getHeldDownTime(); 191 } 192 else 193 { 194 return 0.f; 195 } 196} 197 198// static 199void LLJoystick::onBtnHeldDown(void *userdata) 200{ 201 LLJoystick *self = (LLJoystick *)userdata; 202 if (self) 203 { 204 self->mHeldDown = TRUE; 205 self->onHeldDown(); 206 } 207} 208 209EJoystickQuadrant LLJoystick::selectQuadrant(LLXMLNodePtr node) 210{ 211 212 EJoystickQuadrant quadrant = JQ_RIGHT; 213 214 if (node->hasAttribute("quadrant")) 215 { 216 std::string quadrant_name; 217 node->getAttributeString("quadrant", quadrant_name); 218 219 quadrant = quadrantFromName(quadrant_name); 220 } 221 return quadrant; 222} 223 224 225std::string LLJoystick::nameFromQuadrant(EJoystickQuadrant quadrant) 226{ 227 if (quadrant == JQ_ORIGIN) return std::string("origin"); 228 else if (quadrant == JQ_UP) return std::string("up"); 229 else if (quadrant == JQ_DOWN) return std::string("down"); 230 else if (quadrant == JQ_LEFT) return std::string("left"); 231 else if (quadrant == JQ_RIGHT) return std::string("right"); 232 else return std::string(); 233} 234 235 236EJoystickQuadrant LLJoystick::quadrantFromName(const std::string& sQuadrant) 237{ 238 EJoystickQuadrant quadrant = JQ_RIGHT; 239 240 if (sQuadrant == "origin") 241 { 242 quadrant = JQ_ORIGIN; 243 } 244 else if (sQuadrant == "up") 245 { 246 quadrant = JQ_UP; 247 } 248 else if (sQuadrant == "down") 249 { 250 quadrant = JQ_DOWN; 251 } 252 else if (sQuadrant == "left") 253 { 254 quadrant = JQ_LEFT; 255 } 256 else if (sQuadrant == "right") 257 { 258 quadrant = JQ_RIGHT; 259 } 260 261 return quadrant; 262} 263 264 265//------------------------------------------------------------------------------- 266// LLJoystickAgentTurn 267//------------------------------------------------------------------------------- 268 269void LLJoystickAgentTurn::onHeldDown() 270{ 271 F32 time = getElapsedHeldDownTime(); 272 updateSlop(); 273 274 //llinfos << "move forward/backward (and/or turn)" << llendl; 275 276 S32 dx = mLastMouse.mX - mFirstMouse.mX + mInitialOffset.mX; 277 S32 dy = mLastMouse.mY - mFirstMouse.mY + mInitialOffset.mY; 278 279 float m = (float) (dx)/abs(dy); 280 281 if (m > 1) { 282 m = 1; 283 } 284 else if (m < -1) { 285 m = -1; 286 } 287 gAgent.moveYaw(-LLFloaterMove::getYawRate(time)*m); 288 289 290 // handle forward/back movement 291 if (dy > mVertSlopFar) 292 { 293 // ...if mouse is forward of run region run forward 294 gAgent.moveAt(1); 295 } 296 else if (dy > mVertSlopNear) 297 { 298 if( time < NUDGE_TIME ) 299 { 300 gAgent.moveAtNudge(1); 301 } 302 else 303 { 304 // ...else if mouse is forward of walk region walk forward 305 // JC 9/5/2002 - Always run / move quickly. 306 gAgent.moveAt(1); 307 } 308 } 309 else if (dy < -mVertSlopFar) 310 { 311 // ...else if mouse is behind run region run backward 312 gAgent.moveAt(-1); 313 } 314 else if (dy < -mVertSlopNear) 315 { 316 if( time < NUDGE_TIME ) 317 { 318 gAgent.moveAtNudge(-1); 319 } 320 else 321 { 322 // ...else if mouse is behind walk region walk backward 323 // JC 9/5/2002 - Always run / move quickly. 324 gAgent.moveAt(-1); 325 } 326 } 327} 328 329//------------------------------------------------------------------------------- 330// LLJoystickAgentSlide 331//------------------------------------------------------------------------------- 332 333void LLJoystickAgentSlide::onMouseUp() 334{ 335 F32 time = getElapsedHeldDownTime(); 336 if( time < NUDGE_TIME ) 337 { 338 switch (mInitialQuadrant) 339 { 340 case JQ_LEFT: 341 gAgent.moveLeftNudge(1); 342 break; 343 344 case JQ_RIGHT: 345 gAgent.moveLeftNudge(-1); 346 break; 347 348 default: 349 break; 350 } 351 } 352} 353 354void LLJoystickAgentSlide::onHeldDown() 355{ 356 //llinfos << "slide left/right (and/or move forward/backward)" << llendl; 357 358 updateSlop(); 359 360 S32 dx = mLastMouse.mX - mFirstMouse.mX + mInitialOffset.mX; 361 S32 dy = mLastMouse.mY - mFirstMouse.mY + mInitialOffset.mY; 362 363 // handle left-right sliding 364 if (dx > mHorizSlopNear) 365 { 366 gAgent.moveLeft(-1); 367 } 368 else if (dx < -mHorizSlopNear) 369 { 370 gAgent.moveLeft(1); 371 } 372 373 // handle forward/back movement 374 if (dy > mVertSlopFar) 375 { 376 // ...if mouse is forward of run region run forward 377 gAgent.moveAt(1); 378 } 379 else if (dy > mVertSlopNear) 380 { 381 // ...else if mouse is forward of walk region walk forward 382 gAgent.moveAtNudge(1); 383 } 384 else if (dy < -mVertSlopFar) 385 { 386 // ...else if mouse is behind run region run backward 387 gAgent.moveAt(-1); 388 } 389 else if (dy < -mVertSlopNear) 390 { 391 // ...else if mouse is behind walk region walk backward 392 gAgent.moveAtNudge(-1); 393 } 394} 395 396 397//------------------------------------------------------------------------------- 398// LLJoystickCameraRotate 399//------------------------------------------------------------------------------- 400 401LLJoystickCameraRotate::LLJoystickCameraRotate(const LLJoystickCameraRotate::Params& p) 402: LLJoystick(p), 403 mInLeft( FALSE ), 404 mInTop( FALSE ), 405 mInRight( FALSE ), 406 mInBottom( FALSE ) 407{ } 408 409 410void LLJoystickCameraRotate::updateSlop() 411{ 412 // do the initial offset calculation based on mousedown location 413 414 // small fixed slop region 415 mVertSlopNear = 16; 416 mVertSlopFar = 32; 417 418 mHorizSlopNear = 16; 419 mHorizSlopFar = 32; 420 421 return; 422} 423 424 425BOOL LLJoystickCameraRotate::handleMouseDown(S32 x, S32 y, MASK mask) 426{ 427 updateSlop(); 428 429 // Set initial offset based on initial click location 430 S32 horiz_center = getRect().getWidth() / 2; 431 S32 vert_center = getRect().getHeight() / 2; 432 433 S32 dx = x - horiz_center; 434 S32 dy = y - vert_center; 435 436 if (dy > dx && dy > -dx) 437 { 438 // top 439 mInitialOffset.mX = 0; 440 mInitialOffset.mY = (mVertSlopNear + mVertSlopFar) / 2; 441 mInitialQuadrant = JQ_UP; 442 } 443 else if (dy > dx && dy <= -dx) 444 { 445 // left 446 mInitialOffset.mX = - (mHorizSlopNear + mHorizSlopFar) / 2; 447 mInitialOffset.mY = 0; 448 mInitialQuadrant = JQ_LEFT; 449 } 450 else if (dy <= dx && dy <= -dx) 451 { 452 // bottom 453 mInitialOffset.mX = 0; 454 mInitialOffset.mY = - (mVertSlopNear + mVertSlopFar) / 2; 455 mInitialQuadrant = JQ_DOWN; 456 } 457 else 458 { 459 // right 460 mInitialOffset.mX = (mHorizSlopNear + mHorizSlopFar) / 2; 461 mInitialOffset.mY = 0; 462 mInitialQuadrant = JQ_RIGHT; 463 } 464 465 return LLJoystick::handleMouseDown(x, y, mask); 466} 467 468 469void LLJoystickCameraRotate::onHeldDown() 470{ 471 updateSlop(); 472 473 S32 dx = mLastMouse.mX - mFirstMouse.mX + mInitialOffset.mX; 474 S32 dy = mLastMouse.mY - mFirstMouse.mY + mInitialOffset.mY; 475 476 // left-right rotation 477 if (dx > mHorizSlopNear) 478 { 479 gAgentCamera.unlockView(); 480 gAgentCamera.setOrbitLeftKey(getOrbitRate()); 481 } 482 else if (dx < -mHorizSlopNear) 483 { 484 gAgentCamera.unlockView(); 485 gAgentCamera.setOrbitRightKey(getOrbitRate()); 486 } 487 488 // over/under rotation 489 if (dy > mVertSlopNear) 490 { 491 gAgentCamera.unlockView(); 492 gAgentCamera.setOrbitUpKey(getOrbitRate()); 493 } 494 else if (dy < -mVertSlopNear) 495 { 496 gAgentCamera.unlockView(); 497 gAgentCamera.setOrbitDownKey(getOrbitRate()); 498 } 499} 500 501F32 LLJoystickCameraRotate::getOrbitRate() 502{ 503 F32 time = getElapsedHeldDownTime(); 504 if( time < NUDGE_TIME ) 505 { 506 F32 rate = ORBIT_NUDGE_RATE + time * (1 - ORBIT_NUDGE_RATE)/ NUDGE_TIME; 507 //llinfos << rate << llendl; 508 return rate; 509 } 510 else 511 { 512 return 1; 513 } 514} 515 516 517// Only used for drawing 518void LLJoystickCameraRotate::setToggleState( BOOL left, BOOL top, BOOL right, BOOL bottom ) 519{ 520 mInLeft = left; 521 mInTop = top; 522 mInRight = right; 523 mInBottom = bottom; 524} 525 526void LLJoystickCameraRotate::draw() 527{ 528 LLGLSUIDefault gls_ui; 529 530 getImageUnselected()->draw( 0, 0 ); 531 LLPointer<LLUIImage> image = getImageSelected(); 532 533 if( mInTop ) 534 { 535 drawRotatedImage( getImageSelected(), 0 ); 536 } 537 538 if( mInRight ) 539 { 540 drawRotatedImage( getImageSelected(), 1 ); 541 } 542 543 if( mInBottom ) 544 { 545 drawRotatedImage( getImageSelected(), 2 ); 546 } 547 548 if( mInLeft ) 549 { 550 drawRotatedImage( getImageSelected(), 3 ); 551 } 552} 553 554// Draws image rotated by multiples of 90 degrees 555void LLJoystickCameraRotate::drawRotatedImage( LLPointer<LLUIImage> image, S32 rotations ) 556{ 557 S32 width = image->getWidth(); 558 S32 height = image->getHeight(); 559 LLTexture* texture = image->getImage(); 560 561 /* 562 * Scale texture coordinate system 563 * to handle the different between image size and size of texture. 564 * If we will use default matrix, 565 * it may break texture mapping after rotation. 566 * see EXT-2023 Camera floater: arrows became shifted when pressed. 567 */ 568 F32 uv[][2] = 569 { 570 { (F32)width/texture->getWidth(), (F32)height/texture->getHeight() }, 571 { 0.f, (F32)height/texture->getHeight() }, 572 { 0.f, 0.f }, 573 { (F32)width/texture->getWidth(), 0.f } 574 }; 575 576 gGL.getTexUnit(0)->bind(texture); 577 578 gGL.color4fv(UI_VERTEX_COLOR.mV); 579 580 gGL.begin(LLRender::QUADS); 581 { 582 gGL.texCoord2fv( uv[ (rotations + 0) % 4]); 583 gGL.vertex2i(width, height ); 584 585 gGL.texCoord2fv( uv[ (rotations + 1) % 4]); 586 gGL.vertex2i(0, height ); 587 588 gGL.texCoord2fv( uv[ (rotations + 2) % 4]); 589 gGL.vertex2i(0, 0); 590 591 gGL.texCoord2fv( uv[ (rotations + 3) % 4]); 592 gGL.vertex2i(width, 0); 593 } 594 gGL.end(); 595} 596 597 598 599//------------------------------------------------------------------------------- 600// LLJoystickCameraTrack 601//------------------------------------------------------------------------------- 602 603LLJoystickCameraTrack::Params::Params() 604{ 605 held_down_delay.seconds(0.0); 606} 607 608LLJoystickCameraTrack::LLJoystickCameraTrack(const LLJoystickCameraTrack::Params& p) 609: LLJoystickCameraRotate(p) 610{} 611 612 613void LLJoystickCameraTrack::onHeldDown() 614{ 615 updateSlop(); 616 617 S32 dx = mLastMouse.mX - mFirstMouse.mX + mInitialOffset.mX; 618 S32 dy = mLastMouse.mY - mFirstMouse.mY + mInitialOffset.mY; 619 620 if (dx > mVertSlopNear) 621 { 622 gAgentCamera.unlockView(); 623 gAgentCamera.setPanRightKey(getOrbitRate()); 624 } 625 else if (dx < -mVertSlopNear) 626 { 627 gAgentCamera.unlockView(); 628 gAgentCamera.setPanLeftKey(getOrbitRate()); 629 } 630 631 // over/under rotation 632 if (dy > mVertSlopNear) 633 { 634 gAgentCamera.unlockView(); 635 gAgentCamera.setPanUpKey(getOrbitRate()); 636 } 637 else if (dy < -mVertSlopNear) 638 { 639 gAgentCamera.unlockView(); 640 gAgentCamera.setPanDownKey(getOrbitRate()); 641 } 642}