diff options
Diffstat (limited to 'linden/indra/newview/llnetmap.cpp')
-rw-r--r-- | linden/indra/newview/llnetmap.cpp | 826 |
1 files changed, 826 insertions, 0 deletions
diff --git a/linden/indra/newview/llnetmap.cpp b/linden/indra/newview/llnetmap.cpp new file mode 100644 index 0000000..3db447a --- /dev/null +++ b/linden/indra/newview/llnetmap.cpp | |||
@@ -0,0 +1,826 @@ | |||
1 | /** | ||
2 | * @file llnetmap.cpp | ||
3 | * @author James Cook | ||
4 | * @brief Display of surrounding regions, objects, and agents. View contained by LLFloaterMap. | ||
5 | * | ||
6 | * Copyright (c) 2001-2007, Linden Research, Inc. | ||
7 | * | ||
8 | * The source code in this file ("Source Code") is provided by Linden Lab | ||
9 | * to you under the terms of the GNU General Public License, version 2.0 | ||
10 | * ("GPL"), unless you have obtained a separate licensing agreement | ||
11 | * ("Other License"), formally executed by you and Linden Lab. Terms of | ||
12 | * the GPL can be found in doc/GPL-license.txt in this distribution, or | ||
13 | * online at http://secondlife.com/developers/opensource/gplv2 | ||
14 | * | ||
15 | * There are special exceptions to the terms and conditions of the GPL as | ||
16 | * it is applied to this Source Code. View the full text of the exception | ||
17 | * in the file doc/FLOSS-exception.txt in this software distribution, or | ||
18 | * online at http://secondlife.com/developers/opensource/flossexception | ||
19 | * | ||
20 | * By copying, modifying or distributing this software, you acknowledge | ||
21 | * that you have read and understood your obligations described above, | ||
22 | * and agree to abide by those obligations. | ||
23 | * | ||
24 | * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO | ||
25 | * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, | ||
26 | * COMPLETENESS OR PERFORMANCE. | ||
27 | */ | ||
28 | |||
29 | #include "llviewerprecompiledheaders.h" | ||
30 | |||
31 | #include "llnetmap.h" | ||
32 | |||
33 | #include "indra_constants.h" | ||
34 | #include "llui.h" | ||
35 | #include "linked_lists.h" | ||
36 | #include "llmath.h" // clampf() | ||
37 | #include "llfocusmgr.h" | ||
38 | |||
39 | #include "llagent.h" | ||
40 | #include "llcallingcard.h" | ||
41 | #include "llcolorscheme.h" | ||
42 | #include "llviewercontrol.h" | ||
43 | #include "llfloaterworldmap.h" | ||
44 | #include "llfloatermap.h" | ||
45 | #include "llframetimer.h" | ||
46 | #include "lltracker.h" | ||
47 | #include "llmenugl.h" | ||
48 | #include "llstatgraph.h" | ||
49 | #include "llsurface.h" | ||
50 | #include "lltextbox.h" | ||
51 | #include "llviewercamera.h" | ||
52 | #include "llviewerimage.h" | ||
53 | #include "llviewerimagelist.h" | ||
54 | #include "llviewermenu.h" | ||
55 | #include "llviewerobjectlist.h" | ||
56 | #include "llviewerparceloverlay.h" | ||
57 | #include "llviewerregion.h" | ||
58 | #include "llviewerwindow.h" | ||
59 | #include "llvoavatar.h" | ||
60 | #include "llworld.h" | ||
61 | #include "llworldmapview.h" // shared draw code | ||
62 | #include "viewer.h" // Only for constants! | ||
63 | |||
64 | #include "llglheaders.h" | ||
65 | |||
66 | const F32 MAP_SCALE_MIN = 64; | ||
67 | const F32 MAP_SCALE_MID = 172; | ||
68 | const F32 MAP_SCALE_MAX = 512; | ||
69 | const F32 MAP_SCALE_INCREMENT = 16; | ||
70 | |||
71 | const S32 TRACKING_RADIUS = 3; | ||
72 | |||
73 | //static | ||
74 | BOOL LLNetMap::sRotateMap = FALSE; | ||
75 | LLNetMap* LLNetMap::sInstance = NULL; | ||
76 | |||
77 | LLNetMap::LLNetMap( | ||
78 | const std::string& name, | ||
79 | const LLRect& rect, | ||
80 | const LLColor4& bg_color ) | ||
81 | : | ||
82 | LLUICtrl(name, rect, FALSE, NULL, NULL), mBackgroundColor( bg_color ), | ||
83 | mObjectMapTPM(1.f), | ||
84 | mObjectMapPixels(255.f), | ||
85 | mTargetPanX( 0.f ), | ||
86 | mTargetPanY( 0.f ), | ||
87 | mCurPanX( 0.f ), | ||
88 | mCurPanY( 0.f ), | ||
89 | mUpdateNow( FALSE ) | ||
90 | { | ||
91 | mPixelsPerMeter = gMiniMapScale / REGION_WIDTH_METERS; | ||
92 | |||
93 | LLNetMap::sRotateMap = gSavedSettings.getBOOL( "MiniMapRotate" ); | ||
94 | |||
95 | // Surface texture is dynamically generated/updated. | ||
96 | // createObjectImage(); | ||
97 | |||
98 | mObjectImageCenterGlobal = gAgent.getCameraPositionGlobal(); | ||
99 | |||
100 | const S32 DIR_WIDTH = 10; | ||
101 | const S32 DIR_HEIGHT = 10; | ||
102 | LLRect major_dir_rect( 0, DIR_HEIGHT, DIR_WIDTH, 0 ); | ||
103 | |||
104 | mTextBoxNorth = new LLTextBox( "N", major_dir_rect ); | ||
105 | mTextBoxNorth->setDropshadowVisible( TRUE ); | ||
106 | addChild( mTextBoxNorth ); | ||
107 | |||
108 | LLColor4 minor_color( 1.f, 1.f, 1.f, .7f ); | ||
109 | |||
110 | mTextBoxEast = new LLTextBox( "E", major_dir_rect ); | ||
111 | mTextBoxEast->setColor( minor_color ); | ||
112 | addChild( mTextBoxEast ); | ||
113 | |||
114 | mTextBoxWest = new LLTextBox( "W", major_dir_rect ); | ||
115 | mTextBoxWest->setColor( minor_color ); | ||
116 | addChild( mTextBoxWest ); | ||
117 | |||
118 | mTextBoxSouth = new LLTextBox( "S", major_dir_rect ); | ||
119 | mTextBoxSouth->setColor( minor_color ); | ||
120 | addChild( mTextBoxSouth ); | ||
121 | |||
122 | LLRect minor_dir_rect( 0, DIR_HEIGHT, DIR_WIDTH * 2, 0 ); | ||
123 | |||
124 | mTextBoxSouthEast = new LLTextBox( "SE", minor_dir_rect ); | ||
125 | mTextBoxSouthEast->setColor( minor_color ); | ||
126 | addChild( mTextBoxSouthEast ); | ||
127 | |||
128 | mTextBoxNorthEast = new LLTextBox( "NE", minor_dir_rect ); | ||
129 | mTextBoxNorthEast->setColor( minor_color ); | ||
130 | addChild( mTextBoxNorthEast ); | ||
131 | |||
132 | mTextBoxSouthWest = new LLTextBox( "SW", minor_dir_rect ); | ||
133 | mTextBoxSouthWest->setColor( minor_color ); | ||
134 | addChild( mTextBoxSouthWest ); | ||
135 | |||
136 | mTextBoxNorthWest = new LLTextBox( "NW", minor_dir_rect ); | ||
137 | mTextBoxNorthWest->setColor( minor_color ); | ||
138 | addChild( mTextBoxNorthWest ); | ||
139 | |||
140 | // Right-click menu | ||
141 | LLMenuGL* menu; | ||
142 | menu = new LLMenuGL("popup"); | ||
143 | menu->setCanTearOff(FALSE); | ||
144 | menu->append(new LLMenuItemCallGL("Zoom Close", handleZoomLevel, | ||
145 | NULL, (void*)2) ); | ||
146 | menu->append(new LLMenuItemCallGL("Zoom Medium", handleZoomLevel, | ||
147 | NULL, (void*)1) ); | ||
148 | menu->append(new LLMenuItemCallGL("Zoom Far", handleZoomLevel, | ||
149 | NULL, (void*)0) ); | ||
150 | menu->appendSeparator(); | ||
151 | menu->append(new LLMenuItemCallGL("Stop Tracking", &LLTracker::stopTracking, | ||
152 | &LLTracker::isTracking, NULL) ); | ||
153 | menu->setVisible(FALSE); | ||
154 | addChild(menu); | ||
155 | mPopupMenuHandle = menu->mViewHandle; | ||
156 | |||
157 | sInstance = this; | ||
158 | |||
159 | gSavedSettings.getControl("MiniMapRotate")->addListener(&mNetMapListener); | ||
160 | } | ||
161 | |||
162 | LLNetMap::~LLNetMap() | ||
163 | { | ||
164 | sInstance = NULL; | ||
165 | } | ||
166 | |||
167 | EWidgetType LLNetMap::getWidgetType() const | ||
168 | { | ||
169 | return WIDGET_TYPE_NET_MAP; | ||
170 | } | ||
171 | |||
172 | LLString LLNetMap::getWidgetTag() const | ||
173 | { | ||
174 | return LL_NET_MAP_TAG; | ||
175 | } | ||
176 | |||
177 | |||
178 | void LLNetMap::setScale( F32 scale ) | ||
179 | { | ||
180 | gMiniMapScale = scale; | ||
181 | if (gMiniMapScale == 0.f) | ||
182 | { | ||
183 | gMiniMapScale = 0.1f; | ||
184 | } | ||
185 | |||
186 | if (mObjectImagep.notNull()) | ||
187 | { | ||
188 | F32 half_width = (F32)(mRect.getWidth() / 2); | ||
189 | F32 half_height = (F32)(mRect.getHeight() / 2); | ||
190 | F32 radius = sqrt( half_width * half_width + half_height * half_height ); | ||
191 | |||
192 | F32 region_widths = (2.f*radius)/gMiniMapScale; | ||
193 | |||
194 | F32 meters; | ||
195 | if (!gWorldPointer) | ||
196 | { | ||
197 | // Hack! Sometimes world hasn't been initialized at this point. | ||
198 | meters = 256.f*region_widths; | ||
199 | } | ||
200 | else | ||
201 | { | ||
202 | meters = region_widths * gWorldPointer->getRegionWidthInMeters(); | ||
203 | } | ||
204 | |||
205 | F32 num_pixels = (F32)mObjectImagep->getWidth(); | ||
206 | mObjectMapTPM = num_pixels/meters; | ||
207 | mObjectMapPixels = 2.f*radius; | ||
208 | } | ||
209 | |||
210 | mPixelsPerMeter = gMiniMapScale / REGION_WIDTH_METERS; | ||
211 | |||
212 | mUpdateNow = TRUE; | ||
213 | } | ||
214 | |||
215 | void LLNetMap::translatePan( F32 delta_x, F32 delta_y ) | ||
216 | { | ||
217 | mTargetPanX += delta_x; | ||
218 | mTargetPanY += delta_y; | ||
219 | } | ||
220 | |||
221 | |||
222 | /////////////////////////////////////////////////////////////////////////////////// | ||
223 | |||
224 | void LLNetMap::draw() | ||
225 | { | ||
226 | static LLFrameTimer map_timer; | ||
227 | |||
228 | if (!getVisible() || !gWorldPointer) | ||
229 | { | ||
230 | return; | ||
231 | } | ||
232 | if (mObjectImagep.isNull()) | ||
233 | { | ||
234 | createObjectImage(); | ||
235 | } | ||
236 | |||
237 | mCurPanX = lerp(mCurPanX, mTargetPanX, LLCriticalDamp::getInterpolant(0.1f)); | ||
238 | mCurPanY = lerp(mCurPanY, mTargetPanY, LLCriticalDamp::getInterpolant(0.1f)); | ||
239 | |||
240 | // Prepare a scissor region | ||
241 | // GLint params[4]; | ||
242 | // glGetIntegerv( GL_SCISSOR_BOX, params ); | ||
243 | F32 rotation = 0; | ||
244 | |||
245 | { | ||
246 | LLGLEnable scissor(GL_SCISSOR_TEST); | ||
247 | |||
248 | { | ||
249 | LLGLSNoTexture no_texture; | ||
250 | LLUI::setScissorRegionLocal(LLRect(0, mRect.getHeight(), mRect.getWidth(), 0)); | ||
251 | |||
252 | glMatrixMode(GL_MODELVIEW); | ||
253 | |||
254 | // Draw background rectangle | ||
255 | glColor4fv( mBackgroundColor.mV ); | ||
256 | gl_rect_2d(0, mRect.getHeight(), mRect.getWidth(), 0); | ||
257 | } | ||
258 | |||
259 | // region 0,0 is in the middle | ||
260 | S32 center_sw_left = mRect.getWidth() / 2 + llfloor(mCurPanX); | ||
261 | S32 center_sw_bottom = mRect.getHeight() / 2 + llfloor(mCurPanY); | ||
262 | |||
263 | glPushMatrix(); | ||
264 | |||
265 | glTranslatef( (F32) center_sw_left, (F32) center_sw_bottom, 0.f); | ||
266 | |||
267 | if( LLNetMap::sRotateMap ) | ||
268 | { | ||
269 | // rotate subsequent draws to agent rotation | ||
270 | rotation = atan2( gCamera->getAtAxis().mV[VX], gCamera->getAtAxis().mV[VY] ); | ||
271 | glRotatef( rotation * RAD_TO_DEG, 0.f, 0.f, 1.f); | ||
272 | } | ||
273 | |||
274 | // figure out where agent is | ||
275 | S32 region_width = llround(gWorldPointer->getRegionWidthInMeters()); | ||
276 | |||
277 | LLViewerRegion *regionp; | ||
278 | |||
279 | for (regionp = gWorldPointer->mActiveRegionList.getFirstData(); | ||
280 | regionp; | ||
281 | regionp = gWorldPointer->mActiveRegionList.getNextData()) | ||
282 | { | ||
283 | // Find x and y position relative to camera's center. | ||
284 | LLVector3 origin_agent = regionp->getOriginAgent(); | ||
285 | LLVector3 rel_region_pos = origin_agent - gAgent.getCameraPositionAgent(); | ||
286 | F32 relative_x = (rel_region_pos.mV[0] / region_width) * gMiniMapScale; | ||
287 | F32 relative_y = (rel_region_pos.mV[1] / region_width) * gMiniMapScale; | ||
288 | |||
289 | // background region rectangle | ||
290 | F32 bottom = relative_y; | ||
291 | F32 left = relative_x; | ||
292 | F32 top = bottom + gMiniMapScale ; | ||
293 | F32 right = left + gMiniMapScale ; | ||
294 | |||
295 | if (regionp == gAgent.getRegion()) | ||
296 | { | ||
297 | glColor4f(1.f, 1.f, 1.f, 1.f); | ||
298 | } | ||
299 | else | ||
300 | { | ||
301 | glColor4f(0.8f, 0.8f, 0.8f, 1.f); | ||
302 | } | ||
303 | |||
304 | if (!regionp->mAlive) | ||
305 | { | ||
306 | glColor4f(1.f, 0.5f, 0.5f, 1.f); | ||
307 | } | ||
308 | |||
309 | |||
310 | // Draw using texture. | ||
311 | LLViewerImage::bindTexture(regionp->getLand().getSTexture()); | ||
312 | glBegin(GL_QUADS); | ||
313 | glTexCoord2f(0.f, 1.f); | ||
314 | glVertex2f(left, top); | ||
315 | glTexCoord2f(0.f, 0.f); | ||
316 | glVertex2f(left, bottom); | ||
317 | glTexCoord2f(1.f, 0.f); | ||
318 | glVertex2f(right, bottom); | ||
319 | glTexCoord2f(1.f, 1.f); | ||
320 | glVertex2f(right, top); | ||
321 | glEnd(); | ||
322 | |||
323 | // Draw water | ||
324 | glAlphaFunc(GL_GREATER, ABOVE_WATERLINE_ALPHA / 255.f ); | ||
325 | { | ||
326 | if (regionp->getLand().getWaterTexture()) | ||
327 | { | ||
328 | LLViewerImage::bindTexture(regionp->getLand().getWaterTexture()); | ||
329 | glBegin(GL_QUADS); | ||
330 | glTexCoord2f(0.f, 1.f); | ||
331 | glVertex2f(left, top); | ||
332 | glTexCoord2f(0.f, 0.f); | ||
333 | glVertex2f(left, bottom); | ||
334 | glTexCoord2f(1.f, 0.f); | ||
335 | glVertex2f(right, bottom); | ||
336 | glTexCoord2f(1.f, 1.f); | ||
337 | glVertex2f(right, top); | ||
338 | glEnd(); | ||
339 | } | ||
340 | } | ||
341 | glAlphaFunc(GL_GREATER,0.01f); | ||
342 | } | ||
343 | |||
344 | |||
345 | LLVector3d old_center = mObjectImageCenterGlobal; | ||
346 | LLVector3d new_center = gAgent.getCameraPositionGlobal(); | ||
347 | |||
348 | new_center.mdV[0] = (5.f/mObjectMapTPM)*floor(0.2f*mObjectMapTPM*new_center.mdV[0]); | ||
349 | new_center.mdV[1] = (5.f/mObjectMapTPM)*floor(0.2f*mObjectMapTPM*new_center.mdV[1]); | ||
350 | new_center.mdV[2] = 0.f; | ||
351 | |||
352 | if (mUpdateNow || (map_timer.getElapsedTimeF32() > 0.5f)) | ||
353 | { | ||
354 | mUpdateNow = FALSE; | ||
355 | mObjectImageCenterGlobal = new_center; | ||
356 | |||
357 | // Center moved enough. | ||
358 | // Create the base texture. | ||
359 | U8 *default_texture = mObjectRawImagep->getData(); | ||
360 | memset( default_texture, 0, mObjectImagep->getWidth() * mObjectImagep->getHeight() * mObjectImagep->getComponents() ); | ||
361 | |||
362 | // Draw buildings | ||
363 | gObjectList.renderObjectsForMap(*this); | ||
364 | |||
365 | mObjectImagep->setSubImage(mObjectRawImagep, 0, 0, mObjectImagep->getWidth(), mObjectImagep->getHeight()); | ||
366 | |||
367 | map_timer.reset(); | ||
368 | } | ||
369 | |||
370 | LLVector3 map_center_agent = gAgent.getPosAgentFromGlobal(mObjectImageCenterGlobal); | ||
371 | map_center_agent -= gAgent.getCameraPositionAgent(); | ||
372 | map_center_agent.mV[VX] *= gMiniMapScale/region_width; | ||
373 | map_center_agent.mV[VY] *= gMiniMapScale/region_width; | ||
374 | |||
375 | LLViewerImage::bindTexture(mObjectImagep); | ||
376 | F32 image_half_width = 0.5f*mObjectMapPixels; | ||
377 | F32 image_half_height = 0.5f*mObjectMapPixels; | ||
378 | |||
379 | glBegin(GL_QUADS); | ||
380 | glTexCoord2f(0.f, 1.f); | ||
381 | glVertex2f(map_center_agent.mV[VX] - image_half_width, image_half_height + map_center_agent.mV[VY]); | ||
382 | glTexCoord2f(0.f, 0.f); | ||
383 | glVertex2f(map_center_agent.mV[VX] - image_half_width, map_center_agent.mV[VY] - image_half_height); | ||
384 | glTexCoord2f(1.f, 0.f); | ||
385 | glVertex2f(image_half_width + map_center_agent.mV[VX], map_center_agent.mV[VY] - image_half_height); | ||
386 | glTexCoord2f(1.f, 1.f); | ||
387 | glVertex2f(image_half_width + map_center_agent.mV[VX], image_half_height + map_center_agent.mV[VY]); | ||
388 | glEnd(); | ||
389 | |||
390 | glPopMatrix(); | ||
391 | |||
392 | LLVector3d pos_global; | ||
393 | LLVector3 pos_map; | ||
394 | |||
395 | // Draw avatars | ||
396 | for (regionp = gWorldPointer->mActiveRegionList.getFirstData(); | ||
397 | regionp; | ||
398 | regionp = gWorldPointer->mActiveRegionList.getNextData()) | ||
399 | { | ||
400 | const LLVector3d& origin_global = regionp->getOriginGlobal(); | ||
401 | |||
402 | S32 count = regionp->mMapAvatars.count(); | ||
403 | S32 i; | ||
404 | LLVector3 pos_local; | ||
405 | U32 compact_local; | ||
406 | U8 bits; | ||
407 | for (i = 0; i < count; i++) | ||
408 | { | ||
409 | compact_local = regionp->mMapAvatars.get(i); | ||
410 | |||
411 | bits = compact_local & 0xFF; | ||
412 | pos_local.mV[VZ] = F32(bits) * 4.f; | ||
413 | compact_local >>= 8; | ||
414 | |||
415 | bits = compact_local & 0xFF; | ||
416 | pos_local.mV[VY] = (F32)bits; | ||
417 | compact_local >>= 8; | ||
418 | |||
419 | bits = compact_local & 0xFF; | ||
420 | pos_local.mV[VX] = (F32)bits; | ||
421 | |||
422 | pos_global.setVec( pos_local ); | ||
423 | pos_global += origin_global; | ||
424 | |||
425 | pos_map = globalPosToView(pos_global); | ||
426 | LLWorldMapView::drawAvatar(pos_map.mV[VX], pos_map.mV[VY], | ||
427 | gAvatarMapColor, | ||
428 | pos_map.mV[VZ]); | ||
429 | } | ||
430 | } | ||
431 | |||
432 | // Draw dot for autopilot target | ||
433 | if (gAgent.getAutoPilot()) | ||
434 | { | ||
435 | drawTracking( gAgent.getAutoPilotTargetGlobal(), gTrackColor ); | ||
436 | } | ||
437 | else | ||
438 | { | ||
439 | LLTracker::ETrackingStatus tracking_status = LLTracker::getTrackingStatus(); | ||
440 | if ( LLTracker::TRACKING_AVATAR == tracking_status ) | ||
441 | { | ||
442 | drawTracking( LLAvatarTracker::instance().getGlobalPos(), gTrackColor ); | ||
443 | } | ||
444 | else if ( LLTracker::TRACKING_LANDMARK == tracking_status | ||
445 | || LLTracker::TRACKING_LOCATION == tracking_status ) | ||
446 | { | ||
447 | drawTracking( LLTracker::getTrackedPositionGlobal(), gTrackColor ); | ||
448 | } | ||
449 | } | ||
450 | |||
451 | // Draw dot for self avatar position | ||
452 | //drawTracking( gAgent.getPosGlobalFromAgent(gAgent.getFrameAgent().getCenter()), gSelfMapColor ); | ||
453 | pos_global = gAgent.getPositionGlobal(); | ||
454 | pos_map = globalPosToView(pos_global); | ||
455 | gl_draw_image(llround(pos_map.mV[VX]) - 4, | ||
456 | llround(pos_map.mV[VY]) - 4, | ||
457 | LLWorldMapView::sAvatarYouSmallImage, | ||
458 | LLColor4::white); | ||
459 | |||
460 | // Draw frustum | ||
461 | F32 meters_to_pixels = gMiniMapScale/ gWorldPointer->getRegionWidthInMeters(); | ||
462 | |||
463 | F32 horiz_fov = gCamera->getView() * gCamera->getAspect(); | ||
464 | F32 far_clip_meters = gCamera->getFar(); | ||
465 | F32 far_clip_pixels = far_clip_meters * meters_to_pixels; | ||
466 | |||
467 | F32 half_width_meters = far_clip_meters * tan( horiz_fov / 2 ); | ||
468 | F32 half_width_pixels = half_width_meters * meters_to_pixels; | ||
469 | |||
470 | F32 ctr_x = (F32)center_sw_left; | ||
471 | F32 ctr_y = (F32)center_sw_bottom; | ||
472 | |||
473 | |||
474 | LLGLSNoTexture no_texture; | ||
475 | |||
476 | if( LLNetMap::sRotateMap ) | ||
477 | { | ||
478 | glColor4fv(gFrustumMapColor.mV); | ||
479 | |||
480 | glBegin( GL_TRIANGLES ); | ||
481 | glVertex2f( ctr_x, ctr_y ); | ||
482 | glVertex2f( ctr_x - half_width_pixels, ctr_y + far_clip_pixels ); | ||
483 | glVertex2f( ctr_x + half_width_pixels, ctr_y + far_clip_pixels ); | ||
484 | glEnd(); | ||
485 | } | ||
486 | else | ||
487 | { | ||
488 | glColor4fv(gRotatingFrustumMapColor.mV); | ||
489 | |||
490 | // If we don't rotate the map, we have to rotate the frustum. | ||
491 | glPushMatrix(); | ||
492 | glTranslatef( ctr_x, ctr_y, 0 ); | ||
493 | glRotatef( atan2( gCamera->getAtAxis().mV[VX], gCamera->getAtAxis().mV[VY] ) * RAD_TO_DEG, 0.f, 0.f, -1.f); | ||
494 | glBegin( GL_TRIANGLES ); | ||
495 | glVertex2f( 0, 0 ); | ||
496 | glVertex2f( -half_width_pixels, far_clip_pixels ); | ||
497 | glVertex2f( half_width_pixels, far_clip_pixels ); | ||
498 | glEnd(); | ||
499 | glPopMatrix(); | ||
500 | } | ||
501 | } | ||
502 | |||
503 | // Rotation of 0 means that North is up | ||
504 | setDirectionPos( mTextBoxEast, rotation ); | ||
505 | setDirectionPos( mTextBoxNorth, rotation + F_PI_BY_TWO ); | ||
506 | setDirectionPos( mTextBoxWest, rotation + F_PI ); | ||
507 | setDirectionPos( mTextBoxSouth, rotation + F_PI + F_PI_BY_TWO ); | ||
508 | |||
509 | setDirectionPos( mTextBoxNorthEast, rotation + F_PI_BY_TWO / 2); | ||
510 | setDirectionPos( mTextBoxNorthWest, rotation + F_PI_BY_TWO + F_PI_BY_TWO / 2); | ||
511 | setDirectionPos( mTextBoxSouthWest, rotation + F_PI + F_PI_BY_TWO / 2); | ||
512 | setDirectionPos( mTextBoxSouthEast, rotation + F_PI + F_PI_BY_TWO + F_PI_BY_TWO / 2); | ||
513 | |||
514 | LLUICtrl::draw(); | ||
515 | } | ||
516 | |||
517 | LLVector3 LLNetMap::globalPosToView( const LLVector3d& global_pos ) | ||
518 | { | ||
519 | LLVector3d relative_pos_global = global_pos - gAgent.getCameraPositionGlobal(); | ||
520 | LLVector3 pos_local; | ||
521 | pos_local.setVec(relative_pos_global); // convert to floats from doubles | ||
522 | |||
523 | pos_local.mV[VX] *= mPixelsPerMeter; | ||
524 | pos_local.mV[VY] *= mPixelsPerMeter; | ||
525 | // leave Z component in meters | ||
526 | |||
527 | if( LLNetMap::sRotateMap ) | ||
528 | { | ||
529 | F32 radians = atan2( gCamera->getAtAxis().mV[VX], gCamera->getAtAxis().mV[VY] ); | ||
530 | LLQuaternion rot(radians, LLVector3(0.f, 0.f, 1.f)); | ||
531 | pos_local.rotVec( rot ); | ||
532 | } | ||
533 | |||
534 | pos_local.mV[VX] += mRect.getWidth() / 2 + mCurPanX; | ||
535 | pos_local.mV[VY] += mRect.getHeight() / 2 + mCurPanY; | ||
536 | |||
537 | return pos_local; | ||
538 | } | ||
539 | |||
540 | void LLNetMap::drawTracking(const LLVector3d& pos_global, const LLColor4& color, | ||
541 | BOOL draw_arrow ) | ||
542 | { | ||
543 | LLVector3 pos_local = globalPosToView( pos_global ); | ||
544 | if( (pos_local.mV[VX] < 0) || | ||
545 | (pos_local.mV[VY] < 0) || | ||
546 | (pos_local.mV[VX] >= mRect.getWidth()) || | ||
547 | (pos_local.mV[VY] >= mRect.getHeight()) ) | ||
548 | { | ||
549 | if (draw_arrow) | ||
550 | { | ||
551 | S32 x = llround( pos_local.mV[VX] ); | ||
552 | S32 y = llround( pos_local.mV[VY] ); | ||
553 | LLWorldMapView::drawTrackingCircle( mRect, x, y, color, 1, 10 ); | ||
554 | LLWorldMapView::drawTrackingArrow( mRect, x, y, color ); | ||
555 | } | ||
556 | } | ||
557 | else | ||
558 | { | ||
559 | LLWorldMapView::drawTrackingDot(pos_local.mV[VX], | ||
560 | pos_local.mV[VY], | ||
561 | color, | ||
562 | pos_local.mV[VZ]); | ||
563 | } | ||
564 | } | ||
565 | |||
566 | LLVector3d LLNetMap::viewPosToGlobal( S32 x, S32 y ) | ||
567 | { | ||
568 | x -= llround(mRect.getWidth() / 2 + mCurPanX); | ||
569 | y -= llround(mRect.getHeight() / 2 + mCurPanY); | ||
570 | |||
571 | LLVector3 pos_local( (F32)x, (F32)y, 0 ); | ||
572 | |||
573 | F32 radians = - atan2( gCamera->getAtAxis().mV[VX], gCamera->getAtAxis().mV[VY] ); | ||
574 | |||
575 | if( LLNetMap::sRotateMap ) | ||
576 | { | ||
577 | LLQuaternion rot(radians, LLVector3(0.f, 0.f, 1.f)); | ||
578 | pos_local.rotVec( rot ); | ||
579 | } | ||
580 | |||
581 | pos_local *= ( gWorldPointer->getRegionWidthInMeters() / gMiniMapScale ); | ||
582 | |||
583 | LLVector3d pos_global; | ||
584 | pos_global.setVec( pos_local ); | ||
585 | pos_global += gAgent.getCameraPositionGlobal(); | ||
586 | |||
587 | return pos_global; | ||
588 | } | ||
589 | |||
590 | BOOL LLNetMap::handleScrollWheel(S32 x, S32 y, S32 clicks) | ||
591 | { | ||
592 | // note that clicks are reversed from what you'd think | ||
593 | setScale(llclamp(gMiniMapScale - clicks*MAP_SCALE_INCREMENT, MAP_SCALE_MIN, MAP_SCALE_MAX)); | ||
594 | return TRUE; | ||
595 | } | ||
596 | |||
597 | BOOL LLNetMap::handleToolTip( S32 x, S32 y, LLString& msg, LLRect* sticky_rect_screen ) | ||
598 | { | ||
599 | BOOL handled = FALSE; | ||
600 | if (gDisconnected) | ||
601 | { | ||
602 | return FALSE; | ||
603 | } | ||
604 | if( getVisible() && pointInView( x, y ) ) | ||
605 | { | ||
606 | LLViewerRegion* region = gWorldPointer->getRegionFromPosGlobal( viewPosToGlobal( x, y ) ); | ||
607 | if( region ) | ||
608 | { | ||
609 | msg.assign( region->getName() ); | ||
610 | |||
611 | #ifndef LL_RELEASE_FOR_DOWNLOAD | ||
612 | char buffer[MAX_STRING]; | ||
613 | msg.append("\n"); | ||
614 | region->getHost().getHostName(buffer, MAX_STRING); | ||
615 | msg.append(buffer); | ||
616 | msg.append("\n"); | ||
617 | region->getHost().getString(buffer, MAX_STRING); | ||
618 | msg.append(buffer); | ||
619 | #endif | ||
620 | // *TODO: put this under the control of XUI so it can be | ||
621 | // translated. | ||
622 | msg.append("\n(Double-click to open Map)"); | ||
623 | |||
624 | S32 SLOP = 4; | ||
625 | localPointToScreen( | ||
626 | x - SLOP, y - SLOP, | ||
627 | &(sticky_rect_screen->mLeft), &(sticky_rect_screen->mBottom) ); | ||
628 | sticky_rect_screen->mRight = sticky_rect_screen->mLeft + 2 * SLOP; | ||
629 | sticky_rect_screen->mTop = sticky_rect_screen->mBottom + 2 * SLOP; | ||
630 | } | ||
631 | handled = TRUE; | ||
632 | } | ||
633 | return handled; | ||
634 | } | ||
635 | |||
636 | |||
637 | void LLNetMap::setDirectionPos( LLTextBox* text_box, F32 rotation ) | ||
638 | { | ||
639 | // Rotation is in radians. | ||
640 | // Rotation of 0 means x = 1, y = 0 on the unit circle. | ||
641 | |||
642 | |||
643 | F32 map_half_height = (F32)(mRect.getHeight() / 2); | ||
644 | F32 map_half_width = (F32)(mRect.getWidth() / 2); | ||
645 | F32 text_half_height = (F32)(text_box->getRect().getHeight() / 2); | ||
646 | F32 text_half_width = (F32)(text_box->getRect().getWidth() / 2); | ||
647 | F32 radius = llmin( map_half_height - text_half_height, map_half_width - text_half_width ); | ||
648 | |||
649 | // Inset by a little to account for position display. | ||
650 | radius -= 8.f; | ||
651 | |||
652 | text_box->setOrigin( | ||
653 | llround(map_half_width - text_half_width + radius * cos( rotation )), | ||
654 | llround(map_half_height - text_half_height + radius * sin( rotation )) ); | ||
655 | } | ||
656 | |||
657 | void LLNetMap::renderScaledPointGlobal( const LLVector3d& pos, const LLColor4U &color, F32 radius_meters ) | ||
658 | { | ||
659 | LLVector3 local_pos; | ||
660 | local_pos.setVec( pos - mObjectImageCenterGlobal ); | ||
661 | |||
662 | S32 diameter_pixels = llround(2 * radius_meters * mObjectMapTPM); | ||
663 | renderPoint( local_pos, color, diameter_pixels ); | ||
664 | } | ||
665 | |||
666 | |||
667 | void LLNetMap::renderPoint(const LLVector3 &pos_local, const LLColor4U &color, | ||
668 | S32 diameter, S32 relative_height) | ||
669 | { | ||
670 | if (diameter <= 0) | ||
671 | { | ||
672 | return; | ||
673 | } | ||
674 | |||
675 | const S32 image_width = (S32)mObjectImagep->getWidth(); | ||
676 | const S32 image_height = (S32)mObjectImagep->getHeight(); | ||
677 | |||
678 | S32 x_offset = llround(pos_local.mV[VX] * mObjectMapTPM + image_width / 2); | ||
679 | S32 y_offset = llround(pos_local.mV[VY] * mObjectMapTPM + image_height / 2); | ||
680 | |||
681 | if ((x_offset < 0) || (x_offset >= image_width)) | ||
682 | { | ||
683 | return; | ||
684 | } | ||
685 | if ((y_offset < 0) || (y_offset >= image_height)) | ||
686 | { | ||
687 | return; | ||
688 | } | ||
689 | |||
690 | U8 *datap = mObjectRawImagep->getData(); | ||
691 | |||
692 | S32 neg_radius = diameter / 2; | ||
693 | S32 pos_radius = diameter - neg_radius; | ||
694 | S32 x, y; | ||
695 | |||
696 | if (relative_height > 0) | ||
697 | { | ||
698 | // ...point above agent | ||
699 | S32 px, py; | ||
700 | |||
701 | // vertical line | ||
702 | px = x_offset; | ||
703 | for (y = -neg_radius; y < pos_radius; y++) | ||
704 | { | ||
705 | py = y_offset + y; | ||
706 | if ((py < 0) || (py >= image_height)) | ||
707 | { | ||
708 | continue; | ||
709 | } | ||
710 | S32 offset = px + py * image_width; | ||
711 | ((U32*)datap)[offset] = color.mAll; | ||
712 | } | ||
713 | |||
714 | // top line | ||
715 | py = y_offset + pos_radius - 1; | ||
716 | for (x = -neg_radius; x < pos_radius; x++) | ||
717 | { | ||
718 | px = x_offset + x; | ||
719 | if ((px < 0) || (px >= image_width)) | ||
720 | { | ||
721 | continue; | ||
722 | } | ||
723 | S32 offset = px + py * image_width; | ||
724 | ((U32*)datap)[offset] = color.mAll; | ||
725 | } | ||
726 | } | ||
727 | else | ||
728 | { | ||
729 | // ...point level with agent | ||
730 | for (x = -neg_radius; x < pos_radius; x++) | ||
731 | { | ||
732 | S32 p_x = x_offset + x; | ||
733 | if ((p_x < 0) || (p_x >= image_width)) | ||
734 | { | ||
735 | continue; | ||
736 | } | ||
737 | |||
738 | for (y = -neg_radius; y < pos_radius; y++) | ||
739 | { | ||
740 | S32 p_y = y_offset + y; | ||
741 | if ((p_y < 0) || (p_y >= image_height)) | ||
742 | { | ||
743 | continue; | ||
744 | } | ||
745 | S32 offset = p_x + p_y * image_width; | ||
746 | ((U32*)datap)[offset] = color.mAll; | ||
747 | } | ||
748 | } | ||
749 | } | ||
750 | } | ||
751 | |||
752 | void LLNetMap::createObjectImage() | ||
753 | { | ||
754 | // Find the size of the side of a square that surrounds the circle that surrounds mRect. | ||
755 | F32 half_width = (F32)(mRect.getWidth() / 2); | ||
756 | F32 half_height = (F32)(mRect.getHeight() / 2); | ||
757 | F32 radius = sqrt( half_width * half_width + half_height * half_height ); | ||
758 | S32 square_size = S32( 2 * radius ); | ||
759 | |||
760 | // Find the least power of two >= the minimum size. | ||
761 | const S32 MIN_SIZE = 32; | ||
762 | const S32 MAX_SIZE = 256; | ||
763 | S32 img_size = MIN_SIZE; | ||
764 | while( (img_size*2 < square_size ) && (img_size < MAX_SIZE) ) | ||
765 | { | ||
766 | img_size <<= 1; | ||
767 | } | ||
768 | |||
769 | if( mObjectImagep.isNull() || | ||
770 | (mObjectImagep->getWidth() != img_size) || | ||
771 | (mObjectImagep->getHeight() != img_size) ) | ||
772 | { | ||
773 | mObjectRawImagep = new LLImageRaw(img_size, img_size, 4); | ||
774 | U8* data = mObjectRawImagep->getData(); | ||
775 | memset( data, 0, img_size * img_size * 4 ); | ||
776 | mObjectImagep = new LLImageGL( mObjectRawImagep, FALSE); | ||
777 | setScale(gMiniMapScale); | ||
778 | } | ||
779 | mUpdateNow = TRUE; | ||
780 | } | ||
781 | |||
782 | BOOL LLNetMap::handleDoubleClick( S32 x, S32 y, MASK mask ) | ||
783 | { | ||
784 | LLFloaterWorldMap::show(NULL, FALSE); | ||
785 | return TRUE; | ||
786 | } | ||
787 | |||
788 | BOOL LLNetMap::handleRightMouseDown(S32 x, S32 y, MASK mask) | ||
789 | { | ||
790 | LLMenuGL* menu = (LLMenuGL*)LLView::getViewByHandle(mPopupMenuHandle); | ||
791 | if (menu) | ||
792 | { | ||
793 | menu->buildDrawLabels(); | ||
794 | menu->updateParent(gMenuHolder); | ||
795 | LLMenuGL::showPopup(this, menu, x, y); | ||
796 | } | ||
797 | return TRUE; | ||
798 | } | ||
799 | |||
800 | |||
801 | // static | ||
802 | void LLNetMap::handleZoomLevel(void* which) | ||
803 | { | ||
804 | intptr_t level = (intptr_t)which; | ||
805 | |||
806 | switch(level) | ||
807 | { | ||
808 | case 0: | ||
809 | LLNetMap::sInstance->setScale(MAP_SCALE_MIN); | ||
810 | break; | ||
811 | case 1: | ||
812 | LLNetMap::sInstance->setScale(MAP_SCALE_MID); | ||
813 | break; | ||
814 | case 2: | ||
815 | LLNetMap::sInstance->setScale(MAP_SCALE_MAX); | ||
816 | break; | ||
817 | default: | ||
818 | break; | ||
819 | } | ||
820 | } | ||
821 | |||
822 | bool LLRotateNetMapListener::handleEvent(LLPointer<LLEvent> event, const LLSD& user_data) | ||
823 | { | ||
824 | LLNetMap::setRotateMap(event->getValue().asBoolean()); | ||
825 | return true; | ||
826 | } | ||