aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/linden/indra/newview/llhudtext.cpp
diff options
context:
space:
mode:
authorJacek Antonelli2008-08-15 23:44:46 -0500
committerJacek Antonelli2008-08-15 23:44:46 -0500
commit38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4 (patch)
treeadca584755d22ca041a2dbfc35d4eca01f70b32c /linden/indra/newview/llhudtext.cpp
parentREADME.txt (diff)
downloadmeta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.zip
meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.tar.gz
meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.tar.bz2
meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.tar.xz
Second Life viewer sources 1.13.2.12
Diffstat (limited to 'linden/indra/newview/llhudtext.cpp')
-rw-r--r--linden/indra/newview/llhudtext.cpp1010
1 files changed, 1010 insertions, 0 deletions
diff --git a/linden/indra/newview/llhudtext.cpp b/linden/indra/newview/llhudtext.cpp
new file mode 100644
index 0000000..bacfaa0
--- /dev/null
+++ b/linden/indra/newview/llhudtext.cpp
@@ -0,0 +1,1010 @@
1/**
2 * @file llhudtext.cpp
3 * @brief LLHUDText class implementation
4 *
5 * Copyright (c) 2002-2007, Linden Research, Inc.
6 *
7 * The source code in this file ("Source Code") is provided by Linden Lab
8 * to you under the terms of the GNU General Public License, version 2.0
9 * ("GPL"), unless you have obtained a separate licensing agreement
10 * ("Other License"), formally executed by you and Linden Lab. Terms of
11 * the GPL can be found in doc/GPL-license.txt in this distribution, or
12 * online at http://secondlife.com/developers/opensource/gplv2
13 *
14 * There are special exceptions to the terms and conditions of the GPL as
15 * it is applied to this Source Code. View the full text of the exception
16 * in the file doc/FLOSS-exception.txt in this software distribution, or
17 * online at http://secondlife.com/developers/opensource/flossexception
18 *
19 * By copying, modifying or distributing this software, you acknowledge
20 * that you have read and understood your obligations described above,
21 * and agree to abide by those obligations.
22 *
23 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
24 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
25 * COMPLETENESS OR PERFORMANCE.
26 */
27
28#include "llviewerprecompiledheaders.h"
29
30#include "llhudtext.h"
31
32#include "llagent.h"
33#include "llviewercontrol.h"
34#include "llchatbar.h"
35#include "llcriticaldamp.h"
36#include "lldrawable.h"
37#include "llfontgl.h"
38#include "llglheaders.h"
39#include "llhudrender.h"
40#include "llimagegl.h"
41#include "llui.h"
42#include "llviewercamera.h"
43#include "llviewerimagelist.h"
44#include "llviewerobject.h"
45#include "llvovolume.h"
46#include "llviewerwindow.h"
47#include "viewer.h"
48#include "llstatusbar.h"
49#include "llmenugl.h"
50#include "pipeline.h"
51#include <boost/tokenizer.hpp>
52
53
54const F32 SPRING_STRENGTH = 0.7f;
55const F32 RESTORATION_SPRING_TIME_CONSTANT = 0.1f;
56const F32 HORIZONTAL_PADDING = 15.f;
57const F32 VERTICAL_PADDING = 12.f;
58const F32 BUFFER_SIZE = 2.f;
59const F32 MIN_EDGE_OVERLAP = 3.f;
60F32 HUD_TEXT_MAX_WIDTH = 190.f;
61const F32 HUD_TEXT_MAX_WIDTH_NO_BUBBLE = 1000.f;
62const F32 RESIZE_TIME = 0.f;
63const S32 NUM_OVERLAP_ITERATIONS = 10;
64const F32 NEIGHBOR_FORCE_FRACTION = 1.f;
65const F32 POSITION_DAMPING_TC = 0.2f;
66const F32 MAX_STABLE_CAMERA_VELOCITY = 0.1f;
67const F32 LOD_0_SCREEN_COVERAGE = 0.15f;
68const F32 LOD_1_SCREEN_COVERAGE = 0.30f;
69const F32 LOD_2_SCREEN_COVERAGE = 0.40f;
70
71std::set<LLPointer<LLHUDText> > LLHUDText::sTextObjects;
72std::vector<LLPointer<LLHUDText> > LLHUDText::sVisibleTextObjects;
73std::vector<LLPointer<LLHUDText> > LLHUDText::sVisibleHUDTextObjects;
74
75bool lltextobject_further_away::operator()(const LLPointer<LLHUDText>& lhs, const LLPointer<LLHUDText>& rhs) const
76{
77 return (lhs->getDistance() > rhs->getDistance()) ? true : false;
78}
79
80
81LLHUDText::LLHUDText(const U8 type) :
82 LLHUDObject(type),
83 mUseBubble(FALSE),
84 mUsePixelSize(TRUE),
85 mVisibleOffScreen(FALSE),
86 mWidth(0.f),
87 mHeight(0.f),
88 mFontp(LLFontGL::sSansSerifSmall),
89 mBoldFontp(LLFontGL::sSansSerifBold),
90 mMass(1.f),
91 mMaxLines(10),
92 mOffsetY(0),
93 mTextAlignment(ALIGN_TEXT_CENTER),
94 mVertAlignment(ALIGN_VERT_CENTER),
95 mLOD(0)
96{
97 mColor = LLColor4(1.f, 1.f, 1.f, 1.f);
98 mDoFade = TRUE;
99 mFadeDistance = 8.f;
100 mFadeRange = 4.f;
101 mZCompare = TRUE;
102 mDropShadow = TRUE;
103 mOffscreen = FALSE;
104 mRadius = 0.1f;
105 LLPointer<LLHUDText> ptr(this);
106 sTextObjects.insert(ptr);
107 //LLDebugVarMessageBox::show("max width", &HUD_TEXT_MAX_WIDTH, 500.f, 1.f);
108}
109
110LLHUDText::~LLHUDText()
111{
112}
113
114
115void LLHUDText::render()
116{
117 if (!mOnHUDAttachment)
118 {
119 LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE);
120 renderText(FALSE);
121 }
122}
123
124void LLHUDText::renderForSelect()
125{
126 if (!mOnHUDAttachment)
127 {
128 LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE);
129 renderText(TRUE);
130 }
131}
132
133void LLHUDText::renderText(BOOL for_select)
134{
135 if (!mVisible)
136 {
137 return;
138 }
139
140 // don't pick text that isn't bound to a viewerobject or isn't in a bubble
141 if (for_select &&
142 (!mSourceObject || mSourceObject->mDrawable.isNull() || !mUseBubble))
143 {
144 return;
145 }
146
147 LLGLState gls_tex(GL_TEXTURE_2D, for_select ? FALSE : TRUE);
148 LLGLState gls_blend(GL_BLEND, for_select ? FALSE : TRUE);
149 LLGLState gls_alpha(GL_ALPHA_TEST, for_select ? FALSE : TRUE);
150
151 LLColor4 shadow_color(0.f, 0.f, 0.f, 1.f);
152 F32 alpha_factor = 1.f;
153 LLColor4 text_color = mColor;
154 if (mDoFade)
155 {
156 if (mLastDistance > mFadeDistance)
157 {
158 alpha_factor = llmax(0.f, 1.f - (mLastDistance - mFadeDistance)/mFadeRange);
159 text_color.mV[3] = text_color.mV[3]*alpha_factor;
160 }
161 }
162 if (text_color.mV[3] < 0.01f)
163 {
164 return;
165 }
166 shadow_color.mV[3] = text_color.mV[3];
167
168 mOffsetY = lltrunc(mHeight * ((mVertAlignment == ALIGN_VERT_CENTER) ? 0.5f : 1.f));
169
170 // *TODO: cache this image
171 LLUUID image_id;
172 image_id.set(gViewerArt.getString("rounded_square.tga"));
173 LLViewerImage* imagep = gImageList.getImage(image_id, MIPMAP_FALSE, TRUE);
174
175 // *TODO: make this a per-text setting
176 LLColor4 bg_color = gSavedSettings.getColor4("BackgroundChatColor");
177 bg_color.setAlpha(gSavedSettings.getF32("ChatBubbleOpacity") * alpha_factor);
178
179 const S32 border_height = 16;
180 const S32 border_width = 16;
181
182 // *TODO move this into helper function
183 F32 border_scale = 1.f;
184
185 if (border_height * 2 > mHeight)
186 {
187 border_scale = (F32)mHeight / ((F32)border_height * 2.f);
188 }
189 if (border_width * 2 > mWidth)
190 {
191 border_scale = llmin(border_scale, (F32)mWidth / ((F32)border_width * 2.f));
192 }
193
194 // scale screen size of borders down
195 //RN: for now, text on hud objects is never occluded
196
197 LLVector3 x_pixel_vec;
198 LLVector3 y_pixel_vec;
199
200 if (mOnHUDAttachment)
201 {
202 x_pixel_vec = LLVector3::y_axis / (F32)gViewerWindow->getWindowWidth();
203 y_pixel_vec = LLVector3::z_axis / (F32)gViewerWindow->getWindowHeight();
204 }
205 else
206 {
207 gCamera->getPixelVectors(mPositionAgent, y_pixel_vec, x_pixel_vec);
208 }
209
210 LLVector2 border_scale_vec((F32)border_width / (F32)imagep->getWidth(), (F32)border_height / (F32)imagep->getHeight());
211 LLVector3 width_vec = mWidth * x_pixel_vec;
212 LLVector3 height_vec = mHeight * y_pixel_vec;
213 LLVector3 scaled_border_width = (F32)llfloor(border_scale * (F32)border_width) * x_pixel_vec;
214 LLVector3 scaled_border_height = (F32)llfloor(border_scale * (F32)border_height) * y_pixel_vec;
215
216 mRadius = (width_vec + height_vec).magVec() * 0.5f;
217
218 LLCoordGL screen_pos;
219 gCamera->projectPosAgentToScreen(mPositionAgent, screen_pos, FALSE);
220
221 LLVector2 screen_offset;
222 if (!mUseBubble)
223 {
224 screen_offset = mPositionOffset;
225 }
226 else
227 {
228 screen_offset = updateScreenPos(mPositionOffset);
229 }
230
231 LLVector3 render_position = mPositionAgent
232 + (x_pixel_vec * screen_offset.mV[VX])
233 + (y_pixel_vec * screen_offset.mV[VY]);
234
235 //if (mOnHUD)
236 //{
237 // render_position.mV[VY] -= fmodf(render_position.mV[VY], 1.f / (F32)gViewerWindow->getWindowWidth());
238 // render_position.mV[VZ] -= fmodf(render_position.mV[VZ], 1.f / (F32)gViewerWindow->getWindowHeight());
239 //}
240 //else
241 //{
242 // render_position = gCamera->roundToPixel(render_position);
243 //}
244
245 if (mUseBubble)
246 {
247 LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE);
248 LLUI::pushMatrix();
249 {
250 LLVector3 bg_pos = render_position
251 + (F32)mOffsetY * y_pixel_vec
252 - (width_vec / 2.f)
253 - (height_vec);
254 LLUI::translate(bg_pos.mV[VX], bg_pos.mV[VY], bg_pos.mV[VZ]);
255
256 if (for_select)
257 {
258 LLGLSNoTexture no_texture_state;
259 S32 name = mSourceObject->mGLName;
260 LLColor4U coloru((U8)(name >> 16), (U8)(name >> 8), (U8)name);
261 glColor4ubv(coloru.mV);
262 gl_segmented_rect_3d_tex(border_scale_vec, scaled_border_width, scaled_border_height, width_vec, height_vec);
263 LLUI::popMatrix();
264 return;
265 }
266 else
267 {
268 LLViewerImage::bindTexture(imagep);
269
270 glColor4fv(bg_color.mV);
271 gl_segmented_rect_3d_tex(border_scale_vec, scaled_border_width, scaled_border_height, width_vec, height_vec);
272
273 if ( mLabelSegments.size())
274 {
275 LLUI::pushMatrix();
276 {
277 glColor4f(text_color.mV[VX], text_color.mV[VY], text_color.mV[VZ], gSavedSettings.getF32("ChatBubbleOpacity") * alpha_factor);
278 LLVector3 label_height = (mFontp->getLineHeight() * mLabelSegments.size() + (VERTICAL_PADDING / 3.f)) * y_pixel_vec;
279 LLVector3 label_offset = height_vec - label_height;
280 LLUI::translate(label_offset.mV[VX], label_offset.mV[VY], label_offset.mV[VZ]);
281 gl_segmented_rect_3d_tex_top(border_scale_vec, scaled_border_width, scaled_border_height, width_vec, label_height);
282 }
283 LLUI::popMatrix();
284 }
285 }
286
287 BOOL outside_width = llabs(mPositionOffset.mV[VX]) > mWidth * 0.5f;
288 BOOL outside_height = llabs(mPositionOffset.mV[VY] + (mVertAlignment == ALIGN_VERT_TOP ? mHeight * 0.5f : 0.f)) > mHeight * (mVertAlignment == ALIGN_VERT_TOP ? mHeight * 0.75f : 0.5f);
289
290 // draw line segments pointing to parent object
291 if (!mOffscreen && (outside_width || outside_height))
292 {
293 LLUI::pushMatrix();
294 {
295 glColor4fv(bg_color.mV);
296 LLVector3 target_pos = -1.f * (mPositionOffset.mV[VX] * x_pixel_vec + mPositionOffset.mV[VY] * y_pixel_vec);
297 target_pos += (width_vec / 2.f);
298 target_pos += mVertAlignment == ALIGN_VERT_CENTER ? (height_vec * 0.5f) : LLVector3::zero;
299 target_pos -= 3.f * x_pixel_vec;
300 target_pos -= 6.f * y_pixel_vec;
301 LLUI::translate(target_pos.mV[VX], target_pos.mV[VY], target_pos.mV[VZ]);
302 gl_segmented_rect_3d_tex(border_scale_vec, 3.f * x_pixel_vec, 3.f * y_pixel_vec, 6.f * x_pixel_vec, 6.f * y_pixel_vec);
303 }
304 LLUI::popMatrix();
305
306
307 LLGLDisable gls_texture_2d(GL_TEXTURE_2D);
308 LLGLDepthTest gls_depth(mZCompare ? GL_TRUE : GL_FALSE, GL_FALSE);
309
310 LLVector3 box_center_offset;
311 box_center_offset = (width_vec * 0.5f) + (height_vec * 0.5f);
312 LLUI::translate(box_center_offset.mV[VX], box_center_offset.mV[VY], box_center_offset.mV[VZ]);
313 glColor4fv(bg_color.mV);
314 LLUI::setLineWidth(2.0);
315 glBegin(GL_LINES);
316 {
317 if (outside_width)
318 {
319 LLVector3 vert;
320 // draw line in x then y
321 if (mPositionOffset.mV[VX] < 0.f)
322 {
323 // start at right edge
324 vert = width_vec * 0.5f;
325 glVertex3fv(vert.mV);
326 }
327 else
328 {
329 // start at left edge
330 vert = width_vec * -0.5f;
331 glVertex3fv(vert.mV);
332 }
333 vert = -mPositionOffset.mV[VX] * x_pixel_vec;
334 glVertex3fv(vert.mV);
335 glVertex3fv(vert.mV);
336 vert -= mPositionOffset.mV[VY] * y_pixel_vec;
337 vert -= ((mVertAlignment == ALIGN_VERT_TOP) ? (height_vec * 0.5f) : LLVector3::zero);
338 glVertex3fv(vert.mV);
339 }
340 else
341 {
342 LLVector3 vert;
343 // draw line in y then x
344 if (mPositionOffset.mV[VY] < 0.f)
345 {
346 // start at top edge
347 vert = (height_vec * 0.5f) - (mPositionOffset.mV[VX] * x_pixel_vec);
348 glVertex3fv(vert.mV);
349 }
350 else
351 {
352 // start at bottom edge
353 vert = (height_vec * -0.5f) - (mPositionOffset.mV[VX] * x_pixel_vec);
354 glVertex3fv(vert.mV);
355 }
356 vert = -mPositionOffset.mV[VY] * y_pixel_vec - mPositionOffset.mV[VX] * x_pixel_vec;
357 vert -= ((mVertAlignment == ALIGN_VERT_TOP) ? (height_vec * 0.5f) : LLVector3::zero);
358 glVertex3fv(vert.mV);
359 }
360 }
361 glEnd();
362 LLUI::setLineWidth(1.0);
363
364 }
365 }
366 LLUI::popMatrix();
367 }
368
369 F32 y_offset = (F32)mOffsetY;
370
371 // Render label
372 {
373 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
374
375 for(std::vector<LLHUDTextSegment>::iterator segment_iter = mLabelSegments.begin();
376 segment_iter != mLabelSegments.end(); ++segment_iter )
377 {
378 const LLFontGL* fontp = (segment_iter->mStyle == LLFontGL::BOLD) ? mBoldFontp : mFontp;
379 y_offset -= fontp->getLineHeight();
380
381 F32 x_offset;
382 if (mTextAlignment == ALIGN_TEXT_CENTER)
383 {
384 x_offset = -0.5f*segment_iter->getWidth(fontp);
385 }
386 else // ALIGN_LEFT
387 {
388 x_offset = -0.5f * mWidth + (HORIZONTAL_PADDING / 2.f);
389 }
390
391 LLColor4 label_color(0.f, 0.f, 0.f, 1.f);
392 label_color.mV[VALPHA] = alpha_factor;
393 hud_render_text(segment_iter->getText(), render_position, *fontp, segment_iter->mStyle, x_offset, y_offset, label_color, mOnHUDAttachment);
394 }
395 }
396
397 // Render text
398 {
399 // -1 mMaxLines means unlimited lines.
400 S32 start_segment;
401 S32 max_lines = getMaxLines();
402
403 if (max_lines < 0)
404 {
405 start_segment = 0;
406 }
407 else
408 {
409 start_segment = llmax((S32)0, (S32)mTextSegments.size() - max_lines);
410 }
411
412 for (std::vector<LLHUDTextSegment>::iterator segment_iter = mTextSegments.begin() + start_segment;
413 segment_iter != mTextSegments.end(); ++segment_iter )
414 {
415 const LLFontGL* fontp = (segment_iter->mStyle == LLFontGL::BOLD) ? mBoldFontp : mFontp;
416 y_offset -= fontp->getLineHeight();
417
418 U8 style = segment_iter->mStyle;
419 if (mDropShadow)
420 {
421 style |= LLFontGL::DROP_SHADOW;
422 }
423
424 F32 x_offset;
425 if (mTextAlignment== ALIGN_TEXT_CENTER)
426 {
427 x_offset = -0.5f*segment_iter->getWidth(fontp);
428 }
429 else // ALIGN_LEFT
430 {
431 x_offset = -0.5f * mWidth + (HORIZONTAL_PADDING / 2.f);
432 }
433
434 text_color = segment_iter->mColor;
435 text_color.mV[VALPHA] *= alpha_factor;
436
437 hud_render_text(segment_iter->getText(), render_position, *fontp, style, x_offset, y_offset, text_color, mOnHUDAttachment);
438 }
439 }
440}
441
442void LLHUDText::setStringUTF8(const std::string &wtext)
443{
444 setString(utf8str_to_wstring(wtext));
445}
446
447void LLHUDText::setString(const LLWString &wtext)
448{
449 mTextSegments.clear();
450 addLine(wtext, mColor);
451}
452
453void LLHUDText::clearString()
454{
455 mTextSegments.clear();
456}
457
458
459void LLHUDText::addLine(const std::string &str, const LLColor4& color, const LLFontGL::StyleFlags style)
460{
461 addLine(utf8str_to_wstring(str), color, style);
462}
463
464
465void LLHUDText::addLine(const LLWString &wstr, const LLColor4& color, const LLFontGL::StyleFlags style)
466{
467 if (gNoRender)
468 {
469 return;
470 }
471 if (!wstr.empty())
472 {
473 LLWString wline(wstr);
474 typedef boost::tokenizer<boost::char_separator<llwchar>, LLWString::const_iterator, LLWString > tokenizer;
475 LLWString seps(utf8str_to_wstring("\r\n"));
476 boost::char_separator<llwchar> sep(seps.c_str());
477
478 tokenizer tokens(wline, sep);
479 tokenizer::iterator iter = tokens.begin();
480
481 while (iter != tokens.end())
482 {
483 U32 line_length = 0;
484 do
485 {
486 S32 segment_length = mFontp->maxDrawableChars(iter->substr(line_length).c_str(), mUseBubble ? HUD_TEXT_MAX_WIDTH : HUD_TEXT_MAX_WIDTH_NO_BUBBLE, wline.length(), TRUE);
487 mTextSegments.push_back(LLHUDTextSegment(iter->substr(line_length, segment_length), style, color));
488 line_length += segment_length;
489 }
490 while (line_length != iter->size());
491 ++iter;
492 }
493 }
494}
495
496void LLHUDText::setLabel(const std::string &label)
497{
498 setLabel(utf8str_to_wstring(label));
499}
500
501void LLHUDText::setLabel(const LLWString &wlabel)
502{
503 mLabelSegments.clear();
504
505 if (!wlabel.empty())
506 {
507 LLWString wstr(wlabel);
508 LLWString seps(utf8str_to_wstring("\r\n"));
509 LLWString empty;
510
511 typedef boost::tokenizer<boost::char_separator<llwchar>, LLWString::const_iterator, LLWString > tokenizer;
512 boost::char_separator<llwchar> sep(seps.c_str(), empty.c_str(), boost::keep_empty_tokens);
513
514 tokenizer tokens(wstr, sep);
515 tokenizer::iterator iter = tokens.begin();
516
517 while (iter != tokens.end())
518 {
519 U32 line_length = 0;
520 do
521 {
522 S32 segment_length = mFontp->maxDrawableChars(iter->substr(line_length).c_str(), mUseBubble ? HUD_TEXT_MAX_WIDTH : HUD_TEXT_MAX_WIDTH_NO_BUBBLE, wstr.length(), TRUE);
523 mLabelSegments.push_back(LLHUDTextSegment(iter->substr(line_length, segment_length), LLFontGL::NORMAL, mColor));
524 line_length += segment_length;
525 }
526 while (line_length != iter->size());
527 ++iter;
528 }
529 }
530}
531
532void LLHUDText::setDropShadow(const BOOL do_shadow)
533{
534 mDropShadow = do_shadow;
535}
536
537void LLHUDText::setZCompare(const BOOL zcompare)
538{
539 mZCompare = zcompare;
540}
541
542void LLHUDText::setFont(const LLFontGL* font)
543{
544 mFontp = font;
545}
546
547
548void LLHUDText::setColor(const LLColor4 &color)
549{
550 mColor = color;
551 for (std::vector<LLHUDTextSegment>::iterator segment_iter = mTextSegments.begin();
552 segment_iter != mTextSegments.end(); ++segment_iter )
553 {
554 segment_iter->mColor = color;
555 }
556}
557
558
559void LLHUDText::setUsePixelSize(const BOOL use_pixel_size)
560{
561 mUsePixelSize = use_pixel_size;
562}
563
564void LLHUDText::setDoFade(const BOOL do_fade)
565{
566 mDoFade = do_fade;
567}
568
569void LLHUDText::updateVisibility()
570{
571 if (mSourceObject)
572 {
573 mSourceObject->updateText();
574 }
575
576 mPositionAgent = gAgent.getPosAgentFromGlobal(mPositionGlobal);
577
578 if (!mSourceObject)
579 {
580 //llwarns << "LLHUDText::updateScreenPos -- mSourceObject is NULL!" << llendl;
581 mVisible = TRUE;
582 if (mOnHUDAttachment)
583 {
584 sVisibleHUDTextObjects.push_back(LLPointer<LLHUDText> (this));
585 }
586 else
587 {
588 sVisibleTextObjects.push_back(LLPointer<LLHUDText> (this));
589 }
590 return;
591 }
592
593 // Not visible if parent object is dead
594 if (mSourceObject->isDead())
595 {
596 mVisible = FALSE;
597 return;
598 }
599
600 // for now, all text on hud objects is visible
601 if (mOnHUDAttachment)
602 {
603 mVisible = TRUE;
604 sVisibleHUDTextObjects.push_back(LLPointer<LLHUDText> (this));
605 mLastDistance = mPositionAgent.mV[VX];
606 return;
607 }
608
609 // push text towards camera by radius of object, but not past camera
610 LLVector3 vec_from_camera = mPositionAgent - gCamera->getOrigin();
611 LLVector3 dir_from_camera = vec_from_camera;
612 dir_from_camera.normVec();
613
614 if (dir_from_camera * gCamera->getAtAxis() <= 0.f)
615 {
616 mPositionAgent -= projected_vec(vec_from_camera, gCamera->getAtAxis()) * 1.f;
617 mPositionAgent += gCamera->getAtAxis() * (gCamera->getNear() + 0.1f);
618 }
619 else if (vec_from_camera * gCamera->getAtAxis() <= gCamera->getNear() + 0.1f + mSourceObject->getVObjRadius())
620 {
621 mPositionAgent = gCamera->getOrigin() + vec_from_camera * ((gCamera->getNear() + 0.1f) / (vec_from_camera * gCamera->getAtAxis()));
622 }
623 else
624 {
625 mPositionAgent -= dir_from_camera * mSourceObject->getVObjRadius();
626 }
627
628 mLastDistance = (mPositionAgent - gCamera->getOrigin()).magVec();
629
630 if (mLOD >= 3 || !mTextSegments.size() || (mDoFade && (mLastDistance > mFadeDistance + mFadeRange)))
631 {
632 mVisible = FALSE;
633 return;
634 }
635
636 LLVector3 x_pixel_vec;
637 LLVector3 y_pixel_vec;
638
639 gCamera->getPixelVectors(mPositionAgent, y_pixel_vec, x_pixel_vec);
640
641 LLVector3 render_position = mPositionAgent +
642 (x_pixel_vec * mPositionOffset.mV[VX]) +
643 (y_pixel_vec * mPositionOffset.mV[VY]);
644
645 mOffscreen = FALSE;
646 if (!gCamera->sphereInFrustum(render_position, mRadius))
647 {
648 if (!mVisibleOffScreen)
649 {
650 mVisible = FALSE;
651 return;
652 }
653 else
654 {
655 mOffscreen = TRUE;
656 }
657 }
658
659 mVisible = TRUE;
660 sVisibleTextObjects.push_back(LLPointer<LLHUDText> (this));
661}
662
663LLVector2 LLHUDText::updateScreenPos(LLVector2 &offset)
664{
665 LLCoordGL screen_pos;
666 LLVector2 screen_pos_vec;
667 LLVector3 x_pixel_vec;
668 LLVector3 y_pixel_vec;
669 gCamera->getPixelVectors(mPositionAgent, y_pixel_vec, x_pixel_vec);
670 LLVector3 world_pos = mPositionAgent + (offset.mV[VX] * x_pixel_vec) + (offset.mV[VY] * y_pixel_vec);
671 if (!gCamera->projectPosAgentToScreen(world_pos, screen_pos, FALSE) && mVisibleOffScreen)
672 {
673 // bubble off-screen, so find a spot for it along screen edge
674 LLVector2 window_center(gViewerWindow->getWindowDisplayWidth() * 0.5f, gViewerWindow->getWindowDisplayHeight() * 0.5f);
675 LLVector2 delta_from_center(screen_pos.mX - window_center.mV[VX],
676 screen_pos.mY - window_center.mV[VY]);
677 delta_from_center.normVec();
678
679 F32 camera_aspect = gCamera->getAspect();
680 F32 delta_aspect = llabs(delta_from_center.mV[VX] / delta_from_center.mV[VY]);
681 if (camera_aspect / llmax(delta_aspect, 0.001f) > 1.f)
682 {
683 // camera has wider aspect ratio than offset vector, so clamp to height
684 delta_from_center *= llabs(window_center.mV[VY] / delta_from_center.mV[VY]);
685 }
686 else
687 {
688 // camera has narrower aspect ratio than offset vector, so clamp to width
689 delta_from_center *= llabs(window_center.mV[VX] / delta_from_center.mV[VX]);
690 }
691
692 screen_pos_vec = window_center + delta_from_center;
693 }
694 else
695 {
696 screen_pos_vec.setVec((F32)screen_pos.mX, (F32)screen_pos.mY);
697 }
698 S32 bottom = STATUS_BAR_HEIGHT;
699 if (gChatBar->getVisible())
700 {
701 bottom += CHAT_BAR_HEIGHT;
702 }
703
704 LLVector2 screen_center;
705 screen_center.mV[VX] = llclamp((F32)screen_pos_vec.mV[VX], mWidth * 0.5f, (F32)gViewerWindow->getWindowDisplayWidth() - mWidth * 0.5f);
706
707 if(mVertAlignment == ALIGN_VERT_TOP)
708 {
709 screen_center.mV[VY] = llclamp((F32)screen_pos_vec.mV[VY],
710 (F32)bottom,
711 (F32)gViewerWindow->getWindowDisplayHeight() - mHeight - (F32)MENU_BAR_HEIGHT);
712 mSoftScreenRect.setLeftTopAndSize(screen_center.mV[VX] - (mWidth + BUFFER_SIZE) * 0.5f,
713 screen_center.mV[VY] + (mHeight + BUFFER_SIZE), mWidth + BUFFER_SIZE, mHeight + BUFFER_SIZE);
714 }
715 else
716 {
717 screen_center.mV[VY] = llclamp((F32)screen_pos_vec.mV[VY],
718 (F32)bottom + mHeight * 0.5f,
719 (F32)gViewerWindow->getWindowDisplayHeight() - mHeight * 0.5f - (F32)MENU_BAR_HEIGHT);
720 mSoftScreenRect.setCenterAndSize(screen_center.mV[VX], screen_center.mV[VY], mWidth + BUFFER_SIZE, mHeight + BUFFER_SIZE);
721 }
722
723 return offset + (screen_center - LLVector2((F32)screen_pos.mX, (F32)screen_pos.mY));
724}
725
726void LLHUDText::updateSize()
727{
728 F32 width = 0.f;
729
730 S32 max_lines = getMaxLines();
731 S32 lines = (max_lines < 0) ? (S32)mTextSegments.size() : llmin((S32)mTextSegments.size(), max_lines);
732
733 F32 height = (F32)mFontp->getLineHeight() * (lines + mLabelSegments.size());
734
735 S32 start_segment;
736 if (max_lines < 0) start_segment = 0;
737 else start_segment = llmax((S32)0, (S32)mTextSegments.size() - max_lines);
738
739 std::vector<LLHUDTextSegment>::iterator iter = mTextSegments.begin() + start_segment;
740 while (iter != mTextSegments.end())
741 {
742 width = llmax(width, llmin(iter->getWidth(mFontp), HUD_TEXT_MAX_WIDTH));
743 ++iter;
744 }
745
746 iter = mLabelSegments.begin();
747 while (iter != mLabelSegments.end())
748 {
749 width = llmax(width, llmin(iter->getWidth(mFontp), HUD_TEXT_MAX_WIDTH));
750 ++iter;
751 }
752
753 if (width == 0.f)
754 {
755 return;
756 }
757
758 width += HORIZONTAL_PADDING;
759 height += VERTICAL_PADDING;
760
761 if (!mResizeTimer.getStarted() && (width != mWidth || height != mHeight))
762 {
763 mResizeTimer.start();
764 }
765
766 // *NOTE: removed logic which did a divide by zero.
767 F32 u = 1.f;//llclamp(mResizeTimer.getElapsedTimeF32() / RESIZE_TIME, 0.f, 1.f);
768 if (u == 1.f)
769 {
770 mResizeTimer.stop();
771 }
772
773 mWidth = llmax(width, lerp(mWidth, (F32)width, u));
774 mHeight = llmax(height, lerp(mHeight, (F32)height, u));
775}
776
777void LLHUDText::updateAll()
778{
779 // iterate over all text objects, calculate their restoration forces,
780 // and add them to the visible set if they are on screen and close enough
781 sVisibleTextObjects.clear();
782 sVisibleHUDTextObjects.clear();
783
784 TextObjectIterator text_it;
785 for (text_it = sTextObjects.begin(); text_it != sTextObjects.end(); ++text_it)
786 {
787 LLHUDText* textp = (*text_it);
788 textp->mTargetPositionOffset.clearVec();
789 textp->updateSize();
790 textp->updateVisibility();
791 }
792
793 // sort back to front for rendering purposes
794 std::sort(sVisibleTextObjects.begin(), sVisibleTextObjects.end(), lltextobject_further_away());
795 std::sort(sVisibleHUDTextObjects.begin(), sVisibleHUDTextObjects.end(), lltextobject_further_away());
796
797 // iterate from front to back, and set LOD based on current screen coverage
798 F32 screen_area = (F32)(gViewerWindow->getWindowWidth() * gViewerWindow->getWindowHeight());
799 F32 current_screen_area = 0.f;
800 std::vector<LLPointer<LLHUDText> >::reverse_iterator r_it;
801 for (r_it = sVisibleTextObjects.rbegin(); r_it != sVisibleTextObjects.rend(); ++r_it)
802 {
803 LLHUDText* textp = (*r_it);
804 if (textp->mUseBubble)
805 {
806 if (current_screen_area / screen_area > LOD_2_SCREEN_COVERAGE)
807 {
808 textp->setLOD(3);
809 }
810 else if (current_screen_area / screen_area > LOD_1_SCREEN_COVERAGE)
811 {
812 textp->setLOD(2);
813 }
814 else if (current_screen_area / screen_area > LOD_0_SCREEN_COVERAGE)
815 {
816 textp->setLOD(1);
817 }
818 else
819 {
820 textp->setLOD(0);
821 }
822 textp->updateSize();
823 // find on-screen position and initialize collision rectangle
824 textp->mTargetPositionOffset = textp->updateScreenPos(LLVector2::zero);
825 current_screen_area += (F32)(textp->mSoftScreenRect.getWidth() * textp->mSoftScreenRect.getHeight());
826 }
827 }
828
829 LLStat* camera_vel_stat = gCamera->getVelocityStat();
830 F32 camera_vel = camera_vel_stat->getCurrent();
831 if (camera_vel > MAX_STABLE_CAMERA_VELOCITY)
832 {
833 return;
834 }
835
836 VisibleTextObjectIterator src_it;
837
838 for (S32 i = 0; i < NUM_OVERLAP_ITERATIONS; i++)
839 {
840 for (src_it = sVisibleTextObjects.begin(); src_it != sVisibleTextObjects.end(); ++src_it)
841 {
842 LLHUDText* src_textp = (*src_it);
843
844 if (!src_textp->mUseBubble)
845 {
846 continue;
847 }
848 VisibleTextObjectIterator dst_it = src_it;
849 ++dst_it;
850 for (; dst_it != sVisibleTextObjects.end(); ++dst_it)
851 {
852 LLHUDText* dst_textp = (*dst_it);
853
854 if (!dst_textp->mUseBubble)
855 {
856 continue;
857 }
858 if (src_textp->mSoftScreenRect.rectInRect(&dst_textp->mSoftScreenRect))
859 {
860 LLRectf intersect_rect = src_textp->mSoftScreenRect & dst_textp->mSoftScreenRect;
861 intersect_rect.stretch(-BUFFER_SIZE * 0.5f);
862
863 F32 src_center_x = src_textp->mSoftScreenRect.getCenterX();
864 F32 src_center_y = src_textp->mSoftScreenRect.getCenterY();
865 F32 dst_center_x = dst_textp->mSoftScreenRect.getCenterX();
866 F32 dst_center_y = dst_textp->mSoftScreenRect.getCenterY();
867 F32 intersect_center_x = intersect_rect.getCenterX();
868 F32 intersect_center_y = intersect_rect.getCenterY();
869 LLVector2 force = lerp(LLVector2(dst_center_x - intersect_center_x, dst_center_y - intersect_center_y),
870 LLVector2(intersect_center_x - src_center_x, intersect_center_y - src_center_y),
871 0.5f);
872 force.setVec(dst_center_x - src_center_x, dst_center_y - src_center_y);
873 force.normVec();
874
875 LLVector2 src_force = -1.f * force;
876 LLVector2 dst_force = force;
877
878 LLVector2 force_strength;
879 F32 src_mult = dst_textp->mMass / (dst_textp->mMass + src_textp->mMass);
880 F32 dst_mult = 1.f - src_mult;
881 F32 src_aspect_ratio = src_textp->mSoftScreenRect.getWidth() / src_textp->mSoftScreenRect.getHeight();
882 F32 dst_aspect_ratio = dst_textp->mSoftScreenRect.getWidth() / dst_textp->mSoftScreenRect.getHeight();
883 src_force.mV[VY] *= src_aspect_ratio;
884 src_force.normVec();
885 dst_force.mV[VY] *= dst_aspect_ratio;
886 dst_force.normVec();
887
888 src_force.mV[VX] *= llmin(intersect_rect.getWidth() * src_mult, intersect_rect.getHeight() * SPRING_STRENGTH);
889 src_force.mV[VY] *= llmin(intersect_rect.getHeight() * src_mult, intersect_rect.getWidth() * SPRING_STRENGTH);
890 dst_force.mV[VX] *= llmin(intersect_rect.getWidth() * dst_mult, intersect_rect.getHeight() * SPRING_STRENGTH);
891 dst_force.mV[VY] *= llmin(intersect_rect.getHeight() * dst_mult, intersect_rect.getWidth() * SPRING_STRENGTH);
892
893 src_textp->mTargetPositionOffset += src_force;
894 dst_textp->mTargetPositionOffset += dst_force;
895 src_textp->mTargetPositionOffset = src_textp->updateScreenPos(src_textp->mTargetPositionOffset);
896 dst_textp->mTargetPositionOffset = dst_textp->updateScreenPos(dst_textp->mTargetPositionOffset);
897 }
898 }
899 }
900 }
901
902 VisibleTextObjectIterator this_object_it;
903 for (this_object_it = sVisibleTextObjects.begin(); this_object_it != sVisibleTextObjects.end(); ++this_object_it)
904 {
905 if (!(*this_object_it)->mUseBubble)
906 {
907 continue;
908 }
909 (*this_object_it)->mPositionOffset = lerp((*this_object_it)->mPositionOffset, (*this_object_it)->mTargetPositionOffset, LLCriticalDamp::getInterpolant(POSITION_DAMPING_TC));
910 }
911}
912
913void LLHUDText::setLOD(S32 lod)
914{
915 mLOD = lod;
916 //RN: uncomment this to visualize LOD levels
917 //char label[255];
918 //sprintf(label, "%d", lod);
919 //setLabel(label);
920}
921
922S32 LLHUDText::getMaxLines()
923{
924 switch(mLOD)
925 {
926 case 0:
927 return mMaxLines;
928 case 1:
929 return mMaxLines > 0 ? mMaxLines / 2 : 5;
930 case 2:
931 return mMaxLines > 0 ? mMaxLines / 3 : 2;
932 default:
933 // label only
934 return 0;
935 }
936}
937
938void LLHUDText::markDead()
939{
940 sTextObjects.erase(LLPointer<LLHUDText>(this));
941 LLHUDObject::markDead();
942}
943
944void LLHUDText::renderAllHUD()
945{
946 LLGLEnable color_mat(GL_COLOR_MATERIAL);
947 LLGLDepthTest depth(GL_FALSE, GL_FALSE);
948
949 VisibleTextObjectIterator text_it;
950
951 for (text_it = sVisibleHUDTextObjects.begin(); text_it != sVisibleHUDTextObjects.end(); ++text_it)
952 {
953 (*text_it)->renderText(FALSE);
954 }
955}
956
957//static
958void LLHUDText::addPickable(std::set<LLViewerObject*> &pick_list)
959{
960 //this might put an object on the pick list a second time, overriding it's mGLName, which is ok
961 // *FIX: we should probably cull against pick frustum
962 VisibleTextObjectIterator text_it;
963 for (text_it = sVisibleTextObjects.begin(); text_it != sVisibleTextObjects.end(); ++text_it)
964 {
965 if (!(*text_it)->mUseBubble)
966 {
967 continue;
968 }
969 pick_list.insert((*text_it)->mSourceObject);
970 }
971}
972
973//static
974// called when UI scale changes, to flush font width caches
975void LLHUDText::reshape()
976{
977 TextObjectIterator text_it;
978 for (text_it = sTextObjects.begin(); text_it != sTextObjects.end(); ++text_it)
979 {
980 LLHUDText* textp = (*text_it);
981 std::vector<LLHUDTextSegment>::iterator segment_iter;
982 for (segment_iter = textp->mTextSegments.begin();
983 segment_iter != textp->mTextSegments.end(); ++segment_iter )
984 {
985 segment_iter->clearFontWidthMap();
986 }
987 for(segment_iter = textp->mLabelSegments.begin();
988 segment_iter != textp->mLabelSegments.end(); ++segment_iter )
989 {
990 segment_iter->clearFontWidthMap();
991 }
992 }
993}
994
995//============================================================================
996
997F32 LLHUDText::LLHUDTextSegment::getWidth(const LLFontGL* font)
998{
999 std::map<const LLFontGL*, F32>::iterator iter = mFontWidthMap.find(font);
1000 if (iter != mFontWidthMap.end())
1001 {
1002 return iter->second;
1003 }
1004 else
1005 {
1006 F32 width = font->getWidthF32(mText.c_str());
1007 mFontWidthMap[font] = width;
1008 return width;
1009 }
1010}