diff options
author | Ansariel <ansariel.hiller@phoenixviewer.com> | 2024-05-22 21:25:21 +0200 |
---|---|---|
committer | Andrey Lihatskiy <alihatskiy@productengine.com> | 2024-05-22 22:40:26 +0300 |
commit | e2e37cced861b98de8c1a7c9c0d3a50d2d90e433 (patch) | |
tree | 1bb897489ce524986f6196201c10ac0d8861aa5f /indra/newview/llworldmapview.cpp | |
parent | 069ea06848f766466f1a281144c82a0f2bd79f3a (diff) |
Fix line endlings
Diffstat (limited to 'indra/newview/llworldmapview.cpp')
-rwxr-xr-x | indra/newview/llworldmapview.cpp | 3664 |
1 files changed, 1832 insertions, 1832 deletions
diff --git a/indra/newview/llworldmapview.cpp b/indra/newview/llworldmapview.cpp index e84c34ae21..09a18a9825 100755 --- a/indra/newview/llworldmapview.cpp +++ b/indra/newview/llworldmapview.cpp @@ -1,1832 +1,1832 @@ -/**
- * @file llworldmapview.cpp
- * @brief LLWorldMapView class implementation
- *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#include "llviewerprecompiledheaders.h"
-
-#include "llworldmapview.h"
-
-#include "indra_constants.h"
-#include "llui.h"
-#include "llmath.h" // clampf()
-#include "llregionhandle.h"
-#include "lleventflags.h"
-#include "llfloaterreg.h"
-#include "llrender.h"
-#include "lltooltip.h"
-
-#include "llagent.h"
-#include "llagentcamera.h"
-#include "llcallingcard.h"
-#include "llcommandhandler.h"
-#include "llviewercontrol.h"
-#include "llfloatermap.h"
-#include "llfloaterworldmap.h"
-#include "llfocusmgr.h"
-#include "lllocalcliprect.h"
-#include "lltextbox.h"
-#include "lltextureview.h"
-#include "lltracker.h"
-#include "llviewercamera.h"
-#include "llviewernetwork.h"
-#include "llviewertexture.h"
-#include "llviewertexturelist.h"
-#include "llviewerregion.h"
-#include "llviewerwindow.h"
-#include "lltrans.h"
-
-#include "llglheaders.h"
-
-// # Constants
-static constexpr F32 MAP_DEFAULT_SCALE = 128.f;
-static constexpr F32 MAP_ITERP_TIME_CONSTANT = 0.75f;
-static constexpr F32 MAP_ZOOM_ACCELERATION_TIME = 0.3f;
-static constexpr F32 MAP_ZOOM_MAX_INTERP = 0.5f;
-static constexpr F32 MAP_SCALE_SNAP_THRESHOLD = 0.005f;
-
-// Basically a C++ implementation of the OCEAN_COLOR defined in mapstitcher.py
-// Please ensure consistency between those 2 files (TODO: would be better to get that color from an asset source...)
-// OCEAN_COLOR = "#1D475F"
-constexpr F32 OCEAN_RED = (F32)(0x1D)/255.f;
-constexpr F32 OCEAN_GREEN = (F32)(0x47)/255.f;
-constexpr F32 OCEAN_BLUE = (F32)(0x5F)/255.f;
-
-constexpr F32 GODLY_TELEPORT_HEIGHT = 200.f;
-constexpr F32 BIG_DOT_RADIUS = 5.f;
-bool LLWorldMapView::sHandledLastClick = false;
-
-LLUIImagePtr LLWorldMapView::sAvatarSmallImage = NULL;
-LLUIImagePtr LLWorldMapView::sAvatarYouImage = NULL;
-LLUIImagePtr LLWorldMapView::sAvatarYouLargeImage = NULL;
-LLUIImagePtr LLWorldMapView::sAvatarLevelImage = NULL;
-LLUIImagePtr LLWorldMapView::sAvatarAboveImage = NULL;
-LLUIImagePtr LLWorldMapView::sAvatarBelowImage = NULL;
-LLUIImagePtr LLWorldMapView::sAvatarUnknownImage = NULL;
-
-LLUIImagePtr LLWorldMapView::sTelehubImage = NULL;
-LLUIImagePtr LLWorldMapView::sInfohubImage = NULL;
-LLUIImagePtr LLWorldMapView::sHomeImage = NULL;
-LLUIImagePtr LLWorldMapView::sEventImage = NULL;
-LLUIImagePtr LLWorldMapView::sEventMatureImage = NULL;
-LLUIImagePtr LLWorldMapView::sEventAdultImage = NULL;
-
-LLUIImagePtr LLWorldMapView::sTrackCircleImage = NULL;
-LLUIImagePtr LLWorldMapView::sTrackArrowImage = NULL;
-
-LLUIImagePtr LLWorldMapView::sClassifiedsImage = NULL;
-LLUIImagePtr LLWorldMapView::sForSaleImage = NULL;
-LLUIImagePtr LLWorldMapView::sForSaleAdultImage = NULL;
-
-S32 LLWorldMapView::sTrackingArrowX = 0;
-S32 LLWorldMapView::sTrackingArrowY = 0;
-bool LLWorldMapView::sVisibleTilesLoaded = false;
-F32 LLWorldMapView::sMapScaleSetting = MAP_DEFAULT_SCALE;
-LLVector2 LLWorldMapView::sZoomPivot = LLVector2(0.0f, 0.0f);
-LLFrameTimer LLWorldMapView::sZoomTimer = LLFrameTimer();
-
-std::map<std::string,std::string> LLWorldMapView::sStringsMap;
-
-// Fetch and draw info thresholds
-const F32 DRAW_TEXT_THRESHOLD = 96.f; // Don't draw text under that resolution value (res = width region in meters)
-const S32 DRAW_SIMINFO_THRESHOLD = 3; // Max level for which we load or display sim level information (level in LLWorldMipmap sense)
-const S32 DRAW_LANDFORSALE_THRESHOLD = 2; // Max level for which we load or display land for sale picture data (level in LLWorldMipmap sense)
-
-// When on, draw an outline for each mipmap tile gotten from S3
-#define DEBUG_DRAW_TILE 0
-
-
-void LLWorldMapView::initClass()
-{
- sAvatarSmallImage = LLUI::getUIImage("map_avatar_8.tga");
- sAvatarYouImage = LLUI::getUIImage("map_avatar_16.tga");
- sAvatarYouLargeImage = LLUI::getUIImage("map_avatar_you_32.tga");
- sAvatarLevelImage = LLUI::getUIImage("map_avatar_32.tga");
- sAvatarAboveImage = LLUI::getUIImage("map_avatar_above_32.tga");
- sAvatarBelowImage = LLUI::getUIImage("map_avatar_below_32.tga");
- sAvatarUnknownImage = LLUI::getUIImage("map_avatar_unknown_32.tga");
-
- sHomeImage = LLUI::getUIImage("map_home.tga");
- sTelehubImage = LLUI::getUIImage("map_telehub.tga");
- sInfohubImage = LLUI::getUIImage("map_infohub.tga");
- sEventImage = LLUI::getUIImage("Parcel_PG_Dark");
- sEventMatureImage = LLUI::getUIImage("Parcel_M_Dark");
- // To Do: update the image resource for adult events.
- sEventAdultImage = LLUI::getUIImage("Parcel_R_Dark");
-
- sTrackCircleImage = LLUI::getUIImage("map_track_16.tga");
- sTrackArrowImage = LLUI::getUIImage("direction_arrow.tga");
- sClassifiedsImage = LLUI::getUIImage("icon_top_pick.tga");
- sForSaleImage = LLUI::getUIImage("icon_for_sale.tga");
- // To Do: update the image resource for adult lands on sale.
- sForSaleAdultImage = LLUI::getUIImage("icon_for_sale_adult.tga");
-
- sStringsMap["loading"] = LLTrans::getString("texture_loading");
- sStringsMap["offline"] = LLTrans::getString("worldmap_offline");
-}
-
-// static
-void LLWorldMapView::cleanupClass()
-{
- sAvatarSmallImage = NULL;
- sAvatarYouImage = NULL;
- sAvatarYouLargeImage = NULL;
- sAvatarLevelImage = NULL;
- sAvatarAboveImage = NULL;
- sAvatarBelowImage = NULL;
- sAvatarUnknownImage = NULL;
-
- sTelehubImage = NULL;
- sInfohubImage = NULL;
- sHomeImage = NULL;
- sEventImage = NULL;
- sEventMatureImage = NULL;
- sEventAdultImage = NULL;
-
- sTrackCircleImage = NULL;
- sTrackArrowImage = NULL;
- sClassifiedsImage = NULL;
- sForSaleImage = NULL;
- sForSaleAdultImage = NULL;
-}
-
-LLWorldMapView::LLWorldMapView() :
- LLPanel(),
- mBackgroundColor(LLColor4(OCEAN_RED, OCEAN_GREEN, OCEAN_BLUE, 1.f)),
- mItemPicked(false),
- mPanX(0.f),
- mPanY(0.f),
- mTargetPanX(0.f),
- mTargetPanY(0.f),
- mPanning(false),
- mMouseDownPanX(0),
- mMouseDownPanY(0),
- mMouseDownX(0),
- mMouseDownY(0),
- mSelectIDStart(0),
- mMapScale(0.f),
- mTargetMapScale(0.f),
- mMapIterpTime(MAP_ITERP_TIME_CONSTANT)
-{
- // LL_INFOS("WorldMap") << "Creating the Map -> LLWorldMapView::LLWorldMapView()" << LL_ENDL;
-
- clearLastClick();
-}
-
-bool LLWorldMapView::postBuild()
-{
- mTextBoxNorth = getChild<LLTextBox> ("floater_map_north");
- mTextBoxEast = getChild<LLTextBox> ("floater_map_east");
- mTextBoxWest = getChild<LLTextBox> ("floater_map_west");
- mTextBoxSouth = getChild<LLTextBox> ("floater_map_south");
- mTextBoxSouthEast = getChild<LLTextBox> ("floater_map_southeast");
- mTextBoxNorthEast = getChild<LLTextBox> ("floater_map_northeast");
- mTextBoxSouthWest = getChild<LLTextBox> ("floater_map_southwest");
- mTextBoxNorthWest = getChild<LLTextBox> ("floater_map_northwest");
-
- mTextBoxNorth->setText(getString("world_map_north"));
- mTextBoxEast->setText(getString ("world_map_east"));
- mTextBoxWest->setText(getString("world_map_west"));
- mTextBoxSouth->setText(getString ("world_map_south"));
- mTextBoxSouthEast ->setText(getString ("world_map_southeast"));
- mTextBoxNorthEast ->setText(getString ("world_map_northeast"));
- mTextBoxSouthWest->setText(getString ("world_map_southwest"));
- mTextBoxNorthWest ->setText(getString("world_map_northwest"));
-
- mTextBoxNorth->reshapeToFitText();
- mTextBoxEast->reshapeToFitText();
- mTextBoxWest->reshapeToFitText();
- mTextBoxSouth->reshapeToFitText();
- mTextBoxSouthEast ->reshapeToFitText();
- mTextBoxNorthEast ->reshapeToFitText();
- mTextBoxSouthWest->reshapeToFitText();
- mTextBoxNorthWest ->reshapeToFitText();
-
- sZoomTimer.stop();
- setScale(sMapScaleSetting, true);
-
- return true;
-}
-
-
-LLWorldMapView::~LLWorldMapView()
-{
- //LL_INFOS("WorldMap") << "Destroying the map -> LLWorldMapView::~LLWorldMapView()" << LL_ENDL;
- cleanupTextures();
-}
-
-
-// static
-void LLWorldMapView::cleanupTextures()
-{
-}
-
-void LLWorldMapView::zoom(F32 zoom)
-{
- mTargetMapScale = scaleFromZoom(zoom);
- if (!sZoomTimer.getStarted() && mMapScale != mTargetMapScale)
- {
- sZoomPivot = LLVector2(0, 0);
- sZoomTimer.start();
- }
-}
-
-void LLWorldMapView::zoomWithPivot(F32 zoom, S32 x, S32 y)
-{
- mTargetMapScale = scaleFromZoom(zoom);
- sZoomPivot = LLVector2(x, y);
- if (!sZoomTimer.getStarted() && mMapScale != mTargetMapScale)
- {
- sZoomTimer.start();
- }
-}
-
-F32 LLWorldMapView::getZoom() { return LLWorldMapView::zoomFromScale(mMapScale); }
-
-F32 LLWorldMapView::getScale() { return mMapScale; }
-
-// static
-void LLWorldMapView::setScaleSetting(F32 scaleSetting) { sMapScaleSetting = scaleSetting; }
-
-// static
-F32 LLWorldMapView::getScaleSetting() { return sMapScaleSetting; }
-
-void LLWorldMapView::setScale(F32 scale, bool snap)
-{
- if (scale != mMapScale)
- {
- F32 old_scale = mMapScale;
-
- mMapScale = scale;
- // Set the scale used when saving the setting
- sMapScaleSetting = scale;
- if (mMapScale <= 0.f)
- {
- mMapScale = 0.1f;
- }
- mMapIterpTime = MAP_ITERP_TIME_CONSTANT;
- F32 ratio = (scale / old_scale);
- mPanX *= ratio;
- mPanY *= ratio;
- mTargetPanX = mPanX;
- mTargetPanY = mPanY;
- sVisibleTilesLoaded = false;
-
- // If we are zooming relative to somewhere else rather than the center of the map, compensate for the difference in panning here
- if (!sZoomPivot.isExactlyZero())
- {
- LLVector2 relative_pivot;
- relative_pivot.mV[VX] = sZoomPivot.mV[VX] - (getRect().getWidth() / 2.0);
- relative_pivot.mV[VY] = sZoomPivot.mV[VY] - (getRect().getHeight() / 2.0);
- LLVector2 zoom_pan_offset = relative_pivot - (relative_pivot * scale / old_scale);
- mPanX += zoom_pan_offset.mV[VX];
- mPanY += zoom_pan_offset.mV[VY];
- mTargetPanX += zoom_pan_offset.mV[VX];
- mTargetPanY += zoom_pan_offset.mV[VY];
- }
- }
-
- if (snap)
- {
- mTargetMapScale = scale;
- }
-}
-
-// static
-void LLWorldMapView::translatePan(S32 delta_x, S32 delta_y)
-{
- mPanX += delta_x;
- mPanY += delta_y;
- mTargetPanX = mPanX;
- mTargetPanY = mPanY;
- sVisibleTilesLoaded = false;
-}
-
-
-// static
-void LLWorldMapView::setPan(S32 x, S32 y, bool snap)
-{
- mMapIterpTime = MAP_ITERP_TIME_CONSTANT;
- mTargetPanX = (F32) x;
- mTargetPanY = (F32) y;
- if (snap)
- {
- mPanX = mTargetPanX;
- mPanY = mTargetPanY;
- }
- sVisibleTilesLoaded = false;
-}
-
-// static
-void LLWorldMapView::setPanWithInterpTime(S32 x, S32 y, bool snap, F32 interp_time)
-{
- setPan(x, y, snap);
- mMapIterpTime = interp_time;
-}
-
-bool LLWorldMapView::showRegionInfo() { return LLWorldMipmap::scaleToLevel(mMapScale) <= DRAW_SIMINFO_THRESHOLD; }
-
-///////////////////////////////////////////////////////////////////////////////////
-// HELPERS
-
-bool is_agent_in_region(LLViewerRegion* region, LLSimInfo* info)
-{
- return (region && info && info->isName(region->getName()));
-}
-
-///////////////////////////////////////////////////////////////////////////////////
-
-void LLWorldMapView::draw()
-{
- static LLUIColor map_track_color = LLUIColorTable::instance().getColor("MapTrackColor", LLColor4::white);
-
- LLTextureView::clearDebugImages();
-
- F64 current_time = LLTimer::getElapsedSeconds();
-
- mVisibleRegions.clear();
-
- // animate pan if necessary
- mPanX = lerp(mPanX, mTargetPanX, LLSmoothInterpolation::getInterpolant(mMapIterpTime));
- mPanY = lerp(mPanY, mTargetPanY, LLSmoothInterpolation::getInterpolant(mMapIterpTime));
-
- //RN: snaps to zoom value because interpolation caused jitter in the text rendering
- if (!sZoomTimer.getStarted() && mMapScale != mTargetMapScale)
- {
- sZoomTimer.start();
- }
- bool snap_scale = false;
- F32 interp = llmin(MAP_ZOOM_MAX_INTERP, sZoomTimer.getElapsedTimeF32() / MAP_ZOOM_ACCELERATION_TIME);
- F32 current_zoom_val = zoomFromScale(mMapScale);
- F32 target_zoom_val = zoomFromScale(mTargetMapScale);
- F32 new_zoom_val = lerp(current_zoom_val, target_zoom_val, interp);
- if (abs(new_zoom_val - current_zoom_val) < MAP_SCALE_SNAP_THRESHOLD)
- {
- sZoomTimer.stop();
- snap_scale = true;
- new_zoom_val = target_zoom_val;
- }
- F32 map_scale = scaleFromZoom(new_zoom_val);
- setScale(map_scale, snap_scale);
-
- const S32 width = getRect().getWidth();
- const S32 height = getRect().getHeight();
- const F32 half_width = F32(width) / 2.0f;
- const F32 half_height = F32(height) / 2.0f;
- LLVector3d camera_global = gAgentCamera.getCameraPositionGlobal();
-
- S32 level = LLWorldMipmap::scaleToLevel(mMapScale);
-
- LLLocalClipRect clip(getLocalRect());
- gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
-
- gGL.matrixMode(LLRender::MM_MODELVIEW);
-
- // Draw background rectangle
- gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
- gGL.color4fv(mBackgroundColor.mV);
- gl_rect_2d(0, height, width, 0);
-
- // Draw the image tiles
- drawMipmap(width, height);
-
- // Draw per sim overlayed information (names, mature, offline...)
- for (LLWorldMap::sim_info_map_t::const_iterator it = LLWorldMap::getInstance()->getRegionMap().begin();
- it != LLWorldMap::getInstance()->getRegionMap().end(); ++it)
- {
- U64 handle = it->first;
- LLSimInfo* info = it->second;
-
- LLVector3d origin_global = from_region_handle(handle);
-
- // Find x and y position relative to camera's center.
- LLVector3d rel_region_pos = origin_global - camera_global;
- F32 relative_x = (rel_region_pos.mdV[0] / REGION_WIDTH_METERS) * mMapScale;
- F32 relative_y = (rel_region_pos.mdV[1] / REGION_WIDTH_METERS) * mMapScale;
-
- // Coordinates of the sim in pixels in the UI panel
- // When the view isn't panned, 0,0 = center of rectangle
- F32 bottom = mPanY + half_height + relative_y;
- F32 left = mPanX + half_width + relative_x;
- F32 top = bottom + mMapScale ;
- F32 right = left + mMapScale ;
-
- // Discard if region is outside the screen rectangle (not visible on screen)
- if ((top < 0.f) || (bottom > height) ||
- (right < 0.f) || (left > width) )
- {
- // Drop the "land for sale" fetching priority since it's outside the view rectangle
- info->dropImagePriority();
- continue;
- }
-
- // This list is used by other methods to know which regions are indeed displayed on screen
-
- mVisibleRegions.push_back(handle);
-
- // Update the agent count for that region if we're not too zoomed out already
- if (level <= DRAW_SIMINFO_THRESHOLD)
- {
- info->updateAgentCount(current_time);
- }
-
- if (info->isDown())
- {
- // Draw a transparent red square over down sims
- gGL.color4f(0.2f, 0.0f, 0.0f, 0.4f);
-
- gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
- gGL.begin(LLRender::QUADS);
- gGL.vertex2f(left, top);
- gGL.vertex2f(left, bottom);
- gGL.vertex2f(right, bottom);
- gGL.vertex2f(right, top);
- gGL.end();
- }
- else if (gSavedSettings.getBOOL("MapShowLandForSale") && (level <= DRAW_LANDFORSALE_THRESHOLD))
- {
- // Draw the overlay image "Land for Sale / Land for Auction"
- LLViewerFetchedTexture* overlayimage = info->getLandForSaleImage();
- if (overlayimage)
- {
- // Inform the fetch mechanism of the size we need
- S32 draw_size = ll_round(mMapScale);
- overlayimage->setKnownDrawSize(ll_round(draw_size * LLUI::getScaleFactor().mV[VX]), ll_round(draw_size * LLUI::getScaleFactor().mV[VY]));
- // Draw something whenever we have enough info
- if (overlayimage->hasGLTexture())
- {
- gGL.getTexUnit(0)->bind(overlayimage);
- gGL.color4f(1.f, 1.f, 1.f, 1.f);
- gGL.begin(LLRender::QUADS);
- gGL.texCoord2f(0.f, 1.f);
- gGL.vertex3f(left, top, -0.5f);
- gGL.texCoord2f(0.f, 0.f);
- gGL.vertex3f(left, bottom, -0.5f);
- gGL.texCoord2f(1.f, 0.f);
- gGL.vertex3f(right, bottom, -0.5f);
- gGL.texCoord2f(1.f, 1.f);
- gGL.vertex3f(right, top, -0.5f);
- gGL.end();
- }
- }
- }
- else
- {
- // If we're not displaying the "land for sale", drop its fetching priority
- info->dropImagePriority();
- }
-
- // Draw the region name in the lower left corner
- if (mMapScale >= DRAW_TEXT_THRESHOLD)
- {
- LLFontGL* font = LLFontGL::getFont(LLFontDescriptor("SansSerif", "Small", LLFontGL::BOLD));
- std::string mesg;
- if (info->isDown())
- {
- mesg = llformat( "%s (%s)", info->getName().c_str(), sStringsMap["offline"].c_str());
- }
- else
- {
- mesg = info->getName();
- }
- if (!mesg.empty())
- {
- font->renderUTF8(
- mesg, 0,
- llfloor(left + 3), llfloor(bottom + 2),
- LLColor4::white,
- LLFontGL::LEFT, LLFontGL::BASELINE, LLFontGL::NORMAL, LLFontGL::DROP_SHADOW,
- S32_MAX, //max_chars
- mMapScale, //max_pixels
- NULL,
- /*use_ellipses*/true);
- }
- }
- }
-
- // Draw item infos if we're not zoomed out too much and there's something to draw
- if ((level <= DRAW_SIMINFO_THRESHOLD) && (gSavedSettings.getBOOL("MapShowInfohubs") ||
- gSavedSettings.getBOOL("MapShowTelehubs") ||
- gSavedSettings.getBOOL("MapShowLandForSale") ||
- gSavedSettings.getBOOL("MapShowEvents") ||
- gSavedSettings.getBOOL("ShowMatureEvents") ||
- gSavedSettings.getBOOL("ShowAdultEvents")))
- {
- drawItems();
- }
-
- // Draw the Home location (always)
- LLVector3d home;
- if (gAgent.getHomePosGlobal(&home))
- {
- drawImage(home, sHomeImage);
- }
-
- // Draw the current agent after all that other stuff.
- LLVector3d pos_global = gAgent.getPositionGlobal();
- drawImage(pos_global, sAvatarYouImage);
-
- LLVector3 pos_map = globalPosToView(pos_global);
- if (!pointInView(ll_round(pos_map.mV[VX]), ll_round(pos_map.mV[VY])))
- {
- drawTracking(pos_global,
- lerp(LLColor4::yellow, LLColor4::orange, 0.4f),
- true,
- "You are here",
- "",
- LLFontGL::getFontSansSerifSmall()->getLineHeight()); // offset vertically by one line, to avoid overlap with target tracking
- }
-
- // Draw the current agent viewing angle
- drawFrustum();
-
- // Draw icons for the avatars in each region.
- // Drawn this after the current agent avatar so one can see nearby people
- if (gSavedSettings.getBOOL("MapShowPeople") && (level <= DRAW_SIMINFO_THRESHOLD))
- {
- drawAgents();
- }
-
- // Always draw tracking information
- LLTracker::ETrackingStatus tracking_status = LLTracker::getTrackingStatus();
- if ( LLTracker::TRACKING_AVATAR == tracking_status )
- {
- drawTracking( LLAvatarTracker::instance().getGlobalPos(), map_track_color, true, LLTracker::getLabel(), "" );
- }
- else if ( LLTracker::TRACKING_LANDMARK == tracking_status
- || LLTracker::TRACKING_LOCATION == tracking_status )
- {
- // While fetching landmarks, will have 0,0,0 location for a while,
- // so don't draw. JC
- LLVector3d pos_global = LLTracker::getTrackedPositionGlobal();
- if (!pos_global.isExactlyZero())
- {
- drawTracking( pos_global, map_track_color, true, LLTracker::getLabel(), LLTracker::getToolTip() );
- }
- }
- else if (LLWorldMap::getInstance()->isTracking())
- {
- if (LLWorldMap::getInstance()->isTrackingInvalidLocation())
- {
- // We know this location to be invalid, draw a blue circle
- LLColor4 loading_color(0.0, 0.5, 1.0, 1.0);
- drawTracking( LLWorldMap::getInstance()->getTrackedPositionGlobal(), loading_color, true, getString("InvalidLocation"), "");
- }
- else
- {
- // We don't know yet what that location is, draw a throbing blue circle
- double value = fmod(current_time, 2);
- value = 0.5 + 0.5*cos(value * F_PI);
- LLColor4 loading_color(0.0, F32(value/2), F32(value), 1.0);
- drawTracking( LLWorldMap::getInstance()->getTrackedPositionGlobal(), loading_color, true, getString("Loading"), "");
- }
- }
-
-
- // turn off the scissor
- LLGLDisable no_scissor(GL_SCISSOR_TEST);
-
- updateDirections();
-
- LLView::draw();
-
- // Get sim info for all sims in view
- updateVisibleBlocks();
-} // end draw()
-
-
-//virtual
-void LLWorldMapView::setVisible(bool visible)
-{
- LLPanel::setVisible(visible);
- if (!visible)
- {
- // Drop the download of tiles and images priority to nil if we hide the map
- LLWorldMap::getInstance()->dropImagePriorities();
- }
-}
-
-void LLWorldMapView::drawMipmap(S32 width, S32 height)
-{
- // Compute the level of the mipmap to use for the current scale level
- S32 level = LLWorldMipmap::scaleToLevel(mMapScale);
- // Set the tile boost level so that unused tiles get to 0
- LLWorldMap::getInstance()->equalizeBoostLevels();
-
- // Render whatever we already have loaded if we haven't the current level
- // complete and use it as a background (scaled up or scaled down)
- if (!sVisibleTilesLoaded)
- {
- // Note: the (load = false) parameter avoids missing tiles to be fetched (i.e. we render what we have, no more)
- // Check all the lower res levels and render them in reverse order (worse to best)
- // We need to traverse all the levels as the user can zoom in very fast
- for (S32 l = LLWorldMipmap::MAP_LEVELS; l > level; l--)
- {
- drawMipmapLevel(width, height, l, false);
- }
- // Skip the current level, as we'll do it anyway here under...
-
- // Just go one level down in res as it can really get too much stuff
- // when zooming out and too small to see anyway...
- if (level > 1)
- {
- drawMipmapLevel(width, height, level - 1, false);
- }
- }
- else
- {
- //LL_INFOS("WorldMap") << "Render complete, don't draw background..." << LL_ENDL;
- }
-
- // Render the current level
- sVisibleTilesLoaded = drawMipmapLevel(width, height, level);
-
- return;
-}
-
-// Return true if all the tiles required to render that level have been fetched or are truly missing
-bool LLWorldMapView::drawMipmapLevel(S32 width, S32 height, S32 level, bool load)
-{
- // Check input level
- llassert (level > 0);
- if (level <= 0)
- return false;
-
- // Count tiles hit and completed
- S32 completed_tiles = 0;
- S32 total_tiles = 0;
-
- // Size in meters (global) of each tile of that level
- S32 tile_width = LLWorldMipmap::MAP_TILE_SIZE * (1 << (level - 1));
- // Dimension of the screen in meter at that scale
- LLVector3d pos_SW = viewPosToGlobal(0, 0);
- LLVector3d pos_NE = viewPosToGlobal(width, height);
- // Add external band of tiles on the outskirt so to hit the partially displayed tiles right and top
- pos_NE[VX] += tile_width;
- pos_NE[VY] += tile_width;
-
- // Iterate through the tiles on screen: we just need to ask for one tile every tile_width meters
- U32 grid_x, grid_y;
- for (F64 index_y = pos_SW[VY]; index_y < pos_NE[VY]; index_y += tile_width)
- {
- for (F64 index_x = pos_SW[VX]; index_x < pos_NE[VX]; index_x += tile_width)
- {
- // Compute the world coordinates of the current point
- LLVector3d pos_global(index_x, index_y, pos_SW[VZ]);
- // Convert to the mipmap level coordinates for that point (i.e. which tile to we hit)
- LLWorldMipmap::globalToMipmap(pos_global[VX], pos_global[VY], level, &grid_x, &grid_y);
- // Get the tile. Note: NULL means that the image does not exist (so it's considered "complete" as far as fetching is concerned)
- LLPointer<LLViewerFetchedTexture> simimage = LLWorldMap::getInstance()->getObjectsTile(grid_x, grid_y, level, load);
- if (simimage)
- {
- // Checks that the image has a valid texture
- if (simimage->hasGLTexture())
- {
- // Increment the number of completly fetched tiles
- completed_tiles++;
-
- // Convert those coordinates (SW corner of the mipmap tile) into world (meters) coordinates
- pos_global[VX] = grid_x * REGION_WIDTH_METERS;
- pos_global[VY] = grid_y * REGION_WIDTH_METERS;
- // Now to screen coordinates for SW corner of that tile
- LLVector3 pos_screen = globalPosToView (pos_global);
- F32 left = pos_screen[VX];
- F32 bottom = pos_screen[VY];
- // Compute the NE corner coordinates of the tile now
- pos_global[VX] += tile_width;
- pos_global[VY] += tile_width;
- pos_screen = globalPosToView (pos_global);
- F32 right = pos_screen[VX];
- F32 top = pos_screen[VY];
-
- // Draw the tile
- LLGLSUIDefault gls_ui;
- gGL.getTexUnit(0)->bind(simimage.get());
- simimage->setAddressMode(LLTexUnit::TAM_CLAMP);
-
- gGL.color4f(1.f, 1.0f, 1.0f, 1.0f);
-
- gGL.begin(LLRender::QUADS);
- gGL.texCoord2f(0.f, 1.f);
- gGL.vertex3f(left, top, 0.f);
- gGL.texCoord2f(0.f, 0.f);
- gGL.vertex3f(left, bottom, 0.f);
- gGL.texCoord2f(1.f, 0.f);
- gGL.vertex3f(right, bottom, 0.f);
- gGL.texCoord2f(1.f, 1.f);
- gGL.vertex3f(right, top, 0.f);
- gGL.end();
-#if DEBUG_DRAW_TILE
- drawTileOutline(level, top, left, bottom, right);
-#endif // DEBUG_DRAW_TILE
- }
- //else
- //{
- // Waiting for a tile -> the level is not complete
- // LL_INFOS("WorldMap") << "Unfetched tile. level = " << level << LL_ENDL;
- //}
- }
- else
- {
- // Unexistent tiles are counted as "completed"
- completed_tiles++;
- }
- // Increment the number of tiles in that level / screen
- total_tiles++;
- }
- }
- return (completed_tiles == total_tiles);
-}
-
-// Draw lines (rectangle outline and cross) to visualize the position of the tile
-// Used for debug only
-void LLWorldMapView::drawTileOutline(S32 level, F32 top, F32 left, F32 bottom, F32 right)
-{
- gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
- if (level == 1)
- gGL.color3f(1.f, 0.f, 0.f); // red
- else if (level == 2)
- gGL.color3f(0.f, 1.f, 0.f); // green
- else if (level == 3)
- gGL.color3f(0.f, 0.f, 1.f); // blue
- else if (level == 4)
- gGL.color3f(1.f, 1.f, 0.f); // yellow
- else if (level == 5)
- gGL.color3f(1.f, 0.f, 1.f); // magenta
- else if (level == 6)
- gGL.color3f(0.f, 1.f, 1.f); // cyan
- else if (level == 7)
- gGL.color3f(1.f, 1.f, 1.f); // white
- else
- gGL.color3f(0.f, 0.f, 0.f); // black
- gGL.begin(LLRender::LINE_STRIP);
- gGL.vertex2f(left, top);
- gGL.vertex2f(right, bottom);
- gGL.vertex2f(left, bottom);
- gGL.vertex2f(right, top);
- gGL.vertex2f(left, top);
- gGL.vertex2f(left, bottom);
- gGL.vertex2f(right, bottom);
- gGL.vertex2f(right, top);
- gGL.end();
-}
-
-void LLWorldMapView::drawGenericItems(const LLSimInfo::item_info_list_t& items, LLUIImagePtr image)
-{
- LLSimInfo::item_info_list_t::const_iterator e;
- for (e = items.begin(); e != items.end(); ++e)
- {
- drawGenericItem(*e, image);
- }
-}
-
-void LLWorldMapView::drawGenericItem(const LLItemInfo& item, LLUIImagePtr image)
-{
- drawImage(item.getGlobalPosition(), image);
-}
-
-
-void LLWorldMapView::drawImage(const LLVector3d& global_pos, LLUIImagePtr image, const LLColor4& color)
-{
- LLVector3 pos_map = globalPosToView( global_pos );
- image->draw(ll_round(pos_map.mV[VX] - image->getWidth() /2.f),
- ll_round(pos_map.mV[VY] - image->getHeight()/2.f),
- color);
-}
-
-void LLWorldMapView::drawImageStack(const LLVector3d& global_pos, LLUIImagePtr image, U32 count, F32 offset, const LLColor4& color)
-{
- LLVector3 pos_map = globalPosToView( global_pos );
- for(U32 i=0; i<count; i++)
- {
- image->draw(ll_round(pos_map.mV[VX] - image->getWidth() /2.f),
- ll_round(pos_map.mV[VY] - image->getHeight()/2.f + i*offset),
- color);
- }
-}
-
-void LLWorldMapView::drawItems()
-{
- bool mature_enabled = gAgent.canAccessMature();
- bool adult_enabled = gAgent.canAccessAdult();
-
- bool show_mature = mature_enabled && gSavedSettings.getBOOL("ShowMatureEvents");
- bool show_adult = adult_enabled && gSavedSettings.getBOOL("ShowAdultEvents");
-
- for (handle_list_t::iterator iter = mVisibleRegions.begin(); iter != mVisibleRegions.end(); ++iter)
- {
- U64 handle = *iter;
- LLSimInfo* info = LLWorldMap::getInstance()->simInfoFromHandle(handle);
- if ((info == NULL) || (info->isDown()))
- {
- continue;
- }
- // Infohubs
- if (gSavedSettings.getBOOL("MapShowInfohubs"))
- {
- drawGenericItems(info->getInfoHub(), sInfohubImage);
- }
- // Telehubs
- if (gSavedSettings.getBOOL("MapShowTelehubs"))
- {
- drawGenericItems(info->getTeleHub(), sTelehubImage);
- }
- // Land for sale
- if (gSavedSettings.getBOOL("MapShowLandForSale"))
- {
- drawGenericItems(info->getLandForSale(), sForSaleImage);
- // for 1.23, we're showing normal land and adult land in the same UI; you don't
- // get a choice about which ones you want. If you're currently asking for adult
- // content and land you'll get the adult land.
- if (gAgent.canAccessAdult())
- {
- drawGenericItems(info->getLandForSaleAdult(), sForSaleAdultImage);
- }
- }
- // PG Events
- if (gSavedSettings.getBOOL("MapShowEvents"))
- {
- drawGenericItems(info->getPGEvent(), sEventImage);
- }
- // Mature Events
- if (show_mature)
- {
- drawGenericItems(info->getMatureEvent(), sEventMatureImage);
- }
- // Adult Events
- if (show_adult)
- {
- drawGenericItems(info->getAdultEvent(), sEventAdultImage);
- }
- }
-}
-
-void LLWorldMapView::drawAgents()
-{
- static LLUIColor map_avatar_color = LLUIColorTable::instance().getColor("MapAvatarColor", LLColor4::white);
-
- for (handle_list_t::iterator iter = mVisibleRegions.begin(); iter != mVisibleRegions.end(); ++iter)
- {
- U64 handle = *iter;
- LLSimInfo* siminfo = LLWorldMap::getInstance()->simInfoFromHandle(handle);
- if ((siminfo == NULL) || (siminfo->isDown()))
- {
- continue;
- }
- LLSimInfo::item_info_list_t::const_iterator it = siminfo->getAgentLocation().begin();
- while (it != siminfo->getAgentLocation().end())
- {
- // Show Individual agents (or little stacks where real agents are)
-
- // Here's how we'd choose the color if info.mID were available but it's not being sent:
- // LLColor4 color = (agent_count == 1 && is_agent_friend(info.mID)) ? friend_color : avatar_color;
- drawImageStack(it->getGlobalPosition(), sAvatarSmallImage, it->getCount(), 3.f, map_avatar_color);
- ++it;
- }
- }
-}
-
-void LLWorldMapView::drawFrustum()
-{
- // Draw frustum
- F32 meters_to_pixels = mMapScale/ REGION_WIDTH_METERS;
-
- F32 horiz_fov = LLViewerCamera::getInstance()->getView() * LLViewerCamera::getInstance()->getAspect();
- F32 far_clip_meters = LLViewerCamera::getInstance()->getFar();
- F32 far_clip_pixels = far_clip_meters * meters_to_pixels;
-
- F32 half_width_meters = far_clip_meters * tan( horiz_fov / 2 );
- F32 half_width_pixels = half_width_meters * meters_to_pixels;
-
- // Compute the frustum coordinates. Take the UI scale into account.
- F32 ctr_x = ((getLocalRect().getWidth() * 0.5f + mPanX) * LLUI::getScaleFactor().mV[VX]);
- F32 ctr_y = ((getLocalRect().getHeight() * 0.5f + mPanY) * LLUI::getScaleFactor().mV[VY]);
-
- gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
-
- // Since we don't rotate the map, we have to rotate the frustum.
- gGL.pushMatrix();
- {
- gGL.translatef( ctr_x, ctr_y, 0 );
-
- // Draw triangle with more alpha in far pixels to make it
- // fade out in distance.
- gGL.begin( LLRender::TRIANGLES );
- {
- // get camera look at and left axes
- LLVector3 at_axis = LLViewerCamera::instance().getAtAxis();
- LLVector3 left_axis = LLViewerCamera::instance().getLeftAxis();
-
- // grab components along XY plane
- LLVector2 cam_lookat(at_axis.mV[VX], at_axis.mV[VY]);
- LLVector2 cam_left(left_axis.mV[VX], left_axis.mV[VY]);
-
- // but, when looking near straight up or down...
- if (is_approx_zero(cam_lookat.magVecSquared()))
- {
- //...just fall back to looking down the x axis
- cam_lookat = LLVector2(1.f, 0.f); // x axis
- cam_left = LLVector2(0.f, 1.f); // y axis
- }
-
- // normalize to unit length
- cam_lookat.normVec();
- cam_left.normVec();
-
- gGL.color4f(1.f, 1.f, 1.f, 0.25f);
- gGL.vertex2f( 0, 0 );
-
- gGL.color4f(1.f, 1.f, 1.f, 0.02f);
-
- // use 2d camera vectors to render frustum triangle
- LLVector2 vert = cam_lookat * far_clip_pixels + cam_left * half_width_pixels;
- gGL.vertex2f(vert.mV[VX], vert.mV[VY]);
-
- vert = cam_lookat * far_clip_pixels - cam_left * half_width_pixels;
- gGL.vertex2f(vert.mV[VX], vert.mV[VY]);
- }
- gGL.end();
- }
- gGL.popMatrix();
-}
-
-
-LLVector3 LLWorldMapView::globalPosToView( const LLVector3d& global_pos )
-{
- LLVector3d relative_pos_global = global_pos - gAgentCamera.getCameraPositionGlobal();
- LLVector3 pos_local;
- pos_local.setVec(relative_pos_global); // convert to floats from doubles
-
- pos_local.mV[VX] *= mMapScale / REGION_WIDTH_METERS;
- pos_local.mV[VY] *= mMapScale / REGION_WIDTH_METERS;
- // leave Z component in meters
-
-
- pos_local.mV[VX] += getRect().getWidth() / 2 + mPanX;
- pos_local.mV[VY] += getRect().getHeight() / 2 + mPanY;
-
- return pos_local;
-}
-
-
-void LLWorldMapView::drawTracking(const LLVector3d& pos_global, const LLColor4& color, bool draw_arrow,
- const std::string& label, const std::string& tooltip, S32 vert_offset )
-{
- LLVector3 pos_local = globalPosToView( pos_global );
- S32 x = ll_round( pos_local.mV[VX] );
- S32 y = ll_round( pos_local.mV[VY] );
- LLFontGL* font = LLFontGL::getFontSansSerifSmall();
- S32 text_x = x;
- S32 text_y = (S32)(y - sTrackCircleImage->getHeight()/2 - font->getLineHeight());
-
- if( x < 0
- || y < 0
- || x >= getRect().getWidth()
- || y >= getRect().getHeight() )
- {
- if (draw_arrow)
- {
- drawTrackingCircle( getRect(), x, y, color, 3, 15 );
- drawTrackingArrow( getRect(), x, y, color );
- text_x = sTrackingArrowX;
- text_y = sTrackingArrowY;
- }
- }
- else if (LLTracker::getTrackingStatus() == LLTracker::TRACKING_LOCATION &&
- LLTracker::getTrackedLocationType() != LLTracker::LOCATION_NOTHING)
- {
- drawTrackingCircle( getRect(), x, y, color, 3, 15 );
- }
- else
- {
- drawImage(pos_global, sTrackCircleImage, color);
- }
-
- // clamp text position to on-screen
- const S32 TEXT_PADDING = DEFAULT_TRACKING_ARROW_SIZE + 2;
- S32 half_text_width = llfloor(font->getWidthF32(label) * 0.5f);
- text_x = llclamp(text_x, half_text_width + TEXT_PADDING, getRect().getWidth() - half_text_width - TEXT_PADDING);
- text_y = llclamp(text_y + vert_offset, TEXT_PADDING + vert_offset, getRect().getHeight() - font->getLineHeight() - TEXT_PADDING - vert_offset);
-
- if (label != "")
- {
- font->renderUTF8(
- label, 0,
- text_x,
- text_y,
- LLColor4::white, LLFontGL::HCENTER,
- LLFontGL::BASELINE, LLFontGL::NORMAL, LLFontGL::DROP_SHADOW);
-
- if (tooltip != "")
- {
- text_y -= font->getLineHeight();
-
- font->renderUTF8(
- tooltip, 0,
- text_x,
- text_y,
- LLColor4::white, LLFontGL::HCENTER,
- LLFontGL::BASELINE, LLFontGL::NORMAL, LLFontGL::DROP_SHADOW);
- }
- }
-}
-
-// If you change this, then you need to change LLTracker::getTrackedPositionGlobal() as well
-LLVector3d LLWorldMapView::viewPosToGlobal( S32 x, S32 y )
-{
- x -= llfloor((getRect().getWidth() / 2 + mPanX));
- y -= llfloor((getRect().getHeight() / 2 + mPanY));
-
- LLVector3 pos_local( (F32)x, (F32)y, 0.f );
-
- pos_local *= ( REGION_WIDTH_METERS / mMapScale );
-
- LLVector3d pos_global;
- pos_global.setVec( pos_local );
- pos_global += gAgentCamera.getCameraPositionGlobal();
- if(gAgent.isGodlike())
- {
- pos_global.mdV[VZ] = GODLY_TELEPORT_HEIGHT; // Godly height should always be 200.
- }
- else
- {
- pos_global.mdV[VZ] = gAgent.getPositionAgent().mV[VZ]; // Want agent's height, not camera's
- }
-
- return pos_global;
-}
-
-
-bool LLWorldMapView::handleToolTip( S32 x, S32 y, MASK mask )
-{
- LLVector3d pos_global = viewPosToGlobal(x, y);
- U64 handle = to_region_handle(pos_global);
- std::string tooltip_msg;
-
- LLSimInfo* info = LLWorldMap::getInstance()->simInfoFromHandle(handle);
- if (info)
- {
- LLViewerRegion *region = gAgent.getRegion();
-
- std::string message = llformat("%s (%s)", info->getName().c_str(), info->getAccessString().c_str());
-
- if (!info->isDown())
- {
- S32 agent_count = info->getAgentCount();
- if (region && (region->getHandle() == handle))
- {
- ++agent_count; // Bump by 1 if we're here
- }
-
- // We may not have an agent count when the map is really
- // zoomed out, so don't display anything about the count. JC
- if (agent_count > 0)
- {
- LLStringUtil::format_map_t string_args;
- string_args["[NUMBER]"] = llformat("%d", agent_count);
- message += '\n';
- message += getString((agent_count == 1 ? "world_map_person" : "world_map_people") , string_args);
- }
- }
- tooltip_msg.assign( message );
-
- // Optionally show region flags
- std::string region_flags = info->getFlagsString();
-
- if (!region_flags.empty())
- {
- tooltip_msg += '\n';
- tooltip_msg += region_flags;
- }
-
- const S32 SLOP = 9;
- S32 screen_x, screen_y;
-
- localPointToScreen(x, y, &screen_x, &screen_y);
- LLRect sticky_rect_screen;
- sticky_rect_screen.setCenterAndSize(screen_x, screen_y, SLOP, SLOP);
-
- LLToolTipMgr::instance().show(LLToolTip::Params()
- .message(tooltip_msg)
- .sticky_rect(sticky_rect_screen));
- }
- return true;
-}
-
-// Pass relative Z of 0 to draw at same level.
-// static
-static void drawDot(F32 x_pixels, F32 y_pixels,
- const LLColor4& color,
- F32 relative_z,
- F32 dot_radius,
- LLUIImagePtr dot_image)
-{
- const F32 HEIGHT_THRESHOLD = 7.f;
-
- if(-HEIGHT_THRESHOLD <= relative_z && relative_z <= HEIGHT_THRESHOLD)
- {
- dot_image->draw(ll_round(x_pixels) - dot_image->getWidth()/2,
- ll_round(y_pixels) - dot_image->getHeight()/2,
- color);
- }
- else
- {
- // Draw V indicator for above or below
- // *TODO: Replace this vector drawing with icons
-
- F32 left = x_pixels - dot_radius;
- F32 right = x_pixels + dot_radius;
- F32 center = (left + right) * 0.5f;
- F32 top = y_pixels + dot_radius;
- F32 bottom = y_pixels - dot_radius;
-
- gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
- gGL.color4fv( color.mV );
- LLUI::setLineWidth(3.0f);
- F32 point = relative_z > HEIGHT_THRESHOLD ? top : bottom; // Y pos of the point of the V
- F32 back = relative_z > HEIGHT_THRESHOLD ? bottom : top; // Y pos of the ends of the V
- gGL.begin( LLRender::LINES );
- gGL.vertex2f(left, back);
- gGL.vertex2f(center, point);
- gGL.vertex2f(center, point);
- gGL.vertex2f(right, back);
- gGL.end();
- LLUI::setLineWidth(1.0f);
- }
-}
-
-// Pass relative Z of 0 to draw at same level.
-// static
-void LLWorldMapView::drawAvatar(F32 x_pixels,
- F32 y_pixels,
- const LLColor4& color,
- F32 relative_z,
- F32 dot_radius,
- bool unknown_relative_z)
-{
- const F32 HEIGHT_THRESHOLD = 7.f;
- LLUIImagePtr dot_image = sAvatarLevelImage;
- if (unknown_relative_z && llabs(relative_z) > HEIGHT_THRESHOLD)
- {
- dot_image = sAvatarUnknownImage;
- }
- else
- {
- if(relative_z < -HEIGHT_THRESHOLD)
- {
- dot_image = sAvatarBelowImage;
- }
- else if(relative_z > HEIGHT_THRESHOLD)
- {
- dot_image = sAvatarAboveImage;
- }
- }
- S32 dot_width = ll_round(dot_radius * 2.f);
- dot_image->draw(ll_round(x_pixels - dot_radius),
- ll_round(y_pixels - dot_radius),
- dot_width,
- dot_width,
- color);
-}
-
-// Pass relative Z of 0 to draw at same level.
-// static
-void LLWorldMapView::drawTrackingDot( F32 x_pixels,
- F32 y_pixels,
- const LLColor4& color,
- F32 relative_z,
- F32 dot_radius)
-{
- drawDot(x_pixels, y_pixels, color, relative_z, dot_radius, sTrackCircleImage);
-}
-
-
-// Pass relative Z of 0 to draw at same level.
-// static
-void LLWorldMapView::drawIconName(F32 x_pixels,
- F32 y_pixels,
- const LLColor4& color,
- const std::string& first_line,
- const std::string& second_line)
-{
- const S32 VERT_PAD = 8;
- S32 text_x = ll_round(x_pixels);
- S32 text_y = ll_round(y_pixels
- - BIG_DOT_RADIUS
- - VERT_PAD);
-
- // render text
- LLFontGL::getFontSansSerif()->renderUTF8(first_line, 0,
- text_x,
- text_y,
- color,
- LLFontGL::HCENTER,
- LLFontGL::TOP,
- LLFontGL::NORMAL,
- LLFontGL::DROP_SHADOW);
-
- text_y -= LLFontGL::getFontSansSerif()->getLineHeight();
-
- // render text
- LLFontGL::getFontSansSerif()->renderUTF8(second_line, 0,
- text_x,
- text_y,
- color,
- LLFontGL::HCENTER,
- LLFontGL::TOP,
- LLFontGL::NORMAL,
- LLFontGL::DROP_SHADOW);
-}
-
-
-//static
-void LLWorldMapView::drawTrackingCircle( const LLRect& rect, S32 x, S32 y, const LLColor4& color, S32 min_thickness, S32 overlap )
-{
- F32 start_theta = 0.f;
- F32 end_theta = F_TWO_PI;
- F32 x_delta = 0.f;
- F32 y_delta = 0.f;
-
- if (x < 0)
- {
- x_delta = 0.f - (F32)x;
- start_theta = F_PI + F_PI_BY_TWO;
- end_theta = F_TWO_PI + F_PI_BY_TWO;
- }
- else if (x > rect.getWidth())
- {
- x_delta = (F32)(x - rect.getWidth());
- start_theta = F_PI_BY_TWO;
- end_theta = F_PI + F_PI_BY_TWO;
- }
-
- if (y < 0)
- {
- y_delta = 0.f - (F32)y;
- if (x < 0)
- {
- start_theta = 0.f;
- end_theta = F_PI_BY_TWO;
- }
- else if (x > rect.getWidth())
- {
- start_theta = F_PI_BY_TWO;
- end_theta = F_PI;
- }
- else
- {
- start_theta = 0.f;
- end_theta = F_PI;
- }
- }
- else if (y > rect.getHeight())
- {
- y_delta = (F32)(y - rect.getHeight());
- if (x < 0)
- {
- start_theta = F_PI + F_PI_BY_TWO;
- end_theta = F_TWO_PI;
- }
- else if (x > rect.getWidth())
- {
- start_theta = F_PI;
- end_theta = F_PI + F_PI_BY_TWO;
- }
- else
- {
- start_theta = F_PI;
- end_theta = F_TWO_PI;
- }
- }
-
- F32 distance = sqrtf(x_delta * x_delta + y_delta * y_delta);
-
- distance = llmax(0.1f, distance);
-
- F32 outer_radius = distance + (1.f + (9.f * sqrtf(x_delta * y_delta) / distance)) * (F32)overlap;
- F32 inner_radius = outer_radius - (F32)min_thickness;
-
- F32 angle_adjust_x = asin(x_delta / outer_radius);
- F32 angle_adjust_y = asin(y_delta / outer_radius);
-
- if (angle_adjust_x)
- {
- if (angle_adjust_y)
- {
- F32 angle_adjust = llmin(angle_adjust_x, angle_adjust_y);
- start_theta += angle_adjust;
- end_theta -= angle_adjust;
- }
- else
- {
- start_theta += angle_adjust_x;
- end_theta -= angle_adjust_x;
- }
- }
- else if (angle_adjust_y)
- {
- start_theta += angle_adjust_y;
- end_theta -= angle_adjust_y;
- }
-
- gGL.matrixMode(LLRender::MM_MODELVIEW);
- gGL.pushMatrix();
- gGL.translatef((F32)x * LLUI::getScaleFactor().mV[VX], (F32)y * LLUI::getScaleFactor().mV[VY], 0.f);
- gl_washer_segment_2d(inner_radius, outer_radius, start_theta, end_theta, 40, color, color);
- gGL.popMatrix();
-
-}
-
-// static
-void LLWorldMapView::drawTrackingArrow(const LLRect& rect, S32 x, S32 y,
- const LLColor4& color,
- S32 arrow_size)
-{
- F32 x_center = (F32)rect.getWidth() / 2.f;
- F32 y_center = (F32)rect.getHeight() / 2.f;
-
- F32 x_clamped = (F32)llclamp( x, 0, rect.getWidth() - arrow_size );
- F32 y_clamped = (F32)llclamp( y, 0, rect.getHeight() - arrow_size );
-
- F32 slope = (F32)(y - y_center) / (F32)(x - x_center);
- F32 window_ratio = (F32)rect.getHeight() / (F32)rect.getWidth();
-
- if (llabs(slope) > window_ratio && y_clamped != (F32)y)
- {
- // clamp by y
- x_clamped = (y_clamped - y_center) / slope + x_center;
- // adjust for arrow size
- x_clamped = llclamp(x_clamped , 0.f, (F32)(rect.getWidth() - arrow_size) );
- }
- else if (x_clamped != (F32)x)
- {
- // clamp by x
- y_clamped = (x_clamped - x_center) * slope + y_center;
- // adjust for arrow size
- y_clamped = llclamp( y_clamped, 0.f, (F32)(rect.getHeight() - arrow_size) );
- }
-
- // *FIX: deal with non-square window properly.
- // I do not understand what this comment means -- is it actually
- // broken or is it correctly dealing with non-square
- // windows. Phoenix 2007-01-03.
- S32 half_arrow_size = (S32) (0.5f * arrow_size);
-
- F32 angle = atan2( y + half_arrow_size - y_center, x + half_arrow_size - x_center);
-
- sTrackingArrowX = llfloor(x_clamped);
- sTrackingArrowY = llfloor(y_clamped);
-
- gl_draw_scaled_rotated_image(
- sTrackingArrowX,
- sTrackingArrowY,
- arrow_size, arrow_size,
- RAD_TO_DEG * angle,
- sTrackArrowImage->getImage(),
- color);
-}
-
-void LLWorldMapView::setDirectionPos( LLTextBox* text_box, F32 rotation )
-{
- // Rotation is in radians.
- // Rotation of 0 means x = 1, y = 0 on the unit circle.
-
-
- F32 map_half_height = getRect().getHeight() * 0.5f;
- F32 map_half_width = getRect().getWidth() * 0.5f;
- F32 text_half_height = text_box->getRect().getHeight() * 0.5f;
- F32 text_half_width = text_box->getRect().getWidth() * 0.5f;
- F32 radius = llmin( map_half_height - text_half_height, map_half_width - text_half_width );
-
- text_box->setOrigin(
- ll_round(map_half_width - text_half_width + radius * cos( rotation )),
- ll_round(map_half_height - text_half_height + radius * sin( rotation )) );
-}
-
-
-void LLWorldMapView::updateDirections()
-{
- S32 width = getRect().getWidth();
- S32 height = getRect().getHeight();
-
- S32 text_height = mTextBoxNorth->getRect().getHeight();
- S32 text_width = mTextBoxNorth->getRect().getWidth();
-
- const S32 PAD = 2;
- S32 top = height - text_height - PAD;
- S32 left = PAD*2;
- S32 bottom = PAD;
- S32 right = width - text_width - PAD;
- S32 center_x = width/2 - text_width/2;
- S32 center_y = height/2 - text_height/2;
-
- mTextBoxNorth->setOrigin( center_x, top );
- mTextBoxEast->setOrigin( right, center_y );
- mTextBoxSouth->setOrigin( center_x, bottom );
- mTextBoxWest->setOrigin( left, center_y );
-
- // These have wider text boxes
- text_width = mTextBoxNorthWest->getRect().getWidth();
- right = width - text_width - PAD;
-
- mTextBoxNorthWest->setOrigin(left, top);
- mTextBoxNorthEast->setOrigin(right, top);
- mTextBoxSouthWest->setOrigin(left, bottom);
- mTextBoxSouthEast->setOrigin(right, bottom);
-
-// S32 hint_width = mTextBoxScrollHint->getRect().getWidth();
-// mTextBoxScrollHint->setOrigin( width - hint_width - text_width - 2 * PAD,
-// PAD * 2 + text_height );
-}
-
-
-void LLWorldMapView::reshape( S32 width, S32 height, bool called_from_parent )
-{
- LLView::reshape( width, height, called_from_parent );
-}
-
-bool LLWorldMapView::checkItemHit(S32 x, S32 y, LLItemInfo& item, LLUUID* id, bool track)
-{
- LLVector3 pos_view = globalPosToView(item.getGlobalPosition());
- S32 item_x = ll_round(pos_view.mV[VX]);
- S32 item_y = ll_round(pos_view.mV[VY]);
-
- if (x < item_x - BIG_DOT_RADIUS) return false;
- if (x > item_x + BIG_DOT_RADIUS) return false;
- if (y < item_y - BIG_DOT_RADIUS) return false;
- if (y > item_y + BIG_DOT_RADIUS) return false;
-
- LLSimInfo* sim_info = LLWorldMap::getInstance()->simInfoFromHandle(item.getRegionHandle());
- if (sim_info)
- {
- if (track)
- {
- gFloaterWorldMap->trackLocation(item.getGlobalPosition());
- }
- }
-
- if (track)
- {
- gFloaterWorldMap->trackGenericItem(item);
- }
-
-// item.setSelected(true);
- *id = item.getUUID();
-
- return true;
-}
-
-// Handle a click, which might be on a dot
-void LLWorldMapView::handleClick(S32 x, S32 y, MASK mask,
- S32* hit_type,
- LLUUID* id)
-{
- LLVector3d pos_global = viewPosToGlobal(x, y);
-
- // *HACK: Adjust Z values automatically for liaisons & gods so
- // we swoop down when they click on the map. Sadly, the P2P
- // branch does not pay attention to this value; however, the
- // Distributed Messaging branch honors it.
- if(gAgent.isGodlike())
- {
- pos_global.mdV[VZ] = 200.0;
- }
-
- *hit_type = 0; // hit nothing
-
- LLWorldMap::getInstance()->cancelTracking();
-
- S32 level = LLWorldMipmap::scaleToLevel(mMapScale);
- // If the zoom level is not too far out already, test hits
- if (level <= DRAW_SIMINFO_THRESHOLD)
- {
- bool show_mature = gAgent.canAccessMature() && gSavedSettings.getBOOL("ShowMatureEvents");
- bool show_adult = gAgent.canAccessAdult() && gSavedSettings.getBOOL("ShowAdultEvents");
-
- // Test hits if trackable data are displayed, otherwise, we don't even bother
- if (gSavedSettings.getBOOL("MapShowEvents") || show_mature || show_adult || gSavedSettings.getBOOL("MapShowLandForSale"))
- {
- // Iterate through the visible regions
- for (handle_list_t::iterator iter = mVisibleRegions.begin(); iter != mVisibleRegions.end(); ++iter)
- {
- U64 handle = *iter;
- LLSimInfo* siminfo = LLWorldMap::getInstance()->simInfoFromHandle(handle);
- if ((siminfo == NULL) || (siminfo->isDown()))
- {
- continue;
- }
- // If on screen check hits with the visible item lists
- if (gSavedSettings.getBOOL("MapShowEvents"))
- {
- LLSimInfo::item_info_list_t::const_iterator it = siminfo->getPGEvent().begin();
- while (it != siminfo->getPGEvent().end())
- {
- LLItemInfo event = *it;
- if (checkItemHit(x, y, event, id, false))
- {
- *hit_type = MAP_ITEM_PG_EVENT;
- mItemPicked = true;
- gFloaterWorldMap->trackEvent(event);
- return;
- }
- ++it;
- }
- }
- if (show_mature)
- {
- LLSimInfo::item_info_list_t::const_iterator it = siminfo->getMatureEvent().begin();
- while (it != siminfo->getMatureEvent().end())
- {
- LLItemInfo event = *it;
- if (checkItemHit(x, y, event, id, false))
- {
- *hit_type = MAP_ITEM_MATURE_EVENT;
- mItemPicked = true;
- gFloaterWorldMap->trackEvent(event);
- return;
- }
- ++it;
- }
- }
- if (show_adult)
- {
- LLSimInfo::item_info_list_t::const_iterator it = siminfo->getAdultEvent().begin();
- while (it != siminfo->getAdultEvent().end())
- {
- LLItemInfo event = *it;
- if (checkItemHit(x, y, event, id, false))
- {
- *hit_type = MAP_ITEM_ADULT_EVENT;
- mItemPicked = true;
- gFloaterWorldMap->trackEvent(event);
- return;
- }
- ++it;
- }
- }
- if (gSavedSettings.getBOOL("MapShowLandForSale"))
- {
- LLSimInfo::item_info_list_t::const_iterator it = siminfo->getLandForSale().begin();
- while (it != siminfo->getLandForSale().end())
- {
- LLItemInfo event = *it;
- if (checkItemHit(x, y, event, id, true))
- {
- *hit_type = MAP_ITEM_LAND_FOR_SALE;
- mItemPicked = true;
- return;
- }
- ++it;
- }
- // for 1.23, we're showing normal land and adult land in the same UI; you don't
- // get a choice about which ones you want. If you're currently asking for adult
- // content and land you'll get the adult land.
- if (gAgent.canAccessAdult())
- {
- LLSimInfo::item_info_list_t::const_iterator it = siminfo->getLandForSaleAdult().begin();
- while (it != siminfo->getLandForSaleAdult().end())
- {
- LLItemInfo event = *it;
- if (checkItemHit(x, y, event, id, true))
- {
- *hit_type = MAP_ITEM_LAND_FOR_SALE_ADULT;
- mItemPicked = true;
- return;
- }
- ++it;
- }
- }
- }
- }
- }
- }
-
- // If we get here, we haven't clicked on anything
- gFloaterWorldMap->trackLocation(pos_global);
- mItemPicked = false;
- *id = LLUUID::null;
- return;
-}
-
-
-bool LLWorldMapView::handleMouseDown( S32 x, S32 y, MASK mask )
-{
- gFocusMgr.setMouseCapture( this );
-
- mMouseDownPanX = ll_round(mPanX);
- mMouseDownPanY = ll_round(mPanY);
- mMouseDownX = x;
- mMouseDownY = y;
- sHandledLastClick = true;
- return true;
-}
-
-bool LLWorldMapView::handleMouseUp( S32 x, S32 y, MASK mask )
-{
- if (hasMouseCapture())
- {
- if (mPanning)
- {
- // restore mouse cursor
- S32 local_x, local_y;
- local_x = mMouseDownX + llfloor(mPanX - mMouseDownPanX);
- local_y = mMouseDownY + llfloor(mPanY - mMouseDownPanY);
- LLRect clip_rect = getRect();
- clip_rect.stretch(-8);
- clip_rect.clipPointToRect(mMouseDownX, mMouseDownY, local_x, local_y);
- LLUI::getInstance()->setMousePositionLocal(this, local_x, local_y);
-
- // finish the pan
- mPanning = false;
-
- mMouseDownX = 0;
- mMouseDownY = 0;
- }
- else
- {
- // ignore whether we hit an event or not
- S32 hit_type;
- LLUUID id;
- handleClick(x, y, mask, &hit_type, &id);
- }
- gViewerWindow->showCursor();
- gFocusMgr.setMouseCapture( NULL );
- return true;
- }
- return false;
-}
-
-void LLWorldMapView::updateVisibleBlocks()
-{
- if (LLWorldMipmap::scaleToLevel(mMapScale) > DRAW_SIMINFO_THRESHOLD)
- {
- // If we're zoomed out too much, we just don't load all those sim info: too much!
- return;
- }
-
- // Load the blocks visible in the current World Map view
-
- // Get the World Map view coordinates and boundaries
- LLVector3d camera_global = gAgentCamera.getCameraPositionGlobal();
- const S32 width = getRect().getWidth();
- const S32 height = getRect().getHeight();
- const F32 half_width = F32(width) / 2.0f;
- const F32 half_height = F32(height) / 2.0f;
-
- // Compute center into sim grid coordinates
- S32 world_center_x = S32((-mPanX / mMapScale) + (camera_global.mdV[0] / REGION_WIDTH_METERS));
- S32 world_center_y = S32((-mPanY / mMapScale) + (camera_global.mdV[1] / REGION_WIDTH_METERS));
-
- // Compute the boundaries into sim grid coordinates
- S32 world_left = world_center_x - S32(half_width / mMapScale) - 1;
- S32 world_right = world_center_x + S32(half_width / mMapScale) + 1;
- S32 world_bottom = world_center_y - S32(half_height / mMapScale) - 1;
- S32 world_top = world_center_y + S32(half_height / mMapScale) + 1;
-
- //LL_INFOS("WorldMap") << "LLWorldMapView::updateVisibleBlocks() : mMapScale = " << mMapScale << ", left = " << world_left << ", right = " << world_right << ", bottom = " << world_bottom << ", top = " << world_top << LL_ENDL;
- LLWorldMap::getInstance()->updateRegions(world_left, world_bottom, world_right, world_top);
-}
-
-bool LLWorldMapView::handleHover( S32 x, S32 y, MASK mask )
-{
- if (hasMouseCapture())
- {
- if (mPanning || llabs(x - mMouseDownX) > 1 || llabs(y - mMouseDownY) > 1)
- {
- // just started panning, so hide cursor
- if (!mPanning)
- {
- mPanning = true;
- gViewerWindow->hideCursor();
- }
-
- F32 delta_x = (F32)(gViewerWindow->getCurrentMouseDX());
- F32 delta_y = (F32)(gViewerWindow->getCurrentMouseDY());
-
- // Set pan to value at start of drag + offset
- mPanX += delta_x;
- mPanY += delta_y;
- mTargetPanX = mPanX;
- mTargetPanY = mPanY;
-
- gViewerWindow->moveCursorToCenter();
- }
-
- // doesn't matter, cursor should be hidden
- gViewerWindow->setCursor(UI_CURSOR_CROSS );
- return true;
- }
- else
- {
- // While we're waiting for data from the tracker, we're busy. JC
- LLVector3d pos_global = LLTracker::getTrackedPositionGlobal();
- if (LLTracker::isTracking(NULL)
- && pos_global.isExactlyZero())
- {
- gViewerWindow->setCursor( UI_CURSOR_WAIT );
- }
- else
- {
- gViewerWindow->setCursor( UI_CURSOR_CROSS );
- }
- LL_DEBUGS("UserInput") << "hover handled by LLWorldMapView" << LL_ENDL;
- return true;
- }
-}
-
-
-bool LLWorldMapView::handleDoubleClick( S32 x, S32 y, MASK mask )
-{
- if( sHandledLastClick )
- {
- S32 hit_type;
- LLUUID id;
- handleClick(x, y, mask, &hit_type, &id);
-
- switch (hit_type)
- {
- case MAP_ITEM_PG_EVENT:
- case MAP_ITEM_MATURE_EVENT:
- case MAP_ITEM_ADULT_EVENT:
- {
- LLFloaterReg::hideInstance("world_map");
- // This is an ungainly hack
- std::string uuid_str;
- S32 event_id;
- id.toString(uuid_str);
- uuid_str = uuid_str.substr(28);
- sscanf(uuid_str.c_str(), "%X", &event_id);
- // Invoke the event details floater if someone is clicking on an event.
- LLSD params(LLSD::emptyArray());
- params.append(event_id);
- LLCommandDispatcher::dispatch("event", params, LLSD(), LLGridManager::getInstance()->getGrid(), NULL, LLCommandHandler::NAV_TYPE_CLICKED, true);
- break;
- }
- case MAP_ITEM_LAND_FOR_SALE:
- case MAP_ITEM_LAND_FOR_SALE_ADULT:
- {
- LLVector3d pos_global = viewPosToGlobal(x, y);
- std::string sim_name;
- if (LLWorldMap::getInstance()->simNameFromPosGlobal(pos_global, sim_name))
- {
- LLFloaterReg::hideInstance("world_map");
- LLFloaterReg::showInstance("search", LLSD().with("category", "land").with("query", sim_name));
- }
- break;
- }
- case MAP_ITEM_CLASSIFIED:
- {
- LLFloaterReg::hideInstance("world_map");
- LLFloaterReg::showInstance("search", LLSD().with("category", "classifieds").with("query", id));
- break;
- }
- default:
- {
- if (LLWorldMap::getInstance()->isTracking())
- {
- LLWorldMap::getInstance()->setTrackingDoubleClick();
- }
- else
- {
- // Teleport if we got a valid location
- LLVector3d pos_global = viewPosToGlobal(x,y);
- LLSimInfo* sim_info = LLWorldMap::getInstance()->simInfoFromPosGlobal(pos_global);
- if (sim_info && !sim_info->isDown())
- {
- gAgent.teleportViaLocation( pos_global );
- }
- }
- }
- };
-
- return true;
- }
- return false;
-}
-
-// static
-F32 LLWorldMapView::scaleFromZoom(F32 zoom) { return exp2(zoom) * 256.0f; }
-
-// static
-F32 LLWorldMapView::zoomFromScale(F32 scale) { return log2(scale / 256.f); }
+/** + * @file llworldmapview.cpp + * @brief LLWorldMapView class implementation + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llworldmapview.h" + +#include "indra_constants.h" +#include "llui.h" +#include "llmath.h" // clampf() +#include "llregionhandle.h" +#include "lleventflags.h" +#include "llfloaterreg.h" +#include "llrender.h" +#include "lltooltip.h" + +#include "llagent.h" +#include "llagentcamera.h" +#include "llcallingcard.h" +#include "llcommandhandler.h" +#include "llviewercontrol.h" +#include "llfloatermap.h" +#include "llfloaterworldmap.h" +#include "llfocusmgr.h" +#include "lllocalcliprect.h" +#include "lltextbox.h" +#include "lltextureview.h" +#include "lltracker.h" +#include "llviewercamera.h" +#include "llviewernetwork.h" +#include "llviewertexture.h" +#include "llviewertexturelist.h" +#include "llviewerregion.h" +#include "llviewerwindow.h" +#include "lltrans.h" + +#include "llglheaders.h" + +// # Constants +static constexpr F32 MAP_DEFAULT_SCALE = 128.f; +static constexpr F32 MAP_ITERP_TIME_CONSTANT = 0.75f; +static constexpr F32 MAP_ZOOM_ACCELERATION_TIME = 0.3f; +static constexpr F32 MAP_ZOOM_MAX_INTERP = 0.5f; +static constexpr F32 MAP_SCALE_SNAP_THRESHOLD = 0.005f; + +// Basically a C++ implementation of the OCEAN_COLOR defined in mapstitcher.py +// Please ensure consistency between those 2 files (TODO: would be better to get that color from an asset source...) +// OCEAN_COLOR = "#1D475F" +constexpr F32 OCEAN_RED = (F32)(0x1D)/255.f; +constexpr F32 OCEAN_GREEN = (F32)(0x47)/255.f; +constexpr F32 OCEAN_BLUE = (F32)(0x5F)/255.f; + +constexpr F32 GODLY_TELEPORT_HEIGHT = 200.f; +constexpr F32 BIG_DOT_RADIUS = 5.f; +bool LLWorldMapView::sHandledLastClick = false; + +LLUIImagePtr LLWorldMapView::sAvatarSmallImage = NULL; +LLUIImagePtr LLWorldMapView::sAvatarYouImage = NULL; +LLUIImagePtr LLWorldMapView::sAvatarYouLargeImage = NULL; +LLUIImagePtr LLWorldMapView::sAvatarLevelImage = NULL; +LLUIImagePtr LLWorldMapView::sAvatarAboveImage = NULL; +LLUIImagePtr LLWorldMapView::sAvatarBelowImage = NULL; +LLUIImagePtr LLWorldMapView::sAvatarUnknownImage = NULL; + +LLUIImagePtr LLWorldMapView::sTelehubImage = NULL; +LLUIImagePtr LLWorldMapView::sInfohubImage = NULL; +LLUIImagePtr LLWorldMapView::sHomeImage = NULL; +LLUIImagePtr LLWorldMapView::sEventImage = NULL; +LLUIImagePtr LLWorldMapView::sEventMatureImage = NULL; +LLUIImagePtr LLWorldMapView::sEventAdultImage = NULL; + +LLUIImagePtr LLWorldMapView::sTrackCircleImage = NULL; +LLUIImagePtr LLWorldMapView::sTrackArrowImage = NULL; + +LLUIImagePtr LLWorldMapView::sClassifiedsImage = NULL; +LLUIImagePtr LLWorldMapView::sForSaleImage = NULL; +LLUIImagePtr LLWorldMapView::sForSaleAdultImage = NULL; + +S32 LLWorldMapView::sTrackingArrowX = 0; +S32 LLWorldMapView::sTrackingArrowY = 0; +bool LLWorldMapView::sVisibleTilesLoaded = false; +F32 LLWorldMapView::sMapScaleSetting = MAP_DEFAULT_SCALE; +LLVector2 LLWorldMapView::sZoomPivot = LLVector2(0.0f, 0.0f); +LLFrameTimer LLWorldMapView::sZoomTimer = LLFrameTimer(); + +std::map<std::string,std::string> LLWorldMapView::sStringsMap; + +// Fetch and draw info thresholds +const F32 DRAW_TEXT_THRESHOLD = 96.f; // Don't draw text under that resolution value (res = width region in meters) +const S32 DRAW_SIMINFO_THRESHOLD = 3; // Max level for which we load or display sim level information (level in LLWorldMipmap sense) +const S32 DRAW_LANDFORSALE_THRESHOLD = 2; // Max level for which we load or display land for sale picture data (level in LLWorldMipmap sense) + +// When on, draw an outline for each mipmap tile gotten from S3 +#define DEBUG_DRAW_TILE 0 + + +void LLWorldMapView::initClass() +{ + sAvatarSmallImage = LLUI::getUIImage("map_avatar_8.tga"); + sAvatarYouImage = LLUI::getUIImage("map_avatar_16.tga"); + sAvatarYouLargeImage = LLUI::getUIImage("map_avatar_you_32.tga"); + sAvatarLevelImage = LLUI::getUIImage("map_avatar_32.tga"); + sAvatarAboveImage = LLUI::getUIImage("map_avatar_above_32.tga"); + sAvatarBelowImage = LLUI::getUIImage("map_avatar_below_32.tga"); + sAvatarUnknownImage = LLUI::getUIImage("map_avatar_unknown_32.tga"); + + sHomeImage = LLUI::getUIImage("map_home.tga"); + sTelehubImage = LLUI::getUIImage("map_telehub.tga"); + sInfohubImage = LLUI::getUIImage("map_infohub.tga"); + sEventImage = LLUI::getUIImage("Parcel_PG_Dark"); + sEventMatureImage = LLUI::getUIImage("Parcel_M_Dark"); + // To Do: update the image resource for adult events. + sEventAdultImage = LLUI::getUIImage("Parcel_R_Dark"); + + sTrackCircleImage = LLUI::getUIImage("map_track_16.tga"); + sTrackArrowImage = LLUI::getUIImage("direction_arrow.tga"); + sClassifiedsImage = LLUI::getUIImage("icon_top_pick.tga"); + sForSaleImage = LLUI::getUIImage("icon_for_sale.tga"); + // To Do: update the image resource for adult lands on sale. + sForSaleAdultImage = LLUI::getUIImage("icon_for_sale_adult.tga"); + + sStringsMap["loading"] = LLTrans::getString("texture_loading"); + sStringsMap["offline"] = LLTrans::getString("worldmap_offline"); +} + +// static +void LLWorldMapView::cleanupClass() +{ + sAvatarSmallImage = NULL; + sAvatarYouImage = NULL; + sAvatarYouLargeImage = NULL; + sAvatarLevelImage = NULL; + sAvatarAboveImage = NULL; + sAvatarBelowImage = NULL; + sAvatarUnknownImage = NULL; + + sTelehubImage = NULL; + sInfohubImage = NULL; + sHomeImage = NULL; + sEventImage = NULL; + sEventMatureImage = NULL; + sEventAdultImage = NULL; + + sTrackCircleImage = NULL; + sTrackArrowImage = NULL; + sClassifiedsImage = NULL; + sForSaleImage = NULL; + sForSaleAdultImage = NULL; +} + +LLWorldMapView::LLWorldMapView() : + LLPanel(), + mBackgroundColor(LLColor4(OCEAN_RED, OCEAN_GREEN, OCEAN_BLUE, 1.f)), + mItemPicked(false), + mPanX(0.f), + mPanY(0.f), + mTargetPanX(0.f), + mTargetPanY(0.f), + mPanning(false), + mMouseDownPanX(0), + mMouseDownPanY(0), + mMouseDownX(0), + mMouseDownY(0), + mSelectIDStart(0), + mMapScale(0.f), + mTargetMapScale(0.f), + mMapIterpTime(MAP_ITERP_TIME_CONSTANT) +{ + // LL_INFOS("WorldMap") << "Creating the Map -> LLWorldMapView::LLWorldMapView()" << LL_ENDL; + + clearLastClick(); +} + +bool LLWorldMapView::postBuild() +{ + mTextBoxNorth = getChild<LLTextBox> ("floater_map_north"); + mTextBoxEast = getChild<LLTextBox> ("floater_map_east"); + mTextBoxWest = getChild<LLTextBox> ("floater_map_west"); + mTextBoxSouth = getChild<LLTextBox> ("floater_map_south"); + mTextBoxSouthEast = getChild<LLTextBox> ("floater_map_southeast"); + mTextBoxNorthEast = getChild<LLTextBox> ("floater_map_northeast"); + mTextBoxSouthWest = getChild<LLTextBox> ("floater_map_southwest"); + mTextBoxNorthWest = getChild<LLTextBox> ("floater_map_northwest"); + + mTextBoxNorth->setText(getString("world_map_north")); + mTextBoxEast->setText(getString ("world_map_east")); + mTextBoxWest->setText(getString("world_map_west")); + mTextBoxSouth->setText(getString ("world_map_south")); + mTextBoxSouthEast ->setText(getString ("world_map_southeast")); + mTextBoxNorthEast ->setText(getString ("world_map_northeast")); + mTextBoxSouthWest->setText(getString ("world_map_southwest")); + mTextBoxNorthWest ->setText(getString("world_map_northwest")); + + mTextBoxNorth->reshapeToFitText(); + mTextBoxEast->reshapeToFitText(); + mTextBoxWest->reshapeToFitText(); + mTextBoxSouth->reshapeToFitText(); + mTextBoxSouthEast ->reshapeToFitText(); + mTextBoxNorthEast ->reshapeToFitText(); + mTextBoxSouthWest->reshapeToFitText(); + mTextBoxNorthWest ->reshapeToFitText(); + + sZoomTimer.stop(); + setScale(sMapScaleSetting, true); + + return true; +} + + +LLWorldMapView::~LLWorldMapView() +{ + //LL_INFOS("WorldMap") << "Destroying the map -> LLWorldMapView::~LLWorldMapView()" << LL_ENDL; + cleanupTextures(); +} + + +// static +void LLWorldMapView::cleanupTextures() +{ +} + +void LLWorldMapView::zoom(F32 zoom) +{ + mTargetMapScale = scaleFromZoom(zoom); + if (!sZoomTimer.getStarted() && mMapScale != mTargetMapScale) + { + sZoomPivot = LLVector2(0, 0); + sZoomTimer.start(); + } +} + +void LLWorldMapView::zoomWithPivot(F32 zoom, S32 x, S32 y) +{ + mTargetMapScale = scaleFromZoom(zoom); + sZoomPivot = LLVector2(x, y); + if (!sZoomTimer.getStarted() && mMapScale != mTargetMapScale) + { + sZoomTimer.start(); + } +} + +F32 LLWorldMapView::getZoom() { return LLWorldMapView::zoomFromScale(mMapScale); } + +F32 LLWorldMapView::getScale() { return mMapScale; } + +// static +void LLWorldMapView::setScaleSetting(F32 scaleSetting) { sMapScaleSetting = scaleSetting; } + +// static +F32 LLWorldMapView::getScaleSetting() { return sMapScaleSetting; } + +void LLWorldMapView::setScale(F32 scale, bool snap) +{ + if (scale != mMapScale) + { + F32 old_scale = mMapScale; + + mMapScale = scale; + // Set the scale used when saving the setting + sMapScaleSetting = scale; + if (mMapScale <= 0.f) + { + mMapScale = 0.1f; + } + mMapIterpTime = MAP_ITERP_TIME_CONSTANT; + F32 ratio = (scale / old_scale); + mPanX *= ratio; + mPanY *= ratio; + mTargetPanX = mPanX; + mTargetPanY = mPanY; + sVisibleTilesLoaded = false; + + // If we are zooming relative to somewhere else rather than the center of the map, compensate for the difference in panning here + if (!sZoomPivot.isExactlyZero()) + { + LLVector2 relative_pivot; + relative_pivot.mV[VX] = sZoomPivot.mV[VX] - (getRect().getWidth() / 2.0); + relative_pivot.mV[VY] = sZoomPivot.mV[VY] - (getRect().getHeight() / 2.0); + LLVector2 zoom_pan_offset = relative_pivot - (relative_pivot * scale / old_scale); + mPanX += zoom_pan_offset.mV[VX]; + mPanY += zoom_pan_offset.mV[VY]; + mTargetPanX += zoom_pan_offset.mV[VX]; + mTargetPanY += zoom_pan_offset.mV[VY]; + } + } + + if (snap) + { + mTargetMapScale = scale; + } +} + +// static +void LLWorldMapView::translatePan(S32 delta_x, S32 delta_y) +{ + mPanX += delta_x; + mPanY += delta_y; + mTargetPanX = mPanX; + mTargetPanY = mPanY; + sVisibleTilesLoaded = false; +} + + +// static +void LLWorldMapView::setPan(S32 x, S32 y, bool snap) +{ + mMapIterpTime = MAP_ITERP_TIME_CONSTANT; + mTargetPanX = (F32) x; + mTargetPanY = (F32) y; + if (snap) + { + mPanX = mTargetPanX; + mPanY = mTargetPanY; + } + sVisibleTilesLoaded = false; +} + +// static +void LLWorldMapView::setPanWithInterpTime(S32 x, S32 y, bool snap, F32 interp_time) +{ + setPan(x, y, snap); + mMapIterpTime = interp_time; +} + +bool LLWorldMapView::showRegionInfo() { return LLWorldMipmap::scaleToLevel(mMapScale) <= DRAW_SIMINFO_THRESHOLD; } + +/////////////////////////////////////////////////////////////////////////////////// +// HELPERS + +bool is_agent_in_region(LLViewerRegion* region, LLSimInfo* info) +{ + return (region && info && info->isName(region->getName())); +} + +/////////////////////////////////////////////////////////////////////////////////// + +void LLWorldMapView::draw() +{ + static LLUIColor map_track_color = LLUIColorTable::instance().getColor("MapTrackColor", LLColor4::white); + + LLTextureView::clearDebugImages(); + + F64 current_time = LLTimer::getElapsedSeconds(); + + mVisibleRegions.clear(); + + // animate pan if necessary + mPanX = lerp(mPanX, mTargetPanX, LLSmoothInterpolation::getInterpolant(mMapIterpTime)); + mPanY = lerp(mPanY, mTargetPanY, LLSmoothInterpolation::getInterpolant(mMapIterpTime)); + + //RN: snaps to zoom value because interpolation caused jitter in the text rendering + if (!sZoomTimer.getStarted() && mMapScale != mTargetMapScale) + { + sZoomTimer.start(); + } + bool snap_scale = false; + F32 interp = llmin(MAP_ZOOM_MAX_INTERP, sZoomTimer.getElapsedTimeF32() / MAP_ZOOM_ACCELERATION_TIME); + F32 current_zoom_val = zoomFromScale(mMapScale); + F32 target_zoom_val = zoomFromScale(mTargetMapScale); + F32 new_zoom_val = lerp(current_zoom_val, target_zoom_val, interp); + if (abs(new_zoom_val - current_zoom_val) < MAP_SCALE_SNAP_THRESHOLD) + { + sZoomTimer.stop(); + snap_scale = true; + new_zoom_val = target_zoom_val; + } + F32 map_scale = scaleFromZoom(new_zoom_val); + setScale(map_scale, snap_scale); + + const S32 width = getRect().getWidth(); + const S32 height = getRect().getHeight(); + const F32 half_width = F32(width) / 2.0f; + const F32 half_height = F32(height) / 2.0f; + LLVector3d camera_global = gAgentCamera.getCameraPositionGlobal(); + + S32 level = LLWorldMipmap::scaleToLevel(mMapScale); + + LLLocalClipRect clip(getLocalRect()); + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + + gGL.matrixMode(LLRender::MM_MODELVIEW); + + // Draw background rectangle + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + gGL.color4fv(mBackgroundColor.mV); + gl_rect_2d(0, height, width, 0); + + // Draw the image tiles + drawMipmap(width, height); + + // Draw per sim overlayed information (names, mature, offline...) + for (LLWorldMap::sim_info_map_t::const_iterator it = LLWorldMap::getInstance()->getRegionMap().begin(); + it != LLWorldMap::getInstance()->getRegionMap().end(); ++it) + { + U64 handle = it->first; + LLSimInfo* info = it->second; + + LLVector3d origin_global = from_region_handle(handle); + + // Find x and y position relative to camera's center. + LLVector3d rel_region_pos = origin_global - camera_global; + F32 relative_x = (rel_region_pos.mdV[0] / REGION_WIDTH_METERS) * mMapScale; + F32 relative_y = (rel_region_pos.mdV[1] / REGION_WIDTH_METERS) * mMapScale; + + // Coordinates of the sim in pixels in the UI panel + // When the view isn't panned, 0,0 = center of rectangle + F32 bottom = mPanY + half_height + relative_y; + F32 left = mPanX + half_width + relative_x; + F32 top = bottom + mMapScale ; + F32 right = left + mMapScale ; + + // Discard if region is outside the screen rectangle (not visible on screen) + if ((top < 0.f) || (bottom > height) || + (right < 0.f) || (left > width) ) + { + // Drop the "land for sale" fetching priority since it's outside the view rectangle + info->dropImagePriority(); + continue; + } + + // This list is used by other methods to know which regions are indeed displayed on screen + + mVisibleRegions.push_back(handle); + + // Update the agent count for that region if we're not too zoomed out already + if (level <= DRAW_SIMINFO_THRESHOLD) + { + info->updateAgentCount(current_time); + } + + if (info->isDown()) + { + // Draw a transparent red square over down sims + gGL.color4f(0.2f, 0.0f, 0.0f, 0.4f); + + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + gGL.begin(LLRender::QUADS); + gGL.vertex2f(left, top); + gGL.vertex2f(left, bottom); + gGL.vertex2f(right, bottom); + gGL.vertex2f(right, top); + gGL.end(); + } + else if (gSavedSettings.getBOOL("MapShowLandForSale") && (level <= DRAW_LANDFORSALE_THRESHOLD)) + { + // Draw the overlay image "Land for Sale / Land for Auction" + LLViewerFetchedTexture* overlayimage = info->getLandForSaleImage(); + if (overlayimage) + { + // Inform the fetch mechanism of the size we need + S32 draw_size = ll_round(mMapScale); + overlayimage->setKnownDrawSize(ll_round(draw_size * LLUI::getScaleFactor().mV[VX]), ll_round(draw_size * LLUI::getScaleFactor().mV[VY])); + // Draw something whenever we have enough info + if (overlayimage->hasGLTexture()) + { + gGL.getTexUnit(0)->bind(overlayimage); + gGL.color4f(1.f, 1.f, 1.f, 1.f); + gGL.begin(LLRender::QUADS); + gGL.texCoord2f(0.f, 1.f); + gGL.vertex3f(left, top, -0.5f); + gGL.texCoord2f(0.f, 0.f); + gGL.vertex3f(left, bottom, -0.5f); + gGL.texCoord2f(1.f, 0.f); + gGL.vertex3f(right, bottom, -0.5f); + gGL.texCoord2f(1.f, 1.f); + gGL.vertex3f(right, top, -0.5f); + gGL.end(); + } + } + } + else + { + // If we're not displaying the "land for sale", drop its fetching priority + info->dropImagePriority(); + } + + // Draw the region name in the lower left corner + if (mMapScale >= DRAW_TEXT_THRESHOLD) + { + LLFontGL* font = LLFontGL::getFont(LLFontDescriptor("SansSerif", "Small", LLFontGL::BOLD)); + std::string mesg; + if (info->isDown()) + { + mesg = llformat( "%s (%s)", info->getName().c_str(), sStringsMap["offline"].c_str()); + } + else + { + mesg = info->getName(); + } + if (!mesg.empty()) + { + font->renderUTF8( + mesg, 0, + llfloor(left + 3), llfloor(bottom + 2), + LLColor4::white, + LLFontGL::LEFT, LLFontGL::BASELINE, LLFontGL::NORMAL, LLFontGL::DROP_SHADOW, + S32_MAX, //max_chars + mMapScale, //max_pixels + NULL, + /*use_ellipses*/true); + } + } + } + + // Draw item infos if we're not zoomed out too much and there's something to draw + if ((level <= DRAW_SIMINFO_THRESHOLD) && (gSavedSettings.getBOOL("MapShowInfohubs") || + gSavedSettings.getBOOL("MapShowTelehubs") || + gSavedSettings.getBOOL("MapShowLandForSale") || + gSavedSettings.getBOOL("MapShowEvents") || + gSavedSettings.getBOOL("ShowMatureEvents") || + gSavedSettings.getBOOL("ShowAdultEvents"))) + { + drawItems(); + } + + // Draw the Home location (always) + LLVector3d home; + if (gAgent.getHomePosGlobal(&home)) + { + drawImage(home, sHomeImage); + } + + // Draw the current agent after all that other stuff. + LLVector3d pos_global = gAgent.getPositionGlobal(); + drawImage(pos_global, sAvatarYouImage); + + LLVector3 pos_map = globalPosToView(pos_global); + if (!pointInView(ll_round(pos_map.mV[VX]), ll_round(pos_map.mV[VY]))) + { + drawTracking(pos_global, + lerp(LLColor4::yellow, LLColor4::orange, 0.4f), + true, + "You are here", + "", + LLFontGL::getFontSansSerifSmall()->getLineHeight()); // offset vertically by one line, to avoid overlap with target tracking + } + + // Draw the current agent viewing angle + drawFrustum(); + + // Draw icons for the avatars in each region. + // Drawn this after the current agent avatar so one can see nearby people + if (gSavedSettings.getBOOL("MapShowPeople") && (level <= DRAW_SIMINFO_THRESHOLD)) + { + drawAgents(); + } + + // Always draw tracking information + LLTracker::ETrackingStatus tracking_status = LLTracker::getTrackingStatus(); + if ( LLTracker::TRACKING_AVATAR == tracking_status ) + { + drawTracking( LLAvatarTracker::instance().getGlobalPos(), map_track_color, true, LLTracker::getLabel(), "" ); + } + else if ( LLTracker::TRACKING_LANDMARK == tracking_status + || LLTracker::TRACKING_LOCATION == tracking_status ) + { + // While fetching landmarks, will have 0,0,0 location for a while, + // so don't draw. JC + LLVector3d pos_global = LLTracker::getTrackedPositionGlobal(); + if (!pos_global.isExactlyZero()) + { + drawTracking( pos_global, map_track_color, true, LLTracker::getLabel(), LLTracker::getToolTip() ); + } + } + else if (LLWorldMap::getInstance()->isTracking()) + { + if (LLWorldMap::getInstance()->isTrackingInvalidLocation()) + { + // We know this location to be invalid, draw a blue circle + LLColor4 loading_color(0.0, 0.5, 1.0, 1.0); + drawTracking( LLWorldMap::getInstance()->getTrackedPositionGlobal(), loading_color, true, getString("InvalidLocation"), ""); + } + else + { + // We don't know yet what that location is, draw a throbing blue circle + double value = fmod(current_time, 2); + value = 0.5 + 0.5*cos(value * F_PI); + LLColor4 loading_color(0.0, F32(value/2), F32(value), 1.0); + drawTracking( LLWorldMap::getInstance()->getTrackedPositionGlobal(), loading_color, true, getString("Loading"), ""); + } + } + + + // turn off the scissor + LLGLDisable no_scissor(GL_SCISSOR_TEST); + + updateDirections(); + + LLView::draw(); + + // Get sim info for all sims in view + updateVisibleBlocks(); +} // end draw() + + +//virtual +void LLWorldMapView::setVisible(bool visible) +{ + LLPanel::setVisible(visible); + if (!visible) + { + // Drop the download of tiles and images priority to nil if we hide the map + LLWorldMap::getInstance()->dropImagePriorities(); + } +} + +void LLWorldMapView::drawMipmap(S32 width, S32 height) +{ + // Compute the level of the mipmap to use for the current scale level + S32 level = LLWorldMipmap::scaleToLevel(mMapScale); + // Set the tile boost level so that unused tiles get to 0 + LLWorldMap::getInstance()->equalizeBoostLevels(); + + // Render whatever we already have loaded if we haven't the current level + // complete and use it as a background (scaled up or scaled down) + if (!sVisibleTilesLoaded) + { + // Note: the (load = false) parameter avoids missing tiles to be fetched (i.e. we render what we have, no more) + // Check all the lower res levels and render them in reverse order (worse to best) + // We need to traverse all the levels as the user can zoom in very fast + for (S32 l = LLWorldMipmap::MAP_LEVELS; l > level; l--) + { + drawMipmapLevel(width, height, l, false); + } + // Skip the current level, as we'll do it anyway here under... + + // Just go one level down in res as it can really get too much stuff + // when zooming out and too small to see anyway... + if (level > 1) + { + drawMipmapLevel(width, height, level - 1, false); + } + } + else + { + //LL_INFOS("WorldMap") << "Render complete, don't draw background..." << LL_ENDL; + } + + // Render the current level + sVisibleTilesLoaded = drawMipmapLevel(width, height, level); + + return; +} + +// Return true if all the tiles required to render that level have been fetched or are truly missing +bool LLWorldMapView::drawMipmapLevel(S32 width, S32 height, S32 level, bool load) +{ + // Check input level + llassert (level > 0); + if (level <= 0) + return false; + + // Count tiles hit and completed + S32 completed_tiles = 0; + S32 total_tiles = 0; + + // Size in meters (global) of each tile of that level + S32 tile_width = LLWorldMipmap::MAP_TILE_SIZE * (1 << (level - 1)); + // Dimension of the screen in meter at that scale + LLVector3d pos_SW = viewPosToGlobal(0, 0); + LLVector3d pos_NE = viewPosToGlobal(width, height); + // Add external band of tiles on the outskirt so to hit the partially displayed tiles right and top + pos_NE[VX] += tile_width; + pos_NE[VY] += tile_width; + + // Iterate through the tiles on screen: we just need to ask for one tile every tile_width meters + U32 grid_x, grid_y; + for (F64 index_y = pos_SW[VY]; index_y < pos_NE[VY]; index_y += tile_width) + { + for (F64 index_x = pos_SW[VX]; index_x < pos_NE[VX]; index_x += tile_width) + { + // Compute the world coordinates of the current point + LLVector3d pos_global(index_x, index_y, pos_SW[VZ]); + // Convert to the mipmap level coordinates for that point (i.e. which tile to we hit) + LLWorldMipmap::globalToMipmap(pos_global[VX], pos_global[VY], level, &grid_x, &grid_y); + // Get the tile. Note: NULL means that the image does not exist (so it's considered "complete" as far as fetching is concerned) + LLPointer<LLViewerFetchedTexture> simimage = LLWorldMap::getInstance()->getObjectsTile(grid_x, grid_y, level, load); + if (simimage) + { + // Checks that the image has a valid texture + if (simimage->hasGLTexture()) + { + // Increment the number of completly fetched tiles + completed_tiles++; + + // Convert those coordinates (SW corner of the mipmap tile) into world (meters) coordinates + pos_global[VX] = grid_x * REGION_WIDTH_METERS; + pos_global[VY] = grid_y * REGION_WIDTH_METERS; + // Now to screen coordinates for SW corner of that tile + LLVector3 pos_screen = globalPosToView (pos_global); + F32 left = pos_screen[VX]; + F32 bottom = pos_screen[VY]; + // Compute the NE corner coordinates of the tile now + pos_global[VX] += tile_width; + pos_global[VY] += tile_width; + pos_screen = globalPosToView (pos_global); + F32 right = pos_screen[VX]; + F32 top = pos_screen[VY]; + + // Draw the tile + LLGLSUIDefault gls_ui; + gGL.getTexUnit(0)->bind(simimage.get()); + simimage->setAddressMode(LLTexUnit::TAM_CLAMP); + + gGL.color4f(1.f, 1.0f, 1.0f, 1.0f); + + gGL.begin(LLRender::QUADS); + gGL.texCoord2f(0.f, 1.f); + gGL.vertex3f(left, top, 0.f); + gGL.texCoord2f(0.f, 0.f); + gGL.vertex3f(left, bottom, 0.f); + gGL.texCoord2f(1.f, 0.f); + gGL.vertex3f(right, bottom, 0.f); + gGL.texCoord2f(1.f, 1.f); + gGL.vertex3f(right, top, 0.f); + gGL.end(); +#if DEBUG_DRAW_TILE + drawTileOutline(level, top, left, bottom, right); +#endif // DEBUG_DRAW_TILE + } + //else + //{ + // Waiting for a tile -> the level is not complete + // LL_INFOS("WorldMap") << "Unfetched tile. level = " << level << LL_ENDL; + //} + } + else + { + // Unexistent tiles are counted as "completed" + completed_tiles++; + } + // Increment the number of tiles in that level / screen + total_tiles++; + } + } + return (completed_tiles == total_tiles); +} + +// Draw lines (rectangle outline and cross) to visualize the position of the tile +// Used for debug only +void LLWorldMapView::drawTileOutline(S32 level, F32 top, F32 left, F32 bottom, F32 right) +{ + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + if (level == 1) + gGL.color3f(1.f, 0.f, 0.f); // red + else if (level == 2) + gGL.color3f(0.f, 1.f, 0.f); // green + else if (level == 3) + gGL.color3f(0.f, 0.f, 1.f); // blue + else if (level == 4) + gGL.color3f(1.f, 1.f, 0.f); // yellow + else if (level == 5) + gGL.color3f(1.f, 0.f, 1.f); // magenta + else if (level == 6) + gGL.color3f(0.f, 1.f, 1.f); // cyan + else if (level == 7) + gGL.color3f(1.f, 1.f, 1.f); // white + else + gGL.color3f(0.f, 0.f, 0.f); // black + gGL.begin(LLRender::LINE_STRIP); + gGL.vertex2f(left, top); + gGL.vertex2f(right, bottom); + gGL.vertex2f(left, bottom); + gGL.vertex2f(right, top); + gGL.vertex2f(left, top); + gGL.vertex2f(left, bottom); + gGL.vertex2f(right, bottom); + gGL.vertex2f(right, top); + gGL.end(); +} + +void LLWorldMapView::drawGenericItems(const LLSimInfo::item_info_list_t& items, LLUIImagePtr image) +{ + LLSimInfo::item_info_list_t::const_iterator e; + for (e = items.begin(); e != items.end(); ++e) + { + drawGenericItem(*e, image); + } +} + +void LLWorldMapView::drawGenericItem(const LLItemInfo& item, LLUIImagePtr image) +{ + drawImage(item.getGlobalPosition(), image); +} + + +void LLWorldMapView::drawImage(const LLVector3d& global_pos, LLUIImagePtr image, const LLColor4& color) +{ + LLVector3 pos_map = globalPosToView( global_pos ); + image->draw(ll_round(pos_map.mV[VX] - image->getWidth() /2.f), + ll_round(pos_map.mV[VY] - image->getHeight()/2.f), + color); +} + +void LLWorldMapView::drawImageStack(const LLVector3d& global_pos, LLUIImagePtr image, U32 count, F32 offset, const LLColor4& color) +{ + LLVector3 pos_map = globalPosToView( global_pos ); + for(U32 i=0; i<count; i++) + { + image->draw(ll_round(pos_map.mV[VX] - image->getWidth() /2.f), + ll_round(pos_map.mV[VY] - image->getHeight()/2.f + i*offset), + color); + } +} + +void LLWorldMapView::drawItems() +{ + bool mature_enabled = gAgent.canAccessMature(); + bool adult_enabled = gAgent.canAccessAdult(); + + bool show_mature = mature_enabled && gSavedSettings.getBOOL("ShowMatureEvents"); + bool show_adult = adult_enabled && gSavedSettings.getBOOL("ShowAdultEvents"); + + for (handle_list_t::iterator iter = mVisibleRegions.begin(); iter != mVisibleRegions.end(); ++iter) + { + U64 handle = *iter; + LLSimInfo* info = LLWorldMap::getInstance()->simInfoFromHandle(handle); + if ((info == NULL) || (info->isDown())) + { + continue; + } + // Infohubs + if (gSavedSettings.getBOOL("MapShowInfohubs")) + { + drawGenericItems(info->getInfoHub(), sInfohubImage); + } + // Telehubs + if (gSavedSettings.getBOOL("MapShowTelehubs")) + { + drawGenericItems(info->getTeleHub(), sTelehubImage); + } + // Land for sale + if (gSavedSettings.getBOOL("MapShowLandForSale")) + { + drawGenericItems(info->getLandForSale(), sForSaleImage); + // for 1.23, we're showing normal land and adult land in the same UI; you don't + // get a choice about which ones you want. If you're currently asking for adult + // content and land you'll get the adult land. + if (gAgent.canAccessAdult()) + { + drawGenericItems(info->getLandForSaleAdult(), sForSaleAdultImage); + } + } + // PG Events + if (gSavedSettings.getBOOL("MapShowEvents")) + { + drawGenericItems(info->getPGEvent(), sEventImage); + } + // Mature Events + if (show_mature) + { + drawGenericItems(info->getMatureEvent(), sEventMatureImage); + } + // Adult Events + if (show_adult) + { + drawGenericItems(info->getAdultEvent(), sEventAdultImage); + } + } +} + +void LLWorldMapView::drawAgents() +{ + static LLUIColor map_avatar_color = LLUIColorTable::instance().getColor("MapAvatarColor", LLColor4::white); + + for (handle_list_t::iterator iter = mVisibleRegions.begin(); iter != mVisibleRegions.end(); ++iter) + { + U64 handle = *iter; + LLSimInfo* siminfo = LLWorldMap::getInstance()->simInfoFromHandle(handle); + if ((siminfo == NULL) || (siminfo->isDown())) + { + continue; + } + LLSimInfo::item_info_list_t::const_iterator it = siminfo->getAgentLocation().begin(); + while (it != siminfo->getAgentLocation().end()) + { + // Show Individual agents (or little stacks where real agents are) + + // Here's how we'd choose the color if info.mID were available but it's not being sent: + // LLColor4 color = (agent_count == 1 && is_agent_friend(info.mID)) ? friend_color : avatar_color; + drawImageStack(it->getGlobalPosition(), sAvatarSmallImage, it->getCount(), 3.f, map_avatar_color); + ++it; + } + } +} + +void LLWorldMapView::drawFrustum() +{ + // Draw frustum + F32 meters_to_pixels = mMapScale/ REGION_WIDTH_METERS; + + F32 horiz_fov = LLViewerCamera::getInstance()->getView() * LLViewerCamera::getInstance()->getAspect(); + F32 far_clip_meters = LLViewerCamera::getInstance()->getFar(); + F32 far_clip_pixels = far_clip_meters * meters_to_pixels; + + F32 half_width_meters = far_clip_meters * tan( horiz_fov / 2 ); + F32 half_width_pixels = half_width_meters * meters_to_pixels; + + // Compute the frustum coordinates. Take the UI scale into account. + F32 ctr_x = ((getLocalRect().getWidth() * 0.5f + mPanX) * LLUI::getScaleFactor().mV[VX]); + F32 ctr_y = ((getLocalRect().getHeight() * 0.5f + mPanY) * LLUI::getScaleFactor().mV[VY]); + + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + + // Since we don't rotate the map, we have to rotate the frustum. + gGL.pushMatrix(); + { + gGL.translatef( ctr_x, ctr_y, 0 ); + + // Draw triangle with more alpha in far pixels to make it + // fade out in distance. + gGL.begin( LLRender::TRIANGLES ); + { + // get camera look at and left axes + LLVector3 at_axis = LLViewerCamera::instance().getAtAxis(); + LLVector3 left_axis = LLViewerCamera::instance().getLeftAxis(); + + // grab components along XY plane + LLVector2 cam_lookat(at_axis.mV[VX], at_axis.mV[VY]); + LLVector2 cam_left(left_axis.mV[VX], left_axis.mV[VY]); + + // but, when looking near straight up or down... + if (is_approx_zero(cam_lookat.magVecSquared())) + { + //...just fall back to looking down the x axis + cam_lookat = LLVector2(1.f, 0.f); // x axis + cam_left = LLVector2(0.f, 1.f); // y axis + } + + // normalize to unit length + cam_lookat.normVec(); + cam_left.normVec(); + + gGL.color4f(1.f, 1.f, 1.f, 0.25f); + gGL.vertex2f( 0, 0 ); + + gGL.color4f(1.f, 1.f, 1.f, 0.02f); + + // use 2d camera vectors to render frustum triangle + LLVector2 vert = cam_lookat * far_clip_pixels + cam_left * half_width_pixels; + gGL.vertex2f(vert.mV[VX], vert.mV[VY]); + + vert = cam_lookat * far_clip_pixels - cam_left * half_width_pixels; + gGL.vertex2f(vert.mV[VX], vert.mV[VY]); + } + gGL.end(); + } + gGL.popMatrix(); +} + + +LLVector3 LLWorldMapView::globalPosToView( const LLVector3d& global_pos ) +{ + LLVector3d relative_pos_global = global_pos - gAgentCamera.getCameraPositionGlobal(); + LLVector3 pos_local; + pos_local.setVec(relative_pos_global); // convert to floats from doubles + + pos_local.mV[VX] *= mMapScale / REGION_WIDTH_METERS; + pos_local.mV[VY] *= mMapScale / REGION_WIDTH_METERS; + // leave Z component in meters + + + pos_local.mV[VX] += getRect().getWidth() / 2 + mPanX; + pos_local.mV[VY] += getRect().getHeight() / 2 + mPanY; + + return pos_local; +} + + +void LLWorldMapView::drawTracking(const LLVector3d& pos_global, const LLColor4& color, bool draw_arrow, + const std::string& label, const std::string& tooltip, S32 vert_offset ) +{ + LLVector3 pos_local = globalPosToView( pos_global ); + S32 x = ll_round( pos_local.mV[VX] ); + S32 y = ll_round( pos_local.mV[VY] ); + LLFontGL* font = LLFontGL::getFontSansSerifSmall(); + S32 text_x = x; + S32 text_y = (S32)(y - sTrackCircleImage->getHeight()/2 - font->getLineHeight()); + + if( x < 0 + || y < 0 + || x >= getRect().getWidth() + || y >= getRect().getHeight() ) + { + if (draw_arrow) + { + drawTrackingCircle( getRect(), x, y, color, 3, 15 ); + drawTrackingArrow( getRect(), x, y, color ); + text_x = sTrackingArrowX; + text_y = sTrackingArrowY; + } + } + else if (LLTracker::getTrackingStatus() == LLTracker::TRACKING_LOCATION && + LLTracker::getTrackedLocationType() != LLTracker::LOCATION_NOTHING) + { + drawTrackingCircle( getRect(), x, y, color, 3, 15 ); + } + else + { + drawImage(pos_global, sTrackCircleImage, color); + } + + // clamp text position to on-screen + const S32 TEXT_PADDING = DEFAULT_TRACKING_ARROW_SIZE + 2; + S32 half_text_width = llfloor(font->getWidthF32(label) * 0.5f); + text_x = llclamp(text_x, half_text_width + TEXT_PADDING, getRect().getWidth() - half_text_width - TEXT_PADDING); + text_y = llclamp(text_y + vert_offset, TEXT_PADDING + vert_offset, getRect().getHeight() - font->getLineHeight() - TEXT_PADDING - vert_offset); + + if (label != "") + { + font->renderUTF8( + label, 0, + text_x, + text_y, + LLColor4::white, LLFontGL::HCENTER, + LLFontGL::BASELINE, LLFontGL::NORMAL, LLFontGL::DROP_SHADOW); + + if (tooltip != "") + { + text_y -= font->getLineHeight(); + + font->renderUTF8( + tooltip, 0, + text_x, + text_y, + LLColor4::white, LLFontGL::HCENTER, + LLFontGL::BASELINE, LLFontGL::NORMAL, LLFontGL::DROP_SHADOW); + } + } +} + +// If you change this, then you need to change LLTracker::getTrackedPositionGlobal() as well +LLVector3d LLWorldMapView::viewPosToGlobal( S32 x, S32 y ) +{ + x -= llfloor((getRect().getWidth() / 2 + mPanX)); + y -= llfloor((getRect().getHeight() / 2 + mPanY)); + + LLVector3 pos_local( (F32)x, (F32)y, 0.f ); + + pos_local *= ( REGION_WIDTH_METERS / mMapScale ); + + LLVector3d pos_global; + pos_global.setVec( pos_local ); + pos_global += gAgentCamera.getCameraPositionGlobal(); + if(gAgent.isGodlike()) + { + pos_global.mdV[VZ] = GODLY_TELEPORT_HEIGHT; // Godly height should always be 200. + } + else + { + pos_global.mdV[VZ] = gAgent.getPositionAgent().mV[VZ]; // Want agent's height, not camera's + } + + return pos_global; +} + + +bool LLWorldMapView::handleToolTip( S32 x, S32 y, MASK mask ) +{ + LLVector3d pos_global = viewPosToGlobal(x, y); + U64 handle = to_region_handle(pos_global); + std::string tooltip_msg; + + LLSimInfo* info = LLWorldMap::getInstance()->simInfoFromHandle(handle); + if (info) + { + LLViewerRegion *region = gAgent.getRegion(); + + std::string message = llformat("%s (%s)", info->getName().c_str(), info->getAccessString().c_str()); + + if (!info->isDown()) + { + S32 agent_count = info->getAgentCount(); + if (region && (region->getHandle() == handle)) + { + ++agent_count; // Bump by 1 if we're here + } + + // We may not have an agent count when the map is really + // zoomed out, so don't display anything about the count. JC + if (agent_count > 0) + { + LLStringUtil::format_map_t string_args; + string_args["[NUMBER]"] = llformat("%d", agent_count); + message += '\n'; + message += getString((agent_count == 1 ? "world_map_person" : "world_map_people") , string_args); + } + } + tooltip_msg.assign( message ); + + // Optionally show region flags + std::string region_flags = info->getFlagsString(); + + if (!region_flags.empty()) + { + tooltip_msg += '\n'; + tooltip_msg += region_flags; + } + + const S32 SLOP = 9; + S32 screen_x, screen_y; + + localPointToScreen(x, y, &screen_x, &screen_y); + LLRect sticky_rect_screen; + sticky_rect_screen.setCenterAndSize(screen_x, screen_y, SLOP, SLOP); + + LLToolTipMgr::instance().show(LLToolTip::Params() + .message(tooltip_msg) + .sticky_rect(sticky_rect_screen)); + } + return true; +} + +// Pass relative Z of 0 to draw at same level. +// static +static void drawDot(F32 x_pixels, F32 y_pixels, + const LLColor4& color, + F32 relative_z, + F32 dot_radius, + LLUIImagePtr dot_image) +{ + const F32 HEIGHT_THRESHOLD = 7.f; + + if(-HEIGHT_THRESHOLD <= relative_z && relative_z <= HEIGHT_THRESHOLD) + { + dot_image->draw(ll_round(x_pixels) - dot_image->getWidth()/2, + ll_round(y_pixels) - dot_image->getHeight()/2, + color); + } + else + { + // Draw V indicator for above or below + // *TODO: Replace this vector drawing with icons + + F32 left = x_pixels - dot_radius; + F32 right = x_pixels + dot_radius; + F32 center = (left + right) * 0.5f; + F32 top = y_pixels + dot_radius; + F32 bottom = y_pixels - dot_radius; + + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + gGL.color4fv( color.mV ); + LLUI::setLineWidth(3.0f); + F32 point = relative_z > HEIGHT_THRESHOLD ? top : bottom; // Y pos of the point of the V + F32 back = relative_z > HEIGHT_THRESHOLD ? bottom : top; // Y pos of the ends of the V + gGL.begin( LLRender::LINES ); + gGL.vertex2f(left, back); + gGL.vertex2f(center, point); + gGL.vertex2f(center, point); + gGL.vertex2f(right, back); + gGL.end(); + LLUI::setLineWidth(1.0f); + } +} + +// Pass relative Z of 0 to draw at same level. +// static +void LLWorldMapView::drawAvatar(F32 x_pixels, + F32 y_pixels, + const LLColor4& color, + F32 relative_z, + F32 dot_radius, + bool unknown_relative_z) +{ + const F32 HEIGHT_THRESHOLD = 7.f; + LLUIImagePtr dot_image = sAvatarLevelImage; + if (unknown_relative_z && llabs(relative_z) > HEIGHT_THRESHOLD) + { + dot_image = sAvatarUnknownImage; + } + else + { + if(relative_z < -HEIGHT_THRESHOLD) + { + dot_image = sAvatarBelowImage; + } + else if(relative_z > HEIGHT_THRESHOLD) + { + dot_image = sAvatarAboveImage; + } + } + S32 dot_width = ll_round(dot_radius * 2.f); + dot_image->draw(ll_round(x_pixels - dot_radius), + ll_round(y_pixels - dot_radius), + dot_width, + dot_width, + color); +} + +// Pass relative Z of 0 to draw at same level. +// static +void LLWorldMapView::drawTrackingDot( F32 x_pixels, + F32 y_pixels, + const LLColor4& color, + F32 relative_z, + F32 dot_radius) +{ + drawDot(x_pixels, y_pixels, color, relative_z, dot_radius, sTrackCircleImage); +} + + +// Pass relative Z of 0 to draw at same level. +// static +void LLWorldMapView::drawIconName(F32 x_pixels, + F32 y_pixels, + const LLColor4& color, + const std::string& first_line, + const std::string& second_line) +{ + const S32 VERT_PAD = 8; + S32 text_x = ll_round(x_pixels); + S32 text_y = ll_round(y_pixels + - BIG_DOT_RADIUS + - VERT_PAD); + + // render text + LLFontGL::getFontSansSerif()->renderUTF8(first_line, 0, + text_x, + text_y, + color, + LLFontGL::HCENTER, + LLFontGL::TOP, + LLFontGL::NORMAL, + LLFontGL::DROP_SHADOW); + + text_y -= LLFontGL::getFontSansSerif()->getLineHeight(); + + // render text + LLFontGL::getFontSansSerif()->renderUTF8(second_line, 0, + text_x, + text_y, + color, + LLFontGL::HCENTER, + LLFontGL::TOP, + LLFontGL::NORMAL, + LLFontGL::DROP_SHADOW); +} + + +//static +void LLWorldMapView::drawTrackingCircle( const LLRect& rect, S32 x, S32 y, const LLColor4& color, S32 min_thickness, S32 overlap ) +{ + F32 start_theta = 0.f; + F32 end_theta = F_TWO_PI; + F32 x_delta = 0.f; + F32 y_delta = 0.f; + + if (x < 0) + { + x_delta = 0.f - (F32)x; + start_theta = F_PI + F_PI_BY_TWO; + end_theta = F_TWO_PI + F_PI_BY_TWO; + } + else if (x > rect.getWidth()) + { + x_delta = (F32)(x - rect.getWidth()); + start_theta = F_PI_BY_TWO; + end_theta = F_PI + F_PI_BY_TWO; + } + + if (y < 0) + { + y_delta = 0.f - (F32)y; + if (x < 0) + { + start_theta = 0.f; + end_theta = F_PI_BY_TWO; + } + else if (x > rect.getWidth()) + { + start_theta = F_PI_BY_TWO; + end_theta = F_PI; + } + else + { + start_theta = 0.f; + end_theta = F_PI; + } + } + else if (y > rect.getHeight()) + { + y_delta = (F32)(y - rect.getHeight()); + if (x < 0) + { + start_theta = F_PI + F_PI_BY_TWO; + end_theta = F_TWO_PI; + } + else if (x > rect.getWidth()) + { + start_theta = F_PI; + end_theta = F_PI + F_PI_BY_TWO; + } + else + { + start_theta = F_PI; + end_theta = F_TWO_PI; + } + } + + F32 distance = sqrtf(x_delta * x_delta + y_delta * y_delta); + + distance = llmax(0.1f, distance); + + F32 outer_radius = distance + (1.f + (9.f * sqrtf(x_delta * y_delta) / distance)) * (F32)overlap; + F32 inner_radius = outer_radius - (F32)min_thickness; + + F32 angle_adjust_x = asin(x_delta / outer_radius); + F32 angle_adjust_y = asin(y_delta / outer_radius); + + if (angle_adjust_x) + { + if (angle_adjust_y) + { + F32 angle_adjust = llmin(angle_adjust_x, angle_adjust_y); + start_theta += angle_adjust; + end_theta -= angle_adjust; + } + else + { + start_theta += angle_adjust_x; + end_theta -= angle_adjust_x; + } + } + else if (angle_adjust_y) + { + start_theta += angle_adjust_y; + end_theta -= angle_adjust_y; + } + + gGL.matrixMode(LLRender::MM_MODELVIEW); + gGL.pushMatrix(); + gGL.translatef((F32)x * LLUI::getScaleFactor().mV[VX], (F32)y * LLUI::getScaleFactor().mV[VY], 0.f); + gl_washer_segment_2d(inner_radius, outer_radius, start_theta, end_theta, 40, color, color); + gGL.popMatrix(); + +} + +// static +void LLWorldMapView::drawTrackingArrow(const LLRect& rect, S32 x, S32 y, + const LLColor4& color, + S32 arrow_size) +{ + F32 x_center = (F32)rect.getWidth() / 2.f; + F32 y_center = (F32)rect.getHeight() / 2.f; + + F32 x_clamped = (F32)llclamp( x, 0, rect.getWidth() - arrow_size ); + F32 y_clamped = (F32)llclamp( y, 0, rect.getHeight() - arrow_size ); + + F32 slope = (F32)(y - y_center) / (F32)(x - x_center); + F32 window_ratio = (F32)rect.getHeight() / (F32)rect.getWidth(); + + if (llabs(slope) > window_ratio && y_clamped != (F32)y) + { + // clamp by y + x_clamped = (y_clamped - y_center) / slope + x_center; + // adjust for arrow size + x_clamped = llclamp(x_clamped , 0.f, (F32)(rect.getWidth() - arrow_size) ); + } + else if (x_clamped != (F32)x) + { + // clamp by x + y_clamped = (x_clamped - x_center) * slope + y_center; + // adjust for arrow size + y_clamped = llclamp( y_clamped, 0.f, (F32)(rect.getHeight() - arrow_size) ); + } + + // *FIX: deal with non-square window properly. + // I do not understand what this comment means -- is it actually + // broken or is it correctly dealing with non-square + // windows. Phoenix 2007-01-03. + S32 half_arrow_size = (S32) (0.5f * arrow_size); + + F32 angle = atan2( y + half_arrow_size - y_center, x + half_arrow_size - x_center); + + sTrackingArrowX = llfloor(x_clamped); + sTrackingArrowY = llfloor(y_clamped); + + gl_draw_scaled_rotated_image( + sTrackingArrowX, + sTrackingArrowY, + arrow_size, arrow_size, + RAD_TO_DEG * angle, + sTrackArrowImage->getImage(), + color); +} + +void LLWorldMapView::setDirectionPos( LLTextBox* text_box, F32 rotation ) +{ + // Rotation is in radians. + // Rotation of 0 means x = 1, y = 0 on the unit circle. + + + F32 map_half_height = getRect().getHeight() * 0.5f; + F32 map_half_width = getRect().getWidth() * 0.5f; + F32 text_half_height = text_box->getRect().getHeight() * 0.5f; + F32 text_half_width = text_box->getRect().getWidth() * 0.5f; + F32 radius = llmin( map_half_height - text_half_height, map_half_width - text_half_width ); + + text_box->setOrigin( + ll_round(map_half_width - text_half_width + radius * cos( rotation )), + ll_round(map_half_height - text_half_height + radius * sin( rotation )) ); +} + + +void LLWorldMapView::updateDirections() +{ + S32 width = getRect().getWidth(); + S32 height = getRect().getHeight(); + + S32 text_height = mTextBoxNorth->getRect().getHeight(); + S32 text_width = mTextBoxNorth->getRect().getWidth(); + + const S32 PAD = 2; + S32 top = height - text_height - PAD; + S32 left = PAD*2; + S32 bottom = PAD; + S32 right = width - text_width - PAD; + S32 center_x = width/2 - text_width/2; + S32 center_y = height/2 - text_height/2; + + mTextBoxNorth->setOrigin( center_x, top ); + mTextBoxEast->setOrigin( right, center_y ); + mTextBoxSouth->setOrigin( center_x, bottom ); + mTextBoxWest->setOrigin( left, center_y ); + + // These have wider text boxes + text_width = mTextBoxNorthWest->getRect().getWidth(); + right = width - text_width - PAD; + + mTextBoxNorthWest->setOrigin(left, top); + mTextBoxNorthEast->setOrigin(right, top); + mTextBoxSouthWest->setOrigin(left, bottom); + mTextBoxSouthEast->setOrigin(right, bottom); + +// S32 hint_width = mTextBoxScrollHint->getRect().getWidth(); +// mTextBoxScrollHint->setOrigin( width - hint_width - text_width - 2 * PAD, +// PAD * 2 + text_height ); +} + + +void LLWorldMapView::reshape( S32 width, S32 height, bool called_from_parent ) +{ + LLView::reshape( width, height, called_from_parent ); +} + +bool LLWorldMapView::checkItemHit(S32 x, S32 y, LLItemInfo& item, LLUUID* id, bool track) +{ + LLVector3 pos_view = globalPosToView(item.getGlobalPosition()); + S32 item_x = ll_round(pos_view.mV[VX]); + S32 item_y = ll_round(pos_view.mV[VY]); + + if (x < item_x - BIG_DOT_RADIUS) return false; + if (x > item_x + BIG_DOT_RADIUS) return false; + if (y < item_y - BIG_DOT_RADIUS) return false; + if (y > item_y + BIG_DOT_RADIUS) return false; + + LLSimInfo* sim_info = LLWorldMap::getInstance()->simInfoFromHandle(item.getRegionHandle()); + if (sim_info) + { + if (track) + { + gFloaterWorldMap->trackLocation(item.getGlobalPosition()); + } + } + + if (track) + { + gFloaterWorldMap->trackGenericItem(item); + } + +// item.setSelected(true); + *id = item.getUUID(); + + return true; +} + +// Handle a click, which might be on a dot +void LLWorldMapView::handleClick(S32 x, S32 y, MASK mask, + S32* hit_type, + LLUUID* id) +{ + LLVector3d pos_global = viewPosToGlobal(x, y); + + // *HACK: Adjust Z values automatically for liaisons & gods so + // we swoop down when they click on the map. Sadly, the P2P + // branch does not pay attention to this value; however, the + // Distributed Messaging branch honors it. + if(gAgent.isGodlike()) + { + pos_global.mdV[VZ] = 200.0; + } + + *hit_type = 0; // hit nothing + + LLWorldMap::getInstance()->cancelTracking(); + + S32 level = LLWorldMipmap::scaleToLevel(mMapScale); + // If the zoom level is not too far out already, test hits + if (level <= DRAW_SIMINFO_THRESHOLD) + { + bool show_mature = gAgent.canAccessMature() && gSavedSettings.getBOOL("ShowMatureEvents"); + bool show_adult = gAgent.canAccessAdult() && gSavedSettings.getBOOL("ShowAdultEvents"); + + // Test hits if trackable data are displayed, otherwise, we don't even bother + if (gSavedSettings.getBOOL("MapShowEvents") || show_mature || show_adult || gSavedSettings.getBOOL("MapShowLandForSale")) + { + // Iterate through the visible regions + for (handle_list_t::iterator iter = mVisibleRegions.begin(); iter != mVisibleRegions.end(); ++iter) + { + U64 handle = *iter; + LLSimInfo* siminfo = LLWorldMap::getInstance()->simInfoFromHandle(handle); + if ((siminfo == NULL) || (siminfo->isDown())) + { + continue; + } + // If on screen check hits with the visible item lists + if (gSavedSettings.getBOOL("MapShowEvents")) + { + LLSimInfo::item_info_list_t::const_iterator it = siminfo->getPGEvent().begin(); + while (it != siminfo->getPGEvent().end()) + { + LLItemInfo event = *it; + if (checkItemHit(x, y, event, id, false)) + { + *hit_type = MAP_ITEM_PG_EVENT; + mItemPicked = true; + gFloaterWorldMap->trackEvent(event); + return; + } + ++it; + } + } + if (show_mature) + { + LLSimInfo::item_info_list_t::const_iterator it = siminfo->getMatureEvent().begin(); + while (it != siminfo->getMatureEvent().end()) + { + LLItemInfo event = *it; + if (checkItemHit(x, y, event, id, false)) + { + *hit_type = MAP_ITEM_MATURE_EVENT; + mItemPicked = true; + gFloaterWorldMap->trackEvent(event); + return; + } + ++it; + } + } + if (show_adult) + { + LLSimInfo::item_info_list_t::const_iterator it = siminfo->getAdultEvent().begin(); + while (it != siminfo->getAdultEvent().end()) + { + LLItemInfo event = *it; + if (checkItemHit(x, y, event, id, false)) + { + *hit_type = MAP_ITEM_ADULT_EVENT; + mItemPicked = true; + gFloaterWorldMap->trackEvent(event); + return; + } + ++it; + } + } + if (gSavedSettings.getBOOL("MapShowLandForSale")) + { + LLSimInfo::item_info_list_t::const_iterator it = siminfo->getLandForSale().begin(); + while (it != siminfo->getLandForSale().end()) + { + LLItemInfo event = *it; + if (checkItemHit(x, y, event, id, true)) + { + *hit_type = MAP_ITEM_LAND_FOR_SALE; + mItemPicked = true; + return; + } + ++it; + } + // for 1.23, we're showing normal land and adult land in the same UI; you don't + // get a choice about which ones you want. If you're currently asking for adult + // content and land you'll get the adult land. + if (gAgent.canAccessAdult()) + { + LLSimInfo::item_info_list_t::const_iterator it = siminfo->getLandForSaleAdult().begin(); + while (it != siminfo->getLandForSaleAdult().end()) + { + LLItemInfo event = *it; + if (checkItemHit(x, y, event, id, true)) + { + *hit_type = MAP_ITEM_LAND_FOR_SALE_ADULT; + mItemPicked = true; + return; + } + ++it; + } + } + } + } + } + } + + // If we get here, we haven't clicked on anything + gFloaterWorldMap->trackLocation(pos_global); + mItemPicked = false; + *id = LLUUID::null; + return; +} + + +bool LLWorldMapView::handleMouseDown( S32 x, S32 y, MASK mask ) +{ + gFocusMgr.setMouseCapture( this ); + + mMouseDownPanX = ll_round(mPanX); + mMouseDownPanY = ll_round(mPanY); + mMouseDownX = x; + mMouseDownY = y; + sHandledLastClick = true; + return true; +} + +bool LLWorldMapView::handleMouseUp( S32 x, S32 y, MASK mask ) +{ + if (hasMouseCapture()) + { + if (mPanning) + { + // restore mouse cursor + S32 local_x, local_y; + local_x = mMouseDownX + llfloor(mPanX - mMouseDownPanX); + local_y = mMouseDownY + llfloor(mPanY - mMouseDownPanY); + LLRect clip_rect = getRect(); + clip_rect.stretch(-8); + clip_rect.clipPointToRect(mMouseDownX, mMouseDownY, local_x, local_y); + LLUI::getInstance()->setMousePositionLocal(this, local_x, local_y); + + // finish the pan + mPanning = false; + + mMouseDownX = 0; + mMouseDownY = 0; + } + else + { + // ignore whether we hit an event or not + S32 hit_type; + LLUUID id; + handleClick(x, y, mask, &hit_type, &id); + } + gViewerWindow->showCursor(); + gFocusMgr.setMouseCapture( NULL ); + return true; + } + return false; +} + +void LLWorldMapView::updateVisibleBlocks() +{ + if (LLWorldMipmap::scaleToLevel(mMapScale) > DRAW_SIMINFO_THRESHOLD) + { + // If we're zoomed out too much, we just don't load all those sim info: too much! + return; + } + + // Load the blocks visible in the current World Map view + + // Get the World Map view coordinates and boundaries + LLVector3d camera_global = gAgentCamera.getCameraPositionGlobal(); + const S32 width = getRect().getWidth(); + const S32 height = getRect().getHeight(); + const F32 half_width = F32(width) / 2.0f; + const F32 half_height = F32(height) / 2.0f; + + // Compute center into sim grid coordinates + S32 world_center_x = S32((-mPanX / mMapScale) + (camera_global.mdV[0] / REGION_WIDTH_METERS)); + S32 world_center_y = S32((-mPanY / mMapScale) + (camera_global.mdV[1] / REGION_WIDTH_METERS)); + + // Compute the boundaries into sim grid coordinates + S32 world_left = world_center_x - S32(half_width / mMapScale) - 1; + S32 world_right = world_center_x + S32(half_width / mMapScale) + 1; + S32 world_bottom = world_center_y - S32(half_height / mMapScale) - 1; + S32 world_top = world_center_y + S32(half_height / mMapScale) + 1; + + //LL_INFOS("WorldMap") << "LLWorldMapView::updateVisibleBlocks() : mMapScale = " << mMapScale << ", left = " << world_left << ", right = " << world_right << ", bottom = " << world_bottom << ", top = " << world_top << LL_ENDL; + LLWorldMap::getInstance()->updateRegions(world_left, world_bottom, world_right, world_top); +} + +bool LLWorldMapView::handleHover( S32 x, S32 y, MASK mask ) +{ + if (hasMouseCapture()) + { + if (mPanning || llabs(x - mMouseDownX) > 1 || llabs(y - mMouseDownY) > 1) + { + // just started panning, so hide cursor + if (!mPanning) + { + mPanning = true; + gViewerWindow->hideCursor(); + } + + F32 delta_x = (F32)(gViewerWindow->getCurrentMouseDX()); + F32 delta_y = (F32)(gViewerWindow->getCurrentMouseDY()); + + // Set pan to value at start of drag + offset + mPanX += delta_x; + mPanY += delta_y; + mTargetPanX = mPanX; + mTargetPanY = mPanY; + + gViewerWindow->moveCursorToCenter(); + } + + // doesn't matter, cursor should be hidden + gViewerWindow->setCursor(UI_CURSOR_CROSS ); + return true; + } + else + { + // While we're waiting for data from the tracker, we're busy. JC + LLVector3d pos_global = LLTracker::getTrackedPositionGlobal(); + if (LLTracker::isTracking(NULL) + && pos_global.isExactlyZero()) + { + gViewerWindow->setCursor( UI_CURSOR_WAIT ); + } + else + { + gViewerWindow->setCursor( UI_CURSOR_CROSS ); + } + LL_DEBUGS("UserInput") << "hover handled by LLWorldMapView" << LL_ENDL; + return true; + } +} + + +bool LLWorldMapView::handleDoubleClick( S32 x, S32 y, MASK mask ) +{ + if( sHandledLastClick ) + { + S32 hit_type; + LLUUID id; + handleClick(x, y, mask, &hit_type, &id); + + switch (hit_type) + { + case MAP_ITEM_PG_EVENT: + case MAP_ITEM_MATURE_EVENT: + case MAP_ITEM_ADULT_EVENT: + { + LLFloaterReg::hideInstance("world_map"); + // This is an ungainly hack + std::string uuid_str; + S32 event_id; + id.toString(uuid_str); + uuid_str = uuid_str.substr(28); + sscanf(uuid_str.c_str(), "%X", &event_id); + // Invoke the event details floater if someone is clicking on an event. + LLSD params(LLSD::emptyArray()); + params.append(event_id); + LLCommandDispatcher::dispatch("event", params, LLSD(), LLGridManager::getInstance()->getGrid(), NULL, LLCommandHandler::NAV_TYPE_CLICKED, true); + break; + } + case MAP_ITEM_LAND_FOR_SALE: + case MAP_ITEM_LAND_FOR_SALE_ADULT: + { + LLVector3d pos_global = viewPosToGlobal(x, y); + std::string sim_name; + if (LLWorldMap::getInstance()->simNameFromPosGlobal(pos_global, sim_name)) + { + LLFloaterReg::hideInstance("world_map"); + LLFloaterReg::showInstance("search", LLSD().with("category", "land").with("query", sim_name)); + } + break; + } + case MAP_ITEM_CLASSIFIED: + { + LLFloaterReg::hideInstance("world_map"); + LLFloaterReg::showInstance("search", LLSD().with("category", "classifieds").with("query", id)); + break; + } + default: + { + if (LLWorldMap::getInstance()->isTracking()) + { + LLWorldMap::getInstance()->setTrackingDoubleClick(); + } + else + { + // Teleport if we got a valid location + LLVector3d pos_global = viewPosToGlobal(x,y); + LLSimInfo* sim_info = LLWorldMap::getInstance()->simInfoFromPosGlobal(pos_global); + if (sim_info && !sim_info->isDown()) + { + gAgent.teleportViaLocation( pos_global ); + } + } + } + }; + + return true; + } + return false; +} + +// static +F32 LLWorldMapView::scaleFromZoom(F32 zoom) { return exp2(zoom) * 256.0f; } + +// static +F32 LLWorldMapView::zoomFromScale(F32 scale) { return log2(scale / 256.f); } |