PageRenderTime 220ms CodeModel.GetById 8ms app.highlight 190ms RepoModel.GetById 2ms app.codeStats 1ms

/indra/newview/llworldmapview.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 1786 lines | 1329 code | 238 blank | 219 comment | 202 complexity | 054584634f1c4a725fc6d3cc520d3f91 MD5 | raw file

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