/indra/newview/llworldmapview.cpp
C++ | 1786 lines | 1329 code | 238 blank | 219 comment | 202 complexity | 054584634f1c4a725fc6d3cc520d3f91 MD5 | raw file
Possible License(s): LGPL-2.1
Large files files are truncated, but you can click here to view the full file
1/** 2 * @file llworldmapview.cpp 3 * @brief LLWorldMapView 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 "llworldmapview.h" 30 31#include "indra_constants.h" 32#include "llui.h" 33#include "llmath.h" // clampf() 34#include "llregionhandle.h" 35#include "lleventflags.h" 36#include "llfloaterreg.h" 37#include "llrender.h" 38#include "lltooltip.h" 39 40#include "llagent.h" 41#include "llagentcamera.h" 42#include "llcallingcard.h" 43#include "llcommandhandler.h" 44#include "llviewercontrol.h" 45#include "llfloatermap.h" 46#include "llfloaterworldmap.h" 47#include "llfocusmgr.h" 48#include "lllocalcliprect.h" 49#include "lltextbox.h" 50#include "lltextureview.h" 51#include "lltracker.h" 52#include "llviewercamera.h" 53#include "llviewertexture.h" 54#include "llviewertexturelist.h" 55#include "llviewerregion.h" 56#include "llviewerwindow.h" 57#include "lltrans.h" 58 59#include "llglheaders.h" 60 61// Basically a C++ implementation of the OCEAN_COLOR defined in mapstitcher.py 62// Please ensure consistency between those 2 files (TODO: would be better to get that color from an asset source...) 63// # Constants 64// OCEAN_COLOR = "#1D475F" 65const F32 OCEAN_RED = (F32)(0x1D)/255.f; 66const F32 OCEAN_GREEN = (F32)(0x47)/255.f; 67const F32 OCEAN_BLUE = (F32)(0x5F)/255.f; 68 69const F32 GODLY_TELEPORT_HEIGHT = 200.f; 70const S32 SCROLL_HINT_WIDTH = 65; 71const F32 BIG_DOT_RADIUS = 5.f; 72BOOL LLWorldMapView::sHandledLastClick = FALSE; 73 74LLUIImagePtr LLWorldMapView::sAvatarSmallImage = NULL; 75LLUIImagePtr LLWorldMapView::sAvatarYouImage = NULL; 76LLUIImagePtr LLWorldMapView::sAvatarYouLargeImage = NULL; 77LLUIImagePtr LLWorldMapView::sAvatarLevelImage = NULL; 78LLUIImagePtr LLWorldMapView::sAvatarAboveImage = NULL; 79LLUIImagePtr LLWorldMapView::sAvatarBelowImage = NULL; 80 81LLUIImagePtr LLWorldMapView::sTelehubImage = NULL; 82LLUIImagePtr LLWorldMapView::sInfohubImage = NULL; 83LLUIImagePtr LLWorldMapView::sHomeImage = NULL; 84LLUIImagePtr LLWorldMapView::sEventImage = NULL; 85LLUIImagePtr LLWorldMapView::sEventMatureImage = NULL; 86LLUIImagePtr LLWorldMapView::sEventAdultImage = NULL; 87 88LLUIImagePtr LLWorldMapView::sTrackCircleImage = NULL; 89LLUIImagePtr LLWorldMapView::sTrackArrowImage = NULL; 90 91LLUIImagePtr LLWorldMapView::sClassifiedsImage = NULL; 92LLUIImagePtr LLWorldMapView::sForSaleImage = NULL; 93LLUIImagePtr LLWorldMapView::sForSaleAdultImage = NULL; 94 95F32 LLWorldMapView::sPanX = 0.f; 96F32 LLWorldMapView::sPanY = 0.f; 97F32 LLWorldMapView::sTargetPanX = 0.f; 98F32 LLWorldMapView::sTargetPanY = 0.f; 99S32 LLWorldMapView::sTrackingArrowX = 0; 100S32 LLWorldMapView::sTrackingArrowY = 0; 101bool LLWorldMapView::sVisibleTilesLoaded = false; 102F32 LLWorldMapView::sMapScale = 128.f; 103 104std::map<std::string,std::string> LLWorldMapView::sStringsMap; 105 106// Fetch and draw info thresholds 107const F32 DRAW_TEXT_THRESHOLD = 96.f; // Don't draw text under that resolution value (res = width region in meters) 108const S32 DRAW_SIMINFO_THRESHOLD = 3; // Max level for which we load or display sim level information (level in LLWorldMipmap sense) 109const S32 DRAW_LANDFORSALE_THRESHOLD = 2; // Max level for which we load or display land for sale picture data (level in LLWorldMipmap sense) 110 111// When on, draw an outline for each mipmap tile gotten from S3 112#define DEBUG_DRAW_TILE 0 113 114 115void LLWorldMapView::initClass() 116{ 117 sAvatarSmallImage = LLUI::getUIImage("map_avatar_8.tga"); 118 sAvatarYouImage = LLUI::getUIImage("map_avatar_16.tga"); 119 sAvatarYouLargeImage = LLUI::getUIImage("map_avatar_you_32.tga"); 120 sAvatarLevelImage = LLUI::getUIImage("map_avatar_32.tga"); 121 sAvatarAboveImage = LLUI::getUIImage("map_avatar_above_32.tga"); 122 sAvatarBelowImage = LLUI::getUIImage("map_avatar_below_32.tga"); 123 124 sHomeImage = LLUI::getUIImage("map_home.tga"); 125 sTelehubImage = LLUI::getUIImage("map_telehub.tga"); 126 sInfohubImage = LLUI::getUIImage("map_infohub.tga"); 127 sEventImage = LLUI::getUIImage("Parcel_PG_Dark"); 128 sEventMatureImage = LLUI::getUIImage("Parcel_M_Dark"); 129 // To Do: update the image resource for adult events. 130 sEventAdultImage = LLUI::getUIImage("Parcel_R_Dark"); 131 132 sTrackCircleImage = LLUI::getUIImage("map_track_16.tga"); 133 sTrackArrowImage = LLUI::getUIImage("direction_arrow.tga"); 134 sClassifiedsImage = LLUI::getUIImage("icon_top_pick.tga"); 135 sForSaleImage = LLUI::getUIImage("icon_for_sale.tga"); 136 // To Do: update the image resource for adult lands on sale. 137 sForSaleAdultImage = LLUI::getUIImage("icon_for_sale_adult.tga"); 138 139 sStringsMap["loading"] = LLTrans::getString("texture_loading"); 140 sStringsMap["offline"] = LLTrans::getString("worldmap_offline"); 141} 142 143// static 144void LLWorldMapView::cleanupClass() 145{ 146 sAvatarSmallImage = NULL; 147 sAvatarYouImage = NULL; 148 sAvatarYouLargeImage = NULL; 149 sAvatarLevelImage = NULL; 150 sAvatarAboveImage = NULL; 151 sAvatarBelowImage = NULL; 152 153 sTelehubImage = NULL; 154 sInfohubImage = NULL; 155 sHomeImage = NULL; 156 sEventImage = NULL; 157 sEventMatureImage = NULL; 158 sEventAdultImage = NULL; 159 160 sTrackCircleImage = NULL; 161 sTrackArrowImage = NULL; 162 sClassifiedsImage = NULL; 163 sForSaleImage = NULL; 164 sForSaleAdultImage = NULL; 165} 166 167LLWorldMapView::LLWorldMapView() 168: LLPanel(), 169 mBackgroundColor( LLColor4( OCEAN_RED, OCEAN_GREEN, OCEAN_BLUE, 1.f ) ), 170 mItemPicked(FALSE), 171 mPanning( FALSE ), 172 mMouseDownPanX( 0 ), 173 mMouseDownPanY( 0 ), 174 mMouseDownX( 0 ), 175 mMouseDownY( 0 ), 176 mSelectIDStart(0) 177{ 178 //LL_INFOS("World Map") << "Creating the Map -> LLWorldMapView::LLWorldMapView()" << LL_ENDL; 179 180 clearLastClick(); 181} 182 183BOOL LLWorldMapView::postBuild() 184{ 185 mTextBoxNorth = getChild<LLTextBox> ("floater_map_north"); 186 mTextBoxEast = getChild<LLTextBox> ("floater_map_east"); 187 mTextBoxWest = getChild<LLTextBox> ("floater_map_west"); 188 mTextBoxSouth = getChild<LLTextBox> ("floater_map_south"); 189 mTextBoxSouthEast = getChild<LLTextBox> ("floater_map_southeast"); 190 mTextBoxNorthEast = getChild<LLTextBox> ("floater_map_northeast"); 191 mTextBoxSouthWest = getChild<LLTextBox> ("floater_map_southwest"); 192 mTextBoxNorthWest = getChild<LLTextBox> ("floater_map_northwest"); 193 194 mTextBoxNorth->setText(getString("world_map_north")); 195 mTextBoxEast->setText(getString ("world_map_east")); 196 mTextBoxWest->setText(getString("world_map_west")); 197 mTextBoxSouth->setText(getString ("world_map_south")); 198 mTextBoxSouthEast ->setText(getString ("world_map_southeast")); 199 mTextBoxNorthEast ->setText(getString ("world_map_northeast")); 200 mTextBoxSouthWest->setText(getString ("world_map_southwest")); 201 mTextBoxNorthWest ->setText(getString("world_map_northwest")); 202 203 mTextBoxNorth->reshapeToFitText(); 204 mTextBoxEast->reshapeToFitText(); 205 mTextBoxWest->reshapeToFitText(); 206 mTextBoxSouth->reshapeToFitText(); 207 mTextBoxSouthEast ->reshapeToFitText(); 208 mTextBoxNorthEast ->reshapeToFitText(); 209 mTextBoxSouthWest->reshapeToFitText(); 210 mTextBoxNorthWest ->reshapeToFitText(); 211 212 return true; 213} 214 215 216LLWorldMapView::~LLWorldMapView() 217{ 218 //LL_INFOS("World Map") << "Destroying the map -> LLWorldMapView::~LLWorldMapView()" << LL_ENDL; 219 cleanupTextures(); 220} 221 222 223// static 224void LLWorldMapView::cleanupTextures() 225{ 226} 227 228 229// static 230void LLWorldMapView::setScale( F32 scale ) 231{ 232 if (scale != sMapScale) 233 { 234 F32 old_scale = sMapScale; 235 236 sMapScale = scale; 237 if (sMapScale <= 0.f) 238 { 239 sMapScale = 0.1f; 240 } 241 242 F32 ratio = (scale / old_scale); 243 sPanX *= ratio; 244 sPanY *= ratio; 245 sTargetPanX = sPanX; 246 sTargetPanY = sPanY; 247 sVisibleTilesLoaded = false; 248 } 249} 250 251 252// static 253void LLWorldMapView::translatePan( S32 delta_x, S32 delta_y ) 254{ 255 sPanX += delta_x; 256 sPanY += delta_y; 257 sTargetPanX = sPanX; 258 sTargetPanY = sPanY; 259 sVisibleTilesLoaded = false; 260} 261 262 263// static 264void LLWorldMapView::setPan( S32 x, S32 y, BOOL snap ) 265{ 266 sTargetPanX = (F32)x; 267 sTargetPanY = (F32)y; 268 if (snap) 269 { 270 sPanX = sTargetPanX; 271 sPanY = sTargetPanY; 272 } 273 sVisibleTilesLoaded = false; 274} 275 276bool LLWorldMapView::showRegionInfo() 277{ 278 return (LLWorldMipmap::scaleToLevel(sMapScale) <= DRAW_SIMINFO_THRESHOLD ? true : false); 279} 280 281/////////////////////////////////////////////////////////////////////////////////// 282// HELPERS 283 284BOOL is_agent_in_region(LLViewerRegion* region, LLSimInfo* info) 285{ 286 return (region && info && info->isName(region->getName())); 287} 288 289/////////////////////////////////////////////////////////////////////////////////// 290 291void LLWorldMapView::draw() 292{ 293 static LLUIColor map_track_color = LLUIColorTable::instance().getColor("MapTrackColor", LLColor4::white); 294 295 LLTextureView::clearDebugImages(); 296 297 F64 current_time = LLTimer::getElapsedSeconds(); 298 299 mVisibleRegions.clear(); 300 301 // animate pan if necessary 302 sPanX = lerp(sPanX, sTargetPanX, LLCriticalDamp::getInterpolant(0.1f)); 303 sPanY = lerp(sPanY, sTargetPanY, LLCriticalDamp::getInterpolant(0.1f)); 304 305 const S32 width = getRect().getWidth(); 306 const S32 height = getRect().getHeight(); 307 const F32 half_width = F32(width) / 2.0f; 308 const F32 half_height = F32(height) / 2.0f; 309 LLVector3d camera_global = gAgentCamera.getCameraPositionGlobal(); 310 311 S32 level = LLWorldMipmap::scaleToLevel(sMapScale); 312 313 LLLocalClipRect clip(getLocalRect()); 314 { 315 gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); 316 317 gGL.matrixMode(LLRender::MM_MODELVIEW); 318 319 // Clear the background alpha to 0 320 gGL.flush(); 321 gGL.setColorMask(false, true); 322 gGL.setAlphaRejectSettings(LLRender::CF_GREATER_EQUAL, 0.f); 323 gGL.setSceneBlendType(LLRender::BT_REPLACE); 324 gGL.color4f(0.0f, 0.0f, 0.0f, 0.0f); 325 gl_rect_2d(0, height, width, 0); 326 } 327 328 gGL.flush(); 329 330 gGL.setAlphaRejectSettings(LLRender::CF_DEFAULT); 331 gGL.setColorMask(true, true); 332 333 // Draw the image tiles 334 drawMipmap(width, height); 335 gGL.flush(); 336 337 gGL.setAlphaRejectSettings(LLRender::CF_DEFAULT); 338 gGL.setColorMask(true, true); 339 340 // Draw per sim overlayed information (names, mature, offline...) 341 for (LLWorldMap::sim_info_map_t::const_iterator it = LLWorldMap::getInstance()->getRegionMap().begin(); 342 it != LLWorldMap::getInstance()->getRegionMap().end(); ++it) 343 { 344 U64 handle = it->first; 345 LLSimInfo* info = it->second; 346 347 LLVector3d origin_global = from_region_handle(handle); 348 349 // Find x and y position relative to camera's center. 350 LLVector3d rel_region_pos = origin_global - camera_global; 351 F32 relative_x = (rel_region_pos.mdV[0] / REGION_WIDTH_METERS) * sMapScale; 352 F32 relative_y = (rel_region_pos.mdV[1] / REGION_WIDTH_METERS) * sMapScale; 353 354 // Coordinates of the sim in pixels in the UI panel 355 // When the view isn't panned, 0,0 = center of rectangle 356 F32 bottom = sPanY + half_height + relative_y; 357 F32 left = sPanX + half_width + relative_x; 358 F32 top = bottom + sMapScale ; 359 F32 right = left + sMapScale ; 360 361 // Discard if region is outside the screen rectangle (not visible on screen) 362 if ((top < 0.f) || (bottom > height) || 363 (right < 0.f) || (left > width) ) 364 { 365 // Drop the "land for sale" fetching priority since it's outside the view rectangle 366 info->dropImagePriority(); 367 continue; 368 } 369 370 // This list is used by other methods to know which regions are indeed displayed on screen 371 372 mVisibleRegions.push_back(handle); 373 374 // Update the agent count for that region if we're not too zoomed out already 375 if (level <= DRAW_SIMINFO_THRESHOLD) 376 { 377 info->updateAgentCount(current_time); 378 } 379 380 if (info->isDown()) 381 { 382 // Draw a transparent red square over down sims 383 gGL.blendFunc(LLRender::BF_DEST_ALPHA, LLRender::BF_SOURCE_ALPHA); 384 gGL.color4f(0.2f, 0.0f, 0.0f, 0.4f); 385 386 gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); 387 gGL.begin(LLRender::QUADS); 388 gGL.vertex2f(left, top); 389 gGL.vertex2f(left, bottom); 390 gGL.vertex2f(right, bottom); 391 gGL.vertex2f(right, top); 392 gGL.end(); 393 } 394 // As part of the AO project, we no longer want to draw access indicators; 395 // it's too complicated to get all the rules straight and will only 396 // cause confusion. 397 /********************** 398 else if (!info->isPG() && gAgent.isTeen()) 399 { 400 // If this is a mature region, and you are not, draw a line across it 401 gGL.blendFunc(LLRender::BF_DEST_ALPHA, LLRender::BF_ZERO); 402 403 gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); 404 gGL.color3f(1.f, 0.f, 0.f); 405 gGL.begin(LLRender::LINES); 406 gGL.vertex2f(left, top); 407 gGL.vertex2f(right, bottom); 408 gGL.vertex2f(left, bottom); 409 gGL.vertex2f(right, top); 410 gGL.end(); 411 } 412 **********************/ 413 else if (gSavedSettings.getBOOL("MapShowLandForSale") && (level <= DRAW_LANDFORSALE_THRESHOLD)) 414 { 415 // Draw the overlay image "Land for Sale / Land for Auction" 416 LLViewerFetchedTexture* overlayimage = info->getLandForSaleImage(); 417 if (overlayimage) 418 { 419 // Inform the fetch mechanism of the size we need 420 S32 draw_size = llround(sMapScale); 421 overlayimage->setKnownDrawSize(llround(draw_size * LLUI::sGLScaleFactor.mV[VX]), llround(draw_size * LLUI::sGLScaleFactor.mV[VY])); 422 // Draw something whenever we have enough info 423 if (overlayimage->hasGLTexture()) 424 { 425 gGL.blendFunc(LLRender::BF_SOURCE_ALPHA, LLRender::BF_ONE_MINUS_SOURCE_ALPHA); 426 gGL.getTexUnit(0)->bind(overlayimage); 427 gGL.color4f(1.f, 1.f, 1.f, 1.f); 428 gGL.begin(LLRender::QUADS); 429 gGL.texCoord2f(0.f, 1.f); 430 gGL.vertex3f(left, top, -0.5f); 431 gGL.texCoord2f(0.f, 0.f); 432 gGL.vertex3f(left, bottom, -0.5f); 433 gGL.texCoord2f(1.f, 0.f); 434 gGL.vertex3f(right, bottom, -0.5f); 435 gGL.texCoord2f(1.f, 1.f); 436 gGL.vertex3f(right, top, -0.5f); 437 gGL.end(); 438 } 439 } 440 } 441 else 442 { 443 // If we're not displaying the "land for sale", drop its fetching priority 444 info->dropImagePriority(); 445 } 446 447 // Draw the region name in the lower left corner 448 if (sMapScale >= DRAW_TEXT_THRESHOLD) 449 { 450 LLFontGL* font = LLFontGL::getFont(LLFontDescriptor("SansSerif", "Small", LLFontGL::BOLD)); 451 std::string mesg; 452 if (info->isDown()) 453 { 454 mesg = llformat( "%s (%s)", info->getName().c_str(), sStringsMap["offline"].c_str()); 455 } 456 else 457 { 458 mesg = info->getName(); 459 } 460 if (!mesg.empty()) 461 { 462 font->renderUTF8( 463 mesg, 0, 464 llfloor(left + 3), llfloor(bottom + 2), 465 LLColor4::white, 466 LLFontGL::LEFT, LLFontGL::BASELINE, LLFontGL::NORMAL, LLFontGL::DROP_SHADOW); 467 } 468 } 469 } 470 471 472 473 // Draw background rectangle 474 LLGLSUIDefault gls_ui; 475 { 476 gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); 477 gGL.setAlphaRejectSettings(LLRender::CF_GREATER_EQUAL, 0.f); 478 gGL.blendFunc(LLRender::BF_ONE_MINUS_DEST_ALPHA, LLRender::BF_DEST_ALPHA); 479 gGL.color4fv( mBackgroundColor.mV ); 480 gl_rect_2d(0, height, width, 0); 481 } 482 483 gGL.setAlphaRejectSettings(LLRender::CF_DEFAULT); 484 gGL.setSceneBlendType(LLRender::BT_ALPHA); 485 486 // Draw item infos if we're not zoomed out too much and there's something to draw 487 if ((level <= DRAW_SIMINFO_THRESHOLD) && (gSavedSettings.getBOOL("MapShowInfohubs") || 488 gSavedSettings.getBOOL("MapShowTelehubs") || 489 gSavedSettings.getBOOL("MapShowLandForSale") || 490 gSavedSettings.getBOOL("MapShowEvents") || 491 gSavedSettings.getBOOL("ShowMatureEvents") || 492 gSavedSettings.getBOOL("ShowAdultEvents"))) 493 { 494 drawItems(); 495 } 496 497 // Draw the Home location (always) 498 LLVector3d home; 499 if (gAgent.getHomePosGlobal(&home)) 500 { 501 drawImage(home, sHomeImage); 502 } 503 504 // Draw the current agent after all that other stuff. 505 LLVector3d pos_global = gAgent.getPositionGlobal(); 506 drawImage(pos_global, sAvatarYouImage); 507 508 LLVector3 pos_map = globalPosToView(pos_global); 509 if (!pointInView(llround(pos_map.mV[VX]), llround(pos_map.mV[VY]))) 510 { 511 drawTracking(pos_global, 512 lerp(LLColor4::yellow, LLColor4::orange, 0.4f), 513 TRUE, 514 "You are here", 515 "", 516 llround(LLFontGL::getFontSansSerifSmall()->getLineHeight())); // offset vertically by one line, to avoid overlap with target tracking 517 } 518 519 // Draw the current agent viewing angle 520 drawFrustum(); 521 522 // Draw icons for the avatars in each region. 523 // Drawn this after the current agent avatar so one can see nearby people 524 if (gSavedSettings.getBOOL("MapShowPeople") && (level <= DRAW_SIMINFO_THRESHOLD)) 525 { 526 drawAgents(); 527 } 528 529 // Always draw tracking information 530 LLTracker::ETrackingStatus tracking_status = LLTracker::getTrackingStatus(); 531 if ( LLTracker::TRACKING_AVATAR == tracking_status ) 532 { 533 drawTracking( LLAvatarTracker::instance().getGlobalPos(), map_track_color, TRUE, LLTracker::getLabel(), "" ); 534 } 535 else if ( LLTracker::TRACKING_LANDMARK == tracking_status 536 || LLTracker::TRACKING_LOCATION == tracking_status ) 537 { 538 // While fetching landmarks, will have 0,0,0 location for a while, 539 // so don't draw. JC 540 LLVector3d pos_global = LLTracker::getTrackedPositionGlobal(); 541 if (!pos_global.isExactlyZero()) 542 { 543 drawTracking( pos_global, map_track_color, TRUE, LLTracker::getLabel(), LLTracker::getToolTip() ); 544 } 545 } 546 else if (LLWorldMap::getInstance()->isTracking()) 547 { 548 if (LLWorldMap::getInstance()->isTrackingInvalidLocation()) 549 { 550 // We know this location to be invalid, draw a blue circle 551 LLColor4 loading_color(0.0, 0.5, 1.0, 1.0); 552 drawTracking( LLWorldMap::getInstance()->getTrackedPositionGlobal(), loading_color, TRUE, getString("InvalidLocation"), ""); 553 } 554 else 555 { 556 // We don't know yet what that location is, draw a throbing blue circle 557 double value = fmod(current_time, 2); 558 value = 0.5 + 0.5*cos(value * F_PI); 559 LLColor4 loading_color(0.0, F32(value/2), F32(value), 1.0); 560 drawTracking( LLWorldMap::getInstance()->getTrackedPositionGlobal(), loading_color, TRUE, getString("Loading"), ""); 561 } 562 } 563 564 565 // turn off the scissor 566 LLGLDisable no_scissor(GL_SCISSOR_TEST); 567 568 updateDirections(); 569 570 LLView::draw(); 571 572 // Get sim info for all sims in view 573 updateVisibleBlocks(); 574} // end draw() 575 576 577//virtual 578void LLWorldMapView::setVisible(BOOL visible) 579{ 580 LLPanel::setVisible(visible); 581 if (!visible) 582 { 583 // Drop the download of tiles and images priority to nil if we hide the map 584 LLWorldMap::getInstance()->dropImagePriorities(); 585 } 586} 587 588void LLWorldMapView::drawMipmap(S32 width, S32 height) 589{ 590 // Compute the level of the mipmap to use for the current scale level 591 S32 level = LLWorldMipmap::scaleToLevel(sMapScale); 592 // Set the tile boost level so that unused tiles get to 0 593 LLWorldMap::getInstance()->equalizeBoostLevels(); 594 595 // Render whatever we already have loaded if we haven't the current level 596 // complete and use it as a background (scaled up or scaled down) 597 if (!sVisibleTilesLoaded) 598 { 599 // Note: the (load = false) parameter avoids missing tiles to be fetched (i.e. we render what we have, no more) 600 // Check all the lower res levels and render them in reverse order (worse to best) 601 // We need to traverse all the levels as the user can zoom in very fast 602 for (S32 l = LLWorldMipmap::MAP_LEVELS; l > level; l--) 603 { 604 drawMipmapLevel(width, height, l, false); 605 } 606 // Skip the current level, as we'll do it anyway here under... 607 608 // Just go one level down in res as it can really get too much stuff 609 // when zooming out and too small to see anyway... 610 if (level > 1) 611 { 612 drawMipmapLevel(width, height, level - 1, false); 613 } 614 } 615 else 616 { 617 //LL_INFOS("World Map") << "Render complete, don't draw background..." << LL_ENDL; 618 } 619 620 // Render the current level 621 sVisibleTilesLoaded = drawMipmapLevel(width, height, level); 622 623 return; 624} 625 626// Return true if all the tiles required to render that level have been fetched or are truly missing 627bool LLWorldMapView::drawMipmapLevel(S32 width, S32 height, S32 level, bool load) 628{ 629 // Check input level 630 llassert (level > 0); 631 if (level <= 0) 632 return false; 633 634 // Count tiles hit and completed 635 S32 completed_tiles = 0; 636 S32 total_tiles = 0; 637 638 // Size in meters (global) of each tile of that level 639 S32 tile_width = LLWorldMipmap::MAP_TILE_SIZE * (1 << (level - 1)); 640 // Dimension of the screen in meter at that scale 641 LLVector3d pos_SW = viewPosToGlobal(0, 0); 642 LLVector3d pos_NE = viewPosToGlobal(width, height); 643 // Add external band of tiles on the outskirt so to hit the partially displayed tiles right and top 644 pos_NE[VX] += tile_width; 645 pos_NE[VY] += tile_width; 646 647 // Iterate through the tiles on screen: we just need to ask for one tile every tile_width meters 648 U32 grid_x, grid_y; 649 for (F64 index_y = pos_SW[VY]; index_y < pos_NE[VY]; index_y += tile_width) 650 { 651 for (F64 index_x = pos_SW[VX]; index_x < pos_NE[VX]; index_x += tile_width) 652 { 653 // Compute the world coordinates of the current point 654 LLVector3d pos_global(index_x, index_y, pos_SW[VZ]); 655 // Convert to the mipmap level coordinates for that point (i.e. which tile to we hit) 656 LLWorldMipmap::globalToMipmap(pos_global[VX], pos_global[VY], level, &grid_x, &grid_y); 657 // Get the tile. Note: NULL means that the image does not exist (so it's considered "complete" as far as fetching is concerned) 658 LLPointer<LLViewerFetchedTexture> simimage = LLWorldMap::getInstance()->getObjectsTile(grid_x, grid_y, level, load); 659 if (simimage) 660 { 661 // Checks that the image has a valid texture 662 if (simimage->hasGLTexture()) 663 { 664 // Increment the number of completly fetched tiles 665 completed_tiles++; 666 667 // Convert those coordinates (SW corner of the mipmap tile) into world (meters) coordinates 668 pos_global[VX] = grid_x * REGION_WIDTH_METERS; 669 pos_global[VY] = grid_y * REGION_WIDTH_METERS; 670 // Now to screen coordinates for SW corner of that tile 671 LLVector3 pos_screen = globalPosToView (pos_global); 672 F32 left = pos_screen[VX]; 673 F32 bottom = pos_screen[VY]; 674 // Compute the NE corner coordinates of the tile now 675 pos_global[VX] += tile_width; 676 pos_global[VY] += tile_width; 677 pos_screen = globalPosToView (pos_global); 678 F32 right = pos_screen[VX]; 679 F32 top = pos_screen[VY]; 680 681 // Draw the tile 682 LLGLSUIDefault gls_ui; 683 gGL.getTexUnit(0)->bind(simimage.get()); 684 simimage->setAddressMode(LLTexUnit::TAM_CLAMP); 685 686 gGL.setSceneBlendType(LLRender::BT_ALPHA); 687 gGL.color4f(1.f, 1.0f, 1.0f, 1.0f); 688 689 gGL.begin(LLRender::QUADS); 690 gGL.texCoord2f(0.f, 1.f); 691 gGL.vertex3f(left, top, 0.f); 692 gGL.texCoord2f(0.f, 0.f); 693 gGL.vertex3f(left, bottom, 0.f); 694 gGL.texCoord2f(1.f, 0.f); 695 gGL.vertex3f(right, bottom, 0.f); 696 gGL.texCoord2f(1.f, 1.f); 697 gGL.vertex3f(right, top, 0.f); 698 gGL.end(); 699#if DEBUG_DRAW_TILE 700 drawTileOutline(level, top, left, bottom, right); 701#endif // DEBUG_DRAW_TILE 702 } 703 //else 704 //{ 705 // Waiting for a tile -> the level is not complete 706 // LL_INFOS("World Map") << "Unfetched tile. level = " << level << LL_ENDL; 707 //} 708 } 709 else 710 { 711 // Unexistent tiles are counted as "completed" 712 completed_tiles++; 713 } 714 // Increment the number of tiles in that level / screen 715 total_tiles++; 716 } 717 } 718 return (completed_tiles == total_tiles); 719} 720 721// Draw lines (rectangle outline and cross) to visualize the position of the tile 722// Used for debug only 723void LLWorldMapView::drawTileOutline(S32 level, F32 top, F32 left, F32 bottom, F32 right) 724{ 725 gGL.blendFunc(LLRender::BF_DEST_ALPHA, LLRender::BF_ZERO); 726 727 gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); 728 if (level == 1) 729 gGL.color3f(1.f, 0.f, 0.f); // red 730 else if (level == 2) 731 gGL.color3f(0.f, 1.f, 0.f); // green 732 else if (level == 3) 733 gGL.color3f(0.f, 0.f, 1.f); // blue 734 else if (level == 4) 735 gGL.color3f(1.f, 1.f, 0.f); // yellow 736 else if (level == 5) 737 gGL.color3f(1.f, 0.f, 1.f); // magenta 738 else if (level == 6) 739 gGL.color3f(0.f, 1.f, 1.f); // cyan 740 else if (level == 7) 741 gGL.color3f(1.f, 1.f, 1.f); // white 742 else 743 gGL.color3f(0.f, 0.f, 0.f); // black 744 gGL.begin(LLRender::LINE_STRIP); 745 gGL.vertex2f(left, top); 746 gGL.vertex2f(right, bottom); 747 gGL.vertex2f(left, bottom); 748 gGL.vertex2f(right, top); 749 gGL.vertex2f(left, top); 750 gGL.vertex2f(left, bottom); 751 gGL.vertex2f(right, bottom); 752 gGL.vertex2f(right, top); 753 gGL.end(); 754} 755 756void LLWorldMapView::drawGenericItems(const LLSimInfo::item_info_list_t& items, LLUIImagePtr image) 757{ 758 LLSimInfo::item_info_list_t::const_iterator e; 759 for (e = items.begin(); e != items.end(); ++e) 760 { 761 drawGenericItem(*e, image); 762 } 763} 764 765void LLWorldMapView::drawGenericItem(const LLItemInfo& item, LLUIImagePtr image) 766{ 767 drawImage(item.getGlobalPosition(), image); 768} 769 770 771void LLWorldMapView::drawImage(const LLVector3d& global_pos, LLUIImagePtr image, const LLColor4& color) 772{ 773 LLVector3 pos_map = globalPosToView( global_pos ); 774 image->draw(llround(pos_map.mV[VX] - image->getWidth() /2.f), 775 llround(pos_map.mV[VY] - image->getHeight()/2.f), 776 color); 777} 778 779void LLWorldMapView::drawImageStack(const LLVector3d& global_pos, LLUIImagePtr image, U32 count, F32 offset, const LLColor4& color) 780{ 781 LLVector3 pos_map = globalPosToView( global_pos ); 782 for(U32 i=0; i<count; i++) 783 { 784 image->draw(llround(pos_map.mV[VX] - image->getWidth() /2.f), 785 llround(pos_map.mV[VY] - image->getHeight()/2.f + i*offset), 786 color); 787 } 788} 789 790void LLWorldMapView::drawItems() 791{ 792 bool mature_enabled = gAgent.canAccessMature(); 793 bool adult_enabled = gAgent.canAccessAdult(); 794 795 BOOL show_mature = mature_enabled && gSavedSettings.getBOOL("ShowMatureEvents"); 796 BOOL show_adult = adult_enabled && gSavedSettings.getBOOL("ShowAdultEvents"); 797 798 for (handle_list_t::iterator iter = mVisibleRegions.begin(); iter != mVisibleRegions.end(); ++iter) 799 { 800 U64 handle = *iter; 801 LLSimInfo* info = LLWorldMap::getInstance()->simInfoFromHandle(handle); 802 if ((info == NULL) || (info->isDown())) 803 { 804 continue; 805 } 806 // Infohubs 807 if (gSavedSettings.getBOOL("MapShowInfohubs")) 808 { 809 drawGenericItems(info->getInfoHub(), sInfohubImage); 810 } 811 // Telehubs 812 if (gSavedSettings.getBOOL("MapShowTelehubs")) 813 { 814 drawGenericItems(info->getTeleHub(), sTelehubImage); 815 } 816 // Land for sale 817 if (gSavedSettings.getBOOL("MapShowLandForSale")) 818 { 819 drawGenericItems(info->getLandForSale(), sForSaleImage); 820 // for 1.23, we're showing normal land and adult land in the same UI; you don't 821 // get a choice about which ones you want. If you're currently asking for adult 822 // content and land you'll get the adult land. 823 if (gAgent.canAccessAdult()) 824 { 825 drawGenericItems(info->getLandForSaleAdult(), sForSaleAdultImage); 826 } 827 } 828 // PG Events 829 if (gSavedSettings.getBOOL("MapShowEvents")) 830 { 831 drawGenericItems(info->getPGEvent(), sEventImage); 832 } 833 // Mature Events 834 if (show_mature) 835 { 836 drawGenericItems(info->getMatureEvent(), sEventMatureImage); 837 } 838 // Adult Events 839 if (show_adult) 840 { 841 drawGenericItems(info->getAdultEvent(), sEventAdultImage); 842 } 843 } 844} 845 846void LLWorldMapView::drawAgents() 847{ 848 static LLUIColor map_avatar_color = LLUIColorTable::instance().getColor("MapAvatarColor", LLColor4::white); 849 850 for (handle_list_t::iterator iter = mVisibleRegions.begin(); iter != mVisibleRegions.end(); ++iter) 851 { 852 U64 handle = *iter; 853 LLSimInfo* siminfo = LLWorldMap::getInstance()->simInfoFromHandle(handle); 854 if ((siminfo == NULL) || (siminfo->isDown())) 855 { 856 continue; 857 } 858 LLSimInfo::item_info_list_t::const_iterator it = siminfo->getAgentLocation().begin(); 859 while (it != siminfo->getAgentLocation().end()) 860 { 861 // Show Individual agents (or little stacks where real agents are) 862 863 // Here's how we'd choose the color if info.mID were available but it's not being sent: 864 // LLColor4 color = (agent_count == 1 && is_agent_friend(info.mID)) ? friend_color : avatar_color; 865 drawImageStack(it->getGlobalPosition(), sAvatarSmallImage, it->getCount(), 3.f, map_avatar_color); 866 ++it; 867 } 868 } 869} 870 871void LLWorldMapView::drawFrustum() 872{ 873 // Draw frustum 874 F32 meters_to_pixels = sMapScale/ REGION_WIDTH_METERS; 875 876 F32 horiz_fov = LLViewerCamera::getInstance()->getView() * LLViewerCamera::getInstance()->getAspect(); 877 F32 far_clip_meters = LLViewerCamera::getInstance()->getFar(); 878 F32 far_clip_pixels = far_clip_meters * meters_to_pixels; 879 880 F32 half_width_meters = far_clip_meters * tan( horiz_fov / 2 ); 881 F32 half_width_pixels = half_width_meters * meters_to_pixels; 882 883 // Compute the frustum coordinates. Take the UI scale into account. 884 F32 ui_scale_factor = gSavedSettings.getF32("UIScaleFactor"); 885 F32 ctr_x = (getLocalRect().getWidth() * 0.5f + sPanX) * ui_scale_factor; 886 F32 ctr_y = (getLocalRect().getHeight() * 0.5f + sPanY) * ui_scale_factor; 887 888 gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); 889 890 // Since we don't rotate the map, we have to rotate the frustum. 891 gGL.pushMatrix(); 892 { 893 gGL.translatef( ctr_x, ctr_y, 0 ); 894 895 // Draw triangle with more alpha in far pixels to make it 896 // fade out in distance. 897 gGL.begin( LLRender::TRIANGLES ); 898 { 899 // get camera look at and left axes 900 LLVector3 at_axis = LLViewerCamera::instance().getAtAxis(); 901 LLVector3 left_axis = LLViewerCamera::instance().getLeftAxis(); 902 903 // grab components along XY plane 904 LLVector2 cam_lookat(at_axis.mV[VX], at_axis.mV[VY]); 905 LLVector2 cam_left(left_axis.mV[VX], left_axis.mV[VY]); 906 907 // but, when looking near straight up or down... 908 if (is_approx_zero(cam_lookat.magVecSquared())) 909 { 910 //...just fall back to looking down the x axis 911 cam_lookat = LLVector2(1.f, 0.f); // x axis 912 cam_left = LLVector2(0.f, 1.f); // y axis 913 } 914 915 // normalize to unit length 916 cam_lookat.normVec(); 917 cam_left.normVec(); 918 919 gGL.color4f(1.f, 1.f, 1.f, 0.25f); 920 gGL.vertex2f( 0, 0 ); 921 922 gGL.color4f(1.f, 1.f, 1.f, 0.02f); 923 924 // use 2d camera vectors to render frustum triangle 925 LLVector2 vert = cam_lookat * far_clip_pixels + cam_left * half_width_pixels; 926 gGL.vertex2f(vert.mV[VX], vert.mV[VY]); 927 928 vert = cam_lookat * far_clip_pixels - cam_left * half_width_pixels; 929 gGL.vertex2f(vert.mV[VX], vert.mV[VY]); 930 } 931 gGL.end(); 932 } 933 gGL.popMatrix(); 934} 935 936 937LLVector3 LLWorldMapView::globalPosToView( const LLVector3d& global_pos ) 938{ 939 LLVector3d relative_pos_global = global_pos - gAgentCamera.getCameraPositionGlobal(); 940 LLVector3 pos_local; 941 pos_local.setVec(relative_pos_global); // convert to floats from doubles 942 943 pos_local.mV[VX] *= sMapScale / REGION_WIDTH_METERS; 944 pos_local.mV[VY] *= sMapScale / REGION_WIDTH_METERS; 945 // leave Z component in meters 946 947 948 pos_local.mV[VX] += getRect().getWidth() / 2 + sPanX; 949 pos_local.mV[VY] += getRect().getHeight() / 2 + sPanY; 950 951 return pos_local; 952} 953 954 955void LLWorldMapView::drawTracking(const LLVector3d& pos_global, const LLColor4& color, BOOL draw_arrow, 956 const std::string& label, const std::string& tooltip, S32 vert_offset ) 957{ 958 LLVector3 pos_local = globalPosToView( pos_global ); 959 S32 x = llround( pos_local.mV[VX] ); 960 S32 y = llround( pos_local.mV[VY] ); 961 LLFontGL* font = LLFontGL::getFontSansSerifSmall(); 962 S32 text_x = x; 963 S32 text_y = (S32)(y - sTrackCircleImage->getHeight()/2 - font->getLineHeight()); 964 965 BOOL is_in_window = true; 966 967 if( x < 0 968 || y < 0 969 || x >= getRect().getWidth() 970 || y >= getRect().getHeight() ) 971 { 972 if (draw_arrow) 973 { 974 drawTrackingCircle( getRect(), x, y, color, 3, 15 ); 975 drawTrackingArrow( getRect(), x, y, color ); 976 text_x = sTrackingArrowX; 977 text_y = sTrackingArrowY; 978 } 979 is_in_window = false; 980 } 981 else if (LLTracker::getTrackingStatus() == LLTracker::TRACKING_LOCATION && 982 LLTracker::getTrackedLocationType() != LLTracker::LOCATION_NOTHING) 983 { 984 drawTrackingCircle( getRect(), x, y, color, 3, 15 ); 985 } 986 else 987 { 988 drawImage(pos_global, sTrackCircleImage, color); 989 } 990 991 // clamp text position to on-screen 992 const S32 TEXT_PADDING = DEFAULT_TRACKING_ARROW_SIZE + 2; 993 S32 half_text_width = llfloor(font->getWidthF32(label) * 0.5f); 994 text_x = llclamp(text_x, half_text_width + TEXT_PADDING, getRect().getWidth() - half_text_width - TEXT_PADDING); 995 text_y = llclamp(text_y + vert_offset, TEXT_PADDING + vert_offset, getRect().getHeight() - llround(font->getLineHeight()) - TEXT_PADDING - vert_offset); 996 997 if (label != "") 998 { 999 font->renderUTF8( 1000 label, 0, 1001 text_x, 1002 text_y, 1003 LLColor4::white, LLFontGL::HCENTER, 1004 LLFontGL::BASELINE, LLFontGL::NORMAL, LLFontGL::DROP_SHADOW); 1005 1006 if (tooltip != "") 1007 { 1008 text_y -= (S32)font->getLineHeight(); 1009 1010 font->renderUTF8( 1011 tooltip, 0, 1012 text_x, 1013 text_y, 1014 LLColor4::white, LLFontGL::HCENTER, 1015 LLFontGL::BASELINE, LLFontGL::NORMAL, LLFontGL::DROP_SHADOW); 1016 } 1017 } 1018} 1019 1020// If you change this, then you need to change LLTracker::getTrackedPositionGlobal() as well 1021LLVector3d LLWorldMapView::viewPosToGlobal( S32 x, S32 y ) 1022{ 1023 x -= llfloor((getRect().getWidth() / 2 + sPanX)); 1024 y -= llfloor((getRect().getHeight() / 2 + sPanY)); 1025 1026 LLVector3 pos_local( (F32)x, (F32)y, 0.f ); 1027 1028 pos_local *= ( REGION_WIDTH_METERS / sMapScale ); 1029 1030 LLVector3d pos_global; 1031 pos_global.setVec( pos_local ); 1032 pos_global += gAgentCamera.getCameraPositionGlobal(); 1033 if(gAgent.isGodlike()) 1034 { 1035 pos_global.mdV[VZ] = GODLY_TELEPORT_HEIGHT; // Godly height should always be 200. 1036 } 1037 else 1038 { 1039 pos_global.mdV[VZ] = gAgent.getPositionAgent().mV[VZ]; // Want agent's height, not camera's 1040 } 1041 1042 return pos_global; 1043} 1044 1045 1046BOOL LLWorldMapView::handleToolTip( S32 x, S32 y, MASK mask ) 1047{ 1048 LLVector3d pos_global = viewPosToGlobal(x, y); 1049 U64 handle = to_region_handle(pos_global); 1050 std::string tooltip_msg; 1051 1052 LLSimInfo* info = LLWorldMap::getInstance()->simInfoFromHandle(handle); 1053 if (info) 1054 { 1055 LLViewerRegion *region = gAgent.getRegion(); 1056 1057 std::string message = llformat("%s (%s)", info->getName().c_str(), info->getAccessString().c_str()); 1058 1059 if (!info->isDown()) 1060 { 1061 S32 agent_count = info->getAgentCount(); 1062 if (region && (region->getHandle() == handle)) 1063 { 1064 ++agent_count; // Bump by 1 if we're here 1065 } 1066 1067 // We may not have an agent count when the map is really 1068 // zoomed out, so don't display anything about the count. JC 1069 if (agent_count > 0) 1070 { 1071 LLStringUtil::format_map_t string_args; 1072 string_args["[NUMBER]"] = llformat("%d", agent_count); 1073 message += '\n'; 1074 message += getString((agent_count == 1 ? "world_map_person" : "world_map_people") , string_args); 1075 } 1076 } 1077 tooltip_msg.assign( message ); 1078 1079 // Optionally show region flags 1080 std::string region_flags = info->getFlagsString(); 1081 1082 if (!region_flags.empty()) 1083 { 1084 tooltip_msg += '\n'; 1085 tooltip_msg += region_flags; 1086 } 1087 1088 const S32 SLOP = 9; 1089 S32 screen_x, screen_y; 1090 1091 localPointToScreen(x, y, &screen_x, &screen_y); 1092 LLRect sticky_rect_screen; 1093 sticky_rect_screen.setCenterAndSize(screen_x, screen_y, SLOP, SLOP); 1094 1095 LLToolTipMgr::instance().show(LLToolTip::Params() 1096 .message(tooltip_msg) 1097 .sticky_rect(sticky_rect_screen)); 1098 } 1099 return TRUE; 1100} 1101 1102// Pass relative Z of 0 to draw at same level. 1103// static 1104static void drawDot(F32 x_pixels, F32 y_pixels, 1105 const LLColor4& color, 1106 F32 relative_z, 1107 F32 dot_radius, 1108 LLUIImagePtr dot_image) 1109{ 1110 const F32 HEIGHT_THRESHOLD = 7.f; 1111 1112 if(-HEIGHT_THRESHOLD <= relative_z && relative_z <= HEIGHT_THRESHOLD) 1113 { 1114 dot_image->draw(llround(x_pixels) - dot_image->getWidth()/2, 1115 llround(y_pixels) - dot_image->getHeight()/2, 1116 color); 1117 } 1118 else 1119 { 1120 // Draw V indicator for above or below 1121 // *TODO: Replace this vector drawing with icons 1122 1123 F32 left = x_pixels - dot_radius; 1124 F32 right = x_pixels + dot_radius; 1125 F32 center = (left + right) * 0.5f; 1126 F32 top = y_pixels + dot_radius; 1127 F32 bottom = y_pixels - dot_radius; 1128 1129 gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); 1130 gGL.color4fv( color.mV ); 1131 LLUI::setLineWidth(3.0f); 1132 F32 point = relative_z > HEIGHT_THRESHOLD ? top : bottom; // Y pos of the point of the V 1133 F32 back = relative_z > HEIGHT_THRESHOLD ? bottom : top; // Y pos of the ends of the V 1134 gGL.begin( LLRender::LINES ); 1135 gGL.vertex2f(left, back); 1136 gGL.vertex2f(center, point); 1137 gGL.vertex2f(center, point); 1138 gGL.vertex2f(right, back); 1139 gGL.end(); 1140 LLUI::setLineWidth(1.0f); 1141 } 1142} 1143 1144// Pass relative Z of 0 to draw at same level. 1145// static 1146void LLWorldMapView::drawAvatar(F32 x_pixels, 1147 F32 y_pixels, 1148 const LLColor4& color, 1149 F32 relative_z, 1150 F32 dot_radius) 1151{ 1152 const F32 HEIGHT_THRESHOLD = 7.f; 1153 LLUIImagePtr dot_image = sAvatarLevelImage; 1154 if(relative_z < -HEIGHT_THRESHOLD) 1155 { 1156 dot_image = sAvatarBelowImage; 1157 } 1158 else if(relative_z > HEIGHT_THRESHOLD) 1159 { 1160 dot_image = sAvatarAboveImage; 1161 } 1162 S32 dot_width = llround(dot_radius * 2.f); 1163 dot_image->draw(llround(x_pixels - dot_radius), 1164 llround(y_pixels - dot_radius), 1165 dot_width, 1166 dot_width, 1167 color); 1168} 1169 1170// Pass relative Z of 0 to draw at same level. 1171// static 1172void LLWorldMapView::drawTrackingDot( F32 x_pixels, 1173 F32 y_pixels, 1174 const LLColor4& color, 1175 F32 relative_z, 1176 F32 dot_radius) 1177{ 1178 drawDot(x_pixels, y_pixels, color, relative_z, dot_radius, sTrackCircleImage); 1179} 1180 1181 1182// Pass relative Z of 0 to draw at same level. 1183// static 1184void LLWorldMapView::drawIconName(F32 x_pixels, 1185 F32 y_pixels, 1186 const LLColor4& color, 1187 const std::string& first_line, 1188 const std::string& second_line) 1189{ 1190 const S32 VERT_PAD = 8; 1191 S32 text_x = llround(x_pixels); 1192 S32 text_y = llround(y_pixels 1193 - BIG_DOT_RADIUS 1194 - VERT_PAD); 1195 1196 // render text 1197 LLFontGL::getFontSansSerif()->renderUTF8(first_line, 0, 1198 text_x, 1199 text_y, 1200 color, 1201 LLFontGL::HCENTER, 1202 LLFontGL::TOP, 1203 LLFontGL::NORMAL, 1204 LLFontGL::DROP_SHADOW); 1205 1206 text_y -= llround(LLFontGL::getFontSansSerif()->getLineHeight()); 1207 1208 // render text 1209 LLFontGL::getFontSansSerif()->renderUTF8(second_line, 0, 1210 text_x, 1211 text_y, 1212 color, 1213 LLFontGL::HCENTER, 1214 LLFontGL::TOP, 1215 LLFontGL::NORMAL, 1216 LLFontGL::DROP_SHADOW); 1217} 1218 1219 1220//static 1221void LLWorldMapView::drawTrackingCircle( const LLRect& rect, S32 x, S32 y, const LLColor4& color, S32 min_thickness, S32 overlap ) 1222{ 1223 F32 start_theta = 0.f; 1224 F32 end_theta = F_TWO_PI; 1225 F32 x_delta = 0.f; 1226 F32 y_delta = 0.f; 1227 1228 if (x < 0) 1229 { 1230 x_delta = 0.f - (F32)x; 1231 start_theta = F_PI + F_PI_BY_TWO; 1232 end_theta = F_TWO_PI + F_PI_BY_TWO; 1233 } 1234 else if (x > rect.getWidth()) 1235 { 1236 x_delta = (F32)(x - rect.getWidth()); 1237 start_theta = F_PI_BY_TWO; 1238 end_theta = F_PI + F_PI_BY_TWO; 1239 } 1240 1241 if (y < 0) 1242 { 1243 y_delta = 0.f - (F32)y; 1244 if (x < 0) 1245 { 1246 start_theta = 0.f; 1247 end_theta = F_PI_BY_TWO; 1248 } 1249 else if (x > rect.getWidth()) 1250 { 1251 start_theta = F_PI_BY_TWO; 1252 end_theta = F_PI; 1253 } 1254 else 1255 { 1256 start_theta = 0.f; 1257 end_theta = F_PI; 1258 } 1259 } 1260 else if (y > rect.getHeight()) 1261 { 1262 y_delta = (F32)(y - rect.getHeight()); 1263 if (x < 0) 1264 { 1265 start_theta = F_PI + F_PI_BY_TWO; 1266 end_theta = F_TWO_PI; 1267 } 1268 else if (x > rect.getWidth()) 1269 { 1270 start_theta = F_PI; 1271 end_theta = F_PI + F_PI_BY_TWO; 1272 } 1273 else 1274 { 1275 start_theta = F_PI; 1276 end_theta = F_TWO_PI; 1277 } 1278 } 1279 1280 F32 distance = sqrtf(x_delta * x_delta + y_delta * y_delta); 1281 1282 distance = llmax(0.1f, distance); 1283 1284 F32 outer_radius = distance + (1.f + (9.f * sqrtf(x_delta * y_delta) / distance)) * (F32)overlap; 1285 F32 inner_radius = outer_radius - (F32)min_thickness; 1286 1287 F32 angle_adjust_x = asin(x_delta / outer_radius); 1288 F32 angle_adjust_y = asin(y_delta / outer_radius); 1289 1290 if (angle_adjust_x) 1291 { 1292 if (angle_adjust_y) 1293 { 1294 F32 angle_adjust = llmin(angle_adjust_x, angle_adjust_y); 1295 start_theta += angle_adjust; 1296 end_theta -= angle_adjust; 1297 } 1298 else 1299 { 1300 start_theta += angle_adjust_x; 1301 end_theta -= angle_adjust_x; 1302 } 1303 } 1304 else if (angle_adjust_y) 1305 { 1306 start_theta += angle_adjust_y; 1307 end_theta -= angle_adjust_y; 1308 } 1309 1310 gGL.matrixMode(LLRender::MM_MODELVIEW); 1311 gGL.pushMatrix(); 1312 gGL.translatef((F32)x, (F32)y, 0.f); 1313 gl_washer_segment_2d(inner_radius, outer_radius, start_theta, end_theta, 40, color, color); 1314 gGL.popMatrix(); 1315 1316} 1317 1318// static 1319void LLWorldMapView::drawTrackingArrow(const LLRect& rect, S32 x, S32 y, 1320 const LLColor4& color, 1321 S32 arrow_size) 1322{ 1323 F32 x_center = (F32)rect.getWidth() / 2.f; 1324 F32 y_center = (F32)rect.getHeight() / 2.f; 1325 1326 F32 x_clamped = (F32)llclamp( x, 0, rect.getWidth() - arrow_size ); 1327 F32 y_clamped = (F32)llclamp( y, 0, rect.getHeight() - arrow_size ); 1328 1329 F32 slope = (F32)(y - y_center) / (F32)(x - x_center); 1330 F32 window_ratio = (F32)rect.getHeight() / (F32)rect.getWidth(); 1331 1332 if (llabs(slope) > window_ratio && y_clamped != (F32)y) 1333 { 1334 // clamp by y 1335 x_clamped = (y_clamped - y_center) / slope + x_center; 1336 // adjust for arrow size 1337 x_clamped = llclamp(x_clamped , 0.f, (F32)(rect.getWidth() - arrow_size) ); 1338 } 1339 else if (x_clamped != (F32)x) 1340 { 1341 // clamp by x 1342 y_clamped = (x_clamped - x_center) * slope + y_center; 1343 // adjust for arrow size 1344 y_clamped = llclamp( y_clamped, 0.f, (F32)(rect.getHeight() - arrow_size) ); 1345 } 1346 1347 // *FIX: deal with non-square window properly. 1348 // I do not understand what this comment means -- is it actually 1349 // broken or is it correctly dealing with non-square 1350 // windows. Phoenix 2007-01-03. 1351 S32 half_arrow_size = (S32) (0.5f * arrow_size); 1352 1353 F32 angle = atan2( y + half_arrow_size - y_center, x + half_arrow_size - x_center); 1354 1355 sTrackingArrowX = llfloor(x_clamped); 1356 sTrackingArrowY = llfloor(y_clamped); 1357 1358 gl_draw_scaled_rotated_image( 1359 sTrackingArrowX, 1360 sTrackingArrowY, 1361 arrow_size, arrow_size, 1362 RAD_TO_DEG * angle, 1363 sTrackArrowImage->getImage(), 1364 color); 1365} 1366 1367void LLWorldMapView::setDirectionPos( LLTextBox* text_box, F32 rotation ) 1368{ 1369 // Rotation is in radians. 1370 // Rotation of 0 means x = 1, y = 0 on the unit circle. 1371 1372 1373 F32 map_half_height = getRect().getHeight() * 0.5f; 1374 F32 map_half_width = getRect().getWidth() * 0.5f; 1375 F32 text_half_height = text_box->getRect().getHeight() * 0.5f; 1376 F32 text_half_width = text_box->getRect().getWidth() * 0.5f; 1377 F32 radius = llmin( map_half_height - text_half_height, map_half_width - text_half_width ); 1378 1379 text_box->setOrigin( 1380 llround(map_half_width - text_half_width + radius * cos( rotation )), 1381 llround(map_half_height - text_half_height + radius * sin( rotation )) ); 1382} 1383 1384 1385void LLWorldMapView::updateDirections() 1386{ 1387 S32 width = getRect().getWidth(); 1388 S32 height = getRect().getHeight(); 1389 1390 S32 text_height = mTextBoxNorth->getRect().getHeight(); 1391 S32 text_width = mTextBoxNorth->getRect().getWidth(); 1392 1393 const S32 PAD = 2; 1394 S32 top = height - text_height - PAD; 1395 S32 left = PAD*2; 1396 S32 bottom = PAD; 1397 S32 right = width - text_width - PAD; 1398 S32 center_x = width/2 - text_width/2; 1399 S32 center_y = height/2 - text_height/2; 1400 1401 mTextBoxNorth->setOrigin( center_x, top ); 1402 mTextBoxEast->setOrigin( right, center_y ); 1403 mTextBoxSouth->setOrigin( center_x, bottom ); 1404 mTextBoxWest->setOrigin( left, center_y ); 1405 1406 // These have wider text boxes 1407 text_width = mTextBoxNorthWest->getRect().getWidth(); 1408 right = width - text_width - PAD; 1409 1410 mTextBoxNorthWest->setOrigin(left, top); 1411 mTextBoxNorthEast->setOrigin(right, top); 1412 mTextBoxSouthWest->setOrigin(left, bottom); 1413 mTextBoxSouthEast->setOrigin(right, bottom); 1414 1415// S32 hint_width = mTextBoxScrollHint->getRect().getWidth(); 1416// mTextBoxScrollHint->setOrigin( width - hint_width - text_width - 2 * PAD, 1417// PAD * 2 + text_height ); 1418} 1419 1420 1421void LLWorldMapView::reshape( S32 width, S32 height, BOOL called_from_parent ) 1422{ 1423 LLView::reshape( width, height, called_from_parent ); 1424} 1425 1426bool LLWorldMapView::checkItemHit(S32 x, S32 y, LLItemInfo& item, LLUUID* id, bool track) 1427{ 1428 LLVector3 pos_view = globalPosToView(item.getGlobalPosition()); 1429 S32 item_x = llround(pos_view.mV[VX]); 1430 S32 item_y = llround(pos_view.mV[VY]); 1431 1432 if (x < item_x - BIG_DOT_RADIUS) return false; 1433 if (x > item_x + BIG_DOT_RADIUS) return false; 1434 if (y < item_y - BIG_DOT_RADIUS) return false; 1435 if (y > item_y + BIG_DOT_RADIUS) return false; 1436 1437 LLSimInfo* sim_info = LLWorldMap::getInstance()->simInfoFromHandle(item.getRegionHandle()); 1438 if (sim_info) 1439 { 1440 if (track) 1441 { 1442 gFloaterWorldMap->trackLocation(item.getGlobalPosition()); 1443 } 1444 } 1445 1446 if (track) 1447 { 1448 gFloaterWorldMap->trackGenericItem(item); 1449 } 1450 1451// item.setSelected(true); 1452 *id = item.getUUID(); 1453 1454 return true; 1455} 1456 1457// Handle a click, which might be on a dot 1458void LLWorldMapView::handleClick(S32 x, S32 y, MASK mask, 1459 S32* hit_type, 1460 LLUUID* id) 1461{ 1462 LLVector3d pos_global = viewPosToGlobal(x, y); 1463 1464 // *HACK: Adjust Z values automatically for liaisons & gods so 1465 // we swoop down when they click on the map. Sadly, the P2P 1466 // branch does not pay attention to this value; however, the 1467 // Distributed Messaging branch honors it. 1468 if(gAgent.isGodlike()) 1469 { 1470 pos_global.mdV[VZ] = 200.0; 1471 } 1472 1473 *hit_type = 0; // hit nothing 1474 1475 LLWorldMap::getInstance()->cancelTracking(); 1476 1477 S32 level = LLWorldMipmap::scaleToLevel(sMapScale); 1478 // If the zoom level is not too far out already, test hits 1479 if (level <= DRAW_SIMINFO_THRESHOLD) 1480 { 1481 bool show_mature = gAgent.canAccessMature() && gSavedSettings.getBOOL("ShowMatureEvents"); 1482 bool show_adult = gAgent.canAccessAdult() && gSavedSettings.getBOOL("ShowAdultEvents"); 1483 1484 // Test hits if trackable data are displayed, otherwise, we don't even bother 1485 if (gSavedSettings.getBOOL("MapShowEvents") || show_mature || show_adult || gSavedSettings.getBOOL("MapShowLandForSale")) 1486 { 1487 // Iterate through the visible regions 1488 for (handle_list_t::iterator iter = mVisibleRegions.begin(); iter != mVisibleRegions.end(); ++iter) 1489 { 1490 U64 handle = *iter; 1491 LLSimInfo* siminfo = LLWorldMap::getInstance()->simInfoFromHandle(handle); 1492 if ((siminfo == NULL) || (siminfo->isDown())) 1493 { 1494 continue; 1495 } 1496 // If on screen check hits with the visible item lists 1497 if (gSavedSettings.getBOOL("MapShowEvents")) 1498 { 1499 LLSimInfo::item_info_list_t::const_iterator it = siminfo->getPGEvent().begin(); 1500 while (it != siminfo->getPGEvent().end()) 1501 { 1502 LLItemInfo event = *it; 1503 if (checkItemHit(x, y, event, id, false)) 1504 { 1505 *hit_type = MAP_ITEM_PG_EVENT; 1506 mItemPicked = TRUE; 1507 gFloaterWorldMap->trackEvent(event); 1508 return; 1509 } 1510 ++it; 1511 } 1512 } 1513 if (show_mature) 1514 { 1515 LLSimInfo::item_info_list_t::const_iterator it = siminfo->getMatureEvent().begin(); 1516 while (it != siminfo->getMatureEvent().end()) 1517 { 1518 LLItemInfo event = *it; 1519 if (checkItemHit(x, y, event, id, false)) 1520 { 1521 *hit_type = MAP_ITEM_MATURE_EVENT; 1522 mItemPicked = TRUE; 1523 gFloaterWorldMap->trackEvent(event); 1524 return; 1525 } 1526 ++it; 1527 } 1528 } 1529 if (show_adult) 1530 { 1531 LLSimInfo::item_info_list_t::const_iterator it = siminfo->getAdultEvent().begin(); 1532 while (it != siminfo->getAdultEvent().end()) 1533 { 1534 LLItemInfo event = *it; 1535 if (checkItemHit(x, y, event, id, false)) 1536 { 1537 *hit_type = MAP_ITEM_ADULT_EVENT; 1538 mItemPicked = TRUE; 1539 gFloaterWorldMap->trackEvent(event); 1540 return; 1541 } 1542 ++it; 1543 } 1544 } 1545 if (gSavedSettings.getBOOL("MapShowLandForSale")) 1546 { 1547 LLSimInfo::item_info_list_t::const_iterator it = siminfo->getLandForSale().begin(); 1548 while (it != siminfo->getLandForSale().end()) 1549 { 1550 LLItemInfo event = *it; 1551 if (checkItemHit(x, y, event, id, true)) 1552 { 1553 *hit_type = MAP_ITEM_LAND_FOR_SALE; 1554 mItemPicked = TRUE; 1555 return; 1556 } 1557 ++it; 1558 } 1559 // for 1.23, we're showing normal land and adult land in the same UI; you don't 1560 // get a choice about which ones you want. If you're currently asking for adult 1561 // content and land you'll get the adult land. 1562 if (gAgent.canAccessAdult()) 1563 { 1564 LLSimInfo::item_info_list_t::const_iterator it = siminfo->getLandForSaleAdult().begin(); 1565 while (it != siminfo->getLandForSaleAdult().end()) 1566 { 1567 LLItemInfo event = *it; 1568 if (checkItemHit(x, y, event, id, true)) 1569 { 1570 *hit_type = MAP_ITEM_LAND_FOR_SALE_ADULT; 1571 mItemPicked = TRUE; 1572 return; 1573 } 1574 ++it; 1575 } 1576 } 1577 } 1578 } 1579 } 1580 } 1581 1582 // If we get here, we haven't clicked on anything 1583 gFloaterWorldMap->trackLocation(pos_global); 1584 mItemPicked = FALSE; 1585 *id = LLUUID::null; 1586 return; 1587} 1588 1589 1590BOOL outside_slop(S32 x, S32 y, S32 start_x, S32 start_y) 1591{ 1592 S32 dx = x - start_x; 1593 S32 dy = y - start_y; 1594 1595 return (dx <= -2 || 2 <= dx || dy <= -2 || 2 <= dy); 1596} 1597 1598BOOL LLWorldMapView::handleMouseDown( S32 x, S32 y, MASK mask ) 1599{ 1600 gFocusMgr.setMouseCapture( this ); 1601 1602 mMouseDownPanX = llround(sPanX); 1603 mMouseDownPanY = llround(sPanY); 1604 mMouseDownX = x; 1605 mMouseDownY = y; 1606 sHandledLastClick = TRUE; 1607 return TRUE; 1608} 1609 1610BOOL LLWorldMapView::handleMouseUp( S32 x, S32 y, MASK mask ) 1611{ 1612 if (hasMouseCapture()) 1613 { 1614 if (mPanning) 1615 { 1616 // restore mouse cursor 1617 S32 local_x, local_y; 1618 local_x = mMouseDownX + llfloor(sPanX - mMouseDownPanX); 1619 local_y = mMouseDownY + llfloor(sPanY - mMouseDownPanY); 1620 LLRect clip_rect = getRect(); 1621 clip_rect.stretch(-8); 1622 clip_rect.clipPointToRect(mMouseDownX, mMouseDownY, local_x, local_y); 1623 LLUI::setMousePositionLocal(this, local_x, local_y); 1624 1625 // finish the pan 1626 mPanning = FALSE; 1627 1628 mMouseDownX = 0; 1629 mMouseDownY = 0; 1630 } 1631 else 1632 { 1633 // ignore whether we hit an event or not 1634 S32 hit_type; 1635 LLUUID id; 1636 handleClick(x, y, mask, &hit_type, &id); 1637 } 1638 gViewerWindow->showCursor(); 1639 gFocusMgr.setMouseCapture( NULL ); 1640 return TRUE; 1641 } 1642 return FALSE; 1643} 1644 1645void LLWorldMapView::updateVisibleBlocks() 1646{ 1647 if (LLWorldMipmap::scaleToLevel(sMapScale) > DRAW_SIMINFO_THRESHOLD) 1648 { 1649 // If we're zoomed out too much, we just don't load all those sim info: too much! 1650 return; 1651 } 1652 1653 // Load the blocks visible in the current World Map view 1654 1655 // Get the World Map view coordinates and boundaries 1656 LLVector3d camera_global = gAgentCamera.getCameraPositionGlobal(); 1657 const S32 width = getRect().getWidth(); 1658 const S32 height = getRect().getHeight(); 1659 const F32 half_width = F32(width) / 2.0f; 1660 const F32 half_height = F3…
Large files files are truncated, but you can click here to view the full file