PageRenderTime 78ms CodeModel.GetById 8ms app.highlight 61ms RepoModel.GetById 1ms app.codeStats 0ms

/indra/newview/llnetmap.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 1010 lines | 778 code | 150 blank | 82 comment | 112 complexity | bd42ec7bea077128e9fa75a75d86ea21 MD5 | raw file
   1/** 
   2 * @file llnetmap.cpp
   3 * @author James Cook
   4 * @brief Display of surrounding regions, objects, and agents. 
   5 *
   6 * $LicenseInfo:firstyear=2001&license=viewerlgpl$
   7 * Second Life Viewer Source Code
   8 * Copyright (C) 2001-2010, Linden Research, Inc.
   9 * 
  10 * This library is free software; you can redistribute it and/or
  11 * modify it under the terms of the GNU Lesser General Public
  12 * License as published by the Free Software Foundation;
  13 * version 2.1 of the License only.
  14 * 
  15 * This library is distributed in the hope that it will be useful,
  16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  18 * Lesser General Public License for more details.
  19 * 
  20 * You should have received a copy of the GNU Lesser General Public
  21 * License along with this library; if not, write to the Free Software
  22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
  23 * 
  24 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
  25 * $/LicenseInfo$
  26 */
  27
  28#include "llviewerprecompiledheaders.h"
  29
  30#include "llnetmap.h"
  31
  32// Library includes (should move below)
  33#include "indra_constants.h"
  34#include "llavatarnamecache.h"
  35#include "llmath.h"
  36#include "llfloaterreg.h"
  37#include "llfocusmgr.h"
  38#include "lllocalcliprect.h"
  39#include "llrender.h"
  40#include "llui.h"
  41#include "lltooltip.h"
  42
  43#include "llglheaders.h"
  44
  45// Viewer includes
  46#include "llagent.h"
  47#include "llagentcamera.h"
  48#include "llappviewer.h" // for gDisconnected
  49#include "llcallingcard.h" // LLAvatarTracker
  50#include "llfloaterworldmap.h"
  51#include "lltracker.h"
  52#include "llsurface.h"
  53#include "llviewercamera.h"
  54#include "llviewercontrol.h"
  55#include "llviewertexture.h"
  56#include "llviewertexturelist.h"
  57#include "llviewermenu.h"
  58#include "llviewerobjectlist.h"
  59#include "llviewerregion.h"
  60#include "llviewerwindow.h"
  61#include "llworld.h"
  62#include "llworldmapview.h"		// shared draw code
  63
  64static LLDefaultChildRegistry::Register<LLNetMap> r1("net_map");
  65
  66const F32 LLNetMap::MAP_SCALE_MIN = 32;
  67const F32 LLNetMap::MAP_SCALE_MID = 1024;
  68const F32 LLNetMap::MAP_SCALE_MAX = 4096;
  69
  70const F32 MAP_SCALE_INCREMENT = 16;
  71const F32 MAP_SCALE_ZOOM_FACTOR = 1.04f; // Zoom in factor per click of scroll wheel (4%)
  72const F32 MIN_DOT_RADIUS = 3.5f;
  73const F32 DOT_SCALE = 0.75f;
  74const F32 MIN_PICK_SCALE = 2.f;
  75const S32 MOUSE_DRAG_SLOP = 2;		// How far the mouse needs to move before we think it's a drag
  76
  77LLNetMap::LLNetMap (const Params & p)
  78:	LLUICtrl (p),
  79	mBackgroundColor (p.bg_color()),
  80	mScale( MAP_SCALE_MID ),
  81	mPixelsPerMeter( MAP_SCALE_MID / REGION_WIDTH_METERS ),
  82	mObjectMapTPM(0.f),
  83	mObjectMapPixels(0.f),
  84	mTargetPan(0.f, 0.f),
  85	mCurPan(0.f, 0.f),
  86	mStartPan(0.f, 0.f),
  87	mMouseDown(0, 0),
  88	mPanning(false),
  89	mUpdateNow(false),
  90	mObjectImageCenterGlobal( gAgentCamera.getCameraPositionGlobal() ),
  91	mObjectRawImagep(),
  92	mObjectImagep(),
  93	mClosestAgentToCursor(),
  94	mClosestAgentAtLastRightClick(),
  95	mToolTipMsg(),
  96	mPopupMenu(NULL)
  97{
  98	mDotRadius = llmax(DOT_SCALE * mPixelsPerMeter, MIN_DOT_RADIUS);
  99	setScale(gSavedSettings.getF32("MiniMapScale"));
 100}
 101
 102LLNetMap::~LLNetMap()
 103{
 104	gSavedSettings.setF32("MiniMapScale", mScale);
 105}
 106
 107BOOL LLNetMap::postBuild()
 108{
 109	LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar;
 110	
 111	registrar.add("Minimap.Zoom", boost::bind(&LLNetMap::handleZoom, this, _2));
 112	registrar.add("Minimap.Tracker", boost::bind(&LLNetMap::handleStopTracking, this, _2));
 113
 114	mPopupMenu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_mini_map.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance());
 115	return TRUE;
 116}
 117
 118void LLNetMap::setScale( F32 scale )
 119{
 120	scale = llclamp(scale, MAP_SCALE_MIN, MAP_SCALE_MAX);
 121	mCurPan *= scale / mScale;
 122	mScale = scale;
 123	
 124	if (mObjectImagep.notNull())
 125	{
 126		F32 width = (F32)(getRect().getWidth());
 127		F32 height = (F32)(getRect().getHeight());
 128		F32 diameter = sqrt(width * width + height * height);
 129		F32 region_widths = diameter / mScale;
 130		F32 meters = region_widths * LLWorld::getInstance()->getRegionWidthInMeters();
 131		F32 num_pixels = (F32)mObjectImagep->getWidth();
 132		mObjectMapTPM = num_pixels / meters;
 133		mObjectMapPixels = diameter;
 134	}
 135
 136	mPixelsPerMeter = mScale / REGION_WIDTH_METERS;
 137	mDotRadius = llmax(DOT_SCALE * mPixelsPerMeter, MIN_DOT_RADIUS);
 138
 139	mUpdateNow = true;
 140}
 141
 142
 143///////////////////////////////////////////////////////////////////////////////////
 144
 145void LLNetMap::draw()
 146{
 147 	static LLFrameTimer map_timer;
 148	static LLUIColor map_avatar_color = LLUIColorTable::instance().getColor("MapAvatarColor", LLColor4::white);
 149	static LLUIColor map_avatar_friend_color = LLUIColorTable::instance().getColor("MapAvatarFriendColor", LLColor4::white);
 150	static LLUIColor map_track_color = LLUIColorTable::instance().getColor("MapTrackColor", LLColor4::white);
 151	static LLUIColor map_track_disabled_color = LLUIColorTable::instance().getColor("MapTrackDisabledColor", LLColor4::white);
 152	static LLUIColor map_frustum_color = LLUIColorTable::instance().getColor("MapFrustumColor", LLColor4::white);
 153	static LLUIColor map_frustum_rotating_color = LLUIColorTable::instance().getColor("MapFrustumRotatingColor", LLColor4::white);
 154	
 155	if (mObjectImagep.isNull())
 156	{
 157		createObjectImage();
 158	}
 159
 160	static LLUICachedControl<bool> auto_center("MiniMapAutoCenter", true);
 161	if (auto_center)
 162	{
 163		mCurPan = lerp(mCurPan, mTargetPan, LLCriticalDamp::getInterpolant(0.1f));
 164	}
 165
 166	// Prepare a scissor region
 167	F32 rotation = 0;
 168
 169	gGL.pushMatrix();
 170	gGL.pushUIMatrix();
 171	
 172	LLVector3 offset = gGL.getUITranslation();
 173	LLVector3 scale = gGL.getUIScale();
 174
 175	gGL.loadIdentity();
 176	gGL.loadUIIdentity();
 177
 178	gGL.scalef(scale.mV[0], scale.mV[1], scale.mV[2]);
 179	gGL.translatef(offset.mV[0], offset.mV[1], offset.mV[2]);
 180	
 181	{
 182		LLLocalClipRect clip(getLocalRect());
 183		{
 184			gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
 185
 186			gGL.matrixMode(LLRender::MM_MODELVIEW);
 187
 188			// Draw background rectangle
 189			LLColor4 background_color = mBackgroundColor.get();
 190			gGL.color4fv( background_color.mV );
 191			gl_rect_2d(0, getRect().getHeight(), getRect().getWidth(), 0);
 192		}
 193
 194		// region 0,0 is in the middle
 195		S32 center_sw_left = getRect().getWidth() / 2 + llfloor(mCurPan.mV[VX]);
 196		S32 center_sw_bottom = getRect().getHeight() / 2 + llfloor(mCurPan.mV[VY]);
 197
 198		gGL.pushMatrix();
 199
 200		gGL.translatef( (F32) center_sw_left, (F32) center_sw_bottom, 0.f);
 201
 202		static LLUICachedControl<bool> rotate_map("MiniMapRotate", true);
 203		if( rotate_map )
 204		{
 205			// rotate subsequent draws to agent rotation
 206			rotation = atan2( LLViewerCamera::getInstance()->getAtAxis().mV[VX], LLViewerCamera::getInstance()->getAtAxis().mV[VY] );
 207			gGL.rotatef( rotation * RAD_TO_DEG, 0.f, 0.f, 1.f);
 208		}
 209
 210		// figure out where agent is
 211		S32 region_width = llround(LLWorld::getInstance()->getRegionWidthInMeters());
 212
 213		for (LLWorld::region_list_t::const_iterator iter = LLWorld::getInstance()->getRegionList().begin();
 214			 iter != LLWorld::getInstance()->getRegionList().end(); ++iter)
 215		{
 216			LLViewerRegion* regionp = *iter;
 217			// Find x and y position relative to camera's center.
 218			LLVector3 origin_agent = regionp->getOriginAgent();
 219			LLVector3 rel_region_pos = origin_agent - gAgentCamera.getCameraPositionAgent();
 220			F32 relative_x = (rel_region_pos.mV[0] / region_width) * mScale;
 221			F32 relative_y = (rel_region_pos.mV[1] / region_width) * mScale;
 222
 223			// background region rectangle
 224			F32 bottom =	relative_y;
 225			F32 left =		relative_x;
 226			F32 top =		bottom + mScale ;
 227			F32 right =		left + mScale ;
 228
 229			if (regionp == gAgent.getRegion())
 230			{
 231				gGL.color4f(1.f, 1.f, 1.f, 1.f);
 232			}
 233			else
 234			{
 235				gGL.color4f(0.8f, 0.8f, 0.8f, 1.f);
 236			}
 237
 238			if (!regionp->isAlive())
 239			{
 240				gGL.color4f(1.f, 0.5f, 0.5f, 1.f);
 241			}
 242
 243
 244			// Draw using texture.
 245			gGL.getTexUnit(0)->bind(regionp->getLand().getSTexture());
 246			gGL.begin(LLRender::QUADS);
 247				gGL.texCoord2f(0.f, 1.f);
 248				gGL.vertex2f(left, top);
 249				gGL.texCoord2f(0.f, 0.f);
 250				gGL.vertex2f(left, bottom);
 251				gGL.texCoord2f(1.f, 0.f);
 252				gGL.vertex2f(right, bottom);
 253				gGL.texCoord2f(1.f, 1.f);
 254				gGL.vertex2f(right, top);
 255			gGL.end();
 256
 257			// Draw water
 258			gGL.setAlphaRejectSettings(LLRender::CF_GREATER, ABOVE_WATERLINE_ALPHA / 255.f);
 259			{
 260				if (regionp->getLand().getWaterTexture())
 261				{
 262					gGL.getTexUnit(0)->bind(regionp->getLand().getWaterTexture());
 263					gGL.begin(LLRender::QUADS);
 264						gGL.texCoord2f(0.f, 1.f);
 265						gGL.vertex2f(left, top);
 266						gGL.texCoord2f(0.f, 0.f);
 267						gGL.vertex2f(left, bottom);
 268						gGL.texCoord2f(1.f, 0.f);
 269						gGL.vertex2f(right, bottom);
 270						gGL.texCoord2f(1.f, 1.f);
 271						gGL.vertex2f(right, top);
 272					gGL.end();
 273				}
 274			}
 275			gGL.setAlphaRejectSettings(LLRender::CF_DEFAULT);
 276		}
 277
 278		// Redraw object layer periodically
 279		if (mUpdateNow || (map_timer.getElapsedTimeF32() > 0.5f))
 280		{
 281			mUpdateNow = false;
 282
 283			// Locate the centre of the object layer, accounting for panning
 284			LLVector3 new_center = globalPosToView(gAgentCamera.getCameraPositionGlobal());
 285			new_center.mV[VX] -= mCurPan.mV[VX];
 286			new_center.mV[VY] -= mCurPan.mV[VY];
 287			new_center.mV[VZ] = 0.f;
 288			mObjectImageCenterGlobal = viewPosToGlobal(llfloor(new_center.mV[VX]), llfloor(new_center.mV[VY]));
 289
 290			// Create the base texture.
 291			U8 *default_texture = mObjectRawImagep->getData();
 292			memset( default_texture, 0, mObjectImagep->getWidth() * mObjectImagep->getHeight() * mObjectImagep->getComponents() );
 293
 294			// Draw objects
 295			gObjectList.renderObjectsForMap(*this);
 296
 297			mObjectImagep->setSubImage(mObjectRawImagep, 0, 0, mObjectImagep->getWidth(), mObjectImagep->getHeight());
 298			
 299			map_timer.reset();
 300		}
 301
 302		LLVector3 map_center_agent = gAgent.getPosAgentFromGlobal(mObjectImageCenterGlobal);
 303		map_center_agent -= gAgentCamera.getCameraPositionAgent();
 304		map_center_agent.mV[VX] *= mScale/region_width;
 305		map_center_agent.mV[VY] *= mScale/region_width;
 306
 307		gGL.getTexUnit(0)->bind(mObjectImagep);
 308		F32 image_half_width = 0.5f*mObjectMapPixels;
 309		F32 image_half_height = 0.5f*mObjectMapPixels;
 310
 311		gGL.begin(LLRender::QUADS);
 312			gGL.texCoord2f(0.f, 1.f);
 313			gGL.vertex2f(map_center_agent.mV[VX] - image_half_width, image_half_height + map_center_agent.mV[VY]);
 314			gGL.texCoord2f(0.f, 0.f);
 315			gGL.vertex2f(map_center_agent.mV[VX] - image_half_width, map_center_agent.mV[VY] - image_half_height);
 316			gGL.texCoord2f(1.f, 0.f);
 317			gGL.vertex2f(image_half_width + map_center_agent.mV[VX], map_center_agent.mV[VY] - image_half_height);
 318			gGL.texCoord2f(1.f, 1.f);
 319			gGL.vertex2f(image_half_width + map_center_agent.mV[VX], image_half_height + map_center_agent.mV[VY]);
 320		gGL.end();
 321
 322		gGL.popMatrix();
 323
 324		LLVector3d pos_global;
 325		LLVector3 pos_map;
 326
 327		// Mouse pointer in local coordinates
 328		S32 local_mouse_x;
 329		S32 local_mouse_y;
 330		//localMouse(&local_mouse_x, &local_mouse_y);
 331		LLUI::getMousePositionLocal(this, &local_mouse_x, &local_mouse_y);
 332		mClosestAgentToCursor.setNull();
 333		F32 closest_dist_squared = F32_MAX; // value will be overridden in the loop
 334		F32 min_pick_dist_squared = (mDotRadius * MIN_PICK_SCALE) * (mDotRadius * MIN_PICK_SCALE);
 335
 336		// Draw avatars
 337		for (LLWorld::region_list_t::const_iterator iter = LLWorld::getInstance()->getRegionList().begin();
 338			 iter != LLWorld::getInstance()->getRegionList().end(); ++iter)
 339		{
 340			LLViewerRegion* regionp = *iter;
 341			const LLVector3d& origin_global = regionp->getOriginGlobal();
 342
 343			S32 count = regionp->mMapAvatars.count();
 344			S32 i;
 345			LLVector3 pos_local;
 346			U32 compact_local;
 347			U8 bits;
 348			// TODO: it'd be very cool to draw these in sorted order from lowest Z to highest.
 349			// just be careful to sort the avatar IDs along with the positions. -MG
 350			for (i = 0; i < count; i++)
 351			{
 352				compact_local = regionp->mMapAvatars.get(i);
 353
 354				bits = compact_local & 0xFF;
 355				pos_local.mV[VZ] = F32(bits) * 4.f;
 356				compact_local >>= 8;
 357
 358				bits = compact_local & 0xFF;
 359				pos_local.mV[VY] = (F32)bits;
 360				compact_local >>= 8;
 361
 362				bits = compact_local & 0xFF;
 363				pos_local.mV[VX] = (F32)bits;
 364
 365				pos_global.setVec( pos_local );
 366				pos_global += origin_global;
 367
 368				pos_map = globalPosToView(pos_global);
 369
 370				LLUUID uuid(NULL);
 371				BOOL show_as_friend = FALSE;
 372				if( i < regionp->mMapAvatarIDs.count())
 373				{
 374					uuid = regionp->mMapAvatarIDs.get(i);
 375					show_as_friend = (LLAvatarTracker::instance().getBuddyInfo(uuid) != NULL);
 376				}
 377
 378				LLColor4 color = show_as_friend ? map_avatar_friend_color : map_avatar_color;
 379				LLWorldMapView::drawAvatar(
 380					pos_map.mV[VX], pos_map.mV[VY], 
 381					color, 
 382					pos_map.mV[VZ], mDotRadius);
 383
 384				if(uuid.notNull())
 385				{
 386					bool selected = false;
 387					uuid_vec_t::iterator sel_iter = gmSelected.begin();
 388					for (; sel_iter != gmSelected.end(); sel_iter++)
 389					{
 390						if(*sel_iter == uuid)
 391						{
 392							selected = true;
 393							break;
 394						}
 395					}
 396					if(selected)
 397					{
 398						if( (pos_map.mV[VX] < 0) ||
 399							(pos_map.mV[VY] < 0) ||
 400							(pos_map.mV[VX] >= getRect().getWidth()) ||
 401							(pos_map.mV[VY] >= getRect().getHeight()) )
 402						{
 403							S32 x = llround( pos_map.mV[VX] );
 404							S32 y = llround( pos_map.mV[VY] );
 405							LLWorldMapView::drawTrackingCircle( getRect(), x, y, color, 1, 10);
 406						} else
 407						{
 408							LLWorldMapView::drawTrackingDot(pos_map.mV[VX],pos_map.mV[VY],color,0.f);
 409						}
 410					}
 411				}
 412
 413				F32	dist_to_cursor_squared = dist_vec_squared(LLVector2(pos_map.mV[VX], pos_map.mV[VY]),
 414											  LLVector2(local_mouse_x,local_mouse_y));
 415				if(dist_to_cursor_squared < min_pick_dist_squared && dist_to_cursor_squared < closest_dist_squared)
 416				{
 417					closest_dist_squared = dist_to_cursor_squared;
 418					mClosestAgentToCursor = regionp->mMapAvatarIDs.get(i);
 419				}
 420			}
 421		}
 422
 423		// Draw dot for autopilot target
 424		if (gAgent.getAutoPilot())
 425		{
 426			drawTracking( gAgent.getAutoPilotTargetGlobal(), map_track_color );
 427		}
 428		else
 429		{
 430			LLTracker::ETrackingStatus tracking_status = LLTracker::getTrackingStatus();
 431			if (  LLTracker::TRACKING_AVATAR == tracking_status )
 432			{
 433				drawTracking( LLAvatarTracker::instance().getGlobalPos(), map_track_color );
 434			} 
 435			else if ( LLTracker::TRACKING_LANDMARK == tracking_status 
 436					|| LLTracker::TRACKING_LOCATION == tracking_status )
 437			{
 438				drawTracking( LLTracker::getTrackedPositionGlobal(), map_track_color );
 439			}
 440		}
 441
 442		// Draw dot for self avatar position
 443		pos_global = gAgent.getPositionGlobal();
 444		pos_map = globalPosToView(pos_global);
 445		S32 dot_width = llround(mDotRadius * 2.f);
 446		LLUIImagePtr you = LLWorldMapView::sAvatarYouLargeImage;
 447		if (you)
 448		{
 449			you->draw(llround(pos_map.mV[VX] - mDotRadius),
 450					  llround(pos_map.mV[VY] - mDotRadius),
 451					  dot_width,
 452					  dot_width);
 453
 454			F32	dist_to_cursor_squared = dist_vec_squared(LLVector2(pos_map.mV[VX], pos_map.mV[VY]),
 455										  LLVector2(local_mouse_x,local_mouse_y));
 456			if(dist_to_cursor_squared < min_pick_dist_squared && dist_to_cursor_squared < closest_dist_squared)
 457			{
 458				mClosestAgentToCursor = gAgent.getID();
 459			}
 460		}
 461
 462		// Draw frustum
 463		F32 meters_to_pixels = mScale/ LLWorld::getInstance()->getRegionWidthInMeters();
 464
 465		F32 horiz_fov = LLViewerCamera::getInstance()->getView() * LLViewerCamera::getInstance()->getAspect();
 466		F32 far_clip_meters = LLViewerCamera::getInstance()->getFar();
 467		F32 far_clip_pixels = far_clip_meters * meters_to_pixels;
 468
 469		F32 half_width_meters = far_clip_meters * tan( horiz_fov / 2 );
 470		F32 half_width_pixels = half_width_meters * meters_to_pixels;
 471		
 472		F32 ctr_x = (F32)center_sw_left;
 473		F32 ctr_y = (F32)center_sw_bottom;
 474
 475
 476		gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
 477
 478		if( rotate_map )
 479		{
 480			gGL.color4fv((map_frustum_color()).mV);
 481
 482			gGL.begin( LLRender::TRIANGLES  );
 483				gGL.vertex2f( ctr_x, ctr_y );
 484				gGL.vertex2f( ctr_x - half_width_pixels, ctr_y + far_clip_pixels );
 485				gGL.vertex2f( ctr_x + half_width_pixels, ctr_y + far_clip_pixels );
 486			gGL.end();
 487		}
 488		else
 489		{
 490			gGL.color4fv((map_frustum_rotating_color()).mV);
 491			
 492			// If we don't rotate the map, we have to rotate the frustum.
 493			gGL.pushMatrix();
 494				gGL.translatef( ctr_x, ctr_y, 0 );
 495				gGL.rotatef( atan2( LLViewerCamera::getInstance()->getAtAxis().mV[VX], LLViewerCamera::getInstance()->getAtAxis().mV[VY] ) * RAD_TO_DEG, 0.f, 0.f, -1.f);
 496				gGL.begin( LLRender::TRIANGLES  );
 497					gGL.vertex2f( 0, 0 );
 498					gGL.vertex2f( -half_width_pixels, far_clip_pixels );
 499					gGL.vertex2f(  half_width_pixels, far_clip_pixels );
 500				gGL.end();
 501			gGL.popMatrix();
 502		}
 503	}
 504	
 505	gGL.popMatrix();
 506	gGL.popUIMatrix();
 507
 508	LLUICtrl::draw();
 509}
 510
 511void LLNetMap::reshape(S32 width, S32 height, BOOL called_from_parent)
 512{
 513	LLUICtrl::reshape(width, height, called_from_parent);
 514	createObjectImage();
 515}
 516
 517LLVector3 LLNetMap::globalPosToView( const LLVector3d& global_pos )
 518{
 519	LLVector3d relative_pos_global = global_pos - gAgentCamera.getCameraPositionGlobal();
 520	LLVector3 pos_local;
 521	pos_local.setVec(relative_pos_global);  // convert to floats from doubles
 522
 523	pos_local.mV[VX] *= mPixelsPerMeter;
 524	pos_local.mV[VY] *= mPixelsPerMeter;
 525	// leave Z component in meters
 526
 527	static LLUICachedControl<bool> rotate_map("MiniMapRotate", true);
 528	if( rotate_map )
 529	{
 530		F32 radians = atan2( LLViewerCamera::getInstance()->getAtAxis().mV[VX], LLViewerCamera::getInstance()->getAtAxis().mV[VY] );
 531		LLQuaternion rot(radians, LLVector3(0.f, 0.f, 1.f));
 532		pos_local.rotVec( rot );
 533	}
 534
 535	pos_local.mV[VX] += getRect().getWidth() / 2 + mCurPan.mV[VX];
 536	pos_local.mV[VY] += getRect().getHeight() / 2 + mCurPan.mV[VY];
 537
 538	return pos_local;
 539}
 540
 541void LLNetMap::drawTracking(const LLVector3d& pos_global, const LLColor4& color, 
 542							BOOL draw_arrow )
 543{
 544	LLVector3 pos_local = globalPosToView( pos_global );
 545	if( (pos_local.mV[VX] < 0) ||
 546		(pos_local.mV[VY] < 0) ||
 547		(pos_local.mV[VX] >= getRect().getWidth()) ||
 548		(pos_local.mV[VY] >= getRect().getHeight()) )
 549	{
 550		if (draw_arrow)
 551		{
 552			S32 x = llround( pos_local.mV[VX] );
 553			S32 y = llround( pos_local.mV[VY] );
 554			LLWorldMapView::drawTrackingCircle( getRect(), x, y, color, 1, 10 );
 555			LLWorldMapView::drawTrackingArrow( getRect(), x, y, color );
 556		}
 557	}
 558	else
 559	{
 560		LLWorldMapView::drawTrackingDot(pos_local.mV[VX], 
 561										pos_local.mV[VY], 
 562										color,
 563										pos_local.mV[VZ]);
 564	}
 565}
 566
 567LLVector3d LLNetMap::viewPosToGlobal( S32 x, S32 y )
 568{
 569	x -= llround(getRect().getWidth() / 2 + mCurPan.mV[VX]);
 570	y -= llround(getRect().getHeight() / 2 + mCurPan.mV[VY]);
 571
 572	LLVector3 pos_local( (F32)x, (F32)y, 0 );
 573
 574	F32 radians = - atan2( LLViewerCamera::getInstance()->getAtAxis().mV[VX], LLViewerCamera::getInstance()->getAtAxis().mV[VY] );
 575
 576	static LLUICachedControl<bool> rotate_map("MiniMapRotate", true);
 577	if( rotate_map )
 578	{
 579		LLQuaternion rot(radians, LLVector3(0.f, 0.f, 1.f));
 580		pos_local.rotVec( rot );
 581	}
 582
 583	pos_local *= ( LLWorld::getInstance()->getRegionWidthInMeters() / mScale );
 584	
 585	LLVector3d pos_global;
 586	pos_global.setVec( pos_local );
 587	pos_global += gAgentCamera.getCameraPositionGlobal();
 588
 589	return pos_global;
 590}
 591
 592BOOL LLNetMap::handleScrollWheel(S32 x, S32 y, S32 clicks)
 593{
 594	// note that clicks are reversed from what you'd think: i.e. > 0  means zoom out, < 0 means zoom in
 595	F32 new_scale = mScale * pow(MAP_SCALE_ZOOM_FACTOR, -clicks);
 596	F32 old_scale = mScale;
 597
 598	setScale(new_scale);
 599
 600	static LLUICachedControl<bool> auto_center("MiniMapAutoCenter", true);
 601	if (!auto_center)
 602	{
 603		// Adjust pan to center the zoom on the mouse pointer
 604		LLVector2 zoom_offset;
 605		zoom_offset.mV[VX] = x - getRect().getWidth() / 2;
 606		zoom_offset.mV[VY] = y - getRect().getHeight() / 2;
 607		mCurPan -= zoom_offset * mScale / old_scale - zoom_offset;
 608	}
 609
 610	return TRUE;
 611}
 612
 613BOOL LLNetMap::handleToolTip( S32 x, S32 y, MASK mask )
 614{
 615	if (gDisconnected)
 616	{
 617		return FALSE;
 618	}
 619
 620	// If the cursor is near an avatar on the minimap, a mini-inspector will be
 621	// shown for the avatar, instead of the normal map tooltip.
 622	if (handleToolTipAgent(mClosestAgentToCursor))
 623	{
 624		return TRUE;
 625	}
 626
 627	LLRect sticky_rect;
 628	std::string region_name;
 629	LLViewerRegion*	region = LLWorld::getInstance()->getRegionFromPosGlobal( viewPosToGlobal( x, y ) );
 630	if(region)
 631	{
 632		// set sticky_rect
 633		S32 SLOP = 4;
 634		localPointToScreen(x - SLOP, y - SLOP, &(sticky_rect.mLeft), &(sticky_rect.mBottom));
 635		sticky_rect.mRight = sticky_rect.mLeft + 2 * SLOP;
 636		sticky_rect.mTop = sticky_rect.mBottom + 2 * SLOP;
 637
 638		region_name = region->getName();
 639		if (!region_name.empty())
 640		{
 641			region_name += "\n";
 642		}
 643	}
 644
 645	LLStringUtil::format_map_t args;
 646	args["[REGION]"] = region_name;
 647	std::string msg = mToolTipMsg;
 648	LLStringUtil::format(msg, args);
 649	LLToolTipMgr::instance().show(LLToolTip::Params()
 650		.message(msg)
 651		.sticky_rect(sticky_rect));
 652		
 653	return TRUE;
 654}
 655
 656BOOL LLNetMap::handleToolTipAgent(const LLUUID& avatar_id)
 657{
 658	LLAvatarName av_name;
 659	if (avatar_id.isNull() || !LLAvatarNameCache::get(avatar_id, &av_name))
 660	{
 661		return FALSE;
 662	}
 663
 664	// only show tooltip if same inspector not already open
 665	LLFloater* existing_inspector = LLFloaterReg::findInstance("inspect_avatar");
 666	if (!existing_inspector
 667		|| !existing_inspector->getVisible()
 668		|| existing_inspector->getKey()["avatar_id"].asUUID() != avatar_id)
 669	{
 670		LLInspector::Params p;
 671		p.fillFrom(LLUICtrlFactory::instance().getDefaultParams<LLInspector>());
 672		p.message(av_name.getCompleteName());
 673		p.image.name("Inspector_I");
 674		p.click_callback(boost::bind(showAvatarInspector, avatar_id));
 675		p.visible_time_near(6.f);
 676		p.visible_time_far(3.f);
 677		p.delay_time(0.35f);
 678		p.wrap(false);
 679
 680		LLToolTipMgr::instance().show(p);
 681	}
 682	return TRUE;
 683}
 684
 685// static
 686void LLNetMap::showAvatarInspector(const LLUUID& avatar_id)
 687{
 688	LLSD params;
 689	params["avatar_id"] = avatar_id;
 690
 691	if (LLToolTipMgr::instance().toolTipVisible())
 692	{
 693		LLRect rect = LLToolTipMgr::instance().getToolTipRect();
 694		params["pos"]["x"] = rect.mLeft;
 695		params["pos"]["y"] = rect.mTop;
 696	}
 697
 698	LLFloaterReg::showInstance("inspect_avatar", params);
 699}
 700
 701void LLNetMap::renderScaledPointGlobal( const LLVector3d& pos, const LLColor4U &color, F32 radius_meters )
 702{
 703	LLVector3 local_pos;
 704	local_pos.setVec( pos - mObjectImageCenterGlobal );
 705
 706	S32 diameter_pixels = llround(2 * radius_meters * mObjectMapTPM);
 707	renderPoint( local_pos, color, diameter_pixels );
 708}
 709
 710
 711void LLNetMap::renderPoint(const LLVector3 &pos_local, const LLColor4U &color, 
 712						   S32 diameter, S32 relative_height)
 713{
 714	if (diameter <= 0)
 715	{
 716		return;
 717	}
 718
 719	const S32 image_width = (S32)mObjectImagep->getWidth();
 720	const S32 image_height = (S32)mObjectImagep->getHeight();
 721
 722	S32 x_offset = llround(pos_local.mV[VX] * mObjectMapTPM + image_width / 2);
 723	S32 y_offset = llround(pos_local.mV[VY] * mObjectMapTPM + image_height / 2);
 724
 725	if ((x_offset < 0) || (x_offset >= image_width))
 726	{
 727		return;
 728	}
 729	if ((y_offset < 0) || (y_offset >= image_height))
 730	{
 731		return;
 732	}
 733
 734	U8 *datap = mObjectRawImagep->getData();
 735
 736	S32 neg_radius = diameter / 2;
 737	S32 pos_radius = diameter - neg_radius;
 738	S32 x, y;
 739
 740	if (relative_height > 0)
 741	{
 742		// ...point above agent
 743		S32 px, py;
 744
 745		// vertical line
 746		px = x_offset;
 747		for (y = -neg_radius; y < pos_radius; y++)
 748		{
 749			py = y_offset + y;
 750			if ((py < 0) || (py >= image_height))
 751			{
 752				continue;
 753			}
 754			S32 offset = px + py * image_width;
 755			((U32*)datap)[offset] = color.mAll;
 756		}
 757
 758		// top line
 759		py = y_offset + pos_radius - 1;
 760		for (x = -neg_radius; x < pos_radius; x++)
 761		{
 762			px = x_offset + x;
 763			if ((px < 0) || (px >= image_width))
 764			{
 765				continue;
 766			}
 767			S32 offset = px + py * image_width;
 768			((U32*)datap)[offset] = color.mAll;
 769		}
 770	}
 771	else
 772	{
 773		// ...point level with agent
 774		for (x = -neg_radius; x < pos_radius; x++)
 775		{
 776			S32 p_x = x_offset + x;
 777			if ((p_x < 0) || (p_x >= image_width))
 778			{
 779				continue;
 780			}
 781
 782			for (y = -neg_radius; y < pos_radius; y++)
 783			{
 784				S32 p_y = y_offset + y;
 785				if ((p_y < 0) || (p_y >= image_height))
 786				{
 787					continue;
 788				}
 789				S32 offset = p_x + p_y * image_width;
 790				((U32*)datap)[offset] = color.mAll;
 791			}
 792		}
 793	}
 794}
 795
 796void LLNetMap::createObjectImage()
 797{
 798	// Find the size of the side of a square that surrounds the circle that surrounds getRect().
 799	// ... which is, the diagonal of the rect.
 800	F32 width = (F32)getRect().getWidth();
 801	F32 height = (F32)getRect().getHeight();
 802	S32 square_size = llround( sqrt(width*width + height*height) );
 803
 804	// Find the least power of two >= the minimum size.
 805	const S32 MIN_SIZE = 64;
 806	const S32 MAX_SIZE = 256;
 807	S32 img_size = MIN_SIZE;
 808	while( (img_size*2 < square_size ) && (img_size < MAX_SIZE) )
 809	{
 810		img_size <<= 1;
 811	}
 812
 813	if( mObjectImagep.isNull() ||
 814		(mObjectImagep->getWidth() != img_size) ||
 815		(mObjectImagep->getHeight() != img_size) )
 816	{
 817		mObjectRawImagep = new LLImageRaw(img_size, img_size, 4);
 818		U8* data = mObjectRawImagep->getData();
 819		memset( data, 0, img_size * img_size * 4 );
 820		mObjectImagep = LLViewerTextureManager::getLocalTexture( mObjectRawImagep.get(), FALSE);
 821	}
 822	setScale(mScale);
 823	mUpdateNow = true;
 824}
 825
 826BOOL LLNetMap::handleMouseDown( S32 x, S32 y, MASK mask )
 827{
 828	if (!(mask & MASK_SHIFT)) return FALSE;
 829
 830	// Start panning
 831	gFocusMgr.setMouseCapture(this);
 832
 833	mStartPan = mCurPan;
 834	mMouseDown.mX = x;
 835	mMouseDown.mY = y;
 836	return TRUE;
 837}
 838
 839BOOL LLNetMap::handleMouseUp( S32 x, S32 y, MASK mask )
 840{
 841	if(abs(mMouseDown.mX-x)<3 && abs(mMouseDown.mY-y)<3)
 842		handleClick(x,y,mask);
 843
 844	if (hasMouseCapture())
 845	{
 846		if (mPanning)
 847		{
 848			// restore mouse cursor
 849			S32 local_x, local_y;
 850			local_x = mMouseDown.mX + llfloor(mCurPan.mV[VX] - mStartPan.mV[VX]);
 851			local_y = mMouseDown.mY + llfloor(mCurPan.mV[VY] - mStartPan.mV[VY]);
 852			LLRect clip_rect = getRect();
 853			clip_rect.stretch(-8);
 854			clip_rect.clipPointToRect(mMouseDown.mX, mMouseDown.mY, local_x, local_y);
 855			LLUI::setMousePositionLocal(this, local_x, local_y);
 856
 857			// finish the pan
 858			mPanning = false;
 859
 860			mMouseDown.set(0, 0);
 861
 862			// auto centre
 863			mTargetPan.setZero();
 864		}
 865		gViewerWindow->showCursor();
 866		gFocusMgr.setMouseCapture(NULL);
 867		return TRUE;
 868	}
 869	return FALSE;
 870}
 871
 872BOOL LLNetMap::handleRightMouseDown(S32 x, S32 y, MASK mask)
 873{
 874	if (mPopupMenu)
 875	{
 876		mPopupMenu->buildDrawLabels();
 877		mPopupMenu->updateParent(LLMenuGL::sMenuContainer);
 878		mPopupMenu->setItemEnabled("Stop Tracking", LLTracker::isTracking(0));
 879		LLMenuGL::showPopup(this, mPopupMenu, x, y);
 880	}
 881	return TRUE;
 882}
 883
 884BOOL LLNetMap::handleClick(S32 x, S32 y, MASK mask)
 885{
 886	// TODO: allow clicking an avatar on minimap to select avatar in the nearby avatar list
 887	// if(mClosestAgentToCursor.notNull())
 888	//     mNearbyList->selectUser(mClosestAgentToCursor);
 889	// Needs a registered observer i guess to accomplish this without using
 890	// globals to tell the mNearbyList in llpeoplepanel to select the user
 891	return TRUE;
 892}
 893
 894BOOL LLNetMap::handleDoubleClick(S32 x, S32 y, MASK mask)
 895{
 896	LLVector3d pos_global = viewPosToGlobal(x, y);
 897
 898	bool double_click_teleport = gSavedSettings.getBOOL("DoubleClickTeleport");
 899	bool double_click_show_world_map = gSavedSettings.getBOOL("DoubleClickShowWorldMap");
 900
 901	if (double_click_teleport || double_click_show_world_map)
 902	{
 903		// If we're not tracking a beacon already, double-click will set one 
 904		if (!LLTracker::isTracking(NULL))
 905		{
 906			LLFloaterWorldMap* world_map = LLFloaterWorldMap::getInstance();
 907			if (world_map)
 908			{
 909				world_map->trackLocation(pos_global);
 910			}
 911		}
 912	}
 913
 914	if (double_click_teleport)
 915	{
 916		// If DoubleClickTeleport is on, double clicking the minimap will teleport there
 917		gAgent.teleportViaLocationLookAt(pos_global);
 918	}
 919	else if (double_click_show_world_map)
 920	{
 921		LLFloaterReg::showInstance("world_map");
 922	}
 923	return TRUE;
 924}
 925
 926// static
 927bool LLNetMap::outsideSlop( S32 x, S32 y, S32 start_x, S32 start_y, S32 slop )
 928{
 929	S32 dx = x - start_x;
 930	S32 dy = y - start_y;
 931
 932	return (dx <= -slop || slop <= dx || dy <= -slop || slop <= dy);
 933}
 934
 935BOOL LLNetMap::handleHover( S32 x, S32 y, MASK mask )
 936{
 937	if (hasMouseCapture())
 938	{
 939		if (mPanning || outsideSlop(x, y, mMouseDown.mX, mMouseDown.mY, MOUSE_DRAG_SLOP))
 940		{
 941			if (!mPanning)
 942			{
 943				// just started panning, so hide cursor
 944				mPanning = true;
 945				gViewerWindow->hideCursor();
 946			}
 947
 948			LLVector2 delta(static_cast<F32>(gViewerWindow->getCurrentMouseDX()),
 949							static_cast<F32>(gViewerWindow->getCurrentMouseDY()));
 950
 951			// Set pan to value at start of drag + offset
 952			mCurPan += delta;
 953			mTargetPan = mCurPan;
 954
 955			gViewerWindow->moveCursorToCenter();
 956		}
 957
 958		// Doesn't really matter, cursor should be hidden
 959		gViewerWindow->setCursor( UI_CURSOR_TOOLPAN );
 960	}
 961	else
 962	{
 963		if (mask & MASK_SHIFT)
 964		{
 965			// If shift is held, change the cursor to hint that the map can be dragged
 966			gViewerWindow->setCursor( UI_CURSOR_TOOLPAN );
 967		}
 968		else
 969		{
 970			gViewerWindow->setCursor( UI_CURSOR_CROSS );
 971		}
 972	}
 973
 974	return TRUE;
 975}
 976
 977void LLNetMap::handleZoom(const LLSD& userdata)
 978{
 979	std::string level = userdata.asString();
 980	
 981	F32 scale = 0.0f;
 982	if (level == std::string("default"))
 983	{
 984		LLControlVariable *pvar = gSavedSettings.getControl("MiniMapScale");
 985		if(pvar)
 986		{
 987			pvar->resetToDefault();
 988			scale = gSavedSettings.getF32("MiniMapScale");
 989		}
 990	}
 991	else if (level == std::string("close"))
 992		scale = LLNetMap::MAP_SCALE_MAX;
 993	else if (level == std::string("medium"))
 994		scale = LLNetMap::MAP_SCALE_MID;
 995	else if (level == std::string("far"))
 996		scale = LLNetMap::MAP_SCALE_MIN;
 997	if (scale != 0.0f)
 998	{
 999		setScale(scale);
1000	}
1001}
1002
1003void LLNetMap::handleStopTracking (const LLSD& userdata)
1004{
1005	if (mPopupMenu)
1006	{
1007		mPopupMenu->setItemEnabled ("Stop Tracking", false);
1008		LLTracker::stopTracking ((void*)LLTracker::isTracking(NULL));
1009	}
1010}