diff options
author | James Cook <james@lindenlab.com> | 2007-01-02 08:33:20 +0000 |
---|---|---|
committer | James Cook <james@lindenlab.com> | 2007-01-02 08:33:20 +0000 |
commit | 420b91db29485df39fd6e724e782c449158811cb (patch) | |
tree | b471a94563af914d3ed3edd3e856d21cb1b69945 /indra/newview/llworldmapview.cpp |
Print done when done.
Diffstat (limited to 'indra/newview/llworldmapview.cpp')
-rw-r--r-- | indra/newview/llworldmapview.cpp | 1940 |
1 files changed, 1940 insertions, 0 deletions
diff --git a/indra/newview/llworldmapview.cpp b/indra/newview/llworldmapview.cpp new file mode 100644 index 0000000000..56e496e303 --- /dev/null +++ b/indra/newview/llworldmapview.cpp @@ -0,0 +1,1940 @@ +/** + * @file llworldmapview.cpp + * @brief LLWorldMapView class implementation + * + * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc. + * $License$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llworldmapview.h" + +#include "indra_constants.h" +#include "llui.h" +#include "linked_lists.h" +#include "llmath.h" // clampf() +#include "llregionhandle.h" +#include "lleventflags.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 "llviewerimage.h" +#include "llviewerimagelist.h" +#include "llviewerparceloverlay.h" +#include "llviewerregion.h" +#include "llviewerwindow.h" +#include "llworld.h" +#include "llworldmap.h" +#include "viewer.h" // Only for constants! + +#include "llglheaders.h" + +const F32 GODLY_TELEPORT_HEIGHT = 200.f; +const S32 SCROLL_HINT_WIDTH = 65; +const F32 BIG_DOT_RADIUS = 5.f; +BOOL LLWorldMapView::sHandledLastClick = FALSE; + +LLPointer<LLViewerImage> LLWorldMapView::sAvatarYouSmallImage = NULL; +LLPointer<LLViewerImage> LLWorldMapView::sAvatarSmallImage = NULL; +LLPointer<LLViewerImage> LLWorldMapView::sAvatarLargeImage = NULL; + +LLPointer<LLViewerImage> LLWorldMapView::sTelehubImage = NULL; +LLPointer<LLViewerImage> LLWorldMapView::sInfohubImage = NULL; +LLPointer<LLViewerImage> LLWorldMapView::sHomeImage = NULL; +LLPointer<LLViewerImage> LLWorldMapView::sEventImage = NULL; +LLPointer<LLViewerImage> LLWorldMapView::sEventMatureImage = NULL; + +LLPointer<LLViewerImage> LLWorldMapView::sTrackCircleImage = NULL; +LLPointer<LLViewerImage> LLWorldMapView::sTrackArrowImage = NULL; + +LLPointer<LLViewerImage> LLWorldMapView::sClassifiedsImage = NULL; +LLPointer<LLViewerImage> LLWorldMapView::sPopularImage = NULL; +LLPointer<LLViewerImage> LLWorldMapView::sForSaleImage = 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; +F32 CONE_SIZE = 0.6f; + +void LLWorldMapView::initClass() +{ + LLUUID image_id; + + image_id.set( gViewerArt.getString("map_avatar_you_8.tga") ); + sAvatarYouSmallImage = gImageList.getImage( image_id, MIPMAP_FALSE, TRUE); + + image_id.set( gViewerArt.getString("map_avatar_8.tga") ); + sAvatarSmallImage = gImageList.getImage( image_id, MIPMAP_FALSE, TRUE); + + image_id.set( gViewerArt.getString("map_avatar_16.tga") ); + sAvatarLargeImage = gImageList.getImage( image_id, MIPMAP_FALSE, TRUE); + + image_id.set( gViewerArt.getString("map_home.tga") ); + sHomeImage = gImageList.getImage(image_id, MIPMAP_FALSE, TRUE); + + image_id.set( gViewerArt.getString("map_telehub.tga") ); + sTelehubImage = gImageList.getImage(image_id, MIPMAP_FALSE, TRUE); + + image_id.set( gViewerArt.getString("map_infohub.tga") ); + sInfohubImage = gImageList.getImage(image_id, MIPMAP_FALSE, TRUE); + + image_id.set( gViewerArt.getString("map_event.tga") ); + sEventImage = gImageList.getImage(image_id, MIPMAP_FALSE, TRUE); + + image_id.set( gViewerArt.getString("map_event_mature.tga") ); + sEventMatureImage = gImageList.getImage(image_id, MIPMAP_FALSE, TRUE); + + image_id.set( gViewerArt.getString("map_track_16.tga") ); + sTrackCircleImage = gImageList.getImage(image_id, MIPMAP_FALSE, TRUE); + + image_id.set( gViewerArt.getString("direction_arrow.tga") ); + sTrackArrowImage = gImageList.getImage(image_id, MIPMAP_FALSE, TRUE); + // Make sure tracker arrow doesn't wrap + sTrackArrowImage->bindTexture(0); + sTrackArrowImage->setClamp(TRUE, TRUE); + + image_id.set( gViewerArt.getString("icon_top_pick.tga") ); + sClassifiedsImage = gImageList.getImage(image_id, MIPMAP_FALSE, TRUE); + + image_id.set( gViewerArt.getString("icon_popular.tga") ); + sPopularImage = gImageList.getImage(image_id, MIPMAP_FALSE, TRUE); + + image_id.set( gViewerArt.getString("icon_for_sale.tga") ); + sForSaleImage = gImageList.getImage(image_id, MIPMAP_FALSE, TRUE); +} + +// static +void LLWorldMapView::cleanupClass() +{ + sAvatarYouSmallImage = NULL; + sAvatarSmallImage = NULL; + sAvatarLargeImage = NULL; + sTelehubImage = NULL; + sInfohubImage = NULL; + sHomeImage = NULL; + sEventImage = NULL; + sEventMatureImage = NULL; + sTrackCircleImage = NULL; + sTrackArrowImage = NULL; + sClassifiedsImage = NULL; + sPopularImage = NULL; + sForSaleImage = NULL; +} + +LLWorldMapView::LLWorldMapView(const std::string& name, const LLRect& rect ) +: LLPanel(name, rect, BORDER_NO), + mBackgroundColor( LLColor4( 4.f/255.f, 4.f/255.f, 75.f/255.f, 1.f ) ), + mItemPicked(FALSE), + mPanning( FALSE ), + mMouseDownPanX( 0 ), + mMouseDownPanY( 0 ), + mMouseDownX( 0 ), + mMouseDownY( 0 ), + mSelectIDStart(0), + mAgentCountsUpdateTime(0) +{ + sPixelsPerMeter = gMapScale / 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( "N", major_dir_rect ); + mTextBoxNorth->setDropshadowVisible( TRUE ); + addChild( mTextBoxNorth ); + + LLColor4 minor_color( 1.f, 1.f, 1.f, .7f ); + + mTextBoxEast = new LLTextBox( "E", major_dir_rect ); + mTextBoxEast->setColor( minor_color ); + addChild( mTextBoxEast ); + + mTextBoxWest = new LLTextBox( "W", major_dir_rect ); + mTextBoxWest->setColor( minor_color ); + addChild( mTextBoxWest ); + + mTextBoxSouth = new LLTextBox( "S", major_dir_rect ); + mTextBoxSouth->setColor( minor_color ); + addChild( mTextBoxSouth ); + + LLRect minor_dir_rect( 0, DIR_HEIGHT, DIR_WIDTH * 2, 0 ); + + mTextBoxSouthEast = new LLTextBox( "SE", minor_dir_rect ); + mTextBoxSouthEast->setColor( minor_color ); + addChild( mTextBoxSouthEast ); + + mTextBoxNorthEast = new LLTextBox( "NE", minor_dir_rect ); + mTextBoxNorthEast->setColor( minor_color ); + addChild( mTextBoxNorthEast ); + + mTextBoxSouthWest = new LLTextBox( "SW", minor_dir_rect ); + mTextBoxSouthWest->setColor( minor_color ); + addChild( mTextBoxSouthWest ); + + mTextBoxNorthWest = new LLTextBox( "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 != gMapScale) + { + F32 old_scale = gMapScale; + + gMapScale = scale; + if (gMapScale == 0.f) + { + gMapScale = 0.1f; + } + + F32 ratio = (scale / old_scale); + sPanX *= ratio; + sPanY *= ratio; + sTargetPanX = sPanX; + sTargetPanY = sPanY; + + sPixelsPerMeter = gMapScale / REGION_WIDTH_METERS; + } +} + + +// static +void LLWorldMapView::translatePan( S32 delta_x, S32 delta_y ) +{ + sPanX += delta_x; + sPanY += delta_y; + sTargetPanX = sPanX; + sTargetPanY = sPanY; +} + + +// static +void LLWorldMapView::setPan( S32 x, S32 y, BOOL snap ) +{ + sTargetPanX = (F32)x; + sTargetPanY = (F32)y; + if (snap) + { + sPanX = sTargetPanX; + sPanY = sTargetPanY; + } +} + + +/////////////////////////////////////////////////////////////////////////////////// + +// dumb helper function +BOOL is_agent_in_region(LLViewerRegion* region, LLSimInfo* info) +{ + if((region && info) + && (info->mName == region->getName())) + { + return TRUE; + } + return FALSE; +} + +void LLWorldMapView::draw() +{ + if (!getVisible() || !gWorldPointer) + { + return; + } + + LLTextureView::clearDebugImages(); + + F64 current_time = LLTimer::getElapsedSeconds(); + + if (current_time - mAgentCountsUpdateTime > AGENT_COUNTS_UPDATE_TIME) + { + gWorldMap->sendItemRequest(MAP_ITEM_AGENT_COUNT); + mAgentCountsUpdateTime = current_time; + } + + mVisibleRegions.clear(); + + // animate pan if necessary + sPanX = lerp(sPanX, sTargetPanX, LLCriticalDamp::getInterpolant(0.1f)); + sPanY = lerp(sPanY, sTargetPanY, LLCriticalDamp::getInterpolant(0.1f)); + + LLVector3d pos_global; + LLVector3 pos_map; + + const S32 width = mRect.getWidth(); + const S32 height = mRect.getHeight(); + const S32 half_width = width / 2; + const S32 half_height = height / 2; + LLVector3d camera_global = gAgent.getCameraPositionGlobal(); + + LLGLEnable scissor_test(GL_SCISSOR_TEST); + LLUI::setScissorRegionLocal(LLRect(0, height, width, 0)); + { + LLGLSNoTexture no_texture; + + + glMatrixMode(GL_MODELVIEW); + + // Clear the background alpha to 0 + glColorMask(FALSE, FALSE, FALSE, TRUE); + glAlphaFunc(GL_GEQUAL, 0.00f); + glBlendFunc(GL_ONE, GL_ZERO); + glColor4f(0.0f, 0.0f, 0.0f, 0.0f); + gl_rect_2d(0, height, width, 0); + } + + glAlphaFunc(GL_GEQUAL, 0.01f); + glColorMask(TRUE, TRUE, TRUE, TRUE); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + F32 layer_alpha = 1.f; + + // Draw one image per layer + for (U32 layer_idx=0; layer_idx<gWorldMap->mMapLayers[gWorldMap->mCurrentMap].size(); ++layer_idx) + { + if (!gWorldMap->mMapLayers[gWorldMap->mCurrentMap][layer_idx].LayerDefined) + { + continue; + } + LLWorldMapLayer *layer = &gWorldMap->mMapLayers[gWorldMap->mCurrentMap][layer_idx]; + LLViewerImage *current_image = layer->LayerImage; +#if 1 || LL_RELEASE_FOR_DOWNLOAD + if (current_image->isMissingAsset()) + { + continue; // better to draw nothing than the missing asset image + } +#endif + + 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; + S32 relative_x = lltrunc((rel_region_pos.mdV[0] / REGION_WIDTH_METERS) * gMapScale); + S32 relative_y = lltrunc((rel_region_pos.mdV[1] / REGION_WIDTH_METERS) * gMapScale); + + F32 pix_width = gMapScale*(layer->LayerExtents.getWidth() + 1); + F32 pix_height = gMapScale*(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(LLViewerImage::BOOST_MAP_LAYER); + current_image->setKnownDrawSize(llround(pix_width), llround(pix_height)); + +#if 1 || LL_RELEASE_FOR_DOWNLOAD + if (!current_image->getHasGLTexture()) + { + continue; // better to draw nothing than the default image + } +#endif + + LLTextureView::addDebugImage(current_image); + + // Draw using the texture. If we don't clamp we get artifact at + // the edge. + LLViewerImage::bindTexture(current_image); + + // Draw map image into RGB + //glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glColorMask(TRUE, TRUE, TRUE, FALSE); + glColor4f(1.f, 1.f, 1.f, layer_alpha); + + glBegin(GL_QUADS); + glTexCoord2f(0.0f, 1.0f); + glVertex3f(left, top, -1.0f); + glTexCoord2f(0.0f, 0.0f); + glVertex3f(left, bottom, -1.0f); + glTexCoord2f(1.0f, 0.0f); + glVertex3f(right, bottom, -1.0f); + glTexCoord2f(1.0f, 1.0f); + glVertex3f(right, top, -1.0f); + glEnd(); + + // draw an alpha of 1 where the sims are visible + glColorMask(FALSE, FALSE, FALSE, TRUE); + glColor4f(1.f, 1.f, 1.f, 1.f); + + glBegin(GL_QUADS); + glTexCoord2f(0.0f, 1.0f); + glVertex2f(left, top); + glTexCoord2f(0.0f, 0.0f); + glVertex2f(left, bottom); + glTexCoord2f(1.0f, 0.0f); + glVertex2f(right, bottom); + glTexCoord2f(1.0f, 1.0f); + glVertex2f(right, top); + glEnd(); + } + + glAlphaFunc(GL_GEQUAL, 0.01f); + glColorMask(TRUE, TRUE, TRUE, TRUE); + +#if 1 + F32 sim_alpha = 1.f; + + // Draw one image per region, centered on the camera position. + for (LLWorldMap::sim_info_map_t::iterator it = gWorldMap->mSimInfoMap.begin(); + it != gWorldMap->mSimInfoMap.end(); ++it) + { + U64 handle = (*it).first; + LLSimInfo* info = (*it).second; + + if (info->mCurrentImage.isNull()) + { + info->mCurrentImage = gImageList.getImage(info->mMapImageID[gWorldMap->mCurrentMap], MIPMAP_TRUE, FALSE); + } + if (info->mOverlayImage.isNull() && info->mMapImageID[2].notNull()) + { + info->mOverlayImage = gImageList.getImage(info->mMapImageID[2], MIPMAP_TRUE, FALSE); + info->mOverlayImage->bind(0); + info->mOverlayImage->setClamp(TRUE, TRUE); + } + + LLViewerImage* simimage = info->mCurrentImage; + LLViewerImage* overlayimage = info->mOverlayImage; + + if (gMapScale < 8.f) + { + simimage->setBoostLevel(0); + if (overlayimage) 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; + S32 relative_x = lltrunc((rel_region_pos.mdV[0] / REGION_WIDTH_METERS) * gMapScale); + S32 relative_y = lltrunc((rel_region_pos.mdV[1] / REGION_WIDTH_METERS) * gMapScale); + + // 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 + gMapScale ; + F32 right = left + gMapScale ; + + // 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; + } + + info->mShowAgentLocations = (gMapScale >= SIM_MAP_AGENT_SCALE); + + bool sim_visible = + (gMapScale >= map_scale_cutoff) && + (simimage->getHasGLTexture()); + + 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 ) + { + simimage->setBoostLevel(0); + if (overlayimage) overlayimage->setBoostLevel(0); + continue; + } + + mVisibleRegions.push_back(handle); + // See if the agents need updating + if (current_time - info->mAgentsUpdateTime > AGENTS_UPDATE_TIME) + { + gWorldMap->sendItemRequest(MAP_ITEM_AGENT_LOCATIONS, info->mHandle); + info->mAgentsUpdateTime = current_time; + } + + // 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(gMapScale); + simimage->setBoostLevel(LLViewerImage::BOOST_MAP); + simimage->setKnownDrawSize(draw_size, draw_size); + + if (overlayimage) + { + overlayimage->setBoostLevel(LLViewerImage::BOOST_MAP); + overlayimage->setKnownDrawSize(draw_size, draw_size); + } + + 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; + LLViewerImage::bindTexture(simimage); + + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + F32 alpha = sim_alpha * info->mAlpha; + glColor4f(1.f, 1.0f, 1.0f, alpha); + + glBegin(GL_QUADS); + glTexCoord2f(0.f, 1.f); + glVertex3f(left, top, 0.f); + glTexCoord2f(0.f, 0.f); + glVertex3f(left, bottom, 0.f); + glTexCoord2f(1.f, 0.f); + glVertex3f(right, bottom, 0.f); + glTexCoord2f(1.f, 1.f); + glVertex3f(right, top, 0.f); + glEnd(); + + if (gSavedSettings.getBOOL("MapShowLandForSale") && overlayimage && overlayimage->getHasGLTexture()) + { + LLViewerImage::bindTexture(overlayimage); + glColor4f(1.f, 1.f, 1.f, alpha); + glBegin(GL_QUADS); + glTexCoord2f(0.f, 1.f); + glVertex3f(left, top, -0.5f); + glTexCoord2f(0.f, 0.f); + glVertex3f(left, bottom, -0.5f); + glTexCoord2f(1.f, 0.f); + glVertex3f(right, bottom, -0.5f); + glTexCoord2f(1.f, 1.f); + glVertex3f(right, top, -0.5f); + glEnd(); + } + + if ((info->mRegionFlags & REGION_FLAGS_NULL_LAYER) == 0) + { + // draw an alpha of 1 where the sims are visible (except NULL sims) + glBlendFunc(GL_ONE, GL_ZERO); + glColorMask(FALSE, FALSE, FALSE, TRUE); + glColor4f(1.f, 1.f, 1.f, 1.f); + + LLGLSNoTexture gls_no_texture; + glBegin(GL_QUADS); + glVertex2f(left, top); + glVertex2f(left, bottom); + glVertex2f(right, bottom); + glVertex2f(right, top); + glEnd(); + + glColorMask(TRUE, TRUE, TRUE, TRUE); + } + } + + if (info->mAccess == SIM_ACCESS_DOWN) + { + // Draw a transparent red square over down sims + glBlendFunc(GL_DST_ALPHA, GL_SRC_ALPHA); + glColor4f(0.2f, 0.0f, 0.0f, 0.4f); + + LLGLSNoTexture gls_no_texture; + glBegin(GL_QUADS); + glVertex2f(left, top); + glVertex2f(left, bottom); + glVertex2f(right, bottom); + glVertex2f(right, top); + glEnd(); + } + + // If this is mature, and you are not, draw a line across it + if (info->mAccess != SIM_ACCESS_DOWN && info->mAccess > gAgent.mAccess) + { + glBlendFunc(GL_DST_ALPHA, GL_ZERO); + + LLGLSNoTexture gls_no_texture; + glColor3f(1.f, 0.f, 0.f); + glBegin(GL_LINES); + glVertex2f(left, top); + glVertex2f(right, bottom); + glVertex2f(left, bottom); + glVertex2f(right, top); + glEnd(); + } + + // Draw the region name in the lower left corner + LLFontGL* font = LLFontGL::sSansSerifSmall; + + char mesg[MAX_STRING]; + if (gMapScale < sThresholdA) + { + mesg[0] = '\0'; + } + else if (gMapScale < sThresholdB) + { + //sprintf(mesg, "%d", info->mAgents); + mesg[0] = '\0'; + } + else + { + //sprintf(mesg, "%d / %s (%s)", + // info->mAgents, + // info->mName.c_str(), + // LLViewerRegion::accessToShortString(info->mAccess) ); + if (info->mAccess == SIM_ACCESS_DOWN) + { + sprintf(mesg, "%s (Offline)", info->mName.c_str()); + } + else + { + sprintf(mesg, "%s", info->mName.c_str()); + } + } + + if (mesg[0] != '\0') + { + font->renderUTF8( + mesg, 0, + llfloor(left + 3), + llfloor(bottom + 2), + LLColor4::white, + LLFontGL::LEFT, + LLFontGL::BASELINE, + LLFontGL::DROP_SHADOW); + } + } +#endif + + +#if 1 + // Draw background rectangle + LLGLSUIDefault gls_ui; + { + LLGLSNoTexture gls_no_texture; + glAlphaFunc(GL_GEQUAL, 0.0f); + glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_DST_ALPHA); + glColor4fv( mBackgroundColor.mV ); + gl_rect_2d(0, height, width, 0); + } + + glAlphaFunc(GL_GEQUAL, 0.01f); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + // Infohubs + // Draw under avatar so you can find yourself. + if (gSavedSettings.getBOOL("MapShowInfohubs")) //(gMapScale >= sThresholdB) + { + S32 infohub_icon_size = (S32)sInfohubImage->getWidth(); + for (U32 infohub=0; infohub<gWorldMap->mInfohubs.size(); ++infohub) + { + LLItemInfo *infohub_info = &gWorldMap->mInfohubs[infohub]; + + pos_map = globalPosToView(infohub_info->mPosGlobal); + gl_draw_image(llround(pos_map.mV[VX])-infohub_icon_size/2, + llround(pos_map.mV[VY])-infohub_icon_size/2, + sInfohubImage, + LLColor4::white); + } + } + + // Telehubs + // Draw under avatar so you can find yourself. + if (gSavedSettings.getBOOL("MapShowTelehubs")) //(gMapScale >= sThresholdB) + { + S32 telehub_icon_size = (S32)sTelehubImage->getWidth(); + for (U32 telehub=0; telehub<gWorldMap->mTelehubs.size(); ++telehub) + { + LLItemInfo *telehub_info = &gWorldMap->mTelehubs[telehub]; + + pos_map = globalPosToView(telehub_info->mPosGlobal); + gl_draw_image(llround(pos_map.mV[VX])-telehub_icon_size/2, + llround(pos_map.mV[VY])-telehub_icon_size/2, + sTelehubImage, + LLColor4::white); + } + } + + // Home + // Always Draw + if (1) //(gMapScale >= sThresholdB) + { + S32 home_icon_size = (S32)sHomeImage->getWidth(); + if (gAgent.getHomePosGlobal(&pos_global)) + { + pos_map = globalPosToView(pos_global); + gl_draw_image(llround(pos_map.mV[VX])-home_icon_size/2, + llround(pos_map.mV[VY])-home_icon_size/2, + sHomeImage, + LLColor4::white); + } + } + + // Draw all these under the avatar, so you can find yourself + + if (gSavedSettings.getBOOL("MapShowLandForSale")) + { + drawGenericItems(gWorldMap->mLandForSale, sForSaleImage); + } + + if (gSavedSettings.getBOOL("MapShowClassifieds")) + { + drawGenericItems(gWorldMap->mClassifieds, sClassifiedsImage); + } + + if (gSavedSettings.getBOOL("MapShowPopular")) + { + drawGenericItems(gWorldMap->mPopular, sPopularImage); + } + + if (gSavedSettings.getBOOL("MapShowEvents")) + { + drawEvents(); + } + + // Your avatar. + // Draw avatar position, always, and underneath other avatars + pos_global = gAgent.getPositionGlobal(); + pos_map = globalPosToView(pos_global); + gl_draw_image(llround(pos_map.mV[VX])-8, + llround(pos_map.mV[VY])-8, + sAvatarLargeImage, + LLColor4::white); + + 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::sSansSerifSmall->getLineHeight())); // offset vertically by one line, to avoid overlap with target tracking + } + + drawFrustum(); + + // Draw icons for the avatars in each region. + // Draw after 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 (gWorldMap->mIsTrackingUnknownLocation) + { + if (gWorldMap->mInvalidLocation) + { + // We know this location to be invalid + LLColor4 loading_color(0.0, 0.5, 1.0, 1.0); + drawTracking( gWorldMap->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( gWorldMap->mUnknownLocation, loading_color, TRUE, "Loading...", ""); + } + } +#endif + + // turn off the scissor + LLGLDisable no_scissor(GL_SCISSOR_TEST); + + updateDirections(); + + LLView::draw(); + + updateVisibleBlocks(); +} + +void LLWorldMapView::drawGenericItems(const LLWorldMap::item_info_list_t& items, LLPointer<LLViewerImage> image) +{ + LLWorldMap::item_info_list_t::const_iterator e; + for (e = items.begin(); e != items.end(); ++e) + { + const LLItemInfo& info = *e; + + drawGenericItem(info, image); + } +} + +void LLWorldMapView::drawGenericItem(const LLItemInfo& item, LLPointer<LLViewerImage> image) +{ + S32 half_width = image->getWidth()/2; + S32 half_height = image->getHeight()/2; + + LLVector3 pos_map = globalPosToView( item.mPosGlobal ); + gl_draw_image(llround(pos_map.mV[VX]) - half_width, + llround(pos_map.mV[VY]) - half_height, + image, + LLColor4::white); +} + +void LLWorldMapView::drawAgents() +{ + //S32 half_width = sPopularImage->getWidth()/2; + //S32 half_height = sPopularImage->getHeight()/2; + + F32 agents_scale = (gMapScale * 0.9f) / 256.f; + + for (handle_list_t::iterator iter = mVisibleRegions.begin(); iter != mVisibleRegions.end(); ++iter) + { + U64 handle = *iter; + LLSimInfo* siminfo = gWorldMap->simInfoFromHandle(handle); + if (siminfo && (siminfo->mAccess == SIM_ACCESS_DOWN)) + { + continue; + } + LLWorldMap::agent_list_map_t::iterator countsiter = gWorldMap->mAgentLocationsMap.find(handle); + if (siminfo && siminfo->mShowAgentLocations && countsiter != gWorldMap->mAgentLocationsMap.end()) + { + // Show Individual agents + LLWorldMap::item_info_list_t& agentcounts = countsiter->second; + S32 sim_agent_count = 0; + for (LLWorldMap::item_info_list_t::iterator iter = agentcounts.begin(); + iter != agentcounts.end(); ++iter) + { + const LLItemInfo& info = *iter; + LLVector3 pos_map = globalPosToView( info.mPosGlobal ); + S32 agent_count = info.mExtra; + if (agent_count > 0) + { + sim_agent_count += agent_count; + F32 y = 0; + for (S32 cur_agent = 0; cur_agent < agent_count; cur_agent++) + { + gl_draw_image(llround(pos_map.mV[VX]) - 4, + llround(pos_map.mV[VY] + y) - 4, + sAvatarSmallImage, + LLColor4::white ); + // move up a bit + y += 3.f; + } + } + } + gWorldMap->mNumAgents[handle] = sim_agent_count; // override mNumAgents for this sim + } + else + { + // Show agent 'stack' at center of sim + S32 num_agents = gWorldMap->mNumAgents[handle]; + if (num_agents > 0) + { + LLVector3d region_pos = from_region_handle(handle); + region_pos[VX] += REGION_WIDTH_METERS * .5f; + region_pos[VY] += REGION_WIDTH_METERS * .5f; + LLVector3 pos_map = globalPosToView(region_pos); + // 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; + S32 y = 0; + S32 cur_agent; + for (cur_agent = 0; cur_agent < agent_count; cur_agent++) + { + gl_draw_image( + llround(pos_map.mV[VX]) - 4, + llround(pos_map.mV[VY]) + y - 4, + sAvatarSmallImage, + LLColor4::white ); + // move up a bit + y += 3; + } + } + } + } +} + + +void LLWorldMapView::drawEvents() +{ + BOOL show_mature = gSavedSettings.getBOOL("ShowMatureEvents"); + + // Non-selected events + // Draw under avatar so you can find yourself. + LLWorldMap::item_info_list_t::const_iterator e; + for (e = gWorldMap->mPGEvents.begin(); e != gWorldMap->mPGEvents.end(); ++e) + { + const LLItemInfo& event = *e; + + // Draw, but without relative-Z icons + if (!event.mSelected) + { + LLVector3 pos_map = globalPosToView( event.mPosGlobal ); + gl_draw_image(llround(pos_map.mV[VX]) - sEventImage->getWidth()/2, + llround(pos_map.mV[VY]) - sEventImage->getHeight()/2, + sEventImage, + LLColor4::white); + } + } + if (show_mature) + { + for (e = gWorldMap->mMatureEvents.begin(); e != gWorldMap->mMatureEvents.end(); ++e) + { + const LLItemInfo& event = *e; + + // Draw, but without relative-Z icons + if (!event.mSelected) + { + LLVector3 pos_map = globalPosToView( event.mPosGlobal ); + gl_draw_image(llround(pos_map.mV[VX]) - sEventMatureImage->getWidth()/2, + llround(pos_map.mV[VY]) - sEventMatureImage->getHeight()/2, + sEventMatureImage, + LLColor4::white); + } + } + } + + // Selected events + // Draw under avatar so you can find yourself. + for (e = gWorldMap->mPGEvents.begin(); e != gWorldMap->mPGEvents.end(); ++e) + { + const LLItemInfo& event = *e; + + // Draw, but without relative-Z icons + if (event.mSelected) + { + LLVector3 pos_map = globalPosToView( event.mPosGlobal ); + gl_draw_image(llround(pos_map.mV[VX]) - sEventImage->getWidth()/2, + llround(pos_map.mV[VY]) - sEventImage->getHeight()/2, + sEventImage, + LLColor4::white); + + //drawIconName( + // pos_map.mV[VX], + // pos_map.mV[VY], + // gEventColor, + // event.mToolTip.c_str(), + // event.mName.c_str() ); + } + } + if (show_mature) + { + for (e = gWorldMap->mMatureEvents.begin(); e != gWorldMap->mMatureEvents.end(); ++e) + { + const LLItemInfo& event = *e; + + // Draw, but without relative-Z icons + if (event.mSelected) + { + LLVector3 pos_map = globalPosToView( event.mPosGlobal ); + gl_draw_image(llround(pos_map.mV[VX]) - sEventMatureImage->getWidth()/2, + llround(pos_map.mV[VY]) - sEventMatureImage->getHeight()/2, + sEventMatureImage, + LLColor4::white); + + //drawIconName( + // pos_map.mV[VX], + // pos_map.mV[VY], + // gEventColor, + // event.mToolTip.c_str(), + // event.mName.c_str() ); + } + } + } +} + +void LLWorldMapView::drawDots() +{ + +} + + +void LLWorldMapView::drawFrustum() +{ + // Draw frustum + F32 meters_to_pixels = gMapScale/ REGION_WIDTH_METERS; + + F32 horiz_fov = gCamera->getView() * gCamera->getAspect(); + F32 far_clip_meters = gCamera->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 = mRect.getWidth() * 0.5f + sPanX; + F32 ctr_y = mRect.getHeight() * 0.5f + sPanY; + + LLGLSNoTexture gls_no_texture; + + // Since we don't rotate the map, we have to rotate the frustum. + glPushMatrix(); + glTranslatef( ctr_x, ctr_y, 0 ); + glRotatef( atan2( gCamera->getAtAxis().mV[VX], gCamera->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. + glBegin( GL_TRIANGLES ); + glColor4f(1.f, 1.f, 1.f, 0.25f); + glVertex2f( 0, 0 ); + + glColor4f(1.f, 1.f, 1.f, 0.02f); + glVertex2f( -half_width_pixels, far_clip_pixels ); + + glColor4f(1.f, 1.f, 1.f, 0.02f); + glVertex2f( half_width_pixels, far_clip_pixels ); + glEnd(); + glPopMatrix(); +} + + +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] += mRect.getWidth() / 2 + sPanX; + pos_local.mV[VY] += mRect.getHeight() / 2 + sPanY; + + return pos_local; +} + + +void LLWorldMapView::drawTracking(const LLVector3d& pos_global, const LLColor4& color, + BOOL draw_arrow, LLString label, LLString 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::sSansSerifSmall; + 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 >= mRect.getWidth() + || y >= mRect.getHeight() ) + { + if (draw_arrow) + { + drawTrackingCircle( mRect, x, y, color, 3, 15 ); + drawTrackingArrow( mRect, 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( mRect, x, y, color, 3, 15 ); + } + else + { + // Draw, but without relative Z + gl_draw_image(x - sTrackCircleImage->getWidth()/2, + y - sTrackCircleImage->getHeight()/2, + 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, mRect.getWidth() - half_text_width - TEXT_PADDING); + text_y = llclamp(text_y + vert_offset, TEXT_PADDING + vert_offset, mRect.getHeight() - llround(font->getLineHeight()) - TEXT_PADDING - vert_offset); + + if (label != "") + { + 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((mRect.getWidth() / 2 + sPanX)); + y -= llfloor((mRect.getHeight() / 2 + sPanY)); + + LLVector3 pos_local( (F32)x, (F32)y, 0.f ); + + pos_local *= ( REGION_WIDTH_METERS / gMapScale ); + + 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, LLString& msg, LLRect* sticky_rect_screen ) +{ + if( !getVisible() || !pointInView( x, y ) ) + { + return FALSE; + } + + LLVector3d pos_global = viewPosToGlobal(x, y); + + LLSimInfo* info = gWorldMap->simInfoFromPosGlobal(pos_global); + if (info) + { + LLViewerRegion *region = gAgent.getRegion(); + + std::string message = + llformat("%s (%s)", + info->mName.c_str(), + LLViewerRegion::accessToString(info->mAccess)); + + if (info->mAccess != SIM_ACCESS_DOWN) + { + S32 agent_count = gWorldMap->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 += "person"; + } + else + { + message += "people"; + } + } + } + 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 +void LLWorldMapView::drawAvatar(F32 x_pixels, + F32 y_pixels, + const LLColor4& color, + F32 relative_z, + F32 dot_radius) +{ + 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; + + const F32 HEIGHT_THRESHOLD = 7.f; + + if (relative_z > HEIGHT_THRESHOLD) + { + LLGLSNoTexture gls_no_texture; + glColor4fv( color.mV ); + LLUI::setLineWidth(1.5f); + glBegin( GL_LINES ); + glVertex2f(left, top); + glVertex2f(right, top); + glVertex2f(center, top); + glVertex2f(center, bottom); + glEnd(); + LLUI::setLineWidth(1.0f); + } + else if (relative_z > -HEIGHT_THRESHOLD) + { + gl_draw_image( llround(x_pixels) - sAvatarSmallImage->getWidth()/2, + llround(y_pixels) - sAvatarSmallImage->getHeight()/2, + sAvatarSmallImage, + color); + } + else + { + LLGLSNoTexture gls_no_texture; + glColor4fv( color.mV ); + LLUI::setLineWidth(1.5f); + glBegin( GL_LINES ); + glVertex2f(center, top); + glVertex2f(center, bottom); + glVertex2f(left, bottom); + glVertex2f(right, bottom); + glEnd(); + LLUI::setLineWidth(1.0f); + } +} + +// 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) +{ + 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; + + const F32 HEIGHT_THRESHOLD = 7.f; + + if (relative_z > HEIGHT_THRESHOLD) + { + LLGLSNoTexture gls_no_texture; + glColor4fv( color.mV ); + LLUI::setLineWidth(1.5f); + glBegin( GL_LINES ); + glVertex2f(left, top); + glVertex2f(right, top); + glVertex2f(center, top); + glVertex2f(center, bottom); + glEnd(); + LLUI::setLineWidth(1.0f); + } + else if (relative_z > -HEIGHT_THRESHOLD) + { + gl_draw_image( llround(x_pixels) - sAvatarSmallImage->getWidth()/2, + llround(y_pixels) - sAvatarSmallImage->getHeight()/2, + sTrackCircleImage, + color); + } + else + { + LLGLSNoTexture gls_no_texture; + glColor4fv( color.mV ); + LLUI::setLineWidth(1.5f); + glBegin( GL_LINES ); + glVertex2f(center, top); + glVertex2f(center, bottom); + glVertex2f(left, bottom); + glVertex2f(right, bottom); + glEnd(); + LLUI::setLineWidth(1.0f); + } +} + + +// 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::sSansSerif->renderUTF8(first_line, 0, + text_x, + text_y, + color, + LLFontGL::HCENTER, + LLFontGL::TOP, + LLFontGL::DROP_SHADOW); + + text_y -= llround(LLFontGL::sSansSerif->getLineHeight()); + + // render text + LLFontGL::sSansSerif->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); + glPushMatrix(); + glTranslatef((F32)x, (F32)y, 0.f); + gl_washer_segment_2d(inner_radius, outer_radius, start_theta, end_theta, 40, color, color); + glPopMatrix(); + +} + +// 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) ); + } + + //FIXME: deal with non-square window properly + 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, + 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 = mRect.getHeight() * 0.5f; + F32 map_half_width = mRect.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 = mRect.getWidth(); + S32 height = mRect.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 = gWorldMap->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 kt. + if(gAgent.isGodlike()) + { + pos_global.mdV[VZ] = 200.0; + } + + *hit_type = 0; // hit nothing + + gWorldMap->mIsTrackingUnknownLocation = FALSE; + gWorldMap->mIsTrackingDoubleClick = FALSE; + gWorldMap->mIsTrackingCommit = FALSE; + + LLWorldMap::item_info_list_t::iterator it; + + // clear old selected stuff + for (it = gWorldMap->mPGEvents.begin(); it != gWorldMap->mPGEvents.end(); ++it) + { + (*it).mSelected = FALSE; + } + for (it = gWorldMap->mMatureEvents.begin(); it != gWorldMap->mMatureEvents.end(); ++it) + { + (*it).mSelected = FALSE; + } + for (it = gWorldMap->mPopular.begin(); it != gWorldMap->mPopular.end(); ++it) + { + (*it).mSelected = FALSE; + } + for (it = gWorldMap->mLandForSale.begin(); it != gWorldMap->mLandForSale.end(); ++it) + { + (*it).mSelected = FALSE; + } + for (it = gWorldMap->mClassifieds.begin(); it != gWorldMap->mClassifieds.end(); ++it) + { + (*it).mSelected = FALSE; + } + + // Select event you clicked on + if (gSavedSettings.getBOOL("MapShowEvents")) + { + for (it = gWorldMap->mPGEvents.begin(); it != gWorldMap->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("ShowMatureEvents")) + { + for (it = gWorldMap->mMatureEvents.begin(); it != gWorldMap->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("MapShowPopular")) + { + for (it = gWorldMap->mPopular.begin(); it != gWorldMap->mPopular.end(); ++it) + { + LLItemInfo& popular = *it; + + if (checkItemHit(x, y, popular, id, true)) + { + *hit_type = MAP_ITEM_POPULAR; + mItemPicked = TRUE; + return; + } + } + } + + if (gSavedSettings.getBOOL("MapShowLandForSale")) + { + for (it = gWorldMap->mLandForSale.begin(); it != gWorldMap->mLandForSale.end(); ++it) + { + LLItemInfo& land = *it; + + if (checkItemHit(x, y, land, id, true)) + { + *hit_type = MAP_ITEM_LAND_FOR_SALE; + mItemPicked = TRUE; + return; + } + } + } + + if (gSavedSettings.getBOOL("MapShowClassifieds")) + { + for (it = gWorldMap->mClassifieds.begin(); it != gWorldMap->mClassifieds.end(); ++it) + { + LLItemInfo& classified = *it; + + if (checkItemHit(x, y, classified, id, true)) + { + *hit_type = MAP_ITEM_CLASSIFIED; + 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, NULL ); + + mMouseDownPanX = llround(sPanX); + mMouseDownPanY = llround(sPanY); + mMouseDownX = x; + mMouseDownY = y; + sHandledLastClick = TRUE; + return TRUE; +} + +BOOL LLWorldMapView::handleMouseUp( S32 x, S32 y, MASK mask ) +{ + if (this == gFocusMgr.getMouseCapture()) + { + 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 = mRect; + 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, NULL ); + return TRUE; + } + return FALSE; +} + +void LLWorldMapView::updateBlock(S32 block_x, S32 block_y) +{ + S32 offset = block_x | (block_y * MAP_BLOCK_RES); + if (!gWorldMap->mMapBlockLoaded[gWorldMap->mCurrentMap][offset]) + { +// llinfos << "Loading Block (" << block_x << "," << block_y << ")" << llendl; + gWorldMap->sendMapBlockRequest(block_x << 3, block_y << 3, (block_x << 3) + 7, (block_y << 3) + 7); + gWorldMap->mMapBlockLoaded[gWorldMap->mCurrentMap][offset] = TRUE; + } +} + +void LLWorldMapView::updateVisibleBlocks() +{ + if (gMapScale < SIM_MAP_SCALE) + { + // We don't care what is loaded if we're zoomed out + return; + } + // check if we've loaded the 9 potentially visible zones + LLVector3d camera_global = gAgent.getCameraPositionGlobal(); + + // Convert pan to sim coordinates + S32 world_center_x = S32((-sPanX / gMapScale) + (camera_global.mdV[0] / REGION_WIDTH_METERS)); + S32 world_center_y = S32((-sPanY / gMapScale) + (camera_global.mdV[1] / REGION_WIDTH_METERS)); + + // Find the corresponding 8x8 block + S32 world_block_x = world_center_x >> 3; + S32 world_block_y = world_center_y >> 3; + + for (S32 block_x = llmax(world_block_x-1, 0); block_x <= llmin(world_block_x+1, MAP_BLOCK_RES-1); ++block_x) + { + for (S32 block_y = llmax(world_block_y-1, 0); block_y <= llmin(world_block_y+1, MAP_BLOCK_RES-1); ++block_y) + { + updateBlock(block_x, block_y); + } + } +} + +BOOL LLWorldMapView::handleHover( S32 x, S32 y, MASK mask ) +{ + if (this == gFocusMgr.getMouseCapture()) + { + 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: + { + gFloaterWorldMap->close(); + // This is an ungainly hack + char uuid_str[38]; + S32 event_id; + id.toString(uuid_str); + sscanf(&uuid_str[28], "%X", &event_id); + LLFloaterDirectory::showEvents(event_id); + break; + } + case MAP_ITEM_POPULAR: + { + gFloaterWorldMap->close(); + LLFloaterDirectory::showPopular(id); + break; + } + case MAP_ITEM_LAND_FOR_SALE: + { + gFloaterWorldMap->close(); + LLFloaterDirectory::showLandForSale(id); + break; + } + case MAP_ITEM_CLASSIFIED: + { + gFloaterWorldMap->close(); + LLFloaterDirectory::showClassified(id); + break; + } + default: + { + if (gWorldMap->mIsTrackingUnknownLocation) + { + gWorldMap->mIsTrackingDoubleClick = TRUE; + } + else + { + // Teleport if we got a valid location + LLVector3d pos_global = viewPosToGlobal(x,y); + LLSimInfo* sim_info = gWorldMap->simInfoFromPosGlobal(pos_global); + if (sim_info && sim_info->mAccess != SIM_ACCESS_DOWN) + { + gAgent.teleportViaLocation( pos_global ); + } + } + } + }; + + return TRUE; + } + return FALSE; +} |