Files
SingularityViewer/indra/newview/llworldmapview.cpp
Shyotl e756140e1d Innitial commit of experimental v2 texture system port work. Compiles and runs on windows, at least. Fixing bugs as they come.
Need to test:
localassetbrowser
preview related floaters
hgfloatertexteditor
maps
media textures! Currently very hacky
web browser
alpha masks on avatars
bumpmaps
Are all sky components appearing?
LLViewerDynamicTexture (texture baking, browser, animated textures, anim previews, etc)
Snapshot related features
Customize avatar
vfs floater
UI textures in general
Texture priority issues
2011-03-31 03:22:01 -05:00

2181 lines
63 KiB
C++

/**
* @file llworldmapview.cpp
* @brief LLWorldMapView class implementation
*
* $LicenseInfo:firstyear=2001&license=viewergpl$
*
* Copyright (c) 2001-2009, Linden Research, Inc.
*
* Second Life Viewer Source Code
* The source code in this file ("Source Code") is provided by Linden Lab
* to you under the terms of the GNU General Public License, version 2.0
* ("GPL"), unless you have obtained a separate licensing agreement
* ("Other License"), formally executed by you and Linden Lab. Terms of
* the GPL can be found in doc/GPL-license.txt in this distribution, or
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
*
* There are special exceptions to the terms and conditions of the GPL as
* it is applied to this Source Code. View the full text of the exception
* in the file doc/FLOSS-exception.txt in this software distribution, or
* online at
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
*
* By copying, modifying or distributing this software, you acknowledge
* that you have read and understood your obligations described above,
* and agree to abide by those obligations.
*
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
* COMPLETENESS OR PERFORMANCE.
* $/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 "llrender.h"
#include "llagent.h"
#include "llcallingcard.h"
#include "llcolorscheme.h"
#include "llviewercontrol.h"
#include "llcylinder.h"
#include "llfloaterdirectory.h"
#include "llfloatermap.h"
#include "llfloaterworldmap.h"
#include "llfocusmgr.h"
#include "lltextbox.h"
#include "lltextureview.h"
#include "lltracker.h"
#include "llviewercamera.h"
#include "llviewertexturelist.h"
#include "llviewermenu.h"
#include "llviewerparceloverlay.h"
#include "llviewerregion.h"
#include "llviewerwindow.h"
#include "llworldmap.h"
#include "llworldmipmap.h"
#include "lltexturefetch.h"
#include "llappviewer.h" // Only for constants!
#include "lltrans.h"
#include "llglheaders.h"
#include "hippogridmanager.h"
// [RLVa:KB]
#include "rlvhandler.h"
// [/RLVa:KB]
// 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...)
// # Constants
// OCEAN_COLOR = "#1D475F"
const F32 OCEAN_RED = (F32)(0x1D)/255.f;
const F32 OCEAN_GREEN = (F32)(0x47)/255.f;
const F32 OCEAN_BLUE = (F32)(0x5F)/255.f;
const F32 GODLY_TELEPORT_HEIGHT = 200.f;
const S32 SCROLL_HINT_WIDTH = 65;
const 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::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;
F32 LLWorldMapView::sThresholdA = 48.f;
F32 LLWorldMapView::sThresholdB = 96.f;
F32 LLWorldMapView::sPanX = 0.f;
F32 LLWorldMapView::sPanY = 0.f;
F32 LLWorldMapView::sTargetPanX = 0.f;
F32 LLWorldMapView::sTargetPanY = 0.f;
S32 LLWorldMapView::sTrackingArrowX = 0;
S32 LLWorldMapView::sTrackingArrowY = 0;
F32 LLWorldMapView::sPixelsPerMeter = 1.f;
bool LLWorldMapView::sVisibleTilesLoaded = false;
F32 LLWorldMapView::sMapScale = 128.f;
F32 CONE_SIZE = 0.6f;
std::map<std::string,std::string> LLWorldMapView::sStringsMap;
#define SIM_NULL_MAP_SCALE 4 // width in pixels, where we start drawing "null" sims
#define SIM_MAP_AGENT_SCALE 8 // width in pixels, where we start drawing agents
#define SIM_MAP_SCALE 4 // width in pixels, where we start drawing sim tiles
// Updates for agent locations.
#define AGENTS_UPDATE_TIME 60.0 // in seconds
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");
sHomeImage = LLUI::getUIImage("map_home.tga");
sTelehubImage = LLUI::getUIImage("map_telehub.tga");
sInfohubImage = LLUI::getUIImage("map_infohub.tga");
sEventImage = LLUI::getUIImage("map_event.tga");
sEventMatureImage = LLUI::getUIImage("map_event_mature.tga");
// To Do: update the image resource for adult events.
sEventAdultImage = LLUI::getUIImage("map_event_adult.tga");
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;
sTelehubImage = NULL;
sInfohubImage = NULL;
sHomeImage = NULL;
sEventImage = NULL;
sEventMatureImage = NULL;
sEventAdultImage = NULL;
sTrackCircleImage = NULL;
sTrackArrowImage = NULL;
sClassifiedsImage = NULL;
sForSaleImage = NULL;
sForSaleAdultImage = NULL;
}
LLWorldMapView::LLWorldMapView(const std::string& name, const LLRect& rect )
: LLPanel(name, rect, BORDER_NO),
mBackgroundColor( LLColor4( OCEAN_RED, OCEAN_GREEN, OCEAN_BLUE, 1.f ) ),
mItemPicked(FALSE),
mPanning( FALSE ),
mMouseDownPanX( 0 ),
mMouseDownPanY( 0 ),
mMouseDownX( 0 ),
mMouseDownY( 0 ),
mSelectIDStart(0)
{
sPixelsPerMeter = sMapScale / REGION_WIDTH_METERS;
clearLastClick();
const S32 DIR_WIDTH = 10;
const S32 DIR_HEIGHT = 10;
LLRect major_dir_rect( 0, DIR_HEIGHT, DIR_WIDTH, 0 );
mTextBoxNorth = new LLTextBox( std::string("N"), major_dir_rect );
addChild( mTextBoxNorth );
LLColor4 minor_color( 1.f, 1.f, 1.f, .7f );
mTextBoxEast = new LLTextBox( std::string("E"), major_dir_rect );
mTextBoxEast->setColor( minor_color );
addChild( mTextBoxEast );
major_dir_rect.mRight += 1 ;
mTextBoxWest = new LLTextBox( std::string("W"), major_dir_rect );
mTextBoxWest->setColor( minor_color );
addChild( mTextBoxWest );
major_dir_rect.mRight -= 1 ;
mTextBoxSouth = new LLTextBox( std::string("S"), major_dir_rect );
mTextBoxSouth->setColor( minor_color );
addChild( mTextBoxSouth );
LLRect minor_dir_rect( 0, DIR_HEIGHT, DIR_WIDTH * 2, 0 );
mTextBoxSouthEast = new LLTextBox( std::string("SE"), minor_dir_rect );
mTextBoxSouthEast->setColor( minor_color );
addChild( mTextBoxSouthEast );
mTextBoxNorthEast = new LLTextBox( std::string("NE"), minor_dir_rect );
mTextBoxNorthEast->setColor( minor_color );
addChild( mTextBoxNorthEast );
mTextBoxSouthWest = new LLTextBox( std::string("SW"), minor_dir_rect );
mTextBoxSouthWest->setColor( minor_color );
addChild( mTextBoxSouthWest );
mTextBoxNorthWest = new LLTextBox( std::string("NW"), minor_dir_rect );
mTextBoxNorthWest->setColor( minor_color );
addChild( mTextBoxNorthWest );
}
LLWorldMapView::~LLWorldMapView()
{
cleanupTextures();
}
// static
void LLWorldMapView::cleanupTextures()
{
}
// static
void LLWorldMapView::setScale( F32 scale )
{
if (scale != sMapScale)
{
F32 old_scale = sMapScale;
sMapScale = scale;
if (sMapScale == 0.f)
{
sMapScale = 0.1f;
}
F32 ratio = (scale / old_scale);
sPanX *= ratio;
sPanY *= ratio;
sTargetPanX = sPanX;
sTargetPanY = sPanY;
sPixelsPerMeter = sMapScale / REGION_WIDTH_METERS;
sVisibleTilesLoaded = false;
}
}
// static
void LLWorldMapView::translatePan( S32 delta_x, S32 delta_y )
{
sPanX += delta_x;
sPanY += delta_y;
sTargetPanX = sPanX;
sTargetPanY = sPanY;
sVisibleTilesLoaded = false;
}
// static
void LLWorldMapView::setPan( S32 x, S32 y, BOOL snap )
{
sTargetPanX = (F32)x;
sTargetPanY = (F32)y;
if (snap)
{
sPanX = sTargetPanX;
sPanY = sTargetPanY;
}
sVisibleTilesLoaded = false;
}
///////////////////////////////////////////////////////////////////////////////////
// HELPERS
BOOL is_agent_in_region(LLViewerRegion* region, LLSimInfo* info)
{
return ((region && info) && (info->mName == region->getName()));
}
///////////////////////////////////////////////////////////////////////////////////
void LLWorldMapView::draw()
{
LLTextureView::clearDebugImages();
F64 current_time = LLTimer::getElapsedSeconds();
mVisibleRegions.clear();
// animate pan if necessary
sPanX = lerp(sPanX, sTargetPanX, LLCriticalDamp::getInterpolant(0.1f));
sPanY = lerp(sPanY, sTargetPanY, LLCriticalDamp::getInterpolant(0.1f));
const S32 width = getRect().getWidth();
const S32 height = getRect().getHeight();
LLVector3d camera_global = gAgent.getCameraPositionGlobal();
LLLocalClipRect clip(getLocalRect());
{
gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
glMatrixMode(GL_MODELVIEW);
// Clear the background alpha to 0
gGL.flush();
gGL.setColorMask(false, true);
gGL.setAlphaRejectSettings(LLRender::CF_GREATER_EQUAL, 0.f);
gGL.setSceneBlendType(LLRender::BT_REPLACE);
gGL.color4f(0.0f, 0.0f, 0.0f, 0.0f);
gl_rect_2d(0, height, width, 0);
}
gGL.flush();
gGL.setAlphaRejectSettings(LLRender::CF_DEFAULT);
gGL.setColorMask(true, true);
gGL.setSceneBlendType(LLRender::BT_ALPHA);
if(gHippoGridManager->getConnectedGrid()->isSecondLife())
drawMipmap(width, height);
else
drawTiles(width, height);
LLFontGL* font = LLFontGL::getFontSansSerifSmall();
const F32 half_width = F32(width) / 2.0f;
const F32 half_height = F32(height) / 2.0f;
// Draw the region name in the lower left corner
for (LLWorldMap::sim_info_map_t::iterator it = LLWorldMap::getInstance()->mSimInfoMap.begin();
it != LLWorldMap::getInstance()->mSimInfoMap.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) * sMapScale;
F32 relative_y = (rel_region_pos.mdV[1] / REGION_WIDTH_METERS) * sMapScale;
F32 bottom = sPanY + half_height + relative_y;
F32 left = sPanX + half_width + relative_x;
F32 top = bottom + sMapScale ;
F32 right = left + sMapScale ;
// disregard regions that are outside the rectangle
if (top < 0.f ||
bottom > height ||
right < 0.f ||
left > width )
{
continue;
}
info->mShowAgentLocations = (sMapScale >= SIM_MAP_AGENT_SCALE);
mVisibleRegions.push_back(handle);
// See if the agents need updating
if (current_time - info->mAgentsUpdateTime > AGENTS_UPDATE_TIME)
{
LLWorldMap::getInstance()->sendItemRequest(MAP_ITEM_AGENT_LOCATIONS, info->mHandle);
info->mAgentsUpdateTime = current_time;
}
std::string mesg;
if (sMapScale < sThresholdA)
{
}
else if (sMapScale < sThresholdB)
{
// mesg = llformat( info->mAgents);
}
else
{
//mesg = llformat("%d / %s (%s)",
// info->mAgents,
// info->mName.c_str(),
// LLViewerRegion::accessToShortString(info->mAccess).c_str() );
// [RLVa:KB] - Alternate: Snowglobe-1.2.4 | Checked: 2009-07-04 (RLVa-1.0.0a)
if (gRlvHandler.hasBehaviour(RLV_BHVR_SHOWLOC))
{
mesg = RlvStrings::getString(RLV_STRING_HIDDEN);
}
// [/RLVa:KB]
else if (info->mAccess == SIM_ACCESS_DOWN)
{
mesg = llformat( "%s (%s)", info->mName.c_str(), sStringsMap["offline"].c_str());
}
else
{
mesg = info->mName;
U8 access = info->mAccess;
switch(access)
{
case SIM_ACCESS_MIN:
mesg += " (Min)";
break;
case SIM_ACCESS_PG:
mesg += " (PG)";
break;
case SIM_ACCESS_MATURE:
mesg += " (Mature)";
break;
case SIM_ACCESS_ADULT:
mesg += " (Adult)";
break;
default:
mesg += llformat(" (Access=%d)",access);
break;
}
}
}
if (!mesg.empty())
{
font->renderUTF8(
mesg, 0,
llfloor(left + 3),
llfloor(bottom + 2),
LLColor4::white,
LLFontGL::LEFT,
LLFontGL::BASELINE,
LLFontGL::DROP_SHADOW);
// If map texture is still loading,
// display "Loading" placeholder text.
/*if ((simimage != NULL) &&
simimage->getDiscardLevel() != 1 &&
simimage->getDiscardLevel() != 0)
{
font->renderUTF8(
sStringsMap["loading"], 0,
llfloor(left + 18),
llfloor(top - 25),
LLColor4::white,
LLFontGL::LEFT,
LLFontGL::BASELINE,
LLFontGL::DROP_SHADOW);
}*/
}
}
// Draw background rectangle
LLGLSUIDefault gls_ui;
{
gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
gGL.setAlphaRejectSettings(LLRender::CF_GREATER_EQUAL, 0.f);
gGL.blendFunc(LLRender::BF_ONE_MINUS_DEST_ALPHA, LLRender::BF_DEST_ALPHA);
gGL.color4fv( mBackgroundColor.mV );
gl_rect_2d(0, height, width, 0);
}
gGL.setAlphaRejectSettings(LLRender::CF_DEFAULT);
gGL.setSceneBlendType(LLRender::BT_ALPHA);
// Infohubs
if (gSavedSettings.getBOOL("MapShowInfohubs")) //(sMapScale >= sThresholdB)
{
drawGenericItems(LLWorldMap::getInstance()->mInfohubs, sInfohubImage);
}
// Telehubs
if (gSavedSettings.getBOOL("MapShowTelehubs")) //(sMapScale >= sThresholdB)
{
drawGenericItems(LLWorldMap::getInstance()->mTelehubs, sTelehubImage);
}
// Home Sweet Home
LLVector3d home;
if (gAgent.getHomePosGlobal(&home))
{
drawImage(home, sHomeImage);
}
if (gSavedSettings.getBOOL("MapShowLandForSale"))
{
drawGenericItems(LLWorldMap::getInstance()->mLandForSale, 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(LLWorldMap::getInstance()->mLandForSaleAdult, sForSaleAdultImage);
}
}
if (gSavedSettings.getBOOL("MapShowPGEvents") ||
gSavedSettings.getBOOL("MapShowMatureEvents") ||
gSavedSettings.getBOOL("MapShowAdultEvents") )
{
drawEvents();
}
// Now draw your avatar after all that other stuff.
LLVector3d pos_global = gAgent.getPositionGlobal();
drawImage(pos_global, sAvatarYouImage);
LLVector3 pos_map = globalPosToView(pos_global);
if (!pointInView(llround(pos_map.mV[VX]), llround(pos_map.mV[VY])))
{
drawTracking(pos_global,
lerp(LLColor4::yellow, LLColor4::orange, 0.4f),
TRUE,
"You are here",
"",
llround(LLFontGL::getFontSansSerifSmall()->getLineHeight())); // offset vertically by one line, to avoid overlap with target tracking
}
// Show your viewing angle
drawFrustum();
// Draw icons for the avatars in each region.
// Drawn after your avatar so you can see nearby people.
if (gSavedSettings.getBOOL("MapShowPeople"))
{
drawAgents();
}
// Always draw tracking information
LLTracker::ETrackingStatus tracking_status = LLTracker::getTrackingStatus();
if ( LLTracker::TRACKING_AVATAR == tracking_status )
{
drawTracking( LLAvatarTracker::instance().getGlobalPos(), gTrackColor, 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, gTrackColor, TRUE, LLTracker::getLabel(), LLTracker::getToolTip() );
}
}
else if (LLWorldMap::getInstance()->mIsTrackingUnknownLocation)
{
if (LLWorldMap::getInstance()->mInvalidLocation)
{
// We know this location to be invalid
LLColor4 loading_color(0.0, 0.5, 1.0, 1.0);
drawTracking( LLWorldMap::getInstance()->mUnknownLocation, loading_color, TRUE, "Invalid Location", "");
}
else
{
double value = fmod(current_time, 2);
value = 0.5 + 0.5*cos(value * 3.14159f);
LLColor4 loading_color(0.0, F32(value/2), F32(value), 1.0);
drawTracking( LLWorldMap::getInstance()->mUnknownLocation, loading_color, TRUE, "Loading...", "");
}
}
// #endif used to be here
// turn off the scissor
LLGLDisable no_scissor(GL_SCISSOR_TEST);
updateDirections();
LLView::draw();
updateVisibleBlocks();
} // end draw()
//virtual
void LLWorldMapView::setVisible(BOOL visible)
{
LLPanel::setVisible(visible);
if (!visible)
{
for (S32 map = 0; map < MAP_SIM_IMAGE_TYPES; map++)
{
for (U32 layer_idx=0; layer_idx<LLWorldMap::getInstance()->mMapLayers[map].size(); ++layer_idx)
{
if (LLWorldMap::getInstance()->mMapLayers[map][layer_idx].LayerDefined)
{
LLWorldMapLayer *layer = &LLWorldMap::getInstance()->mMapLayers[map][layer_idx];
layer->LayerImage->setBoostLevel(0);
}
}
}
for (LLWorldMap::sim_info_map_t::iterator it = LLWorldMap::getInstance()->mSimInfoMap.begin();
it != LLWorldMap::getInstance()->mSimInfoMap.end(); ++it)
{
LLSimInfo* info = (*it).second;
if (info->mCurrentImage.notNull())
{
info->mCurrentImage->setBoostLevel(0);
}
if (info->mOverlayImage.notNull())
{
info->mOverlayImage->setBoostLevel(0);
}
}
}
}
void LLWorldMapView::drawTiles(S32 width, S32 height) {
const F32 half_width = F32(width) / 2.0f;
const F32 half_height = F32(height) / 2.0f;
F32 layer_alpha = 1.f;
LLVector3d camera_global = gAgent.getCameraPositionGlobal();
// Draw one image per layer
for (U32 layer_idx=0; layer_idx<LLWorldMap::getInstance()->mMapLayers[LLWorldMap::getInstance()->mCurrentMap].size(); ++layer_idx)
{
if (!LLWorldMap::getInstance()->mMapLayers[LLWorldMap::getInstance()->mCurrentMap][layer_idx].LayerDefined)
{
continue;
}
LLWorldMapLayer *layer = &LLWorldMap::getInstance()->mMapLayers[LLWorldMap::getInstance()->mCurrentMap][layer_idx];
LLViewerTexture *current_image = layer->LayerImage;
if (current_image->isMissingAsset())
{
continue; // better to draw nothing than the missing asset image
}
LLVector3d origin_global((F64)layer->LayerExtents.mLeft * REGION_WIDTH_METERS, (F64)layer->LayerExtents.mBottom * REGION_WIDTH_METERS, 0.f);
// 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) * sMapScale;
F32 relative_y = (rel_region_pos.mdV[1] / REGION_WIDTH_METERS) * sMapScale;
F32 pix_width = sMapScale*(layer->LayerExtents.getWidth() + 1);
F32 pix_height = sMapScale*(layer->LayerExtents.getHeight() + 1);
// When the view isn't panned, 0,0 = center of rectangle
F32 bottom = sPanY + half_height + relative_y;
F32 left = sPanX + half_width + relative_x;
F32 top = bottom + pix_height;
F32 right = left + pix_width;
F32 pixel_area = pix_width*pix_height;
// discard layers that are outside the rectangle
// and discard small layers
if (top < 0.f ||
bottom > height ||
right < 0.f ||
left > width ||
(pixel_area < 4*4))
{
current_image->setBoostLevel(0);
continue;
}
current_image->setBoostLevel(LLViewerTexture::BOOST_MAP_VISIBLE);
current_image->setKnownDrawSize(llround(pix_width * LLUI::sGLScaleFactor.mV[VX]), llround(pix_height * LLUI::sGLScaleFactor.mV[VY]));
if (!current_image->hasGLTexture())
{
continue; // better to draw nothing than the default image
}
// LLTextureView::addDebugImage(current_image);
// Draw using the texture. If we don't clamp we get artifact at
// the edge.
gGL.getTexUnit(0)->bind(current_image);
// Draw map image into RGB
//gGL.blendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
gGL.flush();
gGL.setColorMask(true, false);
gGL.color4f(1.f, 1.f, 1.f, layer_alpha);
gGL.begin(LLRender::QUADS);
gGL.texCoord2f(0.0f, 1.0f);
gGL.vertex3f(left, top, -1.0f);
gGL.texCoord2f(0.0f, 0.0f);
gGL.vertex3f(left, bottom, -1.0f);
gGL.texCoord2f(1.0f, 0.0f);
gGL.vertex3f(right, bottom, -1.0f);
gGL.texCoord2f(1.0f, 1.0f);
gGL.vertex3f(right, top, -1.0f);
gGL.end();
// draw an alpha of 1 where the sims are visible
gGL.flush();
gGL.setColorMask(false, true);
gGL.color4f(1.f, 1.f, 1.f, 1.f);
gGL.begin(LLRender::QUADS);
gGL.texCoord2f(0.0f, 1.0f);
gGL.vertex2f(left, top);
gGL.texCoord2f(0.0f, 0.0f);
gGL.vertex2f(left, bottom);
gGL.texCoord2f(1.0f, 0.0f);
gGL.vertex2f(right, bottom);
gGL.texCoord2f(1.0f, 1.0f);
gGL.vertex2f(right, top);
gGL.end();
}
gGL.flush();
gGL.setAlphaRejectSettings(LLRender::CF_DEFAULT);
gGL.setColorMask(true, true);
F32 sim_alpha = 1.f;
// Draw one image per region, centered on the camera position.
const S32 MAX_SIMULTANEOUS_TEX = 100;
const S32 MAX_REQUEST_PER_TICK = 5;
const S32 MIN_REQUEST_PER_TICK = 1;
S32 textures_requested_this_tick = 0;
bool use_web_map_tiles = LLWorldMap::useWebMapTiles();
for (LLWorldMap::sim_info_map_t::iterator it = LLWorldMap::getInstance()->mSimInfoMap.begin();
it != LLWorldMap::getInstance()->mSimInfoMap.end(); ++it)
{
U64 handle = (*it).first;
LLSimInfo* info = (*it).second;
LLViewerTexture* simimage = info->mCurrentImage;
LLViewerTexture* overlayimage = info->mOverlayImage;
if (sMapScale < SIM_MAP_SCALE)
{
if (simimage != NULL) simimage->setBoostLevel(0);
if (overlayimage != NULL) overlayimage->setBoostLevel(0);
continue;
}
LLVector3d origin_global = from_region_handle(handle);
LLVector3d camera_global = gAgent.getCameraPositionGlobal();
// 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) * sMapScale;
F32 relative_y = (rel_region_pos.mdV[1] / REGION_WIDTH_METERS) * sMapScale;
// When the view isn't panned, 0,0 = center of rectangle
F32 bottom = sPanY + half_height + relative_y;
F32 left = sPanX + half_width + relative_x;
F32 top = bottom + sMapScale ;
F32 right = left + sMapScale ;
// Switch to world map texture (if available for this region) if either:
// 1. Tiles are zoomed out small enough, or
// 2. Sim's texture has not been loaded yet
F32 map_scale_cutoff = SIM_MAP_SCALE;
if ((info->mRegionFlags & REGION_FLAGS_NULL_LAYER) > 0)
{
map_scale_cutoff = SIM_NULL_MAP_SCALE;
}
bool sim_visible =
(sMapScale >= map_scale_cutoff) &&
(simimage != NULL) &&
(simimage->hasGLTexture());
if (sim_visible)
{
// Fade in
if (info->mAlpha < 0.0f)
info->mAlpha = 1.f; // don't fade initially
else
info->mAlpha = lerp(info->mAlpha, 1.f, LLCriticalDamp::getInterpolant(0.15f));
}
else
{
// Fade out
if (info->mAlpha < 0.0f)
info->mAlpha = 0.f; // don't fade initially
else
info->mAlpha = lerp(info->mAlpha, 0.f, LLCriticalDamp::getInterpolant(0.15f));
}
// discard regions that are outside the rectangle
// and discard small regions
if (top < 0.f ||
bottom > height ||
right < 0.f ||
left > width )
{
if (simimage != NULL) simimage->setBoostLevel(0);
if (overlayimage != NULL) overlayimage->setBoostLevel(0);
continue;
}
if (info->mCurrentImage.isNull())
{
if ((textures_requested_this_tick < MIN_REQUEST_PER_TICK) ||
((LLAppViewer::getTextureFetch()->getNumRequests() < MAX_SIMULTANEOUS_TEX) &&
(textures_requested_this_tick < MAX_REQUEST_PER_TICK)))
{
textures_requested_this_tick++;
if (use_web_map_tiles)
{
LLVector3d region_pos = info->getGlobalOrigin();
info->mCurrentImage = LLWorldMap::loadObjectsTile((U32)(region_pos.mdV[VX] / REGION_WIDTH_UNITS), (U32)(region_pos.mdV[VY] / REGION_WIDTH_UNITS));
}
else
{
info->mCurrentImage = LLViewerTextureManager::getFetchedTexture(info->mMapImageID[LLWorldMap::getInstance()->mCurrentMap], MIPMAP_TRUE);
}
info->mCurrentImage->setAddressMode(LLTexUnit::TAM_CLAMP);
simimage = info->mCurrentImage;
gGL.getTexUnit(0)->bind(simimage);
}
}
if (info->mOverlayImage.isNull() && info->mMapImageID[2].notNull())
{
if ((textures_requested_this_tick < MIN_REQUEST_PER_TICK) ||
((LLAppViewer::getTextureFetch()->getNumRequests() < MAX_SIMULTANEOUS_TEX) &&
(textures_requested_this_tick < MAX_REQUEST_PER_TICK)))
{
textures_requested_this_tick++;
info->mOverlayImage = LLViewerTextureManager::getFetchedTexture(info->mMapImageID[2], MIPMAP_TRUE);
info->mOverlayImage->setAddressMode(LLTexUnit::TAM_CLAMP);
overlayimage = info->mOverlayImage;
gGL.getTexUnit(0)->bind(overlayimage);
}
}
// Bias the priority escalation for images nearer
LLVector3d center_global = origin_global;
center_global.mdV[VX] += 128.0;
center_global.mdV[VY] += 128.0;
S32 draw_size = llround(sMapScale);
if (simimage != NULL)
{
simimage->setBoostLevel(LLViewerTexture::BOOST_MAP);
simimage->setKnownDrawSize(llround(draw_size * LLUI::sGLScaleFactor.mV[VX]), llround(draw_size * LLUI::sGLScaleFactor.mV[VY]));
}
if (overlayimage != NULL)
{
overlayimage->setBoostLevel(LLViewerTexture::BOOST_MAP);
overlayimage->setKnownDrawSize(llround(draw_size * LLUI::sGLScaleFactor.mV[VX]), llround(draw_size * LLUI::sGLScaleFactor.mV[VY]));
}
// LLTextureView::addDebugImage(simimage);
if (sim_visible && info->mAlpha > 0.001f)
{
// Draw using the texture. If we don't clamp we get artifact at
// the edge.
LLGLSUIDefault gls_ui;
if (simimage != NULL)
gGL.getTexUnit(0)->bind(simimage);
gGL.setSceneBlendType(LLRender::BT_ALPHA);
F32 alpha = sim_alpha * info->mAlpha;
gGL.color4f(1.f, 1.0f, 1.0f, alpha);
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 (gSavedSettings.getBOOL("MapShowLandForSale") && overlayimage && overlayimage->hasGLTexture())
{
gGL.getTexUnit(0)->bind(overlayimage);
gGL.color4f(1.f, 1.f, 1.f, alpha);
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();
}
if ((info->mRegionFlags & REGION_FLAGS_NULL_LAYER) == 0)
{
// draw an alpha of 1 where the sims are visible (except NULL sims)
gGL.flush();
gGL.setSceneBlendType(LLRender::BT_REPLACE);
gGL.setColorMask(false, true);
gGL.color4f(1.f, 1.f, 1.f, 1.f);
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();
gGL.flush();
gGL.setColorMask(true, true);
}
}
if (info->mAccess == SIM_ACCESS_DOWN)
{
// Draw a transparent red square over down sims
gGL.blendFunc(LLRender::BF_DEST_ALPHA, LLRender::BF_SOURCE_ALPHA);
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();
}
// As part of the AO project, we no longer want to draw access indicators;
// it's too complicated to get all the rules straight and will only
// cause confusion.
/**********************
// If this is mature, and you are not, draw a line across it
if (info->mAccess != SIM_ACCESS_DOWN
&& info->mAccess > SIM_ACCESS_PG
&& gAgent.isTeen())
{
gGL.blendFunc(LLRender::BF_DEST_ALPHA, LLRender::BF_ZERO);
gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
gGL.color3f(1.f, 0.f, 0.f);
gGL.begin(LLRender::LINES);
gGL.vertex2f(left, top);
gGL.vertex2f(right, bottom);
gGL.vertex2f(left, bottom);
gGL.vertex2f(right, top);
gGL.end();
}
**********************/
}
}
void LLWorldMapView::drawGenericItems(const LLWorldMap::item_info_list_t& items, LLUIImagePtr image)
{
LLWorldMap::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.mPosGlobal, image);
}
void LLWorldMapView::drawImage(const LLVector3d& global_pos, LLUIImagePtr image, const LLColor4& color)
{
LLVector3 pos_map = globalPosToView( global_pos );
image->draw(llround(pos_map.mV[VX] - image->getWidth() /2.f),
llround(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(llround(pos_map.mV[VX] - image->getWidth() /2.f),
llround(pos_map.mV[VY] - image->getHeight()/2.f + i*offset),
color);
}
}
void LLWorldMapView::drawAgents()
{
if(sMapScale < SIM_MAP_AGENT_SCALE)
return;
F32 agents_scale = (sMapScale * 0.9f) / 256.f;
LLColor4 avatar_color = gColors.getColor( "MapAvatar" );
// LLColor4 friend_color = gColors.getColor( "MapFriend" );
for (handle_list_t::iterator iter = mVisibleRegions.begin(); iter != mVisibleRegions.end(); ++iter)
{
U64 handle = *iter;
LLSimInfo* siminfo = LLWorldMap::getInstance()->simInfoFromHandle(handle);
if (siminfo && (siminfo->mAccess == SIM_ACCESS_DOWN))
{
continue;
}
LLWorldMap::agent_list_map_t::iterator counts_iter = LLWorldMap::getInstance()->mAgentLocationsMap.find(handle);
if (siminfo && siminfo->mShowAgentLocations && counts_iter != LLWorldMap::getInstance()->mAgentLocationsMap.end())
{
// Show Individual agents (or little stacks where real agents are)
LLWorldMap::item_info_list_t& agentcounts = counts_iter->second;
S32 sim_agent_count = 0;
for (LLWorldMap::item_info_list_t::iterator iter = agentcounts.begin();
iter != agentcounts.end(); ++iter)
{
const LLItemInfo& info = *iter;
S32 agent_count = info.mExtra;
sim_agent_count += info.mExtra;
// 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(info.mPosGlobal, sAvatarSmallImage, agent_count, 3.f, avatar_color);
}
LLWorldMap::getInstance()->mNumAgents[handle] = sim_agent_count; // override mNumAgents for this sim
}
else
{
// Show agent 'stack' at center of sim
S32 num_agents = LLWorldMap::getInstance()->mNumAgents[handle];
if (num_agents > 0)
{
LLVector3d region_center = from_region_handle(handle);
region_center[VX] += REGION_WIDTH_METERS / 2;
region_center[VY] += REGION_WIDTH_METERS / 2;
// Reduce the stack size as you zoom out - always display at lease one agent where there is one or more
S32 agent_count = (S32)(((num_agents-1) * agents_scale + (num_agents-1) * 0.1f)+.1f) + 1;
drawImageStack(region_center, sAvatarSmallImage, agent_count, 3.f, avatar_color);
}
}
}
}
void LLWorldMapView::drawEvents()
{
bool mature_enabled = gAgent.canAccessMature();
bool adult_enabled = gAgent.canAccessAdult();
BOOL show_pg = gSavedSettings.getBOOL("MapShowPGEvents");
BOOL show_mature = mature_enabled && gSavedSettings.getBOOL("MapShowMatureEvents");
BOOL show_adult = adult_enabled && gSavedSettings.getBOOL("MapShowAdultEvents");
// First the non-selected events
LLWorldMap::item_info_list_t::const_iterator e;
if (show_pg)
{
for (e = LLWorldMap::getInstance()->mPGEvents.begin(); e != LLWorldMap::getInstance()->mPGEvents.end(); ++e)
{
if (!e->mSelected)
{
drawGenericItem(*e, sEventImage);
}
}
}
if (show_mature)
{
for (e = LLWorldMap::getInstance()->mMatureEvents.begin(); e != LLWorldMap::getInstance()->mMatureEvents.end(); ++e)
{
if (!e->mSelected)
{
drawGenericItem(*e, sEventMatureImage);
}
}
}
if (show_adult)
{
for (e = LLWorldMap::getInstance()->mAdultEvents.begin(); e != LLWorldMap::getInstance()->mAdultEvents.end(); ++e)
{
if (!e->mSelected)
{
drawGenericItem(*e, sEventAdultImage);
}
}
}
// Then the selected events
if (show_pg)
{
for (e = LLWorldMap::getInstance()->mPGEvents.begin(); e != LLWorldMap::getInstance()->mPGEvents.end(); ++e)
{
if (e->mSelected)
{
drawGenericItem(*e, sEventImage);
}
}
}
if (show_mature)
{
for (e = LLWorldMap::getInstance()->mMatureEvents.begin(); e != LLWorldMap::getInstance()->mMatureEvents.end(); ++e)
{
if (e->mSelected)
{
drawGenericItem(*e, sEventMatureImage);
}
}
}
if (show_adult)
{
for (e = LLWorldMap::getInstance()->mAdultEvents.begin(); e != LLWorldMap::getInstance()->mAdultEvents.end(); ++e)
{
if (e->mSelected)
{
drawGenericItem(*e, sEventAdultImage);
}
}
}
}
void LLWorldMapView::drawFrustum()
{
// Draw frustum
F32 meters_to_pixels = sMapScale/ 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;
F32 ctr_x = getRect().getWidth() * 0.5f + sPanX;
F32 ctr_y = getRect().getHeight() * 0.5f + sPanY;
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 );
glRotatef( atan2( LLViewerCamera::getInstance()->getAtAxis().mV[VX], LLViewerCamera::getInstance()->getAtAxis().mV[VY] ) * RAD_TO_DEG, 0.f, 0.f, -1.f);
// Draw triangle with more alpha in far pixels to make it
// fade out in distance.
gGL.begin( LLRender::TRIANGLES );
gGL.color4f(1.f, 1.f, 1.f, 0.25f);
gGL.vertex2f( 0, 0 );
gGL.color4f(1.f, 1.f, 1.f, 0.02f);
gGL.vertex2f( -half_width_pixels, far_clip_pixels );
gGL.color4f(1.f, 1.f, 1.f, 0.02f);
gGL.vertex2f( half_width_pixels, far_clip_pixels );
gGL.end();
gGL.popMatrix();
}
void LLWorldMapView::drawMipmap(S32 width, S32 height)
{
// Compute the level of the mipmap to use for the current scale level
S32 level = LLWorldMipmap::scaleToLevel(sMapScale);
// 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("World Map") << "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)
{
// Check the texture state
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.setSceneBlendType(LLRender::BT_ALPHA);
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("World Map") << "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);
}
LLVector3 LLWorldMapView::globalPosToView( const LLVector3d& global_pos )
{
LLVector3d relative_pos_global = global_pos - gAgent.getCameraPositionGlobal();
LLVector3 pos_local;
pos_local.setVec(relative_pos_global); // convert to floats from doubles
pos_local.mV[VX] *= sPixelsPerMeter;
pos_local.mV[VY] *= sPixelsPerMeter;
// leave Z component in meters
pos_local.mV[VX] += getRect().getWidth() / 2 + sPanX;
pos_local.mV[VY] += getRect().getHeight() / 2 + sPanY;
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 = llround( pos_local.mV[VX] );
S32 y = llround( pos_local.mV[VY] );
LLFontGL* font = LLFontGL::getFontSansSerifSmall();
S32 text_x = x;
S32 text_y = (S32)(y - sTrackCircleImage->getHeight()/2 - font->getLineHeight());
BOOL is_in_window = true;
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;
}
is_in_window = false;
}
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() - llround(font->getLineHeight()) - TEXT_PADDING - vert_offset);
//if (label != "")
// [RLVa:KB] - Checked: 2009-07-04 (RLVa-1.0.0a) | Added: RLVa-1.0.0a
if ( (label != "") && (!gRlvHandler.hasBehaviour(RLV_BHVR_SHOWLOC)) )
// [/RLVa:KB]
{
font->renderUTF8(
label, 0,
text_x,
text_y,
LLColor4::white, LLFontGL::HCENTER,
LLFontGL::BASELINE, LLFontGL::DROP_SHADOW);
if (tooltip != "")
{
text_y -= (S32)font->getLineHeight();
font->renderUTF8(
tooltip, 0,
text_x,
text_y,
LLColor4::white, LLFontGL::HCENTER,
LLFontGL::BASELINE, 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 + sPanX));
y -= llfloor((getRect().getHeight() / 2 + sPanY));
LLVector3 pos_local( (F32)x, (F32)y, 0.f );
pos_local *= ( REGION_WIDTH_METERS / sMapScale );
LLVector3d pos_global;
pos_global.setVec( pos_local );
pos_global += gAgent.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, std::string& msg, LLRect* sticky_rect_screen )
{
LLVector3d pos_global = viewPosToGlobal(x, y);
LLSimInfo* info = LLWorldMap::getInstance()->simInfoFromPosGlobal(pos_global);
if (info)
{
LLViewerRegion *region = gAgent.getRegion();
// std::string message = llformat("%s (%s)", info->getName().c_str(), info->getAccessString().c_str());
// [RLVa:KB] - Alternate: Snowglobe-1.2.4 | Checked: 2009-07-04 (RLVa-1.0.0a)
std::string message = llformat("%s (%s)",
(!gRlvHandler.hasBehaviour(RLV_BHVR_SHOWLOC)) ? info->mName.c_str() : RlvStrings::getString(RLV_STRING_HIDDEN).c_str(),
LLViewerRegion::accessToString(info->mAccess).c_str());
// [/RLVa:KB]
if (info->mAccess != SIM_ACCESS_DOWN)
{
S32 agent_count = LLWorldMap::getInstance()->mNumAgents[info->mHandle];
if (region && region->getHandle() == info->mHandle)
{
++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)
{
message += llformat("\n%d ", agent_count);
if (agent_count == 1)
{
message += "avatar";
}
else
{
message += "avatars";
}
}
}
msg.assign( message );
// Optionally show region flags
std::string region_flags = LLViewerRegion::regionFlagsToString(info->mRegionFlags);
if (!region_flags.empty())
{
msg += '\n';
msg += region_flags;
}
S32 SLOP = 4;
localPointToScreen(
x - SLOP, y - SLOP,
&(sticky_rect_screen->mLeft), &(sticky_rect_screen->mBottom) );
sticky_rect_screen->mRight = sticky_rect_screen->mLeft + 2 * SLOP;
sticky_rect_screen->mTop = sticky_rect_screen->mBottom + 2 * SLOP;
}
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(llround(x_pixels) - dot_image->getWidth()/2,
llround(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,
LLColor4 color,
F32 relative_z,
F32 dot_radius)
{
const F32 HEIGHT_THRESHOLD = 7.f;
LLUIImagePtr dot_image = sAvatarLevelImage;
if (relative_z == 16000.f) // Unknown altitude (0m or > 1020m)
{
//F32 mag = color.length();
//color=color*0.5f + LLColor4(mag, mag, mag)*0.25f;
dot_image = sAvatarSmallImage;
}
else if (relative_z < -HEIGHT_THRESHOLD)
{
dot_image = sAvatarBelowImage;
}
else if (relative_z > HEIGHT_THRESHOLD)
{
dot_image = sAvatarAboveImage;
}
S32 dot_width = llround(dot_radius * 2.f);
dot_image->draw(llround(x_pixels - dot_radius),
llround(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 = llround(x_pixels);
S32 text_y = llround(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::DROP_SHADOW);
text_y -= llround(LLFontGL::getFontSansSerif()->getLineHeight());
// render text
LLFontGL::getFontSansSerif()->renderUTF8(second_line, 0,
text_x,
text_y,
color,
LLFontGL::HCENTER,
LLFontGL::TOP,
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;
}
glMatrixMode(GL_MODELVIEW);
gGL.pushMatrix();
gGL.translatef((F32)x, (F32)y, 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(
llround(map_half_width - text_half_width + radius * cos( rotation )),
llround(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.mPosGlobal);
S32 item_x = llround(pos_view.mV[VX]);
S32 item_y = llround(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.mRegionHandle);
if (sim_info)
{
if (track)
{
gFloaterWorldMap->trackLocation(item.mPosGlobal);
}
}
if (track)
{
gFloaterWorldMap->trackGenericItem(item);
}
item.mSelected = TRUE;
*id = item.mID;
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()->mIsTrackingUnknownLocation = FALSE;
LLWorldMap::getInstance()->mIsTrackingDoubleClick = FALSE;
LLWorldMap::getInstance()->mIsTrackingCommit = FALSE;
LLWorldMap::item_info_list_t::iterator it;
// clear old selected stuff
for (it = LLWorldMap::getInstance()->mPGEvents.begin(); it != LLWorldMap::getInstance()->mPGEvents.end(); ++it)
{
(*it).mSelected = FALSE;
}
for (it = LLWorldMap::getInstance()->mMatureEvents.begin(); it != LLWorldMap::getInstance()->mMatureEvents.end(); ++it)
{
(*it).mSelected = FALSE;
}
for (it = LLWorldMap::getInstance()->mAdultEvents.begin(); it != LLWorldMap::getInstance()->mAdultEvents.end(); ++it)
{
(*it).mSelected = FALSE;
}
for (it = LLWorldMap::getInstance()->mLandForSale.begin(); it != LLWorldMap::getInstance()->mLandForSale.end(); ++it)
{
(*it).mSelected = FALSE;
}
// Select event you clicked on
if (gSavedSettings.getBOOL("MapShowPGEvents"))
{
for (it = LLWorldMap::getInstance()->mPGEvents.begin(); it != LLWorldMap::getInstance()->mPGEvents.end(); ++it)
{
LLItemInfo& event = *it;
if (checkItemHit(x, y, event, id, false))
{
*hit_type = MAP_ITEM_PG_EVENT;
mItemPicked = TRUE;
gFloaterWorldMap->trackEvent(event);
return;
}
}
}
if (gSavedSettings.getBOOL("MapShowMatureEvents"))
{
for (it = LLWorldMap::getInstance()->mMatureEvents.begin(); it != LLWorldMap::getInstance()->mMatureEvents.end(); ++it)
{
LLItemInfo& event = *it;
if (checkItemHit(x, y, event, id, false))
{
*hit_type = MAP_ITEM_MATURE_EVENT;
mItemPicked = TRUE;
gFloaterWorldMap->trackEvent(event);
return;
}
}
}
if (gSavedSettings.getBOOL("MapShowAdultEvents"))
{
for (it = LLWorldMap::getInstance()->mAdultEvents.begin(); it != LLWorldMap::getInstance()->mAdultEvents.end(); ++it)
{
LLItemInfo& event = *it;
if (checkItemHit(x, y, event, id, false))
{
*hit_type = MAP_ITEM_ADULT_EVENT;
mItemPicked = TRUE;
gFloaterWorldMap->trackEvent(event);
return;
}
}
}
if (gSavedSettings.getBOOL("MapShowLandForSale"))
{
for (it = LLWorldMap::getInstance()->mLandForSale.begin(); it != LLWorldMap::getInstance()->mLandForSale.end(); ++it)
{
LLItemInfo& land = *it;
if (checkItemHit(x, y, land, id, true))
{
*hit_type = MAP_ITEM_LAND_FOR_SALE;
mItemPicked = TRUE;
return;
}
}
for (it = LLWorldMap::getInstance()->mLandForSaleAdult.begin(); it != LLWorldMap::getInstance()->mLandForSaleAdult.end(); ++it)
{
LLItemInfo& land = *it;
if (checkItemHit(x, y, land, id, true))
{
*hit_type = MAP_ITEM_LAND_FOR_SALE_ADULT;
mItemPicked = TRUE;
return;
}
}
}
// If we get here, we haven't clicked on an icon
gFloaterWorldMap->trackLocation(pos_global);
mItemPicked = FALSE;
*id = LLUUID::null;
return;
}
BOOL outside_slop(S32 x, S32 y, S32 start_x, S32 start_y)
{
S32 dx = x - start_x;
S32 dy = y - start_y;
return (dx <= -2 || 2 <= dx || dy <= -2 || 2 <= dy);
}
BOOL LLWorldMapView::handleMouseDown( S32 x, S32 y, MASK mask )
{
gFocusMgr.setMouseCapture( this );
mMouseDownPanX = llround(sPanX);
mMouseDownPanY = llround(sPanY);
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(sPanX - mMouseDownPanX);
local_y = mMouseDownY + llfloor(sPanY - mMouseDownPanY);
LLRect clip_rect = getRect();
clip_rect.stretch(-8);
clip_rect.clipPointToRect(mMouseDownX, mMouseDownY, local_x, local_y);
LLUI::setCursorPositionLocal(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;
}
U32 LLWorldMapView::updateBlock(S32 block_x, S32 block_y)
{
U32 blocks_requested = 0;
S32 offset = block_x | (block_y * MAP_BLOCK_RES);
if (!LLWorldMap::getInstance()->mMapBlockLoaded[LLWorldMap::getInstance()->mCurrentMap][offset])
{
// llinfos << "Loading Block (" << block_x << "," << block_y << ")" << llendl;
LLWorldMap::getInstance()->sendMapBlockRequest(block_x << 3, block_y << 3, (block_x << 3) + 7, (block_y << 3) + 7);
LLWorldMap::getInstance()->mMapBlockLoaded[LLWorldMap::getInstance()->mCurrentMap][offset] = TRUE;
blocks_requested++;
}
return blocks_requested;
}
U32 LLWorldMapView::updateVisibleBlocks()
{
if (sMapScale < SIM_MAP_SCALE)
{
// We don't care what is loaded if we're zoomed out
return 0;
}
LLVector3d camera_global = gAgent.getCameraPositionGlobal();
F32 pixels_per_region = sMapScale;
const S32 width = getRect().getWidth();
const S32 height = getRect().getHeight();
// Convert pan to sim coordinates
S32 world_center_x_lo = S32(((-sPanX - width/2) / pixels_per_region) + (camera_global.mdV[0] / REGION_WIDTH_METERS));
S32 world_center_x_hi = S32(((-sPanX + width/2) / pixels_per_region) + (camera_global.mdV[0] / REGION_WIDTH_METERS));
S32 world_center_y_lo = S32(((-sPanY - height/2) / pixels_per_region) + (camera_global.mdV[1] / REGION_WIDTH_METERS));
S32 world_center_y_hi = S32(((-sPanY + height/2)/ pixels_per_region) + (camera_global.mdV[1] / REGION_WIDTH_METERS));
// Find the corresponding 8x8 block
S32 world_block_x_lo = world_center_x_lo >> 3;
S32 world_block_x_hi = world_center_x_hi >> 3;
S32 world_block_y_lo = world_center_y_lo >> 3;
S32 world_block_y_hi = world_center_y_hi >> 3;
U32 blocks_requested = 0;
const U32 max_blocks_requested = 9;
for (S32 block_x = llmax(world_block_x_lo, 0); block_x <= llmin(world_block_x_hi, MAP_BLOCK_RES-1); ++block_x)
{
for (S32 block_y = llmax(world_block_y_lo, 0); block_y <= llmin(world_block_y_hi, MAP_BLOCK_RES-1); ++block_y)
{
blocks_requested += updateBlock(block_x, block_y);
if (blocks_requested >= max_blocks_requested)
return blocks_requested;
}
}
return blocks_requested;
}
BOOL LLWorldMapView::handleHover( S32 x, S32 y, MASK mask )
{
if (hasMouseCapture())
{
if (mPanning || outside_slop(x, y, mMouseDownX, mMouseDownY))
{
// 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
sPanX += delta_x;
sPanY += delta_y;
sTargetPanX = sPanX;
sTargetPanY = sPanY;
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 );
}
lldebugst(LLERR_USER_INPUT) << "hover handled by LLWorldMapView" << llendl;
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:
{
gFloaterWorldMap->close();
// 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);
LLFloaterDirectory::showEvents(event_id);
break;
}
case MAP_ITEM_LAND_FOR_SALE:
case MAP_ITEM_LAND_FOR_SALE_ADULT:
{
gFloaterWorldMap->close();
LLFloaterDirectory::showLandForSale(id);
break;
}
case MAP_ITEM_CLASSIFIED:
{
gFloaterWorldMap->close();
LLFloaterDirectory::showClassified(id);
break;
}
default:
{
if (LLWorldMap::getInstance()->mIsTrackingUnknownLocation)
{
LLWorldMap::getInstance()->mIsTrackingDoubleClick = TRUE;
}
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->mAccess != SIM_ACCESS_DOWN)
{
gAgent.teleportViaLocation( pos_global );
}
}
}
};
return TRUE;
}
return FALSE;
}